diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..24ab300 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.c text +*.cc text +*.h text +*.txt text diff --git a/.gitignore b/.gitignore index ac51a05..6fa07f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ bazel-* +.DS_Store diff --git a/whitebox-dev/impl/BUILD b/whitebox-dev/impl/BUILD deleted file mode 100644 index 9b3ca1b..0000000 --- a/whitebox-dev/impl/BUILD +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright 2020 Google LLC. All Rights Reserved. - -package(default_visibility = [ - "//visibility:private", -]) - -cc_library( - name = "whitebox_common", - srcs = [ - "common_whitebox.cc", - "get_random_bytes.cc", - ], - visibility = ["//visibility:private"], -) - -cc_library( - name = "aead_whitebox", - srcs = [ - "aead_whitebox.cc", - ], - visibility = ["//visibility:public"], - deps = [ - ":whitebox_common", - "@whitebox_api_repo//api:aead_whitebox", - "@whitebox_api_repo//api:result", - ], -) - -cc_library( - name = "license_whitebox", - srcs = [ - "license_whitebox.cc", - ], - deps = [ - ":whitebox_common", - "@whitebox_api_repo//api:license_whitebox", - "@whitebox_api_repo//api:result", - ], -) - -cc_library( - name = "test_data", - testonly = True, - srcs = [ - "test_data.cc", - ], - hdrs = [ - "test_data_aead_init.h", - "test_data_license_init.h", - ], - deps = [ - "@whitebox_api_repo//api:test_data", - "@whitebox_api_repo//crypto_utils:rsa_test_keys", - ], -) - -cc_test( - name = "aead_whitebox_test", - size = "small", - timeout = "moderate", - deps = [ - ":aead_whitebox", - ":test_data", - "@whitebox_api_repo//api:aead_whitebox_test", - ], -) - -cc_test( - name = "license_whitebox_test", - size = "small", - timeout = "moderate", - deps = [ - ":license_whitebox", - ":test_data", - "@whitebox_api_repo//api:license_whitebox_test", - ], -) - -cc_test( - name = "remote_attestation_and_verification_test", - size = "small", - timeout = "moderate", - deps = [ - ":license_whitebox", - ":test_data", - "@whitebox_api_repo//api:remote_attestation_and_verification_test", - ], -) - -cc_test( - name = "aead_whitebox_benchmark", - size = "small", - deps = [ - ":aead_whitebox", - ":test_data", - "@whitebox_api_repo//api:aead_whitebox_benchmark", - ], -) - -cc_test( - name = "license_whitebox_benchmark", - size = "large", - deps = [ - ":license_whitebox", - ":test_data", - "@whitebox_api_repo//api:license_whitebox_benchmark", - ], -) - -cc_test( - name = "license_whitebox_golden_data", - size = "small", - srcs = [ - "license_whitebox_golden_data_init_data.cc", - ], - deps = [ - ":license_whitebox", - "@whitebox_api_repo//api:license_whitebox_golden_data", - ], -) diff --git a/whitebox-dev/tools/PLACEHOLDER b/whitebox-dev/tools/PLACEHOLDER deleted file mode 100644 index e69de29..0000000 diff --git a/whitebox-dev/WORKSPACE b/whitebox-impl/WORKSPACE similarity index 89% rename from whitebox-dev/WORKSPACE rename to whitebox-impl/WORKSPACE index da48843..cbfd13f 100644 --- a/whitebox-dev/WORKSPACE +++ b/whitebox-impl/WORKSPACE @@ -1,13 +1,9 @@ -# Copyright 2020 Google LLC. All Rights Reserved. - -workspace(name = "whitebox_dev") - load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") local_repository( - name = "whitebox_api_repo", + name = "whitebox_api", path = "../whitebox", ) @@ -69,10 +65,10 @@ http_archive( # ODK new_git_repository( name = "odk_repo", - build_file = "@whitebox_api_repo//external:odk.BUILD", - commit = "565237f8e6900e467eb236040374428387e90bd0", + build_file = "@whitebox_api//external:odk.BUILD", + commit = "c1401c6a1cc6a4378b6aa3d1c3d3f1f58278616e", remote = "https://widevine-partner.googlesource.com/oemcrypto_core_message.git", - repo_mapping = {"@whitebox" : "@whitebox_api_repo"} + repo_mapping = {"@whitebox" : "@whitebox_api"} ) bind( @@ -108,4 +104,4 @@ bind( bind( name = "odk", actual = "@odk_repo//:odk", -) +) \ No newline at end of file diff --git a/whitebox-impl/impl/BUILD b/whitebox-impl/impl/BUILD new file mode 100644 index 0000000..6cfc748 --- /dev/null +++ b/whitebox-impl/impl/BUILD @@ -0,0 +1,26 @@ +# ============================================================================== +# Structure +# ============================================================================== +# +# This BUILD file must expose the following build targets so that the test/BUILD +# file can link against it: +# +# test_aead_whitebox : The target for testing the AEAD white-box. +# +# test_license_whitebox : The target for testing the license white-box. The +# white-box should use the private key provided in +# "//api/test_license_whitebox_keys.cc". + +package(default_visibility = [ + "//visibility:private", +]) + +cc_library( + name = "test_aead_whitebox", + visibility = ["//visibility:public"], +) + +cc_library( + name = "test_license_whitebox", + visibility = ["//visibility:public"], +) \ No newline at end of file diff --git a/whitebox-impl/tests/BUILD b/whitebox-impl/tests/BUILD new file mode 100644 index 0000000..952529c --- /dev/null +++ b/whitebox-impl/tests/BUILD @@ -0,0 +1,80 @@ +# Copyright 2021 Google LLC. All Rights Reserved. + +package(default_visibility = [ + "//visibility:private", +]) + +# ============================================================================== +# Requirements +# ============================================================================== +# +# This BUILD file expects the implementation BUILD file to expose the following +# build targets so that it can link against them: +# +# test_aead_whitebox : The target for testing the AEAD white-box. +# +# test_license_whitebox : The target for testing the license white-box. The +# white-box should use the private key provided in +# "//api/test_license_whitebox_keys.cc". + +# ============================================================================== +# AEAD Test Targets +# ============================================================================== + +cc_test( + name = "aead_whitebox_test", + size = "small", + deps = [ + "@whitebox_api//api:aead_whitebox_test", + "//impl:test_aead_whitebox", + ], +) + +cc_test( + name = "aead_whitebox_benchmark", + size = "small", + deps = [ + "@whitebox_api//api:aead_whitebox_benchmark", + "//impl:test_aead_whitebox", + ], +) + +# ============================================================================== +# License Whitebox Test Targets +# ============================================================================== + +cc_test( + name = "license_whitebox_test", + size = "small", + deps = [ + "@whitebox_api//api:license_whitebox_test", + "//impl:test_license_whitebox", + ], +) + +cc_test( + name = "remote_attestation_and_verification_test", + size = "small", + deps = [ + "@whitebox_api//api:remote_attestation_and_verification_test", + "//impl:test_license_whitebox", + ], +) + +cc_test( + name = "license_whitebox_benchmark", + size = "small", + deps = [ + "@whitebox_api//api:license_whitebox_benchmark", + "//impl:test_license_whitebox", + ], +) + +cc_test( + name = "license_whitebox_uat_test", + size = "small", + deps = [ + "@whitebox_api//api:license_whitebox_uat_test", + "//impl:test_license_whitebox", + ], +) diff --git a/whitebox-impl/tools/PLACEHOLDER b/whitebox-impl/tools/PLACEHOLDER new file mode 100644 index 0000000..cec68f5 --- /dev/null +++ b/whitebox-impl/tools/PLACEHOLDER @@ -0,0 +1,2 @@ +This is a placeholder file to ensure that the "tools" directory appears in the +git repo. \ No newline at end of file diff --git a/whitebox-prod/WORKSPACE b/whitebox-prod/WORKSPACE deleted file mode 100644 index a8eb63d..0000000 --- a/whitebox-prod/WORKSPACE +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright 2020 Google LLC. All Rights Reserved. - -workspace(name = "whitebox_prod") - -load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") -load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository") -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -local_repository( - name = "whitebox_api_repo", - path = "../whitebox", -) - -git_repository( - name = "glog_repo", - commit = "3ba8976592274bc1f907c402ce22558011d6fc5e", # 2020-02-16 - remote = "https://github.com/google/glog.git", -) - -git_repository( - name = "com_github_gflags_gflags", - commit = "2e227c3daae2ea8899f49858a23f3d318ea39b57", # 2020-01-15 - remote = "https://github.com/gflags/gflags.git", -) - -git_repository( - name = "abseil_repo", - commit = "fcb104594b0bb4b8ac306cb2f55ecdad40974683", # 2018-12-04 - remote = "https://github.com/abseil/abseil-cpp.git", -) - -git_repository( - name = "boringssl_repo", - commit = "14164f6fef47b7ebd97cdb0cea1624eabd6fe6b8", # 2018-11-26 - remote = "https://github.com/google/boringssl.git", -) - -git_repository( - name = "googletest_repo", - commit = "b6cd405286ed8635ece71c72f118e659f4ade3fb", # 2019-01-04 - remote = "https://github.com/google/googletest.git", -) - -# We use "com_google_protobuf" instead of "protobuf_repo" because Bazel's proto -# rules implicitly depend on @com_google_protobuf. See -# https://bazel.build/blog/2017/02/27/protocol-buffers.html. -git_repository( - name = "com_google_protobuf", - remote = "https://github.com/google/protobuf.git", - tag = "v3.8.0", -) - -# bazel_skylib is required by google protobuf. -git_repository( - name = "bazel_skylib", - remote = "https://github.com/bazelbuild/bazel-skylib.git", - tag = "1.0.2", -) - -# Protobuf library support. Not included in the recent protobuf release. -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"], -) - -# ODK -new_git_repository( - name = "odk_repo", - build_file = "@whitebox_api_repo//external:odk.BUILD", - commit = "565237f8e6900e467eb236040374428387e90bd0", - remote = "https://widevine-partner.googlesource.com/oemcrypto_core_message.git", - repo_mapping = {"@whitebox" : "@whitebox_api_repo"} -) - -bind( - name = "glog", - actual = "@glog_repo//:glog", -) - -bind( - name = "gflags", - actual = "@com_github_gflags_gflags//:gflags", -) - -bind( - name = "boringssl", - actual = "@boringssl_repo//:crypto", -) - -bind( - name = "gtest", - actual = "@googletest_repo//:gtest", -) - -bind( - name = "gtest_main", - actual = "@googletest_repo//:gtest_main", -) - -bind( - name = "protobuf", - actual = "@com_google_protobuf//:protobuf", -) - -bind( - name = "odk", - actual = "@odk_repo//:odk", -) diff --git a/whitebox-prod/impl/BUILD b/whitebox-prod/impl/BUILD deleted file mode 100644 index 9b3ca1b..0000000 --- a/whitebox-prod/impl/BUILD +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright 2020 Google LLC. All Rights Reserved. - -package(default_visibility = [ - "//visibility:private", -]) - -cc_library( - name = "whitebox_common", - srcs = [ - "common_whitebox.cc", - "get_random_bytes.cc", - ], - visibility = ["//visibility:private"], -) - -cc_library( - name = "aead_whitebox", - srcs = [ - "aead_whitebox.cc", - ], - visibility = ["//visibility:public"], - deps = [ - ":whitebox_common", - "@whitebox_api_repo//api:aead_whitebox", - "@whitebox_api_repo//api:result", - ], -) - -cc_library( - name = "license_whitebox", - srcs = [ - "license_whitebox.cc", - ], - deps = [ - ":whitebox_common", - "@whitebox_api_repo//api:license_whitebox", - "@whitebox_api_repo//api:result", - ], -) - -cc_library( - name = "test_data", - testonly = True, - srcs = [ - "test_data.cc", - ], - hdrs = [ - "test_data_aead_init.h", - "test_data_license_init.h", - ], - deps = [ - "@whitebox_api_repo//api:test_data", - "@whitebox_api_repo//crypto_utils:rsa_test_keys", - ], -) - -cc_test( - name = "aead_whitebox_test", - size = "small", - timeout = "moderate", - deps = [ - ":aead_whitebox", - ":test_data", - "@whitebox_api_repo//api:aead_whitebox_test", - ], -) - -cc_test( - name = "license_whitebox_test", - size = "small", - timeout = "moderate", - deps = [ - ":license_whitebox", - ":test_data", - "@whitebox_api_repo//api:license_whitebox_test", - ], -) - -cc_test( - name = "remote_attestation_and_verification_test", - size = "small", - timeout = "moderate", - deps = [ - ":license_whitebox", - ":test_data", - "@whitebox_api_repo//api:remote_attestation_and_verification_test", - ], -) - -cc_test( - name = "aead_whitebox_benchmark", - size = "small", - deps = [ - ":aead_whitebox", - ":test_data", - "@whitebox_api_repo//api:aead_whitebox_benchmark", - ], -) - -cc_test( - name = "license_whitebox_benchmark", - size = "large", - deps = [ - ":license_whitebox", - ":test_data", - "@whitebox_api_repo//api:license_whitebox_benchmark", - ], -) - -cc_test( - name = "license_whitebox_golden_data", - size = "small", - srcs = [ - "license_whitebox_golden_data_init_data.cc", - ], - deps = [ - ":license_whitebox", - "@whitebox_api_repo//api:license_whitebox_golden_data", - ], -) diff --git a/whitebox-prod/tools/PLACEHOLDER b/whitebox-prod/tools/PLACEHOLDER deleted file mode 100644 index e69de29..0000000 diff --git a/whitebox/api/BUILD b/whitebox/api/BUILD index 1ffab98..346d3f5 100644 --- a/whitebox/api/BUILD +++ b/whitebox/api/BUILD @@ -34,24 +34,24 @@ cc_library( cc_library( name = "test_key_types", - testonly = True, hdrs = [ "test_key_types.h", ], ) cc_library( - name = "test_public_key", - testonly = True, + name = "test_license_whitebox_keys", + srcs = [ + "test_license_whitebox_keys.cc", + ], hdrs = [ - "test_public_key.h", + "test_license_whitebox_keys.h", ], visibility = ["//visibility:public"], ) cc_library( name = "aead_test_data", - testonly = True, hdrs = [ "aead_test_data.h", ], @@ -60,7 +60,6 @@ cc_library( cc_library( name = "golden_data", - testonly = True, srcs = [ "golden_data.cc", ], @@ -75,7 +74,6 @@ cc_library( cc_library( name = "test_license_builder", - testonly = True, srcs = ["test_license_builder.cc"], hdrs = ["test_license_builder.h"], visibility = ["//visibility:public"], @@ -92,7 +90,6 @@ cc_library( cc_library( name = "aead_whitebox_test", - testonly = True, srcs = [ "aead_whitebox_create_test.cc", "aead_whitebox_cross_instance_test.cc", @@ -110,7 +107,6 @@ cc_library( cc_library( name = "aead_whitebox_benchmark", - testonly = True, srcs = [ "aead_whitebox_benchmark.cc", ], @@ -128,7 +124,6 @@ cc_library( cc_library( name = "license_whitebox_test_base", - testonly = True, srcs = [ "license_whitebox_test_base.cc", ], @@ -138,7 +133,7 @@ cc_library( deps = [ ":golden_data", ":license_whitebox", - ":test_public_key", + ":test_license_whitebox_keys", "//chromium_deps/testing", "//crypto_utils:rsa_key", ], @@ -146,7 +141,6 @@ cc_library( cc_library( name = "license_whitebox_test", - testonly = True, srcs = [ "license_whitebox_create_test.cc", "license_whitebox_decrypt_test.cc", @@ -155,6 +149,8 @@ cc_library( "license_whitebox_masked_decrypt_test.cc", "license_whitebox_process_license_response_core_message_test.cc", "license_whitebox_process_license_response_test.cc", + "license_whitebox_query_content_key_status_test.cc", + "license_whitebox_query_signing_key_status_test.cc", "license_whitebox_security_level_test.cc", "license_whitebox_sign_license_request_test.cc", "license_whitebox_sign_renewal_request_test.cc", @@ -179,7 +175,6 @@ cc_library( # white-boxes to ensure that they are not reducing key security levels. cc_library( name = "remote_attestation_and_verification_test", - testonly = True, srcs = [ "remote_attestation_and_verification_test.cc", ], @@ -195,7 +190,6 @@ cc_library( cc_library( name = "license_whitebox_benchmark", - testonly = True, srcs = [ "license_whitebox_benchmark.cc", "license_whitebox_decrypt_benchmark.cc", @@ -211,7 +205,7 @@ cc_library( ":license_whitebox", ":test_key_types", ":test_license_builder", - ":test_public_key", + ":test_license_whitebox_keys", "//benchmarking:data_source", "//benchmarking:measurements", "//chromium_deps/base:glog", @@ -222,10 +216,9 @@ cc_library( ) cc_library( - name = "license_whitebox_golden_data", - testonly = True, + name = "license_whitebox_uat_test", srcs = [ - "license_whitebox_golden_data_test.cc", + "license_whitebox_uat_test.cc", ], visibility = ["//visibility:public"], deps = [ diff --git a/whitebox/api/license_whitebox.h b/whitebox/api/license_whitebox.h index 518fede..4db179c 100644 --- a/whitebox/api/license_whitebox.h +++ b/whitebox/api/license_whitebox.h @@ -20,6 +20,34 @@ typedef enum { WB_CIPHER_MODE_CBC, } WB_CipherMode; +typedef enum { + WB_KEY_QUERY_TYPE_SIGNING_KEY, + WB_KEY_QUERY_TYPE_CONTENT_KEY, +} WB_KeyQueryType; + +typedef enum { + // The key was found in the license but there was something wrong it and could + // not be loaded. + WB_KEY_STATUS_INVALID, + + // The key was found in the license and can be used with + // |WB_License_SignRenewalRequest()| and with + // |WB_License_VerifyRenewalResponse()|. + WB_KEY_STATUS_SIGNING_KEY_VALID, + + // The key was found in the license. However, the permisions of the key are + // different depending on its status. + // + // | DECRYPT | MASKED DECRYPT | + // ---------------+---------+----------------+ + // VALID | No | No | + // MASKED_DECRYPT | No | Yes | + // DECRYPT | Yes | Yes | + WB_KEY_STATUS_CONTENT_KEY_VALID, + WB_KEY_STATUS_CONTENT_KEY_MASKED_DECRYPT, + WB_KEY_STATUS_CONTENT_KEY_DECRYPT, +} WB_KeyStatus; + // Creates a new white-box instance using the implementation's internal private // key|. A pointer to the white-box instance will be returned via |whitebox|. // @@ -132,6 +160,11 @@ WB_Result WB_License_SignLicenseRequest(const WB_License_Whitebox* whitebox, // verification to fail (therefore we return WB_RESULT_INVALID_SIGNATURE). // Doing this was found to allow for better hardening of the license processing // code. +// +// When using proto-buf parsing, if a key is missing the key control block, +// the behaviour is undefined. The two preferrable results are: +// 1. The key is considered value and is loaded. +// 2. The key is considered invalid and is not loaded. WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox, const uint8_t* core_message, size_t core_message_size, @@ -144,6 +177,47 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox, const uint8_t* license_request, size_t license_request_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. +// +// Args: +// whitebox (in) : An initialized white-box instance. +// +// key_type (in) : The type of key being queried. +// +// key_id (in) : The content key id. Only required when |key_type| is +// WB_KEY_QUERY_TYPE_CONTENT_KEY, otherwise it will be ignored. Validation +// rules are not enforced when this value is ignored. +// +// key_id_size (in) : The number of bytes in the key id. Only required when +// |key_type| is WB_KEY_QUERY_TYPE_CONTENT_KEY, otherwise it will be ignored. +// Validation constraints are not enforced when this value is ignored. +// +// key_state (out) : A pointer to a key state object that should be filled in +// with the key state information. +// +// Returns: +// WB_RESULT_OK if the key was present in the license and |key_state| was +// populated with information about the key. +// +// WB_INVALID_PARAMETER if |whitebox| was null, key_id is null and key_type is +// WB_KEY_QUERY_TYPE_CONTENT_KEY, when key_id is zero and key_type is +// WB_KEY_QUERY_TYPE_CONTENT_KEY, or |key_state| was null. +// +// WB_RESULT_KEY_UNAVAILABLE if the requested key was not in the license. +// +// WB_RESULT_INVALID_STATE if |whitebox| had not loaded a license. +// +// Notes: +// Since the white-box can skip invalid/malformed content keys, +// WB_License_QueryKeyState() provides a means to know which keys we +// successfully loaded. +WB_Result WB_License_QueryKeyStatus(const WB_License_Whitebox* whitebox, + WB_KeyQueryType type, + const uint8_t* key_id, + size_t key_id_size, + WB_KeyStatus* key_status); + // Signs |message| and return the signature via |signature| using HMAC and the // client renewal signing key // diff --git a/whitebox/api/license_whitebox_benchmark.cc b/whitebox/api/license_whitebox_benchmark.cc index f632250..e4ed96b 100644 --- a/whitebox/api/license_whitebox_benchmark.cc +++ b/whitebox/api/license_whitebox_benchmark.cc @@ -9,7 +9,7 @@ #include "api/license_whitebox.h" #include "api/test_license_builder.h" -#include "api/test_public_key.h" +#include "api/test_license_whitebox_keys.h" #include "benchmarking/data_source.h" #include "crypto_utils/crypto_util.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/whitebox/api/license_whitebox_chromeos_test.cc b/whitebox/api/license_whitebox_chromeos_test.cc deleted file mode 100644 index 86f96ca..0000000 --- a/whitebox/api/license_whitebox_chromeos_test.cc +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2020 Google LLC. All Rights Reserved. - -#include "api/license_whitebox.h" - -#include - -#include "api/golden_data.h" -#include "api/license_whitebox_test_base.h" -#include "api/result.h" -#include "api/test_license_builder.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace widevine { -namespace { -using RemoteAttestation = TestLicenseBuilder::RemoteAttestation; -using VerificationStatus = TestLicenseBuilder::VerificationStatus; - -// We can't use the actual keys with TEST_P, so define an enum that we can use -// to communicate which key to use. -enum class Key { - kCrypto, - kDecode, - kHardware, -}; - -} // namespace - -class LicenseWhiteboxChromeOSTest - : public LicenseWhiteboxTestBase, - public testing::WithParamInterface< - std::tuple> { - protected: - void SetUp() override { - LicenseWhiteboxTestBase::SetUp(); - - std::tie(key_, remote_attestation_, verification_status_) = GetParam(); - - LoadLicense(); - - // We know that the plaintext will be smaller than the ciphertext, so we - // use that to ensure the buffer is large enough. - plaintext_size_ = golden_data_.CBCContent().ciphertext.size(); - plaintext_.resize(plaintext_size_); - - // We make the assumption that the secret string can never be longer than - // the ciphertext. - secret_string_size_ = golden_data_.CBCContent().ciphertext.size(); - secret_string_.resize(secret_string_size_); - } - - // This requires that the remote attestation and verification status to be - // set before being called. - void LoadLicense() { - TestLicenseBuilder builder; - - // Only use CBC keys so that we can always use the CBC content. - builder.AddContentKey(golden_data_.CBCCryptoKey().level, - golden_data_.CBCCryptoKey().id, - golden_data_.CBCCryptoKey().content->key); - - builder.AddContentKey(golden_data_.CBCDecodeKey().level, - golden_data_.CBCDecodeKey().id, - golden_data_.CBCDecodeKey().content->key); - - builder.AddContentKey(golden_data_.CBCHardwareKey().level, - golden_data_.CBCHardwareKey().id, - golden_data_.CBCHardwareKey().content->key); - - builder.GetSettings().remote_attestation = remote_attestation_; - builder.GetSettings().verification_status = verification_status_; - - License license; - builder.Build(*public_key_, &license); - - ASSERT_EQ( - WB_License_ProcessLicenseResponse( - whitebox_, 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(), - license.request.data(), license.request.size()), - WB_RESULT_OK); - } - - // This is the strictest level of enforcement. It will be the last one - // checked. Any basic overrides, for example a crypto key in decrypt, - // will short-circuit it. - WB_Result GetExpectedPlatformVerificationResult() { - if (remote_attestation_ == RemoteAttestation::kUnverified) { - return WB_RESULT_INSUFFICIENT_SECURITY_LEVEL; - } - - if (verification_status_ == VerificationStatus::kOther) { - return WB_RESULT_INSUFFICIENT_SECURITY_LEVEL; - } - - if (remote_attestation_ == RemoteAttestation::kVerified) { - return WB_RESULT_OK; - } - - if (verification_status_ == VerificationStatus::kHardwareVerified) { - return WB_RESULT_OK; - } - - return WB_RESULT_INSUFFICIENT_SECURITY_LEVEL; - } - - WB_Result GetExpectedDecryptResult() { - if (key_ == Key::kCrypto) { - return WB_RESULT_OK; - } - - return GetExpectedPlatformVerificationResult(); - } - - // Since MaskedDecrypt() and GetSecretString() go hand-in-hand. This function - // will be used for both. - WB_Result GetExpectedMaskedDecryptResult() { - if (key_ == Key::kCrypto || key_ == Key::kDecode) { - return WB_RESULT_OK; - } - - return GetExpectedPlatformVerificationResult(); - } - - const GoldenData::Key* GetContentKey() const { - switch (key_) { - case Key::kCrypto: - return &golden_data_.CBCCryptoKey(); - case Key::kDecode: - return &golden_data_.CBCDecodeKey(); - case Key::kHardware: - return &golden_data_.CBCHardwareKey(); - } - return nullptr; - } - - Key key_; - RemoteAttestation remote_attestation_; - VerificationStatus verification_status_; - - // This is the buffer used to store the output of each decrypt and unmask - // call. - size_t plaintext_size_; - std::vector plaintext_; - - // This is the buffer used to store the secret string used to demask the - // result of WB_License_MaskedDecrypt(). - size_t secret_string_size_; - std::vector secret_string_; -}; - -TEST_P(LicenseWhiteboxChromeOSTest, Decrypt) { - const WB_Result expected_result = GetExpectedDecryptResult(); - - const GoldenData::Key* key = GetContentKey(); - ASSERT_NE(key, nullptr); - - const auto* content = key->content; - ASSERT_EQ(WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, key->id.data(), - key->id.size(), content->ciphertext.data(), - content->plaintext.size(), content->iv.data(), - content->iv.size(), plaintext_.data(), - &plaintext_size_), - expected_result); -} - -TEST_P(LicenseWhiteboxChromeOSTest, MaskedDecrypt) { - const WB_Result expected_result = GetExpectedMaskedDecryptResult(); - - const GoldenData::Key* key = GetContentKey(); - ASSERT_NE(key, nullptr); - - const auto* content = key->content; - ASSERT_EQ(WB_License_MaskedDecrypt( - whitebox_, WB_CIPHER_MODE_CBC, key->id.data(), key->id.size(), - content->ciphertext.data(), content->plaintext.size(), - content->iv.data(), content->iv.size(), plaintext_.data(), - &plaintext_size_), - expected_result); -} - -TEST_P(LicenseWhiteboxChromeOSTest, GetSecretString) { - const WB_Result expected_result = GetExpectedMaskedDecryptResult(); - - const GoldenData::Key* key = GetContentKey(); - ASSERT_NE(key, nullptr); - - ASSERT_EQ(WB_License_GetSecretString( - whitebox_, WB_CIPHER_MODE_CBC, key->id.data(), key->id.size(), - secret_string_.data(), &secret_string_size_), - expected_result); -} - -INSTANTIATE_TEST_SUITE_P( - AllCombinations, - LicenseWhiteboxChromeOSTest, - ::testing::Combine( - ::testing::Values(Key::kCrypto, Key::kDecode, Key::kHardware), - ::testing::Values(RemoteAttestation::kUnavailable, - RemoteAttestation::kUnverified, - RemoteAttestation::kVerified), - ::testing::Values(VerificationStatus::kUnavailable, - VerificationStatus::kOther, - VerificationStatus::kHardwareVerified))); - -} // namespace widevine diff --git a/whitebox/api/license_whitebox_decrypt_test.cc b/whitebox/api/license_whitebox_decrypt_test.cc index c47b24d..1862ebe 100644 --- a/whitebox/api/license_whitebox_decrypt_test.cc +++ b/whitebox/api/license_whitebox_decrypt_test.cc @@ -13,8 +13,14 @@ #include "testing/gtest/include/gtest/gtest.h" namespace widevine { +namespace { -class LicenseWhiteboxDecryptTest : public LicenseWhiteboxTestBase { +using Padding = TestLicenseBuilder::Padding; + +} // namespace + +class LicenseWhiteboxDecryptTest : public LicenseWhiteboxTestBase, + public testing::WithParamInterface { protected: void SetUp() override { LicenseWhiteboxTestBase::SetUp(); @@ -27,11 +33,14 @@ class LicenseWhiteboxDecryptTest : public LicenseWhiteboxTestBase { non_content_key_id_ = golden_data_.GetFreeId(); missing_key_id_ = golden_data_.GetFreeId(); + + padding_ = GetParam(); } - void LoadLicense(TestLicenseBuilder::Padding padding) { + void LoadLicense(const TestLicenseBuilder::Settings& settings) { TestLicenseBuilder builder; - builder.GetSettings().padding = padding; + builder.SetSettings(settings); + builder.GetSettings().padding = padding_; builder.AddContentKey(golden_data_.CBCCryptoKey().level, golden_data_.CBCCryptoKey().id, @@ -72,10 +81,13 @@ class LicenseWhiteboxDecryptTest : public LicenseWhiteboxTestBase { // This is the buffer used to store the output of each decrypt call. size_t plaintext_size_; std::vector plaintext_; + + Padding padding_; }; -TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCbcMode) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCbcMode) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -91,8 +103,9 @@ TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCbcMode) { ASSERT_EQ(plaintext_, golden_data_.CBCCryptoKey().content->plaintext); } -TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCtrMode) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCtrMode) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CTR, @@ -110,8 +123,9 @@ TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCtrMode) { // We try to decrypt CBC encrypted data in CTR mode. All operations should be // successful, but the resulting plaintext should not match. -TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCtrMode) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCtrMode) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CTR, @@ -129,8 +143,9 @@ TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataInCtrMode) { // We try to decrypt CTR encrypted data in CBC mode. All operations should be // successful, but the resulting plaintext should not match. -TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCbcMode) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCbcMode) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -146,44 +161,11 @@ TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataInCbcMode) { ASSERT_NE(plaintext_, golden_data_.CTRCryptoKey().content->plaintext); } -TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) { - LoadLicense(TestLicenseBuilder::Padding::kPKSC8); - - ASSERT_EQ( - WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, - golden_data_.CBCCryptoKey().id.data(), - golden_data_.CBCCryptoKey().id.size(), - golden_data_.CBCCryptoKey().content->ciphertext.data(), - golden_data_.CBCCryptoKey().content->ciphertext.size(), - golden_data_.CBCCryptoKey().content->iv.data(), - golden_data_.CBCCryptoKey().content->iv.size(), - plaintext_.data(), &plaintext_size_), - WB_RESULT_OK); - plaintext_.resize(plaintext_size_); - ASSERT_EQ(plaintext_, golden_data_.CBCCryptoKey().content->plaintext); -} - -TEST_F(LicenseWhiteboxDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) { - LoadLicense(TestLicenseBuilder::Padding::kPKSC8); - - ASSERT_EQ( - WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CTR, - golden_data_.CTRCryptoKey().id.data(), - golden_data_.CTRCryptoKey().id.size(), - golden_data_.CTRCryptoKey().content->ciphertext.data(), - golden_data_.CTRCryptoKey().content->ciphertext.size(), - golden_data_.CTRCryptoKey().content->iv.data(), - golden_data_.CTRCryptoKey().content->iv.size(), - plaintext_.data(), &plaintext_size_), - WB_RESULT_OK); - plaintext_.resize(plaintext_size_); - ASSERT_EQ(plaintext_, golden_data_.CTRCryptoKey().content->plaintext); -} - // Try decrypting two different sets of content to make sure that two // different keys can be used at the same time. -TEST_F(LicenseWhiteboxDecryptTest, SuccessWithMultipleKeys) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, SuccessWithMultipleKeys) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -218,8 +200,9 @@ TEST_F(LicenseWhiteboxDecryptTest, SuccessWithMultipleKeys) { ASSERT_EQ(plaintext_, golden_data_.CTRCryptoKey().content->plaintext); } -TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullWhitebox) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, InvalidParameterForNullWhitebox) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(nullptr, WB_CIPHER_MODE_CBC, @@ -233,8 +216,9 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullWhitebox) { WB_RESULT_INVALID_PARAMETER); } -TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCipherMode) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCipherMode) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); // In order to trick the compiler into letting us pass an invalid enum value // to WB__License_Decrypt(), we need to cast it. If we don't do this, the @@ -252,8 +236,9 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCipherMode) { WB_RESULT_INVALID_PARAMETER); } -TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullKeyId) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, InvalidParameterForNullKeyId) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, nullptr, @@ -266,8 +251,9 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullKeyId) { WB_RESULT_INVALID_PARAMETER); } -TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroKeyIdSize) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, InvalidParameterForZeroKeyIdSize) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -280,8 +266,9 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroKeyIdSize) { WB_RESULT_INVALID_PARAMETER); } -TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullInputData) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, InvalidParameterForNullInputData) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -296,8 +283,9 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullInputData) { // AES CBC requires that the input be block aligned (multiple of 16). CTR does // not care. -TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCBCInputDataSize) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCBCInputDataSize) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -311,8 +299,9 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidCBCInputDataSize) { } // The white-box (using any cipher mode) should reject input with size zero. -TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroInputDataSize) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, InvalidParameterForZeroInputDataSize) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -325,8 +314,9 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForZeroInputDataSize) { WB_RESULT_INVALID_PARAMETER); } -TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullIV) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, InvalidParameterForNullIV) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt( @@ -340,8 +330,9 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullIV) { } // IV size should be 16. Any number other than 16 should fail. -TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidIVSize) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidIVSize) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -354,8 +345,9 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForInvalidIVSize) { WB_RESULT_INVALID_PARAMETER); } -TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutput) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutput) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -369,8 +361,9 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutput) { WB_RESULT_INVALID_PARAMETER); } -TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutputSize) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutputSize) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -387,8 +380,9 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidParameterForNullOutputSize) { // For this test, "missing key id" specifically means a key id that was never // in the license to start with. This is different than "non content key" // and "dropped content key", as those keys were in the license but ignored. -TEST_F(LicenseWhiteboxDecryptTest, KeyUnavailableForMissingKeyId) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, KeyUnavailableForMissingKeyId) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, missing_key_id_.data(), @@ -401,8 +395,9 @@ TEST_F(LicenseWhiteboxDecryptTest, KeyUnavailableForMissingKeyId) { WB_RESULT_KEY_UNAVAILABLE); } -TEST_F(LicenseWhiteboxDecryptTest, KeyUnavailableForNonContentKey) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, KeyUnavailableForNonContentKey) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, @@ -417,9 +412,10 @@ TEST_F(LicenseWhiteboxDecryptTest, KeyUnavailableForNonContentKey) { // Under normal circumstances, a hardware key should be dropped. The exception // to this rule is on ChromeOS with a special license. -TEST_F(LicenseWhiteboxDecryptTest, +TEST_P(LicenseWhiteboxDecryptTest, InsufficientSecurityLevelForHardwareContentKey) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ(WB_License_Decrypt( whitebox_, WB_CIPHER_MODE_CBC, @@ -433,8 +429,9 @@ TEST_F(LicenseWhiteboxDecryptTest, WB_RESULT_INSUFFICIENT_SECURITY_LEVEL); } -TEST_F(LicenseWhiteboxDecryptTest, InsufficientSecurityLevelForDecodeKey) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, InsufficientSecurityLevelForDecodeKey) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); // Use the software decode key as they are limited to // WB_License_Decrypt(). @@ -450,8 +447,9 @@ TEST_F(LicenseWhiteboxDecryptTest, InsufficientSecurityLevelForDecodeKey) { WB_RESULT_INSUFFICIENT_SECURITY_LEVEL); } -TEST_F(LicenseWhiteboxDecryptTest, BufferTooSmall) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxDecryptTest, BufferTooSmall) { + TestLicenseBuilder::Settings settings; + LoadLicense(settings); // Our ciphertext will be large enough that we should not need to worry about // using a constant here. @@ -474,7 +472,7 @@ TEST_F(LicenseWhiteboxDecryptTest, BufferTooSmall) { golden_data_.CBCCryptoKey().content->ciphertext.size()); } -TEST_F(LicenseWhiteboxDecryptTest, InvalidState) { +TEST_P(LicenseWhiteboxDecryptTest, InvalidState) { // Unlike the other tests, we do not call LoadLicense() as the criteria for // WB_RESULT_INVALID_STATE is that no key can be found and keys are provided // via a license. @@ -491,4 +489,31 @@ TEST_F(LicenseWhiteboxDecryptTest, InvalidState) { WB_RESULT_INVALID_STATE); } +TEST_P(LicenseWhiteboxDecryptTest, KeyUnavailableForInvalidKey) { + // There are multiple ways for us to invalidate a content key. We have tests + // that test invalid keys (see the query content key status tests). But here, + // we just need an invalid key, so we use one way of invalidating the key. + TestLicenseBuilder::Settings settings; + settings.include_content_key_iv = false; + LoadLicense(settings); + + ASSERT_EQ( + WB_License_Decrypt(whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCCryptoKey().id.data(), + golden_data_.CBCCryptoKey().id.size(), + golden_data_.CBCCryptoKey().content->ciphertext.data(), + golden_data_.CBCCryptoKey().content->ciphertext.size(), + golden_data_.CBCCryptoKey().content->iv.data(), + golden_data_.CBCCryptoKey().content->iv.size(), + plaintext_.data(), &plaintext_size_), + WB_RESULT_KEY_UNAVAILABLE); +} + +INSTANTIATE_TEST_SUITE_P(NoPadding, + LicenseWhiteboxDecryptTest, + ::testing::Values(Padding::kNone)); + +INSTANTIATE_TEST_SUITE_P(PKSC8, + LicenseWhiteboxDecryptTest, + ::testing::Values(Padding::kPKSC8)); } // 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 5248985..d2e271d 100644 --- a/whitebox/api/license_whitebox_get_secret_string_test.cc +++ b/whitebox/api/license_whitebox_get_secret_string_test.cc @@ -27,8 +27,9 @@ class LicenseWhiteboxGetSecretStringTest : public LicenseWhiteboxTestBase { missing_key_id_ = golden_data_.GetFreeId(); } - void LoadLicense() { + void LoadLicense(const TestLicenseBuilder::Settings& settings) { TestLicenseBuilder builder; + builder.SetSettings(settings); builder.AddContentKey(golden_data_.CBCCryptoKey().level, golden_data_.CBCCryptoKey().id, @@ -67,7 +68,8 @@ class LicenseWhiteboxGetSecretStringTest : public LicenseWhiteboxTestBase { }; TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCBCWithCryptoKey) { - LoadLicense(); + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC, @@ -79,7 +81,8 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCBCWithCryptoKey) { } TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCTRWithCryptoKey) { - LoadLicense(); + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CTR, @@ -91,7 +94,8 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCTRWithCryptoKey) { } TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCBCWithDecodeKey) { - LoadLicense(); + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC, @@ -103,7 +107,8 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCBCWithDecodeKey) { } TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCTRWithDecodeKey) { - LoadLicense(); + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CTR, @@ -115,7 +120,8 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, SuccessForCTRWithDecodeKey) { } TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForNullWhitebox) { - LoadLicense(); + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_GetSecretString(nullptr, WB_CIPHER_MODE_CBC, @@ -127,7 +133,8 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForNullWhitebox) { TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForInvalidCipherMode) { - LoadLicense(); + TestLicenseBuilder::Settings settings; + LoadLicense(settings); // In order to trick the compiler into letting us pass an invalid enum value // to WB__License_Decrypt(), we need to cast it. If we don't do this, the @@ -142,7 +149,8 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, } TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForNullKeyId) { - LoadLicense(); + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC, nullptr, @@ -152,7 +160,8 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForNullKeyId) { } TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForZeroKeyIdSize) { - LoadLicense(); + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC, @@ -163,7 +172,8 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForZeroKeyIdSize) { TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForNullSecretString) { - LoadLicense(); + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ(WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCCryptoKey().id.data(), @@ -174,7 +184,8 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidParameterForNullSecretStringSize) { - LoadLicense(); + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ(WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCCryptoKey().id.data(), @@ -187,7 +198,8 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, // in the license to start with. This is different than "non content key" // and "dropped content key", as those keys were in the license but ignored. TEST_F(LicenseWhiteboxGetSecretStringTest, KeyUnavailableForMissingKeyId) { - LoadLicense(); + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC, @@ -197,7 +209,8 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, KeyUnavailableForMissingKeyId) { } TEST_F(LicenseWhiteboxGetSecretStringTest, KeyUnavailableForNonContentKey) { - LoadLicense(); + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ(WB_License_GetSecretString( whitebox_, WB_CIPHER_MODE_CBC, non_content_key_id_.data(), @@ -210,7 +223,8 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, KeyUnavailableForNonContentKey) { // to this rule is on ChromeOS with a special license. TEST_F(LicenseWhiteboxGetSecretStringTest, InsufficientSecurityLevelForHardwareContentKey) { - LoadLicense(); + TestLicenseBuilder::Settings settings; + LoadLicense(settings); ASSERT_EQ( WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC, @@ -221,7 +235,8 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, } TEST_F(LicenseWhiteboxGetSecretStringTest, BufferTooSmall) { - LoadLicense(); + TestLicenseBuilder::Settings settings; + LoadLicense(settings); // Since the secret string is implementation specific, we don't want to make // any big assumptions about what would be too small. Using 1 is fairly safe @@ -253,4 +268,20 @@ TEST_F(LicenseWhiteboxGetSecretStringTest, InvalidState) { WB_RESULT_INVALID_STATE); } +TEST_F(LicenseWhiteboxGetSecretStringTest, KeyUnavailableForInvalidKey) { + // There are multiple ways for us to invalidate a content key. We have tests + // that test invalid keys (see the query content key status tests). But here, + // we just need an invalid key, so we use one way of invalidating the key. + TestLicenseBuilder::Settings settings; + settings.include_content_key_iv = false; + LoadLicense(settings); + + ASSERT_EQ( + WB_License_GetSecretString(whitebox_, WB_CIPHER_MODE_CBC, + golden_data_.CBCCryptoKey().id.data(), + golden_data_.CBCCryptoKey().id.size(), + secret_string_.data(), &secret_string_size_), + 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 1eb7bc9..1c4bbf6 100644 --- a/whitebox/api/license_whitebox_masked_decrypt_test.cc +++ b/whitebox/api/license_whitebox_masked_decrypt_test.cc @@ -15,8 +15,15 @@ #include "testing/gtest/include/gtest/gtest.h" namespace widevine { +namespace { -class LicenseWhiteboxMaskedDecryptTest : public LicenseWhiteboxTestBase { +using Padding = TestLicenseBuilder::Padding; + +} // namespace + +class LicenseWhiteboxMaskedDecryptTest + : public LicenseWhiteboxTestBase, + public testing::WithParamInterface { protected: void SetUp() override { LicenseWhiteboxTestBase::SetUp(); @@ -34,11 +41,14 @@ class LicenseWhiteboxMaskedDecryptTest : public LicenseWhiteboxTestBase { non_content_key_id_ = golden_data_.GetFreeId(); missing_key_id_ = golden_data_.GetFreeId(); + + padding_ = GetParam(); } - void LoadLicense(TestLicenseBuilder::Padding padding) { + void LoadLicense(const TestLicenseBuilder::Settings& settings) { TestLicenseBuilder builder; - builder.GetSettings().padding = padding; + builder.SetSettings(settings); + builder.GetSettings().padding = padding_; builder.AddContentKey(golden_data_.CBCCryptoKey().level, golden_data_.CBCCryptoKey().id, @@ -87,10 +97,14 @@ class LicenseWhiteboxMaskedDecryptTest : public LicenseWhiteboxTestBase { std::vector masked_text_; std::vector plaintext_; + + Padding padding_; }; -TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCbcMode) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCbcMode) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -127,8 +141,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCbcMode) { ASSERT_EQ(plaintext_, golden_data_.CBCDecodeKey().content->plaintext); } -TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCtrMode) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCtrMode) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -167,8 +183,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCtrMode) { // We try to decrypt CBC encrypted data in CTR mode. All operations should be // successful, but the resulting plaintext should not match. -TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCtrMode) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCtrMode) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -206,8 +224,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCbcDataInCtrMode) { // We try to decrypt CTR encrypted data in CBC mode. All operations should be // successful, but the resulting plaintext should not match. -TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCbcMode) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCbcMode) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -243,8 +263,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, DecodeKeyWithCtrDataInCbcMode) { ASSERT_NE(masked_text_, golden_data_.CTRDecodeKey().content->plaintext); } -TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCbcMode) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCbcMode) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -281,8 +303,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCbcMode) { ASSERT_EQ(plaintext_, golden_data_.CBCDecodeKey().content->plaintext); } -TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCtrMode) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCtrMode) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -321,8 +345,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCtrMode) { // We try to decrypt CBC encrypted data in CTR mode. All operations should be // successful, but the resulting plaintext should not match. -TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCtrMode) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCtrMode) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -360,8 +386,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataInCtrMode) { // We try to decrypt CTR encrypted data in CBC mode. All operations should be // successful, but the resulting plaintext should not match. -TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCbcMode) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCbcMode) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -397,8 +425,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataInCbcMode) { ASSERT_NE(masked_text_, golden_data_.CTRCryptoKey().content->plaintext); } -TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) { - LoadLicense(TestLicenseBuilder::Padding::kPKSC8); +TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kPKSC8; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -435,8 +465,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCbcDataAndPKCS8Padding) { ASSERT_EQ(plaintext_, golden_data_.CBCCryptoKey().content->plaintext); } -TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) { - LoadLicense(TestLicenseBuilder::Padding::kPKSC8); +TEST_P(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kPKSC8; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -478,8 +510,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, CryptoKeyWithCtrDataAndPKCS8Padding) { // // Since we have two CBC keys, try using the decode key and then the crypto // key. -TEST_F(LicenseWhiteboxMaskedDecryptTest, SuccessWithMultipleKeys) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, SuccessWithMultipleKeys) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -558,8 +592,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, SuccessWithMultipleKeys) { ASSERT_EQ(plaintext_, golden_data_.CTRDecodeKey().content->plaintext); } -TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullWhitebox) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullWhitebox) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -573,8 +609,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullWhitebox) { WB_RESULT_INVALID_PARAMETER); } -TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidCipherMode) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidCipherMode) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); // In order to trick the compiler into letting us pass an invalid enum value // to WB__License_MaskedDecrypt(), we need to cast it. If we don't do this, @@ -592,8 +630,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidCipherMode) { WB_RESULT_INVALID_PARAMETER); } -TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullKeyId) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullKeyId) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ(WB_License_MaskedDecrypt( whitebox_, WB_CIPHER_MODE_CBC, nullptr, @@ -606,8 +646,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullKeyId) { WB_RESULT_INVALID_PARAMETER); } -TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullZeroKeyIdSize) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullZeroKeyIdSize) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -620,8 +662,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullZeroKeyIdSize) { WB_RESULT_INVALID_PARAMETER); } -TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullInputData) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullInputData) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -636,9 +680,11 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullInputData) { // AES CBC requires that the input be block aligned (multiple of 16). CTR does // not care. -TEST_F(LicenseWhiteboxMaskedDecryptTest, +TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidCBCInputDataSize) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -652,8 +698,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, } // The white-box (using any cipher mode) should reject input with size zero. -TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForZeroInputDataSize) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForZeroInputDataSize) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -666,8 +714,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForZeroInputDataSize) { WB_RESULT_INVALID_PARAMETER); } -TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullIV) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullIV) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -681,8 +731,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullIV) { } // IV size should be 16. Any number other than 16 should fail. -TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidIVSize) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidIVSize) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -695,8 +747,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForInvalidIVSize) { WB_RESULT_INVALID_PARAMETER); } -TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutput) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutput) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -710,8 +764,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutput) { WB_RESULT_INVALID_PARAMETER); } -TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutputSize) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutputSize) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -728,8 +784,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidParameterForNullOutputSize) { // For this test, "missing key id" specifically means a key id that was never // in the license to start with. This is different than "non content key" // and "dropped content key", as those keys were in the license but ignored. -TEST_F(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForMissingKeyId) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForMissingKeyId) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ(WB_License_MaskedDecrypt( whitebox_, WB_CIPHER_MODE_CBC, missing_key_id_.data(), @@ -742,8 +800,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForMissingKeyId) { WB_RESULT_KEY_UNAVAILABLE); } -TEST_F(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForNonContentKey) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForNonContentKey) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ(WB_License_MaskedDecrypt( whitebox_, WB_CIPHER_MODE_CBC, non_content_key_id_.data(), @@ -758,9 +818,11 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForNonContentKey) { // Under normal circumstances, a hardware key should be dropped. The exception // to this rule is on ChromeOS with a special license. -TEST_F(LicenseWhiteboxMaskedDecryptTest, +TEST_P(LicenseWhiteboxMaskedDecryptTest, InsufficientSecurityLevelForHardwareContentKey) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ(WB_License_MaskedDecrypt( whitebox_, WB_CIPHER_MODE_CBC, @@ -777,7 +839,7 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, // 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_F(LicenseWhiteboxMaskedDecryptTest, InvalidState) { +TEST_P(LicenseWhiteboxMaskedDecryptTest, InvalidState) { ASSERT_EQ( WB_License_MaskedDecrypt( whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(), @@ -790,8 +852,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, InvalidState) { WB_RESULT_INVALID_STATE); } -TEST_F(LicenseWhiteboxMaskedDecryptTest, BufferTooSmall) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, BufferTooSmall) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); // Our ciphertext will be large enough that we should not need to worry about // using a constant here. @@ -816,8 +880,10 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, BufferTooSmall) { // Check that the result of unmasking only a small portion of the data is the // same as when we unmask the whole buffer. -TEST_F(LicenseWhiteboxMaskedDecryptTest, SuccessForSubRangeUnmask) { - LoadLicense(TestLicenseBuilder::Padding::kNone); +TEST_P(LicenseWhiteboxMaskedDecryptTest, SuccessForSubRangeUnmask) { + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_MaskedDecrypt( @@ -852,4 +918,32 @@ TEST_F(LicenseWhiteboxMaskedDecryptTest, SuccessForSubRangeUnmask) { ASSERT_EQ(full_unmask[5], partial_unmask[1]); ASSERT_EQ(full_unmask[6], partial_unmask[2]); } + +TEST_P(LicenseWhiteboxMaskedDecryptTest, KeyUnavailableForInvalidKey) { + // There are multiple ways for us to invalidate a content key. We have tests + // that test invalid keys (see the query content key status tests). But here, + // we just need an invalid key, so we use one way of invalidating the key. + TestLicenseBuilder::Settings settings; + settings.include_content_key_iv = false; + LoadLicense(settings); + + ASSERT_EQ( + WB_License_MaskedDecrypt( + whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCDecodeKey().id.data(), + golden_data_.CBCDecodeKey().id.size(), + golden_data_.CBCDecodeKey().content->ciphertext.data(), + golden_data_.CBCDecodeKey().content->ciphertext.size(), + golden_data_.CBCDecodeKey().content->iv.data(), + golden_data_.CBCDecodeKey().content->iv.size(), masked_text_.data(), + &masked_text_size_), + WB_RESULT_KEY_UNAVAILABLE); +} + +INSTANTIATE_TEST_SUITE_P(NoPadding, + LicenseWhiteboxMaskedDecryptTest, + ::testing::Values(Padding::kNone)); + +INSTANTIATE_TEST_SUITE_P(PKSC8, + LicenseWhiteboxMaskedDecryptTest, + ::testing::Values(Padding::kPKSC8)); } // namespace widevine diff --git a/whitebox/api/license_whitebox_process_license_response_test.cc b/whitebox/api/license_whitebox_process_license_response_test.cc index 1f0b3d1..9898a82 100644 --- a/whitebox/api/license_whitebox_process_license_response_test.cc +++ b/whitebox/api/license_whitebox_process_license_response_test.cc @@ -82,8 +82,11 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseTest, WB_RESULT_OK); } +// If there were multiple signing keys (this can only happen if a license server +// was manipulating the license and we are using protobuf parsing), the +// implementation is free to pick either key. TEST_F(LicenseWhiteboxProcessLicenseResponseTest, - InvalidParameterWithMultipleSigningKeys) { + SuccessWithMultipleSigningKeys) { TestLicenseBuilder builder; builder.GetSettings().padding = TestLicenseBuilder::Padding::kPKSC8; @@ -99,7 +102,7 @@ TEST_F(LicenseWhiteboxProcessLicenseResponseTest, license_.signature.data(), license_.signature.size(), license_.session_key.data(), license_.session_key.size(), license_.request.data(), license_.request.size()), - WB_RESULT_INVALID_PARAMETER); + WB_RESULT_OK); } TEST_F(LicenseWhiteboxProcessLicenseResponseTest, InvalidParameterWithNoKeys) { diff --git a/whitebox/api/license_whitebox_query_content_key_status_test.cc b/whitebox/api/license_whitebox_query_content_key_status_test.cc new file mode 100644 index 0000000..5684840 --- /dev/null +++ b/whitebox/api/license_whitebox_query_content_key_status_test.cc @@ -0,0 +1,328 @@ +// Copyright 2021 Google LLC. All Rights Reserved. + +#include "api/license_whitebox.h" + +#include +#include +#include + +#include "api/golden_data.h" +#include "api/license_whitebox_test_base.h" +#include "api/test_license_builder.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace widevine { + +using OdkVersion = TestLicenseBuilder::OdkVersion; +using KeyControlBlock = TestLicenseBuilder::KeyControlBlock; +using Padding = TestLicenseBuilder::Padding; + +enum class LicenseParsing { + kUseProtobuf, + kUseOdk, +}; + +class LicenseWhiteboxQueryContentKeyStatus + : public LicenseWhiteboxTestBase, + public testing::WithParamInterface> { + protected: + void SetUp() override { + LicenseWhiteboxTestBase::SetUp(); + decrypt_key_ = &golden_data_.CBCCryptoKey(); + masked_decrypt_key_ = &golden_data_.CBCDecodeKey(); + + std::tie(license_parsing_, padding_) = GetParam(); + } + + void SetSettings(const TestLicenseBuilder::Settings& settings) { + builder_.SetSettings(settings); + } + + void AddKey(const GoldenData::Key* key) { + builder_.AddContentKey(key->level, key->id, key->content->key); + } + + WB_Result LoadLicense() { + TestLicenseBuilder::Settings settings = builder_.GetSettings(); + + // To use ODK, we need to use 16.5 (which should have a clear KCB). If we + // want to force Protobuf parsing, we just ned to have no ODK message and/or + // an encrypted KCB. + switch (license_parsing_) { + case LicenseParsing::kUseProtobuf: + settings.odk_version = OdkVersion::kNone; + settings.key_control_block = KeyControlBlock::kEncrypted; + break; + + case LicenseParsing::kUseOdk: + settings.odk_version = OdkVersion::k16_5; + settings.key_control_block = KeyControlBlock::kClear; + break; + + default: + // We can't use an assert here (wrong return type) so instead we'll + // return an error code. + return WB_RESULT_INVALID_PARAMETER; + } + + settings.padding = padding_; + builder_.SetSettings(settings); + + License license; + builder_.Build(*public_key_, &license); + + return WB_License_ProcessLicenseResponse( + whitebox_, 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(), + license.request.data(), license.request.size()); + } + + const GoldenData::Key* decrypt_key_; + const GoldenData::Key* masked_decrypt_key_; + + private: + TestLicenseBuilder builder_; + LicenseParsing license_parsing_; + Padding padding_; +}; + +TEST_P(LicenseWhiteboxQueryContentKeyStatus, ValidDecryptKey) { + AddKey(decrypt_key_); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + // REMINDER: Any key that can be used with decrypt can also be used with + // masked decrypt, however not every key that can be used with masked deecrypt + // can be used with decrypt. + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_CONTENT_KEY, + decrypt_key_->id.data(), + decrypt_key_->id.size(), &key_state), + WB_RESULT_OK); + + // Given how security levels work, if a key can be used with decrypt then it + // should be able to work with masked decrypt. + ASSERT_EQ(key_state, WB_KEY_STATUS_CONTENT_KEY_DECRYPT); +} + +TEST_P(LicenseWhiteboxQueryContentKeyStatus, ValidMaskedDecryptKey) { + AddKey(masked_decrypt_key_); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + // REMINDER: Any key that can be used with decrypt can also be used with + // masked decrypt, however not every key that can be used with masked deecrypt + // can be used with decrypt. + WB_KeyStatus key_state; + ASSERT_EQ( + WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_CONTENT_KEY, + masked_decrypt_key_->id.data(), + masked_decrypt_key_->id.size(), &key_state), + WB_RESULT_OK); + + ASSERT_EQ(key_state, WB_KEY_STATUS_CONTENT_KEY_MASKED_DECRYPT); +} + +TEST_P(LicenseWhiteboxQueryContentKeyStatus, NoKeyForNoId) { + TestLicenseBuilder::Settings settings; + settings.include_content_key_id = false; + SetSettings(settings); + + AddKey(decrypt_key_); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_CONTENT_KEY, + decrypt_key_->id.data(), + decrypt_key_->id.size(), &key_state), + WB_RESULT_KEY_UNAVAILABLE); +} + +TEST_P(LicenseWhiteboxQueryContentKeyStatus, InvalidKeyForNoIv) { + TestLicenseBuilder::Settings settings; + settings.include_content_key_iv = false; + SetSettings(settings); + + AddKey(decrypt_key_); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_CONTENT_KEY, + decrypt_key_->id.data(), + decrypt_key_->id.size(), &key_state), + WB_RESULT_OK); + ASSERT_EQ(key_state, WB_KEY_STATUS_INVALID); +} + +TEST_P(LicenseWhiteboxQueryContentKeyStatus, InvalidKeyForShortIv) { + TestLicenseBuilder::Settings settings; + settings.content_key_iv_size = 7; + SetSettings(settings); + + AddKey(decrypt_key_); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_CONTENT_KEY, + decrypt_key_->id.data(), + decrypt_key_->id.size(), &key_state), + WB_RESULT_OK); + ASSERT_EQ(key_state, WB_KEY_STATUS_INVALID); +} + +TEST_P(LicenseWhiteboxQueryContentKeyStatus, InvalidKeyForNoKeyData) { + TestLicenseBuilder::Settings settings; + settings.include_content_key_key = false; + SetSettings(settings); + + AddKey(decrypt_key_); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_CONTENT_KEY, + decrypt_key_->id.data(), + decrypt_key_->id.size(), &key_state), + WB_RESULT_OK); + ASSERT_EQ(key_state, WB_KEY_STATUS_INVALID); +} + +TEST_P(LicenseWhiteboxQueryContentKeyStatus, InvalidKeyForShortKeyData) { + TestLicenseBuilder::Settings settings; + settings.content_key_key_size_override = true; + settings.content_key_key_size = 7; + SetSettings(settings); + + AddKey(decrypt_key_); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_CONTENT_KEY, + decrypt_key_->id.data(), + decrypt_key_->id.size(), &key_state), + WB_RESULT_OK); + ASSERT_EQ(key_state, WB_KEY_STATUS_INVALID); +} + +TEST_P(LicenseWhiteboxQueryContentKeyStatus, InvalidKeyForLongKeyData) { + TestLicenseBuilder::Settings settings; + settings.content_key_key_size_override = true; + settings.content_key_key_size = 100; + SetSettings(settings); + + AddKey(decrypt_key_); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_CONTENT_KEY, + decrypt_key_->id.data(), + decrypt_key_->id.size(), &key_state), + WB_RESULT_OK); + ASSERT_EQ(key_state, WB_KEY_STATUS_INVALID); +} + +TEST_P(LicenseWhiteboxQueryContentKeyStatus, InvalidParameterForNullWhitebox) { + AddKey(masked_decrypt_key_); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ( + WB_License_QueryKeyStatus(nullptr, WB_KEY_QUERY_TYPE_CONTENT_KEY, + masked_decrypt_key_->id.data(), + masked_decrypt_key_->id.size(), &key_state), + WB_RESULT_INVALID_PARAMETER); +} + +TEST_P(LicenseWhiteboxQueryContentKeyStatus, InvalidParameterForNullKeyId) { + AddKey(masked_decrypt_key_); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_CONTENT_KEY, + nullptr, masked_decrypt_key_->id.size(), + &key_state), + WB_RESULT_INVALID_PARAMETER); +} + +TEST_P(LicenseWhiteboxQueryContentKeyStatus, InvalidParameterForZeroKeyIdSize) { + AddKey(masked_decrypt_key_); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ( + WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_CONTENT_KEY, + masked_decrypt_key_->id.data(), 0, &key_state), + WB_RESULT_INVALID_PARAMETER); +} + +TEST_P(LicenseWhiteboxQueryContentKeyStatus, InvalidParameterForNullKeyStatus) { + AddKey(masked_decrypt_key_); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_CONTENT_KEY, + masked_decrypt_key_->id.data(), + masked_decrypt_key_->id.size(), nullptr), + WB_RESULT_INVALID_PARAMETER); +} + +TEST_P(LicenseWhiteboxQueryContentKeyStatus, InvalidStateForNoLicense) { + WB_KeyStatus key_state; + ASSERT_EQ( + WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_CONTENT_KEY, + masked_decrypt_key_->id.data(), + masked_decrypt_key_->id.size(), &key_state), + WB_RESULT_INVALID_STATE); +} + +INSTANTIATE_TEST_SUITE_P( + ProtbufParsingWithoutPadding, + LicenseWhiteboxQueryContentKeyStatus, + ::testing::Values(std::make_tuple(LicenseParsing::kUseProtobuf, + Padding::kNone))); + +INSTANTIATE_TEST_SUITE_P( + ProtbufParsingWithPadding, + LicenseWhiteboxQueryContentKeyStatus, + ::testing::Values(std::make_tuple(LicenseParsing::kUseProtobuf, + Padding::kPKSC8))); + +INSTANTIATE_TEST_SUITE_P( + OdkParsingWithoutPadding, + LicenseWhiteboxQueryContentKeyStatus, + ::testing::Values(std::make_tuple(LicenseParsing::kUseOdk, + Padding::kNone))); + +INSTANTIATE_TEST_SUITE_P( + OdkParsingWithPadding, + LicenseWhiteboxQueryContentKeyStatus, + ::testing::Values(std::make_tuple(LicenseParsing::kUseOdk, + Padding::kPKSC8))); + +// Some cases are only possible when protobuf parsing is being used as the ODK +// will fail to create a core message for some protobufs (meaning the server +// will not be able to create them). +class LicenseWhiteboxQueryContentKeyStatusProtobuf + : public LicenseWhiteboxQueryContentKeyStatus {}; + +TEST_P(LicenseWhiteboxQueryContentKeyStatusProtobuf, NoKey) { + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_CONTENT_KEY, + decrypt_key_->id.data(), + decrypt_key_->id.size(), &key_state), + WB_RESULT_KEY_UNAVAILABLE); +} + +INSTANTIATE_TEST_SUITE_P( + ProtbufParsingWithoutPadding, + LicenseWhiteboxQueryContentKeyStatusProtobuf, + ::testing::Values(std::make_tuple(LicenseParsing::kUseProtobuf, + Padding::kNone))); + +INSTANTIATE_TEST_SUITE_P( + ProtbufParsingWithPadding, + LicenseWhiteboxQueryContentKeyStatusProtobuf, + ::testing::Values(std::make_tuple(LicenseParsing::kUseProtobuf, + Padding::kPKSC8))); + +} // namespace widevine diff --git a/whitebox/api/license_whitebox_query_signing_key_status_test.cc b/whitebox/api/license_whitebox_query_signing_key_status_test.cc new file mode 100644 index 0000000..21b7c72 --- /dev/null +++ b/whitebox/api/license_whitebox_query_signing_key_status_test.cc @@ -0,0 +1,271 @@ +// Copyright 2021 Google LLC. All Rights Reserved. + +#include "api/license_whitebox.h" + +#include +#include +#include + +#include "api/golden_data.h" +#include "api/license_whitebox_test_base.h" +#include "api/test_license_builder.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace widevine { + +using OdkVersion = TestLicenseBuilder::OdkVersion; +using KeyControlBlock = TestLicenseBuilder::KeyControlBlock; + +enum class LicenseParsing { + kUseProtobuf, + kUseOdk, +}; + +class LicenseWhiteboxQuerySigningKeyStatus + : public LicenseWhiteboxTestBase, + public testing::WithParamInterface { + protected: + void SetUp() override { + LicenseWhiteboxTestBase::SetUp(); + signing_key_ = TestLicenseBuilder::DefaultSigningKey(); + + license_parsing_ = GetParam(); + } + + void SetSettings(const TestLicenseBuilder::Settings& settings) { + builder_.SetSettings(settings); + } + + void AddSigningKey() { builder_.AddSigningKey(signing_key_); } + + WB_Result LoadLicense() { + // To use ODK, we need to use 16.5 (which should have a clear KCB). If we + // want to force Protobuf parsing, we just ned to have no ODK message and/or + // an encrypted KCB. + switch (license_parsing_) { + case LicenseParsing::kUseProtobuf: + builder_.GetSettings().odk_version = OdkVersion::kNone; + builder_.GetSettings().key_control_block = KeyControlBlock::kEncrypted; + break; + + case LicenseParsing::kUseOdk: + builder_.GetSettings().odk_version = OdkVersion::k16_5; + builder_.GetSettings().key_control_block = KeyControlBlock::kClear; + break; + + default: + // We can't use an assert here (wrong return type) so instead we'll + // return an error code. + return WB_RESULT_INVALID_PARAMETER; + } + + // ODK requires there to be at least one content key or else it will fail + // to create the core message. + const auto& content_key = &golden_data_.CBCCryptoKey(); + builder_.AddContentKey(content_key->level, content_key->id, + content_key->content->key); + + License license; + builder_.Build(*public_key_, &license); + + return WB_License_ProcessLicenseResponse( + whitebox_, 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(), + license.request.data(), license.request.size()); + } + + private: + TestLicenseBuilder builder_; + TestLicenseBuilder::SigningKey signing_key_; + + LicenseParsing license_parsing_; +}; + +TEST_P(LicenseWhiteboxQuerySigningKeyStatus, ValidKey) { + AddSigningKey(); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_SIGNING_KEY, + nullptr, 0, &key_state), + WB_RESULT_OK); + ASSERT_EQ(key_state, WB_KEY_STATUS_SIGNING_KEY_VALID); +} + +TEST_P(LicenseWhiteboxQuerySigningKeyStatus, NoKey) { + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_SIGNING_KEY, + nullptr, 0, &key_state), + WB_RESULT_KEY_UNAVAILABLE); +} + +TEST_P(LicenseWhiteboxQuerySigningKeyStatus, IgnoresContentKeyParams) { + AddSigningKey(); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + // The key id value really does not matter. What matters is that it will be + // present and therefore the white-box should just ignore it since we are + // doing a signing key query. + const uint8_t key_id[] = {0x00, 0x01, 0x02, 0x03}; + const size_t key_id_size = sizeof(key_id); + + WB_KeyStatus key_state; + + // Try every permutation of content key params (many invalid for content keys) + // to make sure that the signing key query ignores them. + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_SIGNING_KEY, + key_id, key_id_size, &key_state), + WB_RESULT_OK); + ASSERT_EQ(key_state, WB_KEY_STATUS_SIGNING_KEY_VALID); + + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_SIGNING_KEY, + key_id, 0, &key_state), + WB_RESULT_OK); + ASSERT_EQ(key_state, WB_KEY_STATUS_SIGNING_KEY_VALID); + + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_SIGNING_KEY, + nullptr, 0, &key_state), + WB_RESULT_OK); + ASSERT_EQ(key_state, WB_KEY_STATUS_SIGNING_KEY_VALID); + + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_SIGNING_KEY, + nullptr, key_id_size, &key_state), + WB_RESULT_OK); + ASSERT_EQ(key_state, WB_KEY_STATUS_SIGNING_KEY_VALID); +} + +TEST_P(LicenseWhiteboxQuerySigningKeyStatus, InvalidParameterForNullWhitebox) { + AddSigningKey(); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(nullptr, WB_KEY_QUERY_TYPE_SIGNING_KEY, + nullptr, 0, &key_state), + WB_RESULT_INVALID_PARAMETER); +} + +TEST_P(LicenseWhiteboxQuerySigningKeyStatus, InvalidParameterForNullKeyStatus) { + AddSigningKey(); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_SIGNING_KEY, + nullptr, 0, nullptr), + WB_RESULT_INVALID_PARAMETER); +} + +TEST_P(LicenseWhiteboxQuerySigningKeyStatus, InvalidStateForNoLicense) { + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_SIGNING_KEY, + nullptr, 0, &key_state), + WB_RESULT_INVALID_STATE); +} + +// If there is no key type, ODK will assume that it is a signing key. So the +// protobuf parser should also do this. This is because protobuf will default +// to the first enum value when the value is missing. +TEST_P(LicenseWhiteboxQuerySigningKeyStatus, ValidKeyForNoKeyType) { + TestLicenseBuilder::Settings settings; + settings.include_signing_key_type = false; + SetSettings(settings); + + AddSigningKey(); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_SIGNING_KEY, + nullptr, 0, &key_state), + WB_RESULT_OK); + ASSERT_EQ(key_state, WB_KEY_STATUS_SIGNING_KEY_VALID); +} + +TEST_P(LicenseWhiteboxQuerySigningKeyStatus, InvalidKeyForNoIv) { + TestLicenseBuilder::Settings settings; + settings.include_signing_key_iv = false; + SetSettings(settings); + + AddSigningKey(); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_SIGNING_KEY, + nullptr, 0, &key_state), + WB_RESULT_OK); + ASSERT_EQ(key_state, WB_KEY_STATUS_INVALID); +} + +TEST_P(LicenseWhiteboxQuerySigningKeyStatus, InvalidKeyForShortIv) { + TestLicenseBuilder::Settings settings; + settings.signing_key_iv_size = 7; + SetSettings(settings); + + AddSigningKey(); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_SIGNING_KEY, + nullptr, 0, &key_state), + WB_RESULT_OK); + ASSERT_EQ(key_state, WB_KEY_STATUS_INVALID); +} + +// The ODK will not write the key to the core message if there is no key data. +// This is odd, since it does not check any other fields. +TEST_P(LicenseWhiteboxQuerySigningKeyStatus, KeyNotFoundForNoKeyData) { + TestLicenseBuilder::Settings settings; + settings.include_signing_key_key = false; + SetSettings(settings); + + AddSigningKey(); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_SIGNING_KEY, + nullptr, 0, &key_state), + WB_RESULT_KEY_UNAVAILABLE); +} + +TEST_P(LicenseWhiteboxQuerySigningKeyStatus, InvalidKeyForShortKeyData) { + TestLicenseBuilder::Settings settings; + settings.signing_key_key_size_override = true; + settings.signing_key_key_size = 7; + SetSettings(settings); + + AddSigningKey(); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_SIGNING_KEY, + nullptr, 0, &key_state), + WB_RESULT_OK); + ASSERT_EQ(key_state, WB_KEY_STATUS_INVALID); +} + +TEST_P(LicenseWhiteboxQuerySigningKeyStatus, InvalidKeyForLongKeyData) { + TestLicenseBuilder::Settings settings; + settings.signing_key_key_size_override = true; + settings.signing_key_key_size = 100; + SetSettings(settings); + + AddSigningKey(); + ASSERT_EQ(LoadLicense(), WB_RESULT_OK); + + WB_KeyStatus key_state; + ASSERT_EQ(WB_License_QueryKeyStatus(whitebox_, WB_KEY_QUERY_TYPE_SIGNING_KEY, + nullptr, 0, &key_state), + WB_RESULT_OK); + ASSERT_EQ(key_state, WB_KEY_STATUS_INVALID); +} + +INSTANTIATE_TEST_SUITE_P(ProtbufParsing, + LicenseWhiteboxQuerySigningKeyStatus, + ::testing::Values(LicenseParsing::kUseProtobuf)); + +INSTANTIATE_TEST_SUITE_P(OdkParsing, + LicenseWhiteboxQuerySigningKeyStatus, + ::testing::Values(LicenseParsing::kUseOdk)); + +} // namespace widevine diff --git a/whitebox/api/license_whitebox_sign_renewal_request_test.cc b/whitebox/api/license_whitebox_sign_renewal_request_test.cc index 124bd1e..dd25a73 100644 --- a/whitebox/api/license_whitebox_sign_renewal_request_test.cc +++ b/whitebox/api/license_whitebox_sign_renewal_request_test.cc @@ -25,9 +25,9 @@ class LicenseWhiteboxSignRenewalRequestTest : public LicenseWhiteboxTestBase { signature_.resize(signature_size_); } - void LoadLicense(TestLicenseBuilder::Padding padding) { + void LoadLicense(const TestLicenseBuilder::Settings& settings) { TestLicenseBuilder builder; - builder.GetSettings().padding = padding; + builder.SetSettings(settings); builder.AddSigningKey(signing_key_); // Add a throw away key. We just need a key in the license since a license @@ -79,7 +79,9 @@ class LicenseWhiteboxSignRenewalRequestTest : public LicenseWhiteboxTestBase { }; TEST_F(LicenseWhiteboxSignRenewalRequestTest, SuccessWithInvalidRequest) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(), garbage_request_.size(), @@ -92,7 +94,9 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, SuccessWithInvalidRequest) { TEST_F(LicenseWhiteboxSignRenewalRequestTest, SuccessWithSigningKeyPKSC8Padding) { - LoadLicense(TestLicenseBuilder::Padding::kPKSC8); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kPKSC8; + LoadLicense(settings); ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(), garbage_request_.size(), @@ -104,7 +108,9 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, } TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullWhitebox) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ(WB_License_SignRenewalRequest(nullptr, garbage_request_.data(), garbage_request_.size(), @@ -113,7 +119,9 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullWhitebox) { } TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullMessage) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_SignRenewalRequest(whitebox_, nullptr, garbage_request_.size(), @@ -123,7 +131,9 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullMessage) { TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForZeroMessageSize) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(), 0, signature_.data(), &signature_size_), @@ -132,7 +142,9 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullSignature) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(), garbage_request_.size(), nullptr, @@ -142,7 +154,9 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidParameterForNullSignatureSize) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(), garbage_request_.size(), @@ -151,7 +165,9 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, } TEST_F(LicenseWhiteboxSignRenewalRequestTest, BufferTooSmall) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); // We need the signature to be too small. While it would be possible to use // zero, using a non-zero value ensures that we are not combining "empty" and @@ -180,7 +196,7 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidStateForNoLicense) { WB_RESULT_INVALID_STATE); } -TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidStateForNoSigningKey) { +TEST_F(LicenseWhiteboxSignRenewalRequestTest, KeyUnavailableForNoSigningKey) { // Make a license with no signing key but has a content key. Every license // must have a content key. TestLicenseBuilder builder; @@ -201,7 +217,21 @@ TEST_F(LicenseWhiteboxSignRenewalRequestTest, InvalidStateForNoSigningKey) { ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(), garbage_request_.size(), signature_.data(), &signature_size_), - WB_RESULT_INVALID_STATE); + WB_RESULT_KEY_UNAVAILABLE); +} + +TEST_F(LicenseWhiteboxSignRenewalRequestTest, KeyUnavailableForInvalidKey) { + // There are multiple ways for us to invalid a signing key. We have tests that + // test invalid keys (see the query signing key status tests). But here, we + // just need an invalid key, so we use one way of invalidating the key. + TestLicenseBuilder::Settings settings; + settings.include_signing_key_iv = false; + LoadLicense(settings); + + ASSERT_EQ(WB_License_SignRenewalRequest(whitebox_, garbage_request_.data(), + garbage_request_.size(), + signature_.data(), &signature_size_), + WB_RESULT_KEY_UNAVAILABLE); } } // namespace widevine diff --git a/whitebox/api/license_whitebox_test_base.cc b/whitebox/api/license_whitebox_test_base.cc index 0e9abac..bf47a86 100644 --- a/whitebox/api/license_whitebox_test_base.cc +++ b/whitebox/api/license_whitebox_test_base.cc @@ -4,7 +4,7 @@ #include -#include "api/test_public_key.h" +#include "api/test_license_whitebox_keys.h" namespace widevine { diff --git a/whitebox/api/license_whitebox_golden_data_test.cc b/whitebox/api/license_whitebox_uat_test.cc similarity index 99% rename from whitebox/api/license_whitebox_golden_data_test.cc rename to whitebox/api/license_whitebox_uat_test.cc index 436a260..880f419 100644 --- a/whitebox/api/license_whitebox_golden_data_test.cc +++ b/whitebox/api/license_whitebox_uat_test.cc @@ -302,14 +302,14 @@ const uint8_t kIv[] = { const size_t kIvSize = sizeof(kIv); } // namespace -class LicenseWhiteboxDecryptGoldenData : public ::testing::Test { +class LicenseWhiteboxDecryptUatTest : public ::testing::Test { protected: void TearDown() override { WB_License_Delete(whitebox_); } WB_License_Whitebox* whitebox_ = nullptr; }; -TEST_F(LicenseWhiteboxDecryptGoldenData, CryptoKeyWithCbcDataInCbcMode) { +TEST_F(LicenseWhiteboxDecryptUatTest, CryptoKeyWithCbcDataInCbcMode) { ASSERT_EQ(WB_License_Create(&whitebox_), WB_RESULT_OK); ASSERT_EQ(WB_License_ProcessLicenseResponse( diff --git a/whitebox/api/license_whitebox_verify_renewal_response_test.cc b/whitebox/api/license_whitebox_verify_renewal_response_test.cc index db95065..c6baff3 100644 --- a/whitebox/api/license_whitebox_verify_renewal_response_test.cc +++ b/whitebox/api/license_whitebox_verify_renewal_response_test.cc @@ -22,14 +22,14 @@ class LicenseWhiteboxVerifyRenewalResponseTest garbage_renewal_signature_ = Sign(garbage_renewal_message_); } - void LoadLicense(TestLicenseBuilder::Padding padding) { + void LoadLicense(const TestLicenseBuilder::Settings& settings) { const auto signing_key = TestLicenseBuilder::DefaultSigningKey(); // We need a license so that we can always have a valid signature for our // message(s), but don't load the license as some test will need no // license loaded. TestLicenseBuilder builder; - builder.GetSettings().padding = padding; + builder.SetSettings(settings); builder.AddSigningKey(signing_key); builder.AddStubbedContentKey(); @@ -86,7 +86,9 @@ class LicenseWhiteboxVerifyRenewalResponseTest // SuccessForGarbageMessage - to use the real serialized response. TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, SuccessForGarbageMessage) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, garbage_renewal_message_.data(), @@ -98,7 +100,9 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, SuccessForGarbageMessage) { TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, SuccessWithSigningKeyPKSC8Padding) { - LoadLicense(TestLicenseBuilder::Padding::kPKSC8); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kPKSC8; + LoadLicense(settings); ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, garbage_renewal_message_.data(), @@ -110,7 +114,9 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidParameterForNullWhitebox) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ( WB_License_VerifyRenewalResponse(nullptr, garbage_renewal_message_.data(), @@ -122,7 +128,9 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidParameterForNullMessage) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, nullptr, garbage_renewal_message_.size(), @@ -133,7 +141,9 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidParameterForZeroMessageSize) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, garbage_renewal_message_.data(), 0, @@ -144,7 +154,9 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidParameterForNullSignature) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ(WB_License_VerifyRenewalResponse( whitebox_, garbage_renewal_message_.data(), @@ -155,7 +167,9 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidParameterForInvalidSignatureSize) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); ASSERT_EQ(WB_License_VerifyRenewalResponse( whitebox_, garbage_renewal_message_.data(), @@ -166,7 +180,9 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidSignatureForModifiedMessage) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); Modify(&garbage_renewal_message_); @@ -180,7 +196,9 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidSignatureForModifiedSignature) { - LoadLicense(TestLicenseBuilder::Padding::kNone); + TestLicenseBuilder::Settings settings; + settings.padding = TestLicenseBuilder::Padding::kNone; + LoadLicense(settings); Modify(&garbage_renewal_signature_); @@ -204,7 +222,8 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidStateForNoLicense) { WB_RESULT_INVALID_STATE); } -TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidStateForNoSigningKey) { +TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, + KeyUnavailableForNoSigningKey) { // Create a license with no signing key and one content key (every license // must have a content key). widevine::TestLicenseBuilder builder; @@ -227,7 +246,23 @@ TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, InvalidStateForNoSigningKey) { garbage_renewal_message_.size(), garbage_renewal_signature_.data(), garbage_renewal_signature_.size()), - WB_RESULT_INVALID_STATE); + WB_RESULT_KEY_UNAVAILABLE); +} + +TEST_F(LicenseWhiteboxVerifyRenewalResponseTest, KeyUnavailableForInvalidKey) { + // There are multiple ways for us to invalid a signing key. We have tests that + // test invalid keys (see the query signing key status tests). But here, we + // just need an invalid key, so we use one way of invalidating the key. + TestLicenseBuilder::Settings settings; + settings.include_signing_key_iv = false; + LoadLicense(settings); + + ASSERT_EQ(WB_License_VerifyRenewalResponse(whitebox_, + garbage_renewal_message_.data(), + garbage_renewal_message_.size(), + garbage_renewal_signature_.data(), + garbage_renewal_signature_.size()), + WB_RESULT_KEY_UNAVAILABLE); } } // namespace widevine diff --git a/whitebox/api/test_license_builder.cc b/whitebox/api/test_license_builder.cc index 8d4f7d2..0d9d2de 100644 --- a/whitebox/api/test_license_builder.cc +++ b/whitebox/api/test_license_builder.cc @@ -240,8 +240,13 @@ void AddContentKeyToContainer(const TestLicenseBuilder::ContentKey& key_data, const TestLicenseBuilder::Settings& settings, const std::string& container_key, video_widevine::License_KeyContainer* container) { - container->set_type(video_widevine::License_KeyContainer_KeyType_CONTENT); - container->set_id(key_data.id.data(), key_data.id.size()); + if (settings.include_content_key_type) { + container->set_type(video_widevine::License_KeyContainer_KeyType_CONTENT); + } + + if (settings.include_content_key_id) { + container->set_id(key_data.id.data(), key_data.id.size()); + } // If the security level is undefined it means that no security level should // appear in the key container. When reading the key container, it will be @@ -254,14 +259,26 @@ void AddContentKeyToContainer(const TestLicenseBuilder::ContentKey& key_data, // 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); - std::vector key(key_data.key.begin(), key_data.key.end()); + if (settings.include_content_key_iv) { + container->set_iv(key_iv); + container->mutable_iv()->resize(settings.content_key_iv_size); + } - auto padding = GetPadding(settings.padding); - key.insert(key.end(), padding.begin(), padding.end()); + if (settings.include_content_key_key) { + std::vector key(key_data.key.begin(), key_data.key.end()); - container->set_key(Encrypt(container_key, key_iv, key)); + auto padding = GetPadding(settings.padding); + key.insert(key.end(), padding.begin(), padding.end()); + + auto encrypted_key = Encrypt(container_key, key_iv, key); + + container->set_key(encrypted_key); + + if (settings.content_key_key_size_override) { + container->mutable_key()->resize(settings.content_key_key_size); + } + } // There are three different ways we can add a key control block. It is // possible to have no key control block (it is an optional field). This is @@ -275,10 +292,10 @@ void AddContentKeyToContainer(const TestLicenseBuilder::ContentKey& key_data, break; case TestLicenseBuilder::KeyControlBlock::kClear: { - const auto key_control_block = - CreateKeyControlBlock(SecurityLevelToProto(key_data.level), - container->mutable_key_control()); auto* key_control = container->mutable_key_control(); + const auto key_control_block = CreateKeyControlBlock( + SecurityLevelToProto(key_data.level), key_control); + key_control->set_key_control_block(key_control_block.data(), key_control_block.size()); @@ -289,10 +306,9 @@ void AddContentKeyToContainer(const TestLicenseBuilder::ContentKey& 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. - const auto key_control_block = - CreateKeyControlBlock(SecurityLevelToProto(key_data.level), - container->mutable_key_control()); auto* key_control = container->mutable_key_control(); + const auto key_control_block = CreateKeyControlBlock( + SecurityLevelToProto(key_data.level), key_control); const auto key_control_block_iv = DeriveIV(key_control_block); const auto encrypted_key_control_block = @@ -301,7 +317,6 @@ void AddContentKeyToContainer(const TestLicenseBuilder::ContentKey& key_data, key_control->set_iv(key_control_block_iv); key_control->set_key_control_block(encrypted_key_control_block.data(), encrypted_key_control_block.size()); - break; } @@ -314,20 +329,34 @@ void AddSigningKeyToContainer(const TestLicenseBuilder::SigningKey& key_data, const TestLicenseBuilder::Settings& settings, const std::string& container_key, video_widevine::License_KeyContainer* container) { - container->set_type(video_widevine::License_KeyContainer_KeyType_SIGNING); + if (settings.include_signing_key_type) { + container->set_type(video_widevine::License_KeyContainer_KeyType_SIGNING); + } // 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); - container->set_iv(key_iv); - std::vector key(key_data.begin(), key_data.end()); + if (settings.include_signing_key_iv) { + container->set_iv(key_iv); + container->mutable_iv()->resize(settings.signing_key_iv_size); + } - auto padding = GetPadding(settings.padding); - key.insert(key.end(), padding.begin(), padding.end()); + if (settings.include_signing_key_key) { + std::vector key(key_data.begin(), key_data.end()); - container->set_key(Encrypt(container_key, key_iv, key)); + auto padding = GetPadding(settings.padding); + key.insert(key.end(), padding.begin(), padding.end()); + + auto encrypted_key = Encrypt(container_key, key_iv, key); + + container->set_key(encrypted_key); + + if (settings.signing_key_key_size_override) { + container->mutable_key()->resize(settings.signing_key_key_size); + } + } } void AddOperatorSessionKeyToContainer( diff --git a/whitebox/api/test_license_builder.h b/whitebox/api/test_license_builder.h index fb3fea4..6757170 100644 --- a/whitebox/api/test_license_builder.h +++ b/whitebox/api/test_license_builder.h @@ -82,6 +82,40 @@ class TestLicenseBuilder { KeyControlBlock key_control_block = KeyControlBlock::kClear; RemoteAttestation remote_attestation = RemoteAttestation::kUnavailable; VerificationStatus verification_status = VerificationStatus::kUnavailable; + + // Flags + // + // Default to the "correct" structure. We will flip values to test for + // invalid states (even if they should never happen). These settings are + // global since a license server would be more likely to be consistency + // invalid. + bool include_content_key_id = true; + bool include_content_key_key = true; + bool include_content_key_iv = true; + bool include_content_key_type = true; + + bool include_signing_key_iv = true; + bool include_signing_key_key = true; + bool include_signing_key_type = true; + + // 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 + // override the key size, *_override must be set to true. This is to avoid + // conflicts between padding and key size. + bool content_key_key_size_override = false; + size_t content_key_key_size = 16; + size_t content_key_iv_size = 16; + + // Our signing key's key should always be 64 (see SigningKey definition) and + // the iv size should always be 16 bytes (see AesIV definition), but we can + // use these controls to cut them short. If these values are larger than 64 + // and 16, the key/iv with be padded. In order to override the key size, + // *_override must be set to true. This is to avoid conflicts between + // padding and key size. + bool signing_key_key_size_override = false; + size_t signing_key_key_size = 64; + size_t signing_key_iv_size = 16; }; // Returns a default signing key that can be used with AddSigningKey(). @@ -102,6 +136,7 @@ class TestLicenseBuilder { // key can't be used as a content key. void AddOperatorSessionKey(const KeyId& key_id); + void SetSettings(const Settings& settings) { settings_ = settings; } Settings& GetSettings() { return settings_; } const Settings& GetSettings() const { return settings_; } diff --git a/whitebox/api/test_license_whitebox_keys.cc b/whitebox/api/test_license_whitebox_keys.cc new file mode 100644 index 0000000..f4c34b9 --- /dev/null +++ b/whitebox/api/test_license_whitebox_keys.cc @@ -0,0 +1,149 @@ +// Copyright 2020 Google LLC. All Rights Reserved. + +#include "api/test_license_whitebox_keys.h" + +namespace widevine { +namespace { + +constexpr uint8_t kDevicePublicKey[] = { + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xae, 0xa6, 0xa8, + 0xea, 0xdd, 0xac, 0x6f, 0xb4, 0x41, 0x47, 0xcb, 0x18, 0x81, 0xeb, 0xdf, + 0x4f, 0x17, 0xf7, 0x17, 0xc0, 0xab, 0x6f, 0x47, 0x50, 0xbf, 0xe4, 0x8c, + 0xc9, 0x45, 0x24, 0x5e, 0x1c, 0x2e, 0x86, 0x9f, 0xcb, 0x47, 0x05, 0xf9, + 0xfe, 0x91, 0x90, 0xaf, 0xbd, 0x22, 0x14, 0x47, 0xa7, 0x34, 0x39, 0x79, + 0x44, 0xe9, 0x92, 0x83, 0x4a, 0x80, 0xa8, 0x2a, 0xe6, 0x9f, 0x2b, 0xb7, + 0xda, 0x2a, 0xd2, 0xae, 0x57, 0x5b, 0xfa, 0xb6, 0xdf, 0xca, 0x3e, 0xb1, + 0xb8, 0x42, 0x0b, 0xde, 0x46, 0x36, 0xdb, 0x42, 0x33, 0x8b, 0xda, 0x5c, + 0x60, 0x44, 0x7c, 0x99, 0xb4, 0x98, 0xb4, 0x1e, 0xd8, 0x25, 0x6d, 0x67, + 0x84, 0xc9, 0x67, 0xde, 0x05, 0xe6, 0x06, 0xb5, 0xb5, 0xca, 0xbc, 0xfa, + 0xb0, 0xa7, 0x46, 0x29, 0x3f, 0x63, 0x47, 0x9d, 0x70, 0x8d, 0xa2, 0x8d, + 0x22, 0xc6, 0xeb, 0x06, 0xd4, 0x5c, 0x3b, 0x62, 0x98, 0xc7, 0xda, 0x16, + 0x8f, 0x17, 0x59, 0xd5, 0xcb, 0xd1, 0x5d, 0xe3, 0xe1, 0x07, 0xe6, 0x97, + 0x87, 0xf4, 0x22, 0x53, 0xfa, 0xf9, 0xa9, 0xf5, 0xeb, 0xd7, 0x55, 0xdf, + 0x32, 0x2d, 0x4e, 0x07, 0x86, 0x25, 0x44, 0x93, 0xd6, 0xf7, 0xc6, 0xf9, + 0x78, 0x91, 0x24, 0x1e, 0xd4, 0x6b, 0xe3, 0x4a, 0xff, 0x4a, 0x3a, 0xb9, + 0x89, 0x90, 0x61, 0x87, 0xb9, 0x41, 0x45, 0x02, 0xfd, 0xd0, 0xc5, 0x5a, + 0x98, 0x41, 0x88, 0xa4, 0xe3, 0xe2, 0xa2, 0x9d, 0x9a, 0x90, 0x3f, 0x44, + 0x8e, 0x3a, 0xe1, 0xd1, 0xe9, 0x79, 0x9a, 0xc6, 0xe2, 0x7c, 0x8e, 0x9c, + 0x3d, 0xb2, 0xe0, 0x26, 0x5a, 0x46, 0x13, 0xc8, 0x43, 0x9f, 0xf7, 0x51, + 0x7e, 0xbb, 0x55, 0x6d, 0xcc, 0x97, 0x38, 0xdb, 0xa4, 0x4b, 0x96, 0x40, + 0xe5, 0x2d, 0x8f, 0x43, 0xe3, 0x21, 0x15, 0xda, 0x34, 0x97, 0x7a, 0x9e, + 0xbb, 0x02, 0x03, 0x01, 0x00, 0x01, +}; + +constexpr uint8_t kDevicePrivateKey[] = { + 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xae, 0xa6, 0xa8, 0xea, 0xdd, 0xac, 0x6f, 0xb4, 0x41, 0x47, 0xcb, 0x18, + 0x81, 0xeb, 0xdf, 0x4f, 0x17, 0xf7, 0x17, 0xc0, 0xab, 0x6f, 0x47, 0x50, + 0xbf, 0xe4, 0x8c, 0xc9, 0x45, 0x24, 0x5e, 0x1c, 0x2e, 0x86, 0x9f, 0xcb, + 0x47, 0x05, 0xf9, 0xfe, 0x91, 0x90, 0xaf, 0xbd, 0x22, 0x14, 0x47, 0xa7, + 0x34, 0x39, 0x79, 0x44, 0xe9, 0x92, 0x83, 0x4a, 0x80, 0xa8, 0x2a, 0xe6, + 0x9f, 0x2b, 0xb7, 0xda, 0x2a, 0xd2, 0xae, 0x57, 0x5b, 0xfa, 0xb6, 0xdf, + 0xca, 0x3e, 0xb1, 0xb8, 0x42, 0x0b, 0xde, 0x46, 0x36, 0xdb, 0x42, 0x33, + 0x8b, 0xda, 0x5c, 0x60, 0x44, 0x7c, 0x99, 0xb4, 0x98, 0xb4, 0x1e, 0xd8, + 0x25, 0x6d, 0x67, 0x84, 0xc9, 0x67, 0xde, 0x05, 0xe6, 0x06, 0xb5, 0xb5, + 0xca, 0xbc, 0xfa, 0xb0, 0xa7, 0x46, 0x29, 0x3f, 0x63, 0x47, 0x9d, 0x70, + 0x8d, 0xa2, 0x8d, 0x22, 0xc6, 0xeb, 0x06, 0xd4, 0x5c, 0x3b, 0x62, 0x98, + 0xc7, 0xda, 0x16, 0x8f, 0x17, 0x59, 0xd5, 0xcb, 0xd1, 0x5d, 0xe3, 0xe1, + 0x07, 0xe6, 0x97, 0x87, 0xf4, 0x22, 0x53, 0xfa, 0xf9, 0xa9, 0xf5, 0xeb, + 0xd7, 0x55, 0xdf, 0x32, 0x2d, 0x4e, 0x07, 0x86, 0x25, 0x44, 0x93, 0xd6, + 0xf7, 0xc6, 0xf9, 0x78, 0x91, 0x24, 0x1e, 0xd4, 0x6b, 0xe3, 0x4a, 0xff, + 0x4a, 0x3a, 0xb9, 0x89, 0x90, 0x61, 0x87, 0xb9, 0x41, 0x45, 0x02, 0xfd, + 0xd0, 0xc5, 0x5a, 0x98, 0x41, 0x88, 0xa4, 0xe3, 0xe2, 0xa2, 0x9d, 0x9a, + 0x90, 0x3f, 0x44, 0x8e, 0x3a, 0xe1, 0xd1, 0xe9, 0x79, 0x9a, 0xc6, 0xe2, + 0x7c, 0x8e, 0x9c, 0x3d, 0xb2, 0xe0, 0x26, 0x5a, 0x46, 0x13, 0xc8, 0x43, + 0x9f, 0xf7, 0x51, 0x7e, 0xbb, 0x55, 0x6d, 0xcc, 0x97, 0x38, 0xdb, 0xa4, + 0x4b, 0x96, 0x40, 0xe5, 0x2d, 0x8f, 0x43, 0xe3, 0x21, 0x15, 0xda, 0x34, + 0x97, 0x7a, 0x9e, 0xbb, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, + 0x00, 0x56, 0x73, 0xe7, 0x1f, 0xc3, 0xb5, 0x4c, 0xe2, 0x24, 0x82, 0x5e, + 0x55, 0x76, 0x52, 0x85, 0x0a, 0xc8, 0xe9, 0x26, 0x57, 0xd8, 0x44, 0xd0, + 0x3f, 0x77, 0x8d, 0xb1, 0xe7, 0x1b, 0x93, 0xc2, 0x06, 0x1f, 0x3d, 0xc2, + 0xb1, 0xc4, 0x29, 0x80, 0x33, 0x74, 0x68, 0xf3, 0xa5, 0x22, 0xce, 0x79, + 0x1d, 0x9a, 0x6b, 0x6c, 0xcd, 0x20, 0xf5, 0xc6, 0x89, 0xc5, 0x9f, 0xf9, + 0x04, 0x89, 0xfc, 0x01, 0x19, 0x3c, 0xa3, 0x67, 0x6b, 0x94, 0xfb, 0x49, + 0x35, 0x04, 0x0e, 0xfe, 0xb8, 0x1f, 0xf1, 0x72, 0x08, 0xbd, 0xb4, 0xd1, + 0x53, 0x64, 0xc2, 0x25, 0x81, 0xfd, 0xc4, 0xd3, 0xed, 0x22, 0xbd, 0xde, + 0x9a, 0xce, 0x04, 0x16, 0xff, 0x13, 0x17, 0x98, 0x3e, 0xc1, 0x3b, 0xc7, + 0x0d, 0x03, 0x1b, 0x82, 0xd8, 0x99, 0x24, 0xd0, 0xdc, 0x30, 0xcf, 0xcd, + 0x6e, 0x5e, 0x9d, 0xfd, 0x51, 0x1e, 0xb8, 0x4e, 0x7b, 0x54, 0x83, 0x9b, + 0x4f, 0xf8, 0xa6, 0x03, 0xc1, 0x96, 0xf1, 0x6d, 0xc0, 0xa7, 0x17, 0xbd, + 0xf1, 0x60, 0xcb, 0xe2, 0x05, 0xa5, 0x9b, 0x05, 0x2e, 0xaf, 0xdc, 0xa7, + 0x88, 0xde, 0x53, 0x42, 0xa9, 0xf4, 0x0f, 0xae, 0xf9, 0x96, 0xe9, 0x2c, + 0xa6, 0xe8, 0x9d, 0x2c, 0x6b, 0xbc, 0xd8, 0x0f, 0x09, 0x5f, 0x64, 0xb2, + 0x21, 0x6f, 0xc0, 0x79, 0x3d, 0x6e, 0xad, 0x93, 0x79, 0x35, 0x87, 0x9a, + 0x41, 0xcc, 0x06, 0x24, 0xf0, 0x62, 0x09, 0xfe, 0x46, 0x9a, 0x38, 0xee, + 0xc0, 0xc8, 0x08, 0xce, 0x65, 0xda, 0xe4, 0x89, 0x1a, 0xfb, 0xe9, 0x53, + 0x0c, 0xd1, 0x80, 0x40, 0xfd, 0xc4, 0x97, 0xf8, 0x19, 0x4e, 0x03, 0x90, + 0x4a, 0xda, 0xfd, 0x13, 0x27, 0x89, 0xde, 0x12, 0x8d, 0x52, 0x5a, 0x07, + 0xf1, 0x9a, 0xa4, 0x54, 0x98, 0x86, 0xb2, 0x78, 0x76, 0xbf, 0x3a, 0xa9, + 0x8b, 0xed, 0xc7, 0x8b, 0x31, 0x02, 0x81, 0x81, 0x00, 0xe2, 0xf3, 0xab, + 0x53, 0x7b, 0xee, 0x36, 0xdb, 0xca, 0xa8, 0x74, 0x03, 0xdd, 0xe2, 0xce, + 0x87, 0xe2, 0x8c, 0x55, 0x8e, 0xd4, 0x0f, 0x32, 0xec, 0xd2, 0xf9, 0x8b, + 0x1f, 0x93, 0xdb, 0x84, 0xd2, 0x42, 0xe1, 0xc7, 0x21, 0x24, 0x2e, 0x36, + 0x0c, 0x02, 0x5d, 0x49, 0xea, 0xe0, 0x42, 0xd7, 0x7a, 0x3e, 0xc8, 0x51, + 0x92, 0x39, 0x56, 0x10, 0xd7, 0x90, 0x67, 0xa3, 0x34, 0xd6, 0xc2, 0x4a, + 0x33, 0x74, 0xfd, 0xe2, 0x7e, 0xe1, 0x3e, 0x59, 0xd7, 0x36, 0x6d, 0x7d, + 0xd4, 0xd8, 0x82, 0xfb, 0x2f, 0x1e, 0x5e, 0x32, 0xcd, 0xc3, 0x0a, 0x7f, + 0xbd, 0xb0, 0xb3, 0xf9, 0x77, 0x75, 0xb9, 0x0c, 0x63, 0x54, 0xff, 0x25, + 0xa3, 0xaf, 0x4a, 0x70, 0x61, 0x32, 0x91, 0xde, 0xfb, 0x95, 0x25, 0xb4, + 0x06, 0x98, 0x9d, 0xeb, 0x49, 0xc8, 0xe0, 0xc0, 0x7e, 0x45, 0xfb, 0xe5, + 0xf8, 0x72, 0x5b, 0x6b, 0x19, 0x02, 0x81, 0x81, 0x00, 0xc5, 0x01, 0x4b, + 0xb7, 0x5f, 0x6d, 0xbc, 0xa6, 0x8c, 0xb8, 0xeb, 0xa5, 0xff, 0x0b, 0xd7, + 0x15, 0xd7, 0xef, 0xf6, 0xc9, 0xfe, 0x69, 0xcc, 0xe5, 0xbd, 0x5c, 0xa8, + 0x05, 0xa0, 0x4d, 0x3b, 0x1f, 0xa6, 0xcc, 0x37, 0x7b, 0xb1, 0x46, 0xf2, + 0xc7, 0x67, 0xcd, 0xc1, 0x20, 0xc4, 0x14, 0xbd, 0x0e, 0x01, 0xa7, 0xd6, + 0x3c, 0xe8, 0x18, 0x9d, 0x71, 0x71, 0x37, 0x2a, 0xc0, 0x45, 0x6a, 0x54, + 0xe8, 0x63, 0xf0, 0x6e, 0xd2, 0x9f, 0x95, 0x3b, 0xde, 0xb3, 0xc5, 0x60, + 0x57, 0x3d, 0xed, 0xef, 0x57, 0xcb, 0x3d, 0x35, 0x3a, 0x2e, 0x5d, 0xb8, + 0x0e, 0xf8, 0xff, 0xd2, 0xca, 0xdd, 0xce, 0x0b, 0x10, 0x53, 0xb4, 0xdb, + 0x53, 0xf6, 0x02, 0xa5, 0xf1, 0x23, 0x4d, 0x21, 0x6e, 0xc7, 0x52, 0x5a, + 0x7a, 0x5d, 0x88, 0x32, 0xa8, 0x65, 0x50, 0x21, 0xf5, 0x81, 0x3f, 0x96, + 0xd4, 0x57, 0x48, 0x66, 0xf3, 0x02, 0x81, 0x81, 0x00, 0xdd, 0x83, 0xd6, + 0x62, 0x9a, 0xe1, 0x0c, 0xfc, 0x84, 0x96, 0xdc, 0xfd, 0xf5, 0x31, 0xee, + 0x42, 0x25, 0x76, 0xb1, 0xff, 0xc1, 0xad, 0xc0, 0x17, 0xf5, 0x68, 0x8a, + 0x49, 0x5d, 0x08, 0xf3, 0x60, 0x42, 0xd5, 0x9a, 0x86, 0x17, 0x89, 0x5f, + 0x49, 0x63, 0x79, 0x68, 0xaf, 0x6f, 0x0a, 0xee, 0xc4, 0xab, 0xc8, 0xdc, + 0x0d, 0x6c, 0x17, 0x3c, 0x43, 0x1a, 0xf8, 0x7d, 0x0d, 0x12, 0xdc, 0xfa, + 0x8d, 0xb5, 0x10, 0x25, 0x65, 0x90, 0x36, 0x4a, 0x7c, 0x4b, 0xec, 0x9c, + 0xd8, 0x06, 0x27, 0xfa, 0x41, 0xa8, 0x53, 0x6b, 0x24, 0xf8, 0xcd, 0x23, + 0x97, 0xa3, 0x84, 0x56, 0xe7, 0x29, 0xa9, 0x5f, 0x95, 0x08, 0x9e, 0x2d, + 0x3f, 0xd1, 0xd5, 0x47, 0x51, 0x27, 0x89, 0xc7, 0x6a, 0x29, 0xce, 0x6e, + 0x23, 0xce, 0x0c, 0xbd, 0x5d, 0xfc, 0x4a, 0x9a, 0xb7, 0xe5, 0x59, 0x13, + 0xc2, 0xe6, 0xe3, 0xa1, 0xe9, 0x02, 0x81, 0x81, 0x00, 0xc3, 0x6f, 0x98, + 0xa4, 0xae, 0x97, 0xd7, 0xb9, 0xc6, 0x0a, 0xc1, 0x43, 0xa8, 0xf4, 0x1f, + 0x08, 0xfd, 0x72, 0x81, 0xfa, 0x3b, 0x58, 0xcc, 0x3a, 0xf1, 0x93, 0x54, + 0xe0, 0x57, 0xf9, 0xa5, 0xf8, 0xad, 0x69, 0x14, 0x75, 0xb2, 0x15, 0x77, + 0x4d, 0xd8, 0xad, 0xa6, 0xb5, 0x11, 0xb0, 0x9d, 0x28, 0xa2, 0xfd, 0xd4, + 0xac, 0x11, 0x78, 0x31, 0xe0, 0xd3, 0x76, 0xee, 0x03, 0x56, 0x19, 0xb9, + 0x67, 0xdd, 0x95, 0x2c, 0xeb, 0xe8, 0x02, 0x8d, 0x25, 0x4e, 0x64, 0x35, + 0x41, 0xf7, 0x1e, 0xee, 0xfc, 0xc2, 0x93, 0xd3, 0x15, 0x07, 0xe0, 0x53, + 0x73, 0x0f, 0x14, 0x03, 0x12, 0xdb, 0xdd, 0xc6, 0xde, 0x08, 0x9c, 0x77, + 0xa5, 0x20, 0x7d, 0xda, 0x0f, 0x91, 0x7c, 0xb7, 0xf9, 0x04, 0xe5, 0xae, + 0xfa, 0x8b, 0x85, 0x4c, 0xf3, 0xff, 0xa5, 0xf2, 0x3a, 0x72, 0x61, 0x1a, + 0x09, 0x47, 0x19, 0x7d, 0x7f, 0x02, 0x81, 0x80, 0x65, 0xce, 0x33, 0x53, + 0xca, 0xfb, 0xe0, 0x29, 0x83, 0x12, 0x93, 0x6c, 0xd9, 0xeb, 0x3b, 0xaa, + 0xc5, 0xc4, 0xd1, 0xb0, 0x01, 0x85, 0xba, 0xc7, 0x6d, 0xdb, 0x3f, 0x86, + 0x06, 0x4c, 0x7e, 0xc4, 0x64, 0x65, 0x14, 0x5d, 0x9c, 0xe9, 0x54, 0x62, + 0x5c, 0xf2, 0x6e, 0xe3, 0x14, 0x80, 0x48, 0x0c, 0xbc, 0xb4, 0xa1, 0xb6, + 0x6d, 0x2f, 0xa3, 0x21, 0xc0, 0xfc, 0x45, 0xa9, 0x2e, 0x3d, 0x34, 0x2d, + 0x05, 0x39, 0x4f, 0x4b, 0xf1, 0x8c, 0xd3, 0x61, 0xbb, 0x80, 0x2d, 0xa3, + 0x50, 0x5c, 0xe0, 0xf4, 0xcd, 0xff, 0x95, 0xdc, 0xa8, 0x23, 0x8f, 0x92, + 0x69, 0xcd, 0x36, 0x8a, 0xba, 0xa5, 0xe3, 0xfe, 0xce, 0x8e, 0x67, 0xc5, + 0x54, 0x41, 0x8c, 0x44, 0xc5, 0x50, 0x55, 0x7a, 0x7c, 0x91, 0xc9, 0x2e, + 0x9e, 0x32, 0x63, 0x37, 0x42, 0x68, 0x29, 0x76, 0x41, 0xdb, 0x77, 0xfd, + 0xcb, 0x6a, 0x73, 0x10, +}; + +} // namespace + +std::vector GetLicensePrivateKey() { + return std::vector(kDevicePrivateKey, + kDevicePrivateKey + sizeof(kDevicePrivateKey)); +} + +std::vector GetLicensePublicKey() { + return std::vector(kDevicePublicKey, + kDevicePublicKey + sizeof(kDevicePublicKey)); +} + +} // namespace widevine diff --git a/whitebox/api/test_license_whitebox_keys.h b/whitebox/api/test_license_whitebox_keys.h new file mode 100644 index 0000000..6cf3c1f --- /dev/null +++ b/whitebox/api/test_license_whitebox_keys.h @@ -0,0 +1,24 @@ +// Copyright 2020 Google LLC. All Rights Reserved. + +#ifndef WHITEBOX_API_TEST_LICENSE_WHITEBOX_KEYS_H_ +#define WHITEBOX_API_TEST_LICENSE_WHITEBOX_KEYS_H_ + +#include +#include + +namespace widevine { + +// Returns the private key that the tests expect/require the white-box to use. +// This key can be considered a constant, so it can be used outside of the tests +// to generate the white-box init data needed to initialize the white-box. The +// key will be a DER encoded PKCS#1 RSAPrivateKey. +std::vector GetLicensePrivateKey(); + +// Returns the public key that matches the private key returned by +// |GetLicensePrivateKey()|. The public key will be a DER encoded PKCS#1 +// RSAPublicKey. +std::vector GetLicensePublicKey(); + +} // namespace widevine + +#endif // WHITEBOX_API_TEST_LICENSE_WHITEBOX_KEYS_H_ diff --git a/whitebox/api/test_public_key.h b/whitebox/api/test_public_key.h deleted file mode 100644 index 072e4be..0000000 --- a/whitebox/api/test_public_key.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2020 Google LLC. All Rights Reserved. - -#ifndef WHITEBOX_API_TEST_PUBLIC_KEY_H_ -#define WHITEBOX_API_TEST_PUBLIC_KEY_H_ - -#include -#include - -namespace widevine { - -// Returns the public key that matches the private key used by the license -// white-box. The public key should be a DER encoded PKCS#1 RSAPublicKey. -std::vector GetLicensePublicKey(); - -} // namespace widevine - -#endif // WHITEBOX_API_TEST_PUBLIC_KEY_H_ diff --git a/whitebox/benchmarking/BUILD b/whitebox/benchmarking/BUILD index 177bdd7..67179b1 100644 --- a/whitebox/benchmarking/BUILD +++ b/whitebox/benchmarking/BUILD @@ -6,7 +6,6 @@ package(default_visibility = [ cc_library( name = "data_source", - testonly = True, srcs = [ "data_source.cc", ], @@ -17,7 +16,6 @@ cc_library( cc_library( name = "measurements", - testonly = True, srcs = [ "measurements.cc", ], diff --git a/whitebox/external/odk.BUILD b/whitebox/external/odk.BUILD index c9ee630..9cb9a06 100644 --- a/whitebox/external/odk.BUILD +++ b/whitebox/external/odk.BUILD @@ -17,24 +17,24 @@ cc_library( "oemcrypto/odk/src/odk.c", "oemcrypto/odk/src/odk_assert.h", "oemcrypto/odk/src/odk_endian.h", + "oemcrypto/odk/src/odk_message.c", + "oemcrypto/odk/src/odk_message_priv.h", "oemcrypto/odk/src/odk_overflow.c", "oemcrypto/odk/src/odk_overflow.h", "oemcrypto/odk/src/odk_serialize.c", "oemcrypto/odk/src/odk_timer.c", "oemcrypto/odk/src/odk_util.c", "oemcrypto/odk/src/odk_util.h", - "oemcrypto/odk/src/odk_message_priv.h", - "oemcrypto/odk/src/odk_message.c", "oemcrypto/odk/src/serialization_base.c", ":odk_common_hdrs", ], hdrs = [ "oemcrypto/odk/include/OEMCryptoCENCCommon.h", "oemcrypto/odk/include/odk.h", + "oemcrypto/odk/include/odk_attributes.h", + "oemcrypto/odk/include/odk_message.h", "oemcrypto/odk/include/odk_structs.h", "oemcrypto/odk/include/odk_target.h", - "oemcrypto/odk/include/odk_message.h", - "oemcrypto/odk/include/odk_attributes.h", ], copts = ["-std=c99"], includes = [ @@ -56,19 +56,19 @@ cc_library( "oemcrypto/odk/include/core_message_serialize_proto.h", "oemcrypto/odk/include/core_message_types.h", ], - deps = [ - ":core", - "@whitebox//odk_deps" - ], includes = [ "oemcrypto/odk/include", ], + deps = [ + ":core", + "@whitebox//odk_deps", + ], ) cc_library( name = "odk", deps = [ - ":core", - ":serialization", + ":core", + ":serialization", ], ) diff --git a/whitebox/impl/reference/BUILD b/whitebox/impl/reference/BUILD deleted file mode 100644 index 1e7eeb3..0000000 --- a/whitebox/impl/reference/BUILD +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright 2020 Google LLC. All Rights Reserved. - -package(default_visibility = [ - "//visibility:private", -]) - -cc_library( - name = "aead_whitebox", - srcs = [ - "aead_whitebox_impl.cc", - ], - visibility = ["//visibility:public"], - deps = [ - ":memory_util", - "//api:aead_whitebox", - "//api:result", - "//chromium_deps/third_party/boringssl", - "//crypto_utils:crypto_util", - ], -) - -cc_library( - name = "odk", - srcs = ["odk.cc"], - hdrs = ["odk.h"], - deps = [ - "//api:result", - "//chromium_deps/base:glog", - "//external:odk", - ], -) - -cc_library( - name = "license_private_key", - hdrs = [ - "license_private_key.h", - ], -) - -cc_library( - name = "license_whitebox", - srcs = [ - "license_whitebox_impl.cc", - ], - deps = [ - ":license_private_key", - ":memory_util", - ":odk", - "//api:license_whitebox", - "//api:result", - "//chromium_deps/cdm/keys:dev_certs", - "//chromium_deps/cdm/protos:license_protocol_proto", - "//crypto_utils:aes_cbc_decryptor", - "//crypto_utils:aes_ctr_encryptor", - "//crypto_utils:crypto_util", - "//crypto_utils:rsa_key", - ], -) - -cc_library( - name = "aead_test_data", - testonly = True, - srcs = [ - "aead_test_data.cc", - ], - deps = [ - "//api:aead_test_data", - ], -) - -cc_library( - name = "license_test_data", - testonly = True, - srcs = [ - "license_test_data.cc", - ], - deps = [ - ":license_private_key", - "//api:test_public_key", - "//crypto_utils:rsa_test_keys", - ], -) - -cc_test( - name = "aead_whitebox_test", - size = "small", - deps = [ - ":aead_test_data", - ":aead_whitebox", - "//api:aead_whitebox_test", - ], -) - -cc_test( - name = "aead_whitebox_benchmark", - size = "small", - deps = [ - ":aead_test_data", - ":aead_whitebox", - "//api:aead_whitebox_benchmark", - ], -) - -cc_test( - name = "license_whitebox_test", - size = "small", - deps = [ - ":license_test_data", - ":license_whitebox", - "//api:license_whitebox_test", - ], -) - -cc_test( - name = "remote_attestation_and_verification_test", - size = "small", - deps = [ - ":license_test_data", - ":license_whitebox", - "//api:remote_attestation_and_verification_test", - ], -) - -cc_test( - name = "license_whitebox_benchmark", - size = "small", - deps = [ - ":license_test_data", - ":license_whitebox", - "//api:license_whitebox_benchmark", - ], -) - -cc_test( - name = "license_whitebox_golden_data", - size = "small", - srcs = [ - "license_whitebox_golden_data_init_data.cc", - ], - deps = [ - ":license_whitebox", - "//api:license_whitebox_golden_data", - ], -) - -cc_library( - name = "memory_util", - srcs = ["memory_util.cc"], - hdrs = ["memory_util.h"], -) diff --git a/whitebox/impl/reference/license_private_key.h b/whitebox/impl/reference/license_private_key.h deleted file mode 100644 index 9586ce4..0000000 --- a/whitebox/impl/reference/license_private_key.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2020 Google LLC. All Rights Reserved. - -#ifndef WHITEBOX_IMPL_REFERENCE_LICENSE_PRIVATE_KEY_H_ -#define WHITEBOX_IMPL_REFERENCE_LICENSE_PRIVATE_KEY_H_ - -#include -#include - -namespace widevine { - -// Return a RSA 2048-bit private key. -std::vector GetLicensePrivateKey(); - -} // namespace widevine - -#endif // WHITEBOX_IMPL_REFERENCE_LICENSE_PRIVATE_KEY_H_ diff --git a/whitebox/impl/reference/license_test_data.cc b/whitebox/impl/reference/license_test_data.cc deleted file mode 100644 index 360b8d4..0000000 --- a/whitebox/impl/reference/license_test_data.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2020 Google LLC. All Rights Reserved. - -#include - -#include "api/test_public_key.h" -#include "crypto_utils/rsa_test_keys.h" -#include "impl/reference/license_private_key.h" - -namespace widevine { - -std::vector GetLicensePrivateKey() { - widevine::RsaTestKeys key_generator; - std::string init_data = key_generator.private_test_key_2_2048_bits(); - return std::vector(init_data.begin(), init_data.end()); -} - -std::vector GetLicensePublicKey() { - widevine::RsaTestKeys key_generator; - std::string init_data = key_generator.public_test_key_2_2048_bits(); - return std::vector(init_data.begin(), init_data.end()); -} - -} // namespace widevine diff --git a/whitebox/impl/reference/license_whitebox_golden_data_init_data.cc b/whitebox/impl/reference/license_whitebox_golden_data_init_data.cc deleted file mode 100644 index 074fc2f..0000000 --- a/whitebox/impl/reference/license_whitebox_golden_data_init_data.cc +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2020 Google LLC. All Rights Reserved. - -#include -#include - -#include "impl/reference/license_private_key.h" - -namespace widevine { - -std::vector GetLicensePrivateKey() { - return { - 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, - 0xae, 0xa6, 0xa8, 0xea, 0xdd, 0xac, 0x6f, 0xb4, 0x41, 0x47, 0xcb, 0x18, - 0x81, 0xeb, 0xdf, 0x4f, 0x17, 0xf7, 0x17, 0xc0, 0xab, 0x6f, 0x47, 0x50, - 0xbf, 0xe4, 0x8c, 0xc9, 0x45, 0x24, 0x5e, 0x1c, 0x2e, 0x86, 0x9f, 0xcb, - 0x47, 0x05, 0xf9, 0xfe, 0x91, 0x90, 0xaf, 0xbd, 0x22, 0x14, 0x47, 0xa7, - 0x34, 0x39, 0x79, 0x44, 0xe9, 0x92, 0x83, 0x4a, 0x80, 0xa8, 0x2a, 0xe6, - 0x9f, 0x2b, 0xb7, 0xda, 0x2a, 0xd2, 0xae, 0x57, 0x5b, 0xfa, 0xb6, 0xdf, - 0xca, 0x3e, 0xb1, 0xb8, 0x42, 0x0b, 0xde, 0x46, 0x36, 0xdb, 0x42, 0x33, - 0x8b, 0xda, 0x5c, 0x60, 0x44, 0x7c, 0x99, 0xb4, 0x98, 0xb4, 0x1e, 0xd8, - 0x25, 0x6d, 0x67, 0x84, 0xc9, 0x67, 0xde, 0x05, 0xe6, 0x06, 0xb5, 0xb5, - 0xca, 0xbc, 0xfa, 0xb0, 0xa7, 0x46, 0x29, 0x3f, 0x63, 0x47, 0x9d, 0x70, - 0x8d, 0xa2, 0x8d, 0x22, 0xc6, 0xeb, 0x06, 0xd4, 0x5c, 0x3b, 0x62, 0x98, - 0xc7, 0xda, 0x16, 0x8f, 0x17, 0x59, 0xd5, 0xcb, 0xd1, 0x5d, 0xe3, 0xe1, - 0x07, 0xe6, 0x97, 0x87, 0xf4, 0x22, 0x53, 0xfa, 0xf9, 0xa9, 0xf5, 0xeb, - 0xd7, 0x55, 0xdf, 0x32, 0x2d, 0x4e, 0x07, 0x86, 0x25, 0x44, 0x93, 0xd6, - 0xf7, 0xc6, 0xf9, 0x78, 0x91, 0x24, 0x1e, 0xd4, 0x6b, 0xe3, 0x4a, 0xff, - 0x4a, 0x3a, 0xb9, 0x89, 0x90, 0x61, 0x87, 0xb9, 0x41, 0x45, 0x02, 0xfd, - 0xd0, 0xc5, 0x5a, 0x98, 0x41, 0x88, 0xa4, 0xe3, 0xe2, 0xa2, 0x9d, 0x9a, - 0x90, 0x3f, 0x44, 0x8e, 0x3a, 0xe1, 0xd1, 0xe9, 0x79, 0x9a, 0xc6, 0xe2, - 0x7c, 0x8e, 0x9c, 0x3d, 0xb2, 0xe0, 0x26, 0x5a, 0x46, 0x13, 0xc8, 0x43, - 0x9f, 0xf7, 0x51, 0x7e, 0xbb, 0x55, 0x6d, 0xcc, 0x97, 0x38, 0xdb, 0xa4, - 0x4b, 0x96, 0x40, 0xe5, 0x2d, 0x8f, 0x43, 0xe3, 0x21, 0x15, 0xda, 0x34, - 0x97, 0x7a, 0x9e, 0xbb, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, - 0x00, 0x56, 0x73, 0xe7, 0x1f, 0xc3, 0xb5, 0x4c, 0xe2, 0x24, 0x82, 0x5e, - 0x55, 0x76, 0x52, 0x85, 0x0a, 0xc8, 0xe9, 0x26, 0x57, 0xd8, 0x44, 0xd0, - 0x3f, 0x77, 0x8d, 0xb1, 0xe7, 0x1b, 0x93, 0xc2, 0x06, 0x1f, 0x3d, 0xc2, - 0xb1, 0xc4, 0x29, 0x80, 0x33, 0x74, 0x68, 0xf3, 0xa5, 0x22, 0xce, 0x79, - 0x1d, 0x9a, 0x6b, 0x6c, 0xcd, 0x20, 0xf5, 0xc6, 0x89, 0xc5, 0x9f, 0xf9, - 0x04, 0x89, 0xfc, 0x01, 0x19, 0x3c, 0xa3, 0x67, 0x6b, 0x94, 0xfb, 0x49, - 0x35, 0x04, 0x0e, 0xfe, 0xb8, 0x1f, 0xf1, 0x72, 0x08, 0xbd, 0xb4, 0xd1, - 0x53, 0x64, 0xc2, 0x25, 0x81, 0xfd, 0xc4, 0xd3, 0xed, 0x22, 0xbd, 0xde, - 0x9a, 0xce, 0x04, 0x16, 0xff, 0x13, 0x17, 0x98, 0x3e, 0xc1, 0x3b, 0xc7, - 0x0d, 0x03, 0x1b, 0x82, 0xd8, 0x99, 0x24, 0xd0, 0xdc, 0x30, 0xcf, 0xcd, - 0x6e, 0x5e, 0x9d, 0xfd, 0x51, 0x1e, 0xb8, 0x4e, 0x7b, 0x54, 0x83, 0x9b, - 0x4f, 0xf8, 0xa6, 0x03, 0xc1, 0x96, 0xf1, 0x6d, 0xc0, 0xa7, 0x17, 0xbd, - 0xf1, 0x60, 0xcb, 0xe2, 0x05, 0xa5, 0x9b, 0x05, 0x2e, 0xaf, 0xdc, 0xa7, - 0x88, 0xde, 0x53, 0x42, 0xa9, 0xf4, 0x0f, 0xae, 0xf9, 0x96, 0xe9, 0x2c, - 0xa6, 0xe8, 0x9d, 0x2c, 0x6b, 0xbc, 0xd8, 0x0f, 0x09, 0x5f, 0x64, 0xb2, - 0x21, 0x6f, 0xc0, 0x79, 0x3d, 0x6e, 0xad, 0x93, 0x79, 0x35, 0x87, 0x9a, - 0x41, 0xcc, 0x06, 0x24, 0xf0, 0x62, 0x09, 0xfe, 0x46, 0x9a, 0x38, 0xee, - 0xc0, 0xc8, 0x08, 0xce, 0x65, 0xda, 0xe4, 0x89, 0x1a, 0xfb, 0xe9, 0x53, - 0x0c, 0xd1, 0x80, 0x40, 0xfd, 0xc4, 0x97, 0xf8, 0x19, 0x4e, 0x03, 0x90, - 0x4a, 0xda, 0xfd, 0x13, 0x27, 0x89, 0xde, 0x12, 0x8d, 0x52, 0x5a, 0x07, - 0xf1, 0x9a, 0xa4, 0x54, 0x98, 0x86, 0xb2, 0x78, 0x76, 0xbf, 0x3a, 0xa9, - 0x8b, 0xed, 0xc7, 0x8b, 0x31, 0x02, 0x81, 0x81, 0x00, 0xe2, 0xf3, 0xab, - 0x53, 0x7b, 0xee, 0x36, 0xdb, 0xca, 0xa8, 0x74, 0x03, 0xdd, 0xe2, 0xce, - 0x87, 0xe2, 0x8c, 0x55, 0x8e, 0xd4, 0x0f, 0x32, 0xec, 0xd2, 0xf9, 0x8b, - 0x1f, 0x93, 0xdb, 0x84, 0xd2, 0x42, 0xe1, 0xc7, 0x21, 0x24, 0x2e, 0x36, - 0x0c, 0x02, 0x5d, 0x49, 0xea, 0xe0, 0x42, 0xd7, 0x7a, 0x3e, 0xc8, 0x51, - 0x92, 0x39, 0x56, 0x10, 0xd7, 0x90, 0x67, 0xa3, 0x34, 0xd6, 0xc2, 0x4a, - 0x33, 0x74, 0xfd, 0xe2, 0x7e, 0xe1, 0x3e, 0x59, 0xd7, 0x36, 0x6d, 0x7d, - 0xd4, 0xd8, 0x82, 0xfb, 0x2f, 0x1e, 0x5e, 0x32, 0xcd, 0xc3, 0x0a, 0x7f, - 0xbd, 0xb0, 0xb3, 0xf9, 0x77, 0x75, 0xb9, 0x0c, 0x63, 0x54, 0xff, 0x25, - 0xa3, 0xaf, 0x4a, 0x70, 0x61, 0x32, 0x91, 0xde, 0xfb, 0x95, 0x25, 0xb4, - 0x06, 0x98, 0x9d, 0xeb, 0x49, 0xc8, 0xe0, 0xc0, 0x7e, 0x45, 0xfb, 0xe5, - 0xf8, 0x72, 0x5b, 0x6b, 0x19, 0x02, 0x81, 0x81, 0x00, 0xc5, 0x01, 0x4b, - 0xb7, 0x5f, 0x6d, 0xbc, 0xa6, 0x8c, 0xb8, 0xeb, 0xa5, 0xff, 0x0b, 0xd7, - 0x15, 0xd7, 0xef, 0xf6, 0xc9, 0xfe, 0x69, 0xcc, 0xe5, 0xbd, 0x5c, 0xa8, - 0x05, 0xa0, 0x4d, 0x3b, 0x1f, 0xa6, 0xcc, 0x37, 0x7b, 0xb1, 0x46, 0xf2, - 0xc7, 0x67, 0xcd, 0xc1, 0x20, 0xc4, 0x14, 0xbd, 0x0e, 0x01, 0xa7, 0xd6, - 0x3c, 0xe8, 0x18, 0x9d, 0x71, 0x71, 0x37, 0x2a, 0xc0, 0x45, 0x6a, 0x54, - 0xe8, 0x63, 0xf0, 0x6e, 0xd2, 0x9f, 0x95, 0x3b, 0xde, 0xb3, 0xc5, 0x60, - 0x57, 0x3d, 0xed, 0xef, 0x57, 0xcb, 0x3d, 0x35, 0x3a, 0x2e, 0x5d, 0xb8, - 0x0e, 0xf8, 0xff, 0xd2, 0xca, 0xdd, 0xce, 0x0b, 0x10, 0x53, 0xb4, 0xdb, - 0x53, 0xf6, 0x02, 0xa5, 0xf1, 0x23, 0x4d, 0x21, 0x6e, 0xc7, 0x52, 0x5a, - 0x7a, 0x5d, 0x88, 0x32, 0xa8, 0x65, 0x50, 0x21, 0xf5, 0x81, 0x3f, 0x96, - 0xd4, 0x57, 0x48, 0x66, 0xf3, 0x02, 0x81, 0x81, 0x00, 0xdd, 0x83, 0xd6, - 0x62, 0x9a, 0xe1, 0x0c, 0xfc, 0x84, 0x96, 0xdc, 0xfd, 0xf5, 0x31, 0xee, - 0x42, 0x25, 0x76, 0xb1, 0xff, 0xc1, 0xad, 0xc0, 0x17, 0xf5, 0x68, 0x8a, - 0x49, 0x5d, 0x08, 0xf3, 0x60, 0x42, 0xd5, 0x9a, 0x86, 0x17, 0x89, 0x5f, - 0x49, 0x63, 0x79, 0x68, 0xaf, 0x6f, 0x0a, 0xee, 0xc4, 0xab, 0xc8, 0xdc, - 0x0d, 0x6c, 0x17, 0x3c, 0x43, 0x1a, 0xf8, 0x7d, 0x0d, 0x12, 0xdc, 0xfa, - 0x8d, 0xb5, 0x10, 0x25, 0x65, 0x90, 0x36, 0x4a, 0x7c, 0x4b, 0xec, 0x9c, - 0xd8, 0x06, 0x27, 0xfa, 0x41, 0xa8, 0x53, 0x6b, 0x24, 0xf8, 0xcd, 0x23, - 0x97, 0xa3, 0x84, 0x56, 0xe7, 0x29, 0xa9, 0x5f, 0x95, 0x08, 0x9e, 0x2d, - 0x3f, 0xd1, 0xd5, 0x47, 0x51, 0x27, 0x89, 0xc7, 0x6a, 0x29, 0xce, 0x6e, - 0x23, 0xce, 0x0c, 0xbd, 0x5d, 0xfc, 0x4a, 0x9a, 0xb7, 0xe5, 0x59, 0x13, - 0xc2, 0xe6, 0xe3, 0xa1, 0xe9, 0x02, 0x81, 0x81, 0x00, 0xc3, 0x6f, 0x98, - 0xa4, 0xae, 0x97, 0xd7, 0xb9, 0xc6, 0x0a, 0xc1, 0x43, 0xa8, 0xf4, 0x1f, - 0x08, 0xfd, 0x72, 0x81, 0xfa, 0x3b, 0x58, 0xcc, 0x3a, 0xf1, 0x93, 0x54, - 0xe0, 0x57, 0xf9, 0xa5, 0xf8, 0xad, 0x69, 0x14, 0x75, 0xb2, 0x15, 0x77, - 0x4d, 0xd8, 0xad, 0xa6, 0xb5, 0x11, 0xb0, 0x9d, 0x28, 0xa2, 0xfd, 0xd4, - 0xac, 0x11, 0x78, 0x31, 0xe0, 0xd3, 0x76, 0xee, 0x03, 0x56, 0x19, 0xb9, - 0x67, 0xdd, 0x95, 0x2c, 0xeb, 0xe8, 0x02, 0x8d, 0x25, 0x4e, 0x64, 0x35, - 0x41, 0xf7, 0x1e, 0xee, 0xfc, 0xc2, 0x93, 0xd3, 0x15, 0x07, 0xe0, 0x53, - 0x73, 0x0f, 0x14, 0x03, 0x12, 0xdb, 0xdd, 0xc6, 0xde, 0x08, 0x9c, 0x77, - 0xa5, 0x20, 0x7d, 0xda, 0x0f, 0x91, 0x7c, 0xb7, 0xf9, 0x04, 0xe5, 0xae, - 0xfa, 0x8b, 0x85, 0x4c, 0xf3, 0xff, 0xa5, 0xf2, 0x3a, 0x72, 0x61, 0x1a, - 0x09, 0x47, 0x19, 0x7d, 0x7f, 0x02, 0x81, 0x80, 0x65, 0xce, 0x33, 0x53, - 0xca, 0xfb, 0xe0, 0x29, 0x83, 0x12, 0x93, 0x6c, 0xd9, 0xeb, 0x3b, 0xaa, - 0xc5, 0xc4, 0xd1, 0xb0, 0x01, 0x85, 0xba, 0xc7, 0x6d, 0xdb, 0x3f, 0x86, - 0x06, 0x4c, 0x7e, 0xc4, 0x64, 0x65, 0x14, 0x5d, 0x9c, 0xe9, 0x54, 0x62, - 0x5c, 0xf2, 0x6e, 0xe3, 0x14, 0x80, 0x48, 0x0c, 0xbc, 0xb4, 0xa1, 0xb6, - 0x6d, 0x2f, 0xa3, 0x21, 0xc0, 0xfc, 0x45, 0xa9, 0x2e, 0x3d, 0x34, 0x2d, - 0x05, 0x39, 0x4f, 0x4b, 0xf1, 0x8c, 0xd3, 0x61, 0xbb, 0x80, 0x2d, 0xa3, - 0x50, 0x5c, 0xe0, 0xf4, 0xcd, 0xff, 0x95, 0xdc, 0xa8, 0x23, 0x8f, 0x92, - 0x69, 0xcd, 0x36, 0x8a, 0xba, 0xa5, 0xe3, 0xfe, 0xce, 0x8e, 0x67, 0xc5, - 0x54, 0x41, 0x8c, 0x44, 0xc5, 0x50, 0x55, 0x7a, 0x7c, 0x91, 0xc9, 0x2e, - 0x9e, 0x32, 0x63, 0x37, 0x42, 0x68, 0x29, 0x76, 0x41, 0xdb, 0x77, 0xfd, - 0xcb, 0x6a, 0x73, 0x10, - }; -} - -} // namespace widevine diff --git a/whitebox/reference/impl/BUILD b/whitebox/reference/impl/BUILD new file mode 100644 index 0000000..a0ec96b --- /dev/null +++ b/whitebox/reference/impl/BUILD @@ -0,0 +1,145 @@ +# Copyright 2020 Google LLC. All Rights Reserved. + +package(default_visibility = [ + "//visibility:private", +]) + +# ============================================================================== +# Structure +# ============================================================================== +# +# This BUILD file must expose the following build targets so that the test/BUILD +# file can link against it: +# +# test_aead_whitebox : The target for testing the AEAD white-box. +# +# test_license_whitebox : The target for testing the license white-box. The +# white-box should use the private key provided in +# "//api/test_license_whitebox_keys.cc". + +# ============================================================================== +# Internal Targets +# ============================================================================== + +cc_library( + name = "odk", + srcs = ["odk.cc"], + hdrs = ["odk.h"], + deps = [ + "//api:result", + "//chromium_deps/base:glog", + "//external:odk", + ], +) + +cc_library( + name = "memory_util", + srcs = ["memory_util.cc"], + hdrs = ["memory_util.h"], +) + +cc_library( + name = "content_key", + hdrs = ["content_key.h"], + deps = [ + "//api:license_whitebox", + "//chromium_deps/cdm/protos:license_protocol_proto", + ] +) + +cc_library( + name = "renewal_key", + hdrs = ["renewal_key.h"], + deps = [ + "//api:license_whitebox", + ], +) + +cc_library( + name = "license_parser", + srcs = ["license_parser.cc"], + hdrs = ["license_parser.h"], + deps = [ + ":content_key", + ":renewal_key", + ":odk", + "//api:result", + "//chromium_deps/cdm/protos:license_protocol_proto", + "//crypto_utils:aes_cbc_decryptor", + "//crypto_utils:crypto_util", + ], +) + +cc_library( + name = "odk_license_parser", + srcs = ["odk_license_parser.cc"], + hdrs = ["odk_license_parser.h"], + deps = [ + ":license_parser", + "//chromium_deps/base:glog", + "//crypto_utils:crypto_util", + ], +) + +cc_library( + name = "protobuf_license_parser", + srcs = ["protobuf_license_parser.cc"], + hdrs = ["protobuf_license_parser.h"], + deps = [ + ":license_parser", + "//chromium_deps/base:glog", + "//chromium_deps/cdm/protos:license_protocol_proto", + "//crypto_utils:crypto_util", + ], +) + +# ============================================================================== +# AEAD White-box Targets +# ============================================================================== + +cc_library( + name = "test_aead_whitebox", + srcs = [ + "aead_test_data.cc", + "aead_whitebox_impl.cc", + ], + visibility = ["//visibility:public"], + deps = [ + ":memory_util", + "//api:aead_test_data", + "//api:aead_whitebox", + "//api:result", + "//chromium_deps/third_party/boringssl", + "//crypto_utils:crypto_util", + ], +) + +# ============================================================================== +# License White-box Targets +# ============================================================================== + +# This target is shared between both license white-box targets. The only this +# target lacks is the actual key data. +cc_library( + name = "test_license_whitebox", + srcs = [ + "license_whitebox_impl.cc", + ], + visibility = ["//visibility:public"], + deps = [ + ":license_parser", + ":memory_util", + ":odk", + ":odk_license_parser", + ":protobuf_license_parser", + "//api:license_whitebox", + "//api:result", + "//api:test_license_whitebox_keys", + "//chromium_deps/cdm/keys:dev_certs", + "//chromium_deps/cdm/protos:license_protocol_proto", + "//crypto_utils:aes_cbc_decryptor", + "//crypto_utils:aes_ctr_encryptor", + "//crypto_utils:crypto_util", + "//crypto_utils:rsa_key", + ], +) diff --git a/whitebox/impl/reference/aead_test_data.cc b/whitebox/reference/impl/aead_test_data.cc similarity index 100% rename from whitebox/impl/reference/aead_test_data.cc rename to whitebox/reference/impl/aead_test_data.cc diff --git a/whitebox/impl/reference/aead_whitebox_impl.cc b/whitebox/reference/impl/aead_whitebox_impl.cc similarity index 99% rename from whitebox/impl/reference/aead_whitebox_impl.cc rename to whitebox/reference/impl/aead_whitebox_impl.cc index 0bf1fab..390eb7c 100644 --- a/whitebox/impl/reference/aead_whitebox_impl.cc +++ b/whitebox/reference/impl/aead_whitebox_impl.cc @@ -8,7 +8,7 @@ #include "base/check_op.h" #include "base/logging.h" #include "crypto_utils/crypto_util.h" -#include "impl/reference/memory_util.h" +#include "reference/impl/memory_util.h" #include "third_party/boringssl/src/include/openssl/aead.h" #include "third_party/boringssl/src/include/openssl/rand.h" diff --git a/whitebox/reference/impl/content_key.h b/whitebox/reference/impl/content_key.h new file mode 100644 index 0000000..2ef7103 --- /dev/null +++ b/whitebox/reference/impl/content_key.h @@ -0,0 +1,43 @@ +// Copyright 2021 Google LLC. All Rights Reserved. + +#ifndef WHITEBOX_REFERENCE_IMPL_CONTENT_KEY_H_ +#define WHITEBOX_REFERENCE_IMPL_CONTENT_KEY_H_ + +#include +#include +#include + +#include "api/license_whitebox.h" +#include "cdm/protos/license_protocol.pb.h" + +namespace widevine { + +struct ContentKey { + // This is the status will be returned in |WB_License_QueryKeyStatus()|. + WB_KeyStatus status = WB_KEY_STATUS_INVALID; + + // These are the permission flags that will be used internally to check if + // we can use a key. + // + // | Valid | Masked | Decrypt + // | | Decrypt | + // -----------------------------------------+-------+---------+-------- + // WB_KEY_STATUS_INVALID | false | false | false + // WB_KEY_STATUS_CONTENT_KEY_VALID | true | false | false + // WB_KEY_STATUS_CONTENT_KEY_MASKED_DECRYPT | true | true | false + // WB_KEY_STATUS_CONTENT_KEY_DECRYPT | true | true | true + bool is_valid = false; + bool can_decrypt = false; + bool can_masked_decrypt = false; + + std::array key; +}; + +ContentKey CreateContentKey( + video_widevine::License_KeyContainer_SecurityLevel level, + bool is_hw_verified, + const std::string& key); + +} // namespace widevine + +#endif // WHITEBOX_REFERENCE_IMPL_CONTENT_KEY_H_ \ No newline at end of file diff --git a/whitebox/reference/impl/license_parser.cc b/whitebox/reference/impl/license_parser.cc new file mode 100644 index 0000000..ae28d29 --- /dev/null +++ b/whitebox/reference/impl/license_parser.cc @@ -0,0 +1,81 @@ +// Copyright 2021 Google LLC. All Rights Reserved. + +#include "reference/impl/license_parser.h" + +#include "base/check.h" +#include "base/check_op.h" +#include "crypto_utils/aes_cbc_decryptor.h" + +namespace widevine { + +bool LicenseParser::Decrypt(const std::string& key, + const std::string& iv, + const std::string& encrypted, + std::string* decrypted) { + CHECK_EQ(key.size(), 16u); + CHECK_EQ(iv.size(), 16u); + CHECK(decrypted); + CHECK_GE(decrypted->size(), encrypted.size()); + + decrypted->resize(encrypted.size()); + + AesCbcDecryptor decryptor; + CHECK(decryptor.SetKey(reinterpret_cast(key.data()), + key.size())); + + return decryptor.Decrypt( + reinterpret_cast(iv.data()), iv.size(), + reinterpret_cast(encrypted.data()), encrypted.size(), + reinterpret_cast(&decrypted->front())); +} + +ContentKey LicenseParser::CreateContentKey( + video_widevine::License_KeyContainer_SecurityLevel level, + bool is_hw_verified, + const std::string& key) { + ContentKey content_key; + + switch (level) { + case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO: + content_key.status = WB_KEY_STATUS_CONTENT_KEY_DECRYPT; + content_key.is_valid = true; + content_key.can_masked_decrypt = true; + content_key.can_decrypt = true; + break; + + case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE: + content_key.status = WB_KEY_STATUS_CONTENT_KEY_MASKED_DECRYPT; + content_key.is_valid = true; + content_key.can_masked_decrypt = true; + break; + + default: + // For example, this could be a hardware key - a valid key but can't be + // used by the CDM. However, this may get override later if the device is + // hardware verified. + content_key.status = WB_KEY_STATUS_CONTENT_KEY_VALID; + content_key.is_valid = true; + break; + } + + // If the device is hardware verified, then we can override the status to + // allow masked decrypt and decrypt. + if (is_hw_verified) { + content_key.status = WB_KEY_STATUS_CONTENT_KEY_DECRYPT; + content_key.is_valid = true; + content_key.can_masked_decrypt = true; + content_key.can_decrypt = true; + } + + // 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 + // errors correctly. + if (content_key.can_decrypt || content_key.can_masked_decrypt) { + CHECK_EQ(key.size(), content_key.key.size()); + std::copy(key.begin(), key.end(), content_key.key.begin()); + } + + return content_key; +} + +} // namespace widevine \ No newline at end of file diff --git a/whitebox/reference/impl/license_parser.h b/whitebox/reference/impl/license_parser.h new file mode 100644 index 0000000..e8e716c --- /dev/null +++ b/whitebox/reference/impl/license_parser.h @@ -0,0 +1,53 @@ +// Copyright 2021 Google LLC. All Rights Reserved. + +#ifndef WHITEBOX_REFERENCE_IMPL_LICENSE_PARSER_H_ +#define WHITEBOX_REFERENCE_IMPL_LICENSE_PARSER_H_ + +#include +#include +#include "cdm/protos/license_protocol.pb.h" +#include "reference/impl/content_key.h" +#include "reference/impl/odk.h" +#include "reference/impl/renewal_key.h" + +namespace widevine { + +class LicenseParser { + public: + LicenseParser() = default; + virtual ~LicenseParser() = default; + + // Disable copy (and move) semantics. + LicenseParser(const LicenseParser&) = delete; + LicenseParser& operator=(const LicenseParser&) = delete; + + virtual WB_Result Parse(const std::string& decryption_key, + const ODKContext& odk_context, + const std::string& message) = 0; + + // If there is no renewal key, then `nullptr` should be returned. + virtual const widevine::RenewalKey* GetRenewalKey() const = 0; + + virtual const std::map& GetContentKeys() const = 0; + + protected: + static bool Decrypt(const std::string& key, + const std::string& iv, + const std::string& encrypted, + std::string* decrypted); + + // Creates and returns a ContentKey based on the values provided. + // |level| determines whether decrypt or masked_decrypt is allowed. + // |is_hw_verified|, if set, overrides |level| so that both decrypt and + // masked_decrypt is allowed. |Key| is the decryption key, and is only + // returned in ContentKey if decrypt or masked_decrypt is allowed. + // Otherwise |key| is dropped. + static ContentKey CreateContentKey( + video_widevine::License_KeyContainer_SecurityLevel level, + bool is_hw_verified, + const std::string& key); +}; + +} // namespace widevine + +#endif // WHITEBOX_REFERENCE_IMPL_LICENSE_PARSER_H_ diff --git a/whitebox/impl/reference/license_whitebox_impl.cc b/whitebox/reference/impl/license_whitebox_impl.cc similarity index 61% rename from whitebox/impl/reference/license_whitebox_impl.cc rename to whitebox/reference/impl/license_whitebox_impl.cc index 05d76b1..0a1ad6f 100644 --- a/whitebox/impl/reference/license_whitebox_impl.cc +++ b/whitebox/reference/impl/license_whitebox_impl.cc @@ -2,12 +2,14 @@ #include "api/license_whitebox.h" -#include +#include +#include #include #include #include #include +#include "api/test_license_whitebox_keys.h" #include "base/check.h" #include "base/check_op.h" #include "base/logging.h" @@ -16,14 +18,18 @@ #include "crypto_utils/aes_ctr_encryptor.h" #include "crypto_utils/crypto_util.h" #include "crypto_utils/rsa_key.h" -#include "impl/reference/license_private_key.h" -#include "impl/reference/memory_util.h" -#include "impl/reference/odk.h" #include "oemcrypto/odk/include/odk.h" #include "oemcrypto/odk/include/odk_message.h" #include "oemcrypto/odk/include/odk_structs.h" #include "oemcrypto/odk/src/odk_serialize.h" #include "oemcrypto/odk/src/serialization_base.h" +#include "reference/impl/content_key.h" +#include "reference/impl/license_parser.h" +#include "reference/impl/memory_util.h" +#include "reference/impl/odk.h" +#include "reference/impl/odk_license_parser.h" +#include "reference/impl/protobuf_license_parser.h" +#include "reference/impl/renewal_key.h" #include "third_party/boringssl/src/include/openssl/aes.h" #include "third_party/boringssl/src/include/openssl/cmac.h" #include "third_party/boringssl/src/include/openssl/err.h" @@ -38,33 +44,6 @@ using AesCtrDecryptor = widevine::AesCtrEncryptor; using KeyContainer = video_widevine::License_KeyContainer; using RsaPrivateKey = widevine::RsaPrivateKey; -struct ContentKey { - // When we store a key, we create our own little policy for the key saying - // what functions may use it. This allows us to "blacklist" a key by setting - // all "allow_*" to false. - bool allow_decrypt; - bool allow_masked_decrypt; - - // Key used to decrypt content. - std::vector key; -}; - -// Helper function to decrypt |encrypted| into |decrypted| using |decryptor| -// and |iv|. Done as the protobuf and ODK code use std::string, AesCbcDecryptor -// requires uint8_t* + size_t parameters. -bool Decrypt(AesCbcDecryptor& decryptor, - const std::string& iv, - const std::string& encrypted, - std::string* decrypted) { - DCHECK_EQ(iv.size(), 16u); - DCHECK_GE(decrypted->size(), encrypted.size()); - - return decryptor.Decrypt( - reinterpret_cast(iv.data()), iv.size(), - reinterpret_cast(encrypted.data()), encrypted.size(), - reinterpret_cast(&decrypted->front())); -} - bool IsOdkVersionSupported(uint16_t major_version, uint16_t minor_version) { // Only ODK v16.5 and later support the fields needed. constexpr uint16_t first_major_version_supported = 16; @@ -75,99 +54,6 @@ bool IsOdkVersionSupported(uint16_t major_version, uint16_t minor_version) { (minor_version >= first_minor_version_supported)); } -// Helper function to extract the substring |item| from the provided |buffer|. -std::string ExtractItem(const OEMCrypto_Substring& item, - const std::string& buffer) { - return buffer.substr(item.offset, item.length); -} - -bool ExtractLevel( - const std::string& key_control_block, - video_widevine::License_KeyContainer_SecurityLevel* security_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. - // - // Bytes 0..3: Verification. - // Constant bytes “kctl”, “kc09”, “kc10”, “kc11”, ... “kc15”. - // Bytes 4..7: Obsolete. - // Bytes 8..11: Nonce. - // Bytes 12..15: Control Bits - // Bits 27..26: Security_Level (Only for L3 white-box implementations) - // 0 = SW_SECURE_CRYPTO - // 1 = SW_SECURE_DECODE - // 2 = HW_SECURE_CRYPTO - // 3 = HW_SECURE_DECODE or HW_SECURE_ALL - - // Make sure this is a valid key control block. Ideally the signature - // verification should have taken care of this. - if ((key_control_block.size() != 16u) || (key_control_block[0] != 'k') || - (key_control_block[1] != 'c')) { - return false; - } - - // Extract bits 26..27 from Control Bits. - switch ((key_control_block[12] & 0x0C) >> 2) { - case 0: - *security_level = - video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO; - break; - case 1: - *security_level = - video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE; - break; - case 2: - *security_level = - video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO; - break; - default: - *security_level = - video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_DECODE; - break; - } - - return true; -} - -// Creates and returns a ContentKey based on the values provided. -// |level| determines whether decrypt or masked_decrypt is allowed. -// |is_hw_verified|, if set, overrides |level| so that both decrypt and -// masked_decrypt is allowed. |Key| is the decryption key, and is only -// returned in ContentKey if decrypt or masked_decrypt is allowed. -// Otherwise |key| is dropped. -ContentKey CreateContentKey( - video_widevine::License_KeyContainer_SecurityLevel level, - bool is_hw_verified, - const std::string& key) { - ContentKey content_key; - - switch (level) { - case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO: - content_key.allow_decrypt = true; - content_key.allow_masked_decrypt = true; - break; - case video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE: - content_key.allow_decrypt = false; - content_key.allow_masked_decrypt = true; - break; - default: - content_key.allow_decrypt = false; - content_key.allow_masked_decrypt = false; - break; - } - - content_key.allow_decrypt |= is_hw_verified; - content_key.allow_masked_decrypt |= is_hw_verified; - - // 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 errors correctly. - if (content_key.allow_decrypt || content_key.allow_masked_decrypt) { - content_key.key.assign(key.begin(), key.end()); - } - - return content_key; -} - // This function uses 16 non-linear bijections that are applied to a byte. // This is "Example Masking Function 1" from the shared document // https://docs.google.com/document/d/1xWPwlFHyjT8YzWhY3TyaC02SQvclC_dkEpliGPOUk_o#heading=h.j64j2z3b9v99 @@ -197,16 +83,19 @@ uint8_t InverseMaskingFunction1(uint8_t input) { } // namespace -// The white-box type can't be in the namespace as it is defined in the header. +// The white-box type can't be in the namespace as it is declared in the header. struct WB_License_Whitebox { + // A basic flag to track whether or not we have loaded a license. We do this + // to avoid relying on signing keys and content keys to know if we loaded a + // license. + bool initialized = false; + // CDM key, used for license requests. std::unique_ptr key; - // Keys used for license renewal. - std::string server_signing_key; - std::string client_signing_key; + std::unique_ptr renewal_key; - std::map content_keys; + std::map content_keys; }; namespace { @@ -237,9 +126,9 @@ std::vector GetSecretStringFor(WB_CipherMode mode) { kCTRSecretStringPattern + sizeof(kCTRSecretStringPattern)); } -const ContentKey* FindKey(const WB_License_Whitebox* whitebox, - const uint8_t* id, - size_t id_size) { +const widevine::ContentKey* FindKey(const WB_License_Whitebox* whitebox, + const uint8_t* id, + size_t id_size) { DCHECK(whitebox); DCHECK(id); DCHECK_GT(id_size, 0u); @@ -249,7 +138,8 @@ const ContentKey* FindKey(const WB_License_Whitebox* whitebox, } WB_Result DecryptBuffer(WB_CipherMode mode, - const std::vector& key, + const uint8_t* key, + const size_t key_size, const uint8_t* input_data, size_t input_data_size, const uint8_t* iv, @@ -287,18 +177,24 @@ WB_Result DecryptBuffer(WB_CipherMode mode, return WB_RESULT_BUFFER_TOO_SMALL; } + // If we passed a key to this function, it should be the correct size. If it + // wasn't, we should not have saved the content key. + CHECK(key) << "Missing key data"; + CHECK_EQ(key_size, 16u) << "Incorrect key size. Should be 16, but was " + << key_size; + // By this point, we have verified everything that need to be verified. // Decryption should just work. if (mode == WB_CIPHER_MODE_CBC) { AesCbcDecryptor decryptor; - CHECK(decryptor.SetKey(key.data(), key.size())); + CHECK(decryptor.SetKey(key, key_size)); *output_data_size = input_data_size; CHECK(decryptor.Decrypt(iv, iv_size, input_data, input_data_size, output_data)); } else if (mode == WB_CIPHER_MODE_CTR) { AesCtrDecryptor decryptor; - CHECK(decryptor.SetKey(key.data(), key.size())); + CHECK(decryptor.SetKey(key, key_size)); // Encrypt and Decrypt for CBC use the same interface. *output_data_size = input_data_size; @@ -311,50 +207,6 @@ WB_Result DecryptBuffer(WB_CipherMode mode, return WB_RESULT_OK; } - -// We use "remote_attestation_verified" and "platform_verification_status" to -// determine whether the platform is hardware verified. -// -// Each variable can be in one of three states. Each variable has a missing -// value, a true value, and a false value. -// -// |----------------------------------------------------------| -// | | RA N/A | RA VERIFIED | RA NOT VERIFIED | -// |----------------------------------------------------------| -// | VMP N/A | 0 | 1 | 0 | -// | VMP HW_VERIFIED | 1 | 1 | 0 | -// | VMP OTHER | 0 | 0 | 0 | -// |----------------------------------------------------------| -bool IsPlatformHardwareVerified(const video_widevine::License& license) { - int ra; - if (!license.has_remote_attestation_verified()) { - ra = 0; - } else if (license.remote_attestation_verified()) { - ra = 1; - } else { - ra = 2; - } - - int vmp; - if (!license.has_platform_verification_status()) { - vmp = 0; - } else if (license.platform_verification_status() == - video_widevine::PLATFORM_HARDWARE_VERIFIED) { - vmp = 1; - } else { - vmp = 2; - } - - // Use int to match the table we have in the comment above. - const int table[3][3] = { - {0, 1, 0}, - {1, 1, 0}, - {0, 0, 0}, - }; - - return table[vmp][ra] == 1; -} - } // namespace WB_Result WB_License_Create(WB_License_Whitebox** whitebox) { @@ -521,14 +373,7 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox, } } - AesCbcDecryptor decryptor; - CHECK( - decryptor.SetKey(reinterpret_cast(decryption_key.data()), - decryption_key.size())); - - std::map content_keys; - std::vector server_renewal_keys; - std::vector client_renewal_keys; + std::unique_ptr parser; // Even if |core_message| is provided, only ODK v16.5 and later support the // fields needed. If an older API is used, ignore it and use the protobuf as @@ -536,150 +381,82 @@ WB_Result WB_License_ProcessLicenseResponse(WB_License_Whitebox* whitebox, if (odk_context.is_valid && !HasEncryptedKeyControlBlock(odk_context) && IsOdkVersionSupported(odk_context.major_version, odk_context.minor_version)) { - // Start by extracting the signing key. - const std::string signing_key_encrypted = - ExtractItem(odk_context.license.enc_mac_keys, message_str); - const std::string signing_key_iv = - ExtractItem(odk_context.license.enc_mac_keys_iv, message_str); - if (!signing_key_encrypted.empty() && !signing_key_iv.empty()) { - std::string unwrapped_signing_key(signing_key_encrypted); - - if (!Decrypt(decryptor, signing_key_iv, signing_key_encrypted, - &unwrapped_signing_key)) { - DVLOG(1) << "Invalid parameter: Invalid enc_mac_keys."; - return WB_RESULT_INVALID_PARAMETER; - } - - if (unwrapped_signing_key.size() < kSigningKeySizeBytes * 2) { - DVLOG(1) << "Invalid parameter: Invalid signing key."; - return WB_RESULT_INVALID_PARAMETER; - } - - server_renewal_keys.push_back( - unwrapped_signing_key.substr(0, kSigningKeySizeBytes)); - client_renewal_keys.push_back(unwrapped_signing_key.substr( - kSigningKeySizeBytes, kSigningKeySizeBytes)); - } - - // Now extract all the content keys. - for (size_t i = 0; i < odk_context.license.key_array_length; ++i) { - const OEMCrypto_KeyObject& key = odk_context.license.key_array[i]; - - const std::string key_id = ExtractItem(key.key_id, message_str); - DCHECK_GT(key_id.size(), 0u); - - const std::string iv = ExtractItem(key.key_data_iv, message_str); - DCHECK_EQ(iv.size(), 16u); - - const std::string wrapped_key = ExtractItem(key.key_data, message_str); - DCHECK_EQ(wrapped_key.size(), 16u); - - std::string unwrapped_key(wrapped_key); - if (!Decrypt(decryptor, iv, wrapped_key, &unwrapped_key)) { - DVLOG(1) << "Invalid parameter: Invalid key.key_data."; - return WB_RESULT_INVALID_PARAMETER; - } - - constexpr size_t kContentKeySizeBytes = 16; - if (unwrapped_key.size() < kContentKeySizeBytes) { - DVLOG(1) << "Invalid parameter: Invalid content key."; - return WB_RESULT_INVALID_PARAMETER; - } - - unwrapped_key.resize(kContentKeySizeBytes); - - // 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 std::string key_control_block = - ExtractItem(key.key_control, message_str); - - video_widevine::License_KeyContainer_SecurityLevel security_level; - CHECK(ExtractLevel(key_control_block, &security_level)); - - content_keys[key_id] = CreateContentKey( - security_level, /* is_hw_verified */ false, unwrapped_key); - } + parser.reset(new widevine::OdkLicenseParser); } else { - // Core message not provided or an old version, so extract the keys from - // the protobuf. - video_widevine::License license; - if (!license.ParseFromArray(message, message_size)) { - DVLOG(1) << "Invalid parameter: Invalid license."; - return WB_RESULT_INVALID_PARAMETER; - } - - // When the platform is hardware verified, all keys are unlocked and are - // available to be used with either decrypt function. Use this flag to - // overwrite the calculated values for the internal policies to enable - // this behaviour. - const bool is_verified = IsPlatformHardwareVerified(license); - - for (const auto& key : license.key()) { - // If this is not a key we're interested in, skip it as soon as possible. - // Don't even bother unwrapping it. - if (key.type() != KeyContainer::SIGNING && - key.type() != KeyContainer::CONTENT) { - continue; - } - - const std::string wrapped_key = key.key(); - std::string unwrapped_key(wrapped_key); - - if (!Decrypt(decryptor, key.iv(), wrapped_key, &unwrapped_key)) { - // The input has to be a specific length, so if it is not, it means that - // something is wrong with the license. - DVLOG(1) << "Invalid parameter: Invalid license."; - return WB_RESULT_INVALID_PARAMETER; - } - - if (key.type() == KeyContainer::SIGNING) { - if (unwrapped_key.size() < kSigningKeySizeBytes * 2) { - DVLOG(1) << "Invalid parameter: Invalid signing key."; - return WB_RESULT_INVALID_PARAMETER; - } - - server_renewal_keys.push_back( - unwrapped_key.substr(0, kSigningKeySizeBytes)); - client_renewal_keys.push_back( - unwrapped_key.substr(kSigningKeySizeBytes, kSigningKeySizeBytes)); - } else if (key.type() == KeyContainer::CONTENT) { - constexpr size_t kContentKeySizeBytes = 16; - - if (unwrapped_key.size() < kContentKeySizeBytes) { - DVLOG(1) << "Invalid parameter: Invalid content key."; - return WB_RESULT_INVALID_PARAMETER; - } - - unwrapped_key.resize(kContentKeySizeBytes); - content_keys[key.id()] = - CreateContentKey(key.level(), is_verified, unwrapped_key); - } else { - // We should have already skipped over this key. - CHECK(false); - } - } + parser.reset(new widevine::ProtobufLicenseParser); } - DCHECK_EQ(server_renewal_keys.size(), client_renewal_keys.size()); - if (server_renewal_keys.size() > 1) { - return WB_RESULT_INVALID_PARAMETER; + WB_Result result = parser->Parse(decryption_key, odk_context, message_str); + + if (result != WB_RESULT_OK) { + return result; } - // Add an empty string so that we can always reference a valid string. By - // adding it last, a valid key will get priority over this fallback value. - server_renewal_keys.push_back(""); - client_renewal_keys.push_back(""); // Copy the loaded state over to the white-box instance now that we know we // have a valid state. - whitebox->server_signing_key.swap(server_renewal_keys[0]); - whitebox->client_signing_key.swap(client_renewal_keys[0]); - whitebox->content_keys.swap(content_keys); + auto* renewal_key = parser->GetRenewalKey(); + if (renewal_key) { + whitebox->renewal_key.reset(new widevine::RenewalKey(*renewal_key)); + } + + whitebox->content_keys = parser->GetContentKeys(); + + whitebox->initialized = true; return WB_RESULT_OK; } +WB_Result WB_License_QueryKeyStatus(const WB_License_Whitebox* whitebox, + WB_KeyQueryType type, + const uint8_t* key_id, + size_t key_id_size, + WB_KeyStatus* key_status) { + if (!whitebox || !key_status) { + 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; + } + + switch (type) { + case WB_KEY_QUERY_TYPE_SIGNING_KEY: { + if (whitebox->renewal_key == nullptr) { + return WB_RESULT_KEY_UNAVAILABLE; + } + + *key_status = whitebox->renewal_key->status; + return WB_RESULT_OK; + } + + case WB_KEY_QUERY_TYPE_CONTENT_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; + } + + const widevine::ContentKey* content_key = + FindKey(whitebox, key_id, key_id_size); + + if (content_key == nullptr) { + return WB_RESULT_KEY_UNAVAILABLE; + } + + *key_status = content_key->status; + return WB_RESULT_OK; + } + } + + return WB_RESULT_INVALID_PARAMETER; // Unknown query type. +} + WB_Result WB_License_SignRenewalRequest(const WB_License_Whitebox* whitebox, const uint8_t* message, size_t message_size, @@ -695,19 +472,25 @@ WB_Result WB_License_SignRenewalRequest(const WB_License_Whitebox* whitebox, return WB_RESULT_INVALID_PARAMETER; } - if (whitebox->content_keys.empty()) { - DVLOG(1) << "Invalid state: missing license."; + if (!whitebox->initialized) { + DVLOG(1) << "Invalid state: no license loaded."; return WB_RESULT_INVALID_STATE; } - if (whitebox->client_signing_key.empty()) { - DVLOG(1) << "Invalid state: license does not support renewals."; - return WB_RESULT_INVALID_STATE; + if (whitebox->renewal_key == nullptr) { + DVLOG(1) << "Key Unavailable: no signing key in license."; + return WB_RESULT_KEY_UNAVAILABLE; + } + + if (whitebox->renewal_key->status != WB_KEY_STATUS_SIGNING_KEY_VALID) { + DVLOG(1) << "Key Unavailable: invalid signing key in license."; + return WB_RESULT_KEY_UNAVAILABLE; } const std::string computed_signature = widevine::crypto_util::CreateSignatureHmacSha256( - whitebox->client_signing_key, + std::string(whitebox->renewal_key->client.begin(), + whitebox->renewal_key->client.end()), std::string(message, message + message_size)); if (!widevine::MemCopy(computed_signature.data(), computed_signature.size(), @@ -737,19 +520,25 @@ WB_Result WB_License_VerifyRenewalResponse(const WB_License_Whitebox* whitebox, return WB_RESULT_INVALID_PARAMETER; } - if (whitebox->content_keys.empty()) { - DVLOG(1) << "Invalid state: missing license."; + if (!whitebox->initialized) { + DVLOG(1) << "Invalid state: no license loaded."; return WB_RESULT_INVALID_STATE; } - if (whitebox->server_signing_key.empty()) { - DVLOG(1) << "Invalid state: license does not support renewals."; - return WB_RESULT_INVALID_STATE; + if (whitebox->renewal_key == nullptr) { + DVLOG(1) << "Key Unavailable: no signing key in license."; + return WB_RESULT_KEY_UNAVAILABLE; + } + + if (whitebox->renewal_key->status != WB_KEY_STATUS_SIGNING_KEY_VALID) { + DVLOG(1) << "Key Unavailable: invalid signing key in license."; + return WB_RESULT_KEY_UNAVAILABLE; } const std::string computed_signature = widevine::crypto_util::CreateSignatureHmacSha256( - whitebox->server_signing_key, + std::string(whitebox->renewal_key->server.begin(), + whitebox->renewal_key->server.end()), std::string(message, message + message_size)); if (signature_size != computed_signature.size()) { @@ -777,8 +566,8 @@ WB_Result WB_License_GetSecretString(const WB_License_Whitebox* whitebox, return WB_RESULT_INVALID_PARAMETER; } - if (whitebox->content_keys.empty()) { - DVLOG(1) << "Invalid state: missing license."; + if (!whitebox->initialized) { + DVLOG(1) << "Invalid state: no license loaded."; return WB_RESULT_INVALID_STATE; } @@ -794,14 +583,19 @@ WB_Result WB_License_GetSecretString(const WB_License_Whitebox* whitebox, // The secret string can differ between keys, so we need to make sure that // the key id is actually a content key. - const ContentKey* content_key = FindKey(whitebox, key_id, key_id_size); + const auto* content_key = FindKey(whitebox, key_id, key_id_size); if (content_key == nullptr) { DVLOG(1) << "Key unavailable: could not find key."; return WB_RESULT_KEY_UNAVAILABLE; } - if (!content_key->allow_masked_decrypt) { + if (!content_key->is_valid) { + DVLOG(1) << "Key unavailable: invalid key."; + return WB_RESULT_KEY_UNAVAILABLE; + } + + 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; @@ -834,8 +628,8 @@ WB_Result WB_License_Decrypt(const WB_License_Whitebox* whitebox, return WB_RESULT_INVALID_PARAMETER; } - if (whitebox->content_keys.empty()) { - DVLOG(1) << "Invalid state: missing license."; + if (!whitebox->initialized) { + DVLOG(1) << "Invalid state: no license loaded."; return WB_RESULT_INVALID_STATE; } @@ -844,22 +638,28 @@ WB_Result WB_License_Decrypt(const WB_License_Whitebox* whitebox, return WB_RESULT_INVALID_PARAMETER; } - const ContentKey* content_key = FindKey(whitebox, key_id, key_id_size); + const auto* content_key = FindKey(whitebox, key_id, key_id_size); if (content_key == nullptr) { DVLOG(1) << "Key unavailable: could not find key."; return WB_RESULT_KEY_UNAVAILABLE; } - if (!content_key->allow_decrypt) { + if (!content_key->is_valid) { + DVLOG(1) << "Key unavailable: invalid key."; + return WB_RESULT_KEY_UNAVAILABLE; + } + + if (!content_key->can_decrypt) { DVLOG(1) << "Insufficient security level: key policy does not allow use " "with Decrypt()."; return WB_RESULT_INSUFFICIENT_SECURITY_LEVEL; } // DecryptBuffer() will validate the remaining decryption parameters. - return DecryptBuffer(mode, content_key->key, input_data, input_data_size, iv, - iv_size, output_data, output_data_size); + return DecryptBuffer(mode, content_key->key.data(), content_key->key.size(), + input_data, input_data_size, iv, iv_size, output_data, + output_data_size); } WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox, @@ -877,8 +677,8 @@ WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox, return WB_RESULT_INVALID_PARAMETER; } - if (whitebox->content_keys.empty()) { - DVLOG(1) << "Invalid state: missing license."; + if (!whitebox->initialized) { + DVLOG(1) << "Invalid state: no license loaded."; return WB_RESULT_INVALID_STATE; } @@ -887,14 +687,19 @@ WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox, return WB_RESULT_INVALID_PARAMETER; } - const ContentKey* content_key = FindKey(whitebox, key_id, key_id_size); + const auto* content_key = FindKey(whitebox, key_id, key_id_size); if (content_key == nullptr) { DVLOG(1) << "Key unavailable: could not find key."; return WB_RESULT_KEY_UNAVAILABLE; } - if (!content_key->allow_masked_decrypt) { + if (!content_key->is_valid) { + DVLOG(1) << "Key unavailable: invalid key."; + return WB_RESULT_KEY_UNAVAILABLE; + } + + 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; @@ -910,9 +715,9 @@ WB_Result WB_License_MaskedDecrypt(const WB_License_Whitebox* whitebox, // DecryptBuffer() will validate the remaining decryption parameters and set // |masked_output_data_size|. - const WB_Result result = - DecryptBuffer(mode, content_key->key, input_data, input_data_size, iv, - iv_size, output.data(), masked_output_data_size); + const WB_Result result = DecryptBuffer( + mode, content_key->key.data(), content_key->key.size(), input_data, + input_data_size, iv, iv_size, output.data(), masked_output_data_size); if (result != WB_RESULT_OK) { return result; diff --git a/whitebox/impl/reference/memory_util.cc b/whitebox/reference/impl/memory_util.cc similarity index 91% rename from whitebox/impl/reference/memory_util.cc rename to whitebox/reference/impl/memory_util.cc index d3dd07b..086570b 100644 --- a/whitebox/impl/reference/memory_util.cc +++ b/whitebox/reference/impl/memory_util.cc @@ -1,6 +1,6 @@ // Copyright 2020 Google LLC. All Rights Reserved. -#include "impl/reference/memory_util.h" +#include "reference/impl/memory_util.h" #include diff --git a/whitebox/impl/reference/memory_util.h b/whitebox/reference/impl/memory_util.h similarity index 85% rename from whitebox/impl/reference/memory_util.h rename to whitebox/reference/impl/memory_util.h index b50c870..5f104a7 100644 --- a/whitebox/impl/reference/memory_util.h +++ b/whitebox/reference/impl/memory_util.h @@ -1,7 +1,7 @@ // Copyright 2020 Google LLC. All Rights Reserved. -#ifndef WHITEBOX_IMPL_REFERENCE_MEMORY_UTIL_H_ -#define WHITEBOX_IMPL_REFERENCE_MEMORY_UTIL_H_ +#ifndef WHITEBOX_REFERENCE_IMPL_MEMORY_UTIL_H_ +#define WHITEBOX_REFERENCE_IMPL_MEMORY_UTIL_H_ #include #include @@ -25,4 +25,4 @@ bool MemCopy(const void* src, size_t src_size, void* dest, size_t dest_size); } // namespace widevine -#endif // WHITEBOX_IMPL_REFERENCE_MEMORY_UTIL_H_ +#endif // WHITEBOX_REFERENCE_IMPL_MEMORY_UTIL_H_ diff --git a/whitebox/impl/reference/odk.cc b/whitebox/reference/impl/odk.cc similarity index 98% rename from whitebox/impl/reference/odk.cc rename to whitebox/reference/impl/odk.cc index 739a835..75a4ae8 100644 --- a/whitebox/impl/reference/odk.cc +++ b/whitebox/reference/impl/odk.cc @@ -1,6 +1,6 @@ // cOpyright 2021 Google LLC. All Rights Reserved. -#include "impl/reference/odk.h" +#include "reference/impl/odk.h" #include diff --git a/whitebox/impl/reference/odk.h b/whitebox/reference/impl/odk.h similarity index 81% rename from whitebox/impl/reference/odk.h rename to whitebox/reference/impl/odk.h index 2836860..8ad384c 100644 --- a/whitebox/impl/reference/odk.h +++ b/whitebox/reference/impl/odk.h @@ -1,7 +1,7 @@ // Copyright 2021 Google LLC. All Rights Reserved. -#ifndef WHITEBOX_IMPL_REFERENCE_ODK_H_ -#define WHITEBOX_IMPL_REFERENCE_ODK_H_ +#ifndef WHITEBOX_REFERENCE_IMPL_ODK_H_ +#define WHITEBOX_REFERENCE_IMPL_ODK_H_ #include #include @@ -22,4 +22,4 @@ WB_Result GetODKContext(const std::string& combined_message, bool HasEncryptedKeyControlBlock(const ODKContext& context); -#endif // WHITEBOX_IMPL_REFERENCE_ODK_H_ +#endif // WHITEBOX_REFERENCE_IMPL_ODK_H_ diff --git a/whitebox/reference/impl/odk_license_parser.cc b/whitebox/reference/impl/odk_license_parser.cc new file mode 100644 index 0000000..aa9f7cd --- /dev/null +++ b/whitebox/reference/impl/odk_license_parser.cc @@ -0,0 +1,191 @@ +// Copyright 2021 Google LLC. All Rights Reserved. + +#include "reference/impl/odk_license_parser.h" + +#include "base/logging.h" +#include "crypto_utils/crypto_util.h" + +namespace widevine { +namespace { + +bool ExtractLevel( + const std::string& key_control_block, + video_widevine::License_KeyContainer_SecurityLevel* security_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. + // + // Bytes 0..3: Verification. + // Constant bytes “kctl”, “kc09”, “kc10”, “kc11”, ... “kc15”. + // Bytes 4..7: Obsolete. + // Bytes 8..11: Nonce. + // Bytes 12..15: Control Bits + // Bits 27..26: Security_Level (Only for L3 white-box implementations) + // 0 = SW_SECURE_CRYPTO + // 1 = SW_SECURE_DECODE + // 2 = HW_SECURE_CRYPTO + // 3 = HW_SECURE_DECODE or HW_SECURE_ALL + + // Make sure this is a valid key control block. Ideally the signature + // verification should have taken care of this. + if ((key_control_block.size() != 16u) || (key_control_block[0] != 'k') || + (key_control_block[1] != 'c')) { + return false; + } + + // Extract bits 26..27 from Control Bits. + switch ((key_control_block[12] & 0x0C) >> 2) { + case 0: + *security_level = + video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO; + break; + case 1: + *security_level = + video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_DECODE; + break; + case 2: + *security_level = + video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_CRYPTO; + break; + default: + *security_level = + video_widevine::License_KeyContainer_SecurityLevel_HW_SECURE_DECODE; + break; + } + + return true; +} + +// Helper function to extract the substring |item| from the provided |buffer|. +std::string ExtractItem(const OEMCrypto_Substring& item, + const std::string& buffer) { + return buffer.substr(item.offset, item.length); +} + +} // namespace + +WB_Result OdkLicenseParser::Parse(const std::string& decryption_key, + const ODKContext& odk_context, + const std::string& message) { + // Extract the signing keys. + const std::string signing_key_encrypted = + ExtractItem(odk_context.license.enc_mac_keys, message); + const std::string signing_key_iv = + ExtractItem(odk_context.license.enc_mac_keys_iv, message); + + if (!signing_key_encrypted.empty() || !signing_key_iv.empty()) { + renewal_keys_.push_back(ParseSigningKeys( + decryption_key, signing_key_encrypted, signing_key_iv)); + } + + // Extract the content keys. + for (size_t i = 0; i < odk_context.license.key_array_length; ++i) { + const OEMCrypto_KeyObject& key = odk_context.license.key_array[i]; + + const std::string key_id = ExtractItem(key.key_id, message); + + // If there is no key id, we can't add an invalid entry since there would + // be no way to query it. + if (key_id.empty()) { + VLOG(3) << "Invalid key at index " << i << " : no key id."; + continue; + } + + // 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. + content_keys_[key_id] = ParseContentKey(decryption_key, message, key); + } + + return WB_RESULT_OK; +} + +const RenewalKey* OdkLicenseParser::GetRenewalKey() const { + DCHECK_LE(renewal_keys_.size(), 1u); + return renewal_keys_.empty() ? nullptr : &renewal_keys_[0]; +} + +const std::map& OdkLicenseParser::GetContentKeys() + const { + return content_keys_; +} + +RenewalKey OdkLicenseParser::ParseSigningKeys(const std::string& decryption_key, + const std::string& key, + const std::string& iv) const { + // This should have been verified before calling the parser. + CHECK_EQ(decryption_key.size(), 16u) << "Incorrect decryption key size."; + + const size_t kSizeWithoutPadding = 2 * crypto_util::kSigningKeySizeBytes; + const size_t kSizeWithPadding = 2 * crypto_util::kSigningKeySizeBytes + 16u; + + if (key.size() != kSizeWithoutPadding && key.size() != kSizeWithPadding) { + VLOG(3) << "Invalid renewal key size (" << key.size() << ")."; + return RenewalKey(); + } + + if (iv.size() != 16u) { + VLOG(3) << "Invalid iv size."; + return RenewalKey(); + } + + const std::string wrapped_key = key.substr(0, kSizeWithoutPadding); + std::string unwrapped_key(wrapped_key); + + if (!Decrypt(decryption_key, iv, wrapped_key, &unwrapped_key)) { + VLOG(3) << "Failed to decrypt renewal key."; + return RenewalKey(); + } + + RenewalKey renewal_key; + renewal_key.status = WB_KEY_STATUS_SIGNING_KEY_VALID; + std::copy(unwrapped_key.begin(), + unwrapped_key.begin() + crypto_util::kSigningKeySizeBytes, + renewal_key.server.begin()); + std::copy(unwrapped_key.begin() + crypto_util::kSigningKeySizeBytes, + unwrapped_key.end(), renewal_key.client.begin()); + + return renewal_key; +} + +ContentKey OdkLicenseParser::ParseContentKey( + const std::string& decryption_key, + const std::string& message, + const OEMCrypto_KeyObject& key) const { + // This should have been verified before calling the parser. + CHECK_EQ(decryption_key.size(), 16u) << "Incorrect decryption key size."; + + const std::string iv = ExtractItem(key.key_data_iv, message); + + if (iv.size() != 16u) { + VLOG(3) << "Invalid content iv size."; + return ContentKey(); + } + + const 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. + if (wrapped_key.size() != 16u) { + VLOG(3) << "Invalid content key size (" << wrapped_key.size() << ")."; + return ContentKey(); + } + + std::string unwrapped_key(wrapped_key); + if (!Decrypt(decryption_key, iv, wrapped_key, &unwrapped_key)) { + VLOG(3) << "Failed to decrypt content key."; + return ContentKey(); + } + + const std::string key_control_block = ExtractItem(key.key_control, message); + + 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 CreateContentKey(security_level, is_hw_verified, unwrapped_key); +} + +} // namespace widevine \ No newline at end of file diff --git a/whitebox/reference/impl/odk_license_parser.h b/whitebox/reference/impl/odk_license_parser.h new file mode 100644 index 0000000..fb6ad9a --- /dev/null +++ b/whitebox/reference/impl/odk_license_parser.h @@ -0,0 +1,36 @@ +// Copyright 2021 Google LLC. All Rights Reserved. + +#ifndef WHITEBOX_REFERENCE_IMPL_ODK_LICENSE_PARSER_H_ +#define WHITEBOX_REFERENCE_IMPL_ODK_LICENSE_PARSER_H_ + +#include "reference/impl/content_key.h" +#include "reference/impl/license_parser.h" + +namespace widevine { + +class OdkLicenseParser : public LicenseParser { + public: + WB_Result Parse(const std::string& decryption_key, + const ODKContext& odk_context, + const std::string& message) override; + + const widevine::RenewalKey* GetRenewalKey() const override; + + const std::map& GetContentKeys() const override; + + private: + RenewalKey ParseSigningKeys(const std::string& decryption_key, + const std::string& key, + const std::string& iv) const; + + ContentKey ParseContentKey(const std::string& decryption_key, + const std::string& message, + const OEMCrypto_KeyObject& key) const; + + std::vector renewal_keys_; + std::map content_keys_; +}; + +} // namespace widevine + +#endif // WHITEBOX_REFERENCE_IMPL_ODK_LICENSE_PARSER_H_ diff --git a/whitebox/reference/impl/protobuf_license_parser.cc b/whitebox/reference/impl/protobuf_license_parser.cc new file mode 100644 index 0000000..72710a8 --- /dev/null +++ b/whitebox/reference/impl/protobuf_license_parser.cc @@ -0,0 +1,191 @@ +// Copyright 2021 Google LLC. All Rights Reserved. + +#include "reference/impl/protobuf_license_parser.h" + +#include "base/logging.h" +#include "crypto_utils/crypto_util.h" + +namespace widevine { +namespace { +// We use "remote_attestation_verified" and "platform_verification_status" to +// determine whether the platform is hardware verified. +// +// Each variable can be in one of three states. Each variable has a missing +// value, a true value, and a false value. +// +// |----------------------------------------------------------| +// | | RA N/A | RA VERIFIED | RA NOT VERIFIED | +// |----------------------------------------------------------| +// | VMP N/A | 0 | 1 | 0 | +// | VMP HW_VERIFIED | 1 | 1 | 0 | +// | VMP OTHER | 0 | 0 | 0 | +// |----------------------------------------------------------| +bool IsPlatformHardwareVerified(const video_widevine::License& license) { + int ra; + if (!license.has_remote_attestation_verified()) { + ra = 0; + } else if (license.remote_attestation_verified()) { + ra = 1; + } else { + ra = 2; + } + + int vmp; + if (!license.has_platform_verification_status()) { + vmp = 0; + } else if (license.platform_verification_status() == + video_widevine::PLATFORM_HARDWARE_VERIFIED) { + vmp = 1; + } else { + vmp = 2; + } + + // Use int to match the table we have in the comment above. + const int table[3][3] = { + {0, 1, 0}, + {1, 1, 0}, + {0, 0, 0}, + }; + + return table[vmp][ra] == 1; +} +} // namespace + +WB_Result ProtobufLicenseParser::Parse(const std::string& decryption_key, + const ODKContext& odk_context, + const std::string& message) { + video_widevine::License license; + if (!license.ParseFromString(message)) { + VLOG(3) << "Invalid parameter: Invalid license."; + return WB_RESULT_INVALID_PARAMETER; + } + + // When the platform is hardware verified, all keys are unlocked and are + // available to be used with either decrypt function. Use this flag to + // overwrite the calculated values for the internal policies to enable + // this behaviour. + const bool is_verified = IsPlatformHardwareVerified(license); + + for (const auto& key : license.key()) { + // The default `type()` value will be SIGNING when not present in the + // protobuf. + switch (key.type()) { + case video_widevine::License_KeyContainer::SIGNING: + // The ODK will not write the key to the core message if there is no key + // data. This is odd, since it does not check any other fields. To be + // consistent, we have the protobuf implementation do the same. + if (key.key().empty()) { + VLOG(3) << "Skipping signing key : no key data."; + } else { + renewal_keys_.push_back(ParseSigningKey(decryption_key, key)); + } + break; + + case video_widevine::License_KeyContainer::CONTENT: + if (key.id().empty()) { + VLOG(3) << "Skipping content key : no key id."; + } else { + content_keys_[key.id()] = + ParseContentKey(decryption_key, key, is_verified); + } + break; + default: + VLOG(3) << "Skipping key of type " << key.type() << "."; + } + } + + // This should not happen, but if it happens, the implementation is free to + // use any of them. + if (renewal_keys_.size() > 1) { + VLOG(3) << "Multiple renewal keys found. Extra keys will be ignored."; + } + + return WB_RESULT_OK; +} + +const widevine::RenewalKey* ProtobufLicenseParser::GetRenewalKey() const { + return renewal_keys_.empty() ? nullptr : &renewal_keys_[0]; +} + +const std::map& ProtobufLicenseParser::GetContentKeys() + const { + return content_keys_; +} + +RenewalKey ProtobufLicenseParser::ParseSigningKey( + const std::string& decryption_key, + const video_widevine::License_KeyContainer& key) const { + CHECK_EQ(decryption_key.size(), 16u); + CHECK_EQ(key.type(), video_widevine::License_KeyContainer::SIGNING); + + const size_t kSizeWithoutPadding = 2 * crypto_util::kSigningKeySizeBytes; + const size_t kSizeWithPadding = 2 * crypto_util::kSigningKeySizeBytes + 16u; + + if (key.key().size() != kSizeWithoutPadding && + key.key().size() != kSizeWithPadding) { + VLOG(3) << "Invalid renewal key size (" << key.key().size() << ")."; + return RenewalKey(); + } + + if (key.iv().size() != 16u) { + VLOG(3) << "Invalid iv size."; + return RenewalKey(); + } + + const std::string wrapped_key = key.key().substr(0, kSizeWithoutPadding); + std::string unwrapped_key(wrapped_key); + + if (!Decrypt(decryption_key, key.iv(), wrapped_key, &unwrapped_key)) { + // The input has to be a specific length, so if it is not, it means that + // something is wrong with the license. + VLOG(3) << "Failed to decrypt renewal key."; + return RenewalKey(); + } + + widevine::RenewalKey renewal_key; + renewal_key.status = WB_KEY_STATUS_SIGNING_KEY_VALID; + std::copy(unwrapped_key.begin(), + unwrapped_key.begin() + crypto_util::kSigningKeySizeBytes, + renewal_key.server.begin()); + std::copy(unwrapped_key.begin() + crypto_util::kSigningKeySizeBytes, + unwrapped_key.end(), renewal_key.client.begin()); + + return renewal_key; +} + +ContentKey ProtobufLicenseParser::ParseContentKey( + const std::string& decryption_key, + const video_widevine::License_KeyContainer& key, + bool is_verified) const { + // This should have been verified before calling the parser. + CHECK_EQ(decryption_key.size(), 16u) << "Incorrect decryption key size."; + CHECK_EQ(key.type(), video_widevine::License_KeyContainer::CONTENT); + + constexpr size_t kKeySizeWithoutPadding = 16u; + constexpr size_t kKeySizeWithPadding = 32u; + + if (key.key().size() != kKeySizeWithoutPadding && + key.key().size() != kKeySizeWithPadding) { + VLOG(3) << "Invalid content key size (" << key.key().size() << ")."; + return ContentKey(); + } + + if (key.iv().size() != 16u) { + VLOG(3) << "Invalid iv size."; + return ContentKey(); + } + + std::string wrapped_key = key.key().substr(0, kKeySizeWithoutPadding); + std::string unwrapped_key(wrapped_key); + + if (!Decrypt(decryption_key, key.iv(), wrapped_key, &unwrapped_key)) { + VLOG(3) << "Failed to decrypt content key."; + return ContentKey(); + } + + // The default value for `level()` will be SW_SECURE_CRYPTO. This means that + // if the level value was not set, it will be treated as SW_SECURE_CRYPTO. + return CreateContentKey(key.level(), is_verified, unwrapped_key); +} + +} // namespace widevine \ No newline at end of file diff --git a/whitebox/reference/impl/protobuf_license_parser.h b/whitebox/reference/impl/protobuf_license_parser.h new file mode 100644 index 0000000..4f59538 --- /dev/null +++ b/whitebox/reference/impl/protobuf_license_parser.h @@ -0,0 +1,37 @@ +// Copyright 2021 Google LLC. All Rights Reserved. + +#ifndef WHITEBOX_REFERENCE_IMPL_PROTOBUF_LICENSE_PARSER_H_ +#define WHITEBOX_REFERENCE_IMPL_PROTOBUF_LICENSE_PARSER_H_ + +#include "cdm/protos/license_protocol.pb.h" +#include "reference/impl/content_key.h" +#include "reference/impl/license_parser.h" + +namespace widevine { + +class ProtobufLicenseParser : public LicenseParser { + public: + WB_Result Parse(const std::string& decryption_key, + const ODKContext& odk_context, + const std::string& message) override; + + const widevine::RenewalKey* GetRenewalKey() const override; + + const std::map& GetContentKeys() const override; + + RenewalKey ParseSigningKey( + const std::string& decryption_key, + const video_widevine::License_KeyContainer& key) const; + + ContentKey ParseContentKey(const std::string& decryption_key, + const video_widevine::License_KeyContainer& key, + bool is_verified) const; + + private: + std::vector renewal_keys_; + std::map content_keys_; +}; + +} // namespace widevine + +#endif // WHITEBOX_REFERENCE_IMPL_LICENSE_PARSER_H_ diff --git a/whitebox/reference/impl/renewal_key.h b/whitebox/reference/impl/renewal_key.h new file mode 100644 index 0000000..a1cae87 --- /dev/null +++ b/whitebox/reference/impl/renewal_key.h @@ -0,0 +1,17 @@ +// Copyright 2021 Google LLC. All Rights Reserved. + +#ifndef WHITEBOX_REFERENCE_IMPL_RENEWAL_KEY_H_ +#define WHITEBOX_REFERENCE_IMPL_RENEWAL_KEY_H_ + +namespace widevine { + +struct RenewalKey { + WB_KeyStatus status = WB_KEY_STATUS_INVALID; + + std::array server; + std::array client; +}; + +} // namespace widevine + +#endif // WHITEBOX_REFERENCE_IMPL_RENEWAL_KEY_H_ \ No newline at end of file diff --git a/whitebox/reference/tests/BUILD b/whitebox/reference/tests/BUILD new file mode 100644 index 0000000..79fb56e --- /dev/null +++ b/whitebox/reference/tests/BUILD @@ -0,0 +1,80 @@ +# Copyright 2021 Google LLC. All Rights Reserved. + +package(default_visibility = [ + "//visibility:private", +]) + +# ============================================================================== +# Requirements +# ============================================================================== +# +# This BUILD file expects the implementation BUILD file to expose the following +# build targets so that it can link against them: +# +# test_aead_whitebox : The target for testing the AEAD white-box. +# +# test_license_whitebox : The target for testing the license white-box. The +# white-box should use the private key provided in +# "//api/test_license_whitebox_keys.cc". + +# ============================================================================== +# AEAD Test Targets +# ============================================================================== + +cc_test( + name = "aead_whitebox_test", + size = "small", + deps = [ + "//api:aead_whitebox_test", + "//reference/impl:test_aead_whitebox", + ], +) + +cc_test( + name = "aead_whitebox_benchmark", + size = "small", + deps = [ + "//api:aead_whitebox_benchmark", + "//reference/impl:test_aead_whitebox", + ], +) + +# ============================================================================== +# License Whitebox Test Targets +# ============================================================================== + +cc_test( + name = "license_whitebox_test", + size = "small", + deps = [ + "//api:license_whitebox_test", + "//reference/impl:test_license_whitebox", + ], +) + +cc_test( + name = "remote_attestation_and_verification_test", + size = "small", + deps = [ + "//api:remote_attestation_and_verification_test", + "//reference/impl:test_license_whitebox", + ], +) + +cc_test( + name = "license_whitebox_benchmark", + size = "small", + deps = [ + "//api:license_whitebox_benchmark", + "//reference/impl:test_license_whitebox", + ], +) + +cc_test( + name = "license_whitebox_uat_test", + size = "small", + deps = [ + "//api:license_whitebox_uat_test", + "//reference/impl:test_license_whitebox", + ], +) diff --git a/whitebox/resources/keygen.py b/whitebox/resources/keygen.py new file mode 100755 index 0000000..d163c40 --- /dev/null +++ b/whitebox/resources/keygen.py @@ -0,0 +1,121 @@ +#!/usr/local/bin/python3 +# Lint as: python3 +"""Load keys from our provisioning cert file. + +To make it easier to get the keys from the provisioning cert and create the +private and public keys, this script will read the cert file, extract the keys, +convert them to the correct format, and write them as a C++ byte array. +""" + +import pathlib +import argparse + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey + + +def _load_rsa_key(key: bytes) -> RSAPrivateKey: + # Regardless of the format (as long as it is a DER file) this will consume + # it, allowing us to consume any format of private key. + return serialization.load_der_private_key( + key, + password=None, + backend=default_backend(), + ) + + +def _get_private_key(rsa_key: RSAPrivateKey) -> bytes: + # "TraditionalOpenSSL" will ensure we use PKCS1. + return rsa_key.private_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption(), + ) + + +def _get_public_key(rsa_key: RSAPrivateKey) -> bytes: + return rsa_key.public_key().public_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PublicFormat.PKCS1, + ) + + +# Take the binary data and convert it to a list of hex strings that represent +# the same binary data and write it the file at `output_path`. For example, +# `b'\xab\xcd' would become `['0xab', '0xcd']`. +def _create_key_file(key: bytes, output_file: pathlib.Path): + assert key is not None + assert output_file.parent.is_dir() + + output_file.write_text(', '.join(['0x%02x' % byte for byte in key])) + + +def _parse_cert(cert_file: pathlib.Path): + assert cert_file.is_file() + + values = { + 'Certificate: ': None, + 'Public key: ': None, + 'Private key: ': None, + } + + with cert_file.open() as file_in: + for line in file_in: + for header in values: + if line.startswith(header): + values[header] = line[len(header):] # remove the header + break + + for value in values.values(): + assert value is not None + + return { + 'certificate': bytes.fromhex(values['Certificate: ']), + 'public-key': bytes.fromhex(values['Public key: ']), + 'private-key': bytes.fromhex(values['Private key: ']), + } + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + '--cert', + required=True, + help='The path to the cert file (e.g. 7912.txt).', + ) + parser.add_argument( + '--out-private-key', + '--private-key', + required=True, + help='The path to where the private key should be written.', + ) + parser.add_argument( + '--out-public-key', + '--public-key', + required=True, + help='The path to where the public key should be written.', + ) + args = parser.parse_args() + + cert_path = pathlib.Path(args.cert).resolve() + private_key_path = pathlib.Path(args.out_private_key).resolve() + public_key_path = pathlib.Path(args.out_public_key).resolve() + + assert cert_path.is_file() + assert public_key_path.parent.is_dir() + assert private_key_path.parent.is_dir() + + cert = _parse_cert(cert_path) + + rsa_key = _load_rsa_key(cert['private-key']) + + _create_key_file( + _get_private_key(rsa_key), + private_key_path, + ) + _create_key_file(_get_public_key(rsa_key), public_key_path) + + +if __name__ == '__main__': + main()