From 3c513cc46c918a8ef63b94d2ae99cf1c77a4caad Mon Sep 17 00:00:00 2001 From: Aaron Vaage Date: Mon, 15 Mar 2021 13:21:42 -0700 Subject: [PATCH] Adding WB_License_QueryKeyStatus() The two major changes in this code drop are: 1. The introduction of WB_License_QueryKeyStatus(). This function makes it possible for the White-box to skip keys in the license and report the usefulness of the key to the CDM. 2. The restructuring of the repo, making it easier to share test BUILD files and set the foundation for the new code drop structure. This change brings the partner repo in sync with the internal repo at commit f3b472a541262ca4d425d2b294de39a99385a3d2. --- .gitattributes | 9 + .gitignore | 1 + whitebox-dev/impl/BUILD | 120 ----- whitebox-dev/tools/PLACEHOLDER | 0 {whitebox-dev => whitebox-impl}/WORKSPACE | 14 +- whitebox-impl/impl/BUILD | 26 + whitebox-impl/tests/BUILD | 80 +++ whitebox-impl/tools/PLACEHOLDER | 2 + whitebox-prod/WORKSPACE | 111 ---- whitebox-prod/impl/BUILD | 120 ----- whitebox-prod/tools/PLACEHOLDER | 0 whitebox/api/BUILD | 29 +- whitebox/api/license_whitebox.h | 74 +++ whitebox/api/license_whitebox_benchmark.cc | 2 +- .../api/license_whitebox_chromeos_test.cc | 207 ------- whitebox/api/license_whitebox_decrypt_test.cc | 185 ++++--- ...license_whitebox_get_secret_string_test.cc | 61 ++- .../license_whitebox_masked_decrypt_test.cc | 210 ++++++-- ..._whitebox_process_license_response_test.cc | 7 +- ..._whitebox_query_content_key_status_test.cc | 328 ++++++++++++ ..._whitebox_query_signing_key_status_test.cc | 271 ++++++++++ ...ense_whitebox_sign_renewal_request_test.cc | 54 +- whitebox/api/license_whitebox_test_base.cc | 2 +- ...a_test.cc => license_whitebox_uat_test.cc} | 4 +- ...e_whitebox_verify_renewal_response_test.cc | 61 ++- whitebox/api/test_license_builder.cc | 69 ++- whitebox/api/test_license_builder.h | 35 ++ whitebox/api/test_license_whitebox_keys.cc | 149 ++++++ whitebox/api/test_license_whitebox_keys.h | 24 + whitebox/api/test_public_key.h | 17 - whitebox/benchmarking/BUILD | 2 - whitebox/external/odk.BUILD | 20 +- whitebox/impl/reference/BUILD | 150 ------ whitebox/impl/reference/license_private_key.h | 16 - whitebox/impl/reference/license_test_data.cc | 23 - .../license_whitebox_golden_data_init_data.cc | 115 ---- whitebox/reference/impl/BUILD | 145 +++++ .../impl}/aead_test_data.cc | 0 .../impl}/aead_whitebox_impl.cc | 2 +- whitebox/reference/impl/content_key.h | 43 ++ whitebox/reference/impl/license_parser.cc | 81 +++ whitebox/reference/impl/license_parser.h | 53 ++ .../impl}/license_whitebox_impl.cc | 503 ++++++------------ .../impl}/memory_util.cc | 2 +- .../impl}/memory_util.h | 6 +- .../{impl/reference => reference/impl}/odk.cc | 2 +- .../{impl/reference => reference/impl}/odk.h | 6 +- whitebox/reference/impl/odk_license_parser.cc | 191 +++++++ whitebox/reference/impl/odk_license_parser.h | 36 ++ .../reference/impl/protobuf_license_parser.cc | 191 +++++++ .../reference/impl/protobuf_license_parser.h | 37 ++ whitebox/reference/impl/renewal_key.h | 17 + whitebox/reference/tests/BUILD | 80 +++ whitebox/resources/keygen.py | 121 +++++ 54 files changed, 2634 insertions(+), 1480 deletions(-) create mode 100644 .gitattributes delete mode 100644 whitebox-dev/impl/BUILD delete mode 100644 whitebox-dev/tools/PLACEHOLDER rename {whitebox-dev => whitebox-impl}/WORKSPACE (89%) create mode 100644 whitebox-impl/impl/BUILD create mode 100644 whitebox-impl/tests/BUILD create mode 100644 whitebox-impl/tools/PLACEHOLDER delete mode 100644 whitebox-prod/WORKSPACE delete mode 100644 whitebox-prod/impl/BUILD delete mode 100644 whitebox-prod/tools/PLACEHOLDER delete mode 100644 whitebox/api/license_whitebox_chromeos_test.cc create mode 100644 whitebox/api/license_whitebox_query_content_key_status_test.cc create mode 100644 whitebox/api/license_whitebox_query_signing_key_status_test.cc rename whitebox/api/{license_whitebox_golden_data_test.cc => license_whitebox_uat_test.cc} (99%) create mode 100644 whitebox/api/test_license_whitebox_keys.cc create mode 100644 whitebox/api/test_license_whitebox_keys.h delete mode 100644 whitebox/api/test_public_key.h delete mode 100644 whitebox/impl/reference/BUILD delete mode 100644 whitebox/impl/reference/license_private_key.h delete mode 100644 whitebox/impl/reference/license_test_data.cc delete mode 100644 whitebox/impl/reference/license_whitebox_golden_data_init_data.cc create mode 100644 whitebox/reference/impl/BUILD rename whitebox/{impl/reference => reference/impl}/aead_test_data.cc (100%) rename whitebox/{impl/reference => reference/impl}/aead_whitebox_impl.cc (99%) create mode 100644 whitebox/reference/impl/content_key.h create mode 100644 whitebox/reference/impl/license_parser.cc create mode 100644 whitebox/reference/impl/license_parser.h rename whitebox/{impl/reference => reference/impl}/license_whitebox_impl.cc (61%) rename whitebox/{impl/reference => reference/impl}/memory_util.cc (91%) rename whitebox/{impl/reference => reference/impl}/memory_util.h (85%) rename whitebox/{impl/reference => reference/impl}/odk.cc (98%) rename whitebox/{impl/reference => reference/impl}/odk.h (81%) create mode 100644 whitebox/reference/impl/odk_license_parser.cc create mode 100644 whitebox/reference/impl/odk_license_parser.h create mode 100644 whitebox/reference/impl/protobuf_license_parser.cc create mode 100644 whitebox/reference/impl/protobuf_license_parser.h create mode 100644 whitebox/reference/impl/renewal_key.h create mode 100644 whitebox/reference/tests/BUILD create mode 100755 whitebox/resources/keygen.py 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()