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.
This commit is contained in:
9
.gitattributes
vendored
Normal file
9
.gitattributes
vendored
Normal file
@@ -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
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
bazel-*
|
||||
.DS_Store
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
@@ -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(
|
||||
26
whitebox-impl/impl/BUILD
Normal file
26
whitebox-impl/impl/BUILD
Normal file
@@ -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"],
|
||||
)
|
||||
80
whitebox-impl/tests/BUILD
Normal file
80
whitebox-impl/tests/BUILD
Normal file
@@ -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",
|
||||
],
|
||||
)
|
||||
2
whitebox-impl/tools/PLACEHOLDER
Normal file
2
whitebox-impl/tools/PLACEHOLDER
Normal file
@@ -0,0 +1,2 @@
|
||||
This is a placeholder file to ensure that the "tools" directory appears in the
|
||||
git repo.
|
||||
@@ -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",
|
||||
)
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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
|
||||
//
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,207 +0,0 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#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<Key, RemoteAttestation, VerificationStatus>> {
|
||||
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<uint8_t> 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<uint8_t> 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
|
||||
@@ -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<Padding> {
|
||||
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<uint8_t> 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Padding> {
|
||||
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<uint8_t> masked_text_;
|
||||
|
||||
std::vector<uint8_t> 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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
328
whitebox/api/license_whitebox_query_content_key_status_test.cc
Normal file
328
whitebox/api/license_whitebox_query_content_key_status_test.cc
Normal file
@@ -0,0 +1,328 @@
|
||||
// Copyright 2021 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<std::tuple<LicenseParsing, Padding>> {
|
||||
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
|
||||
271
whitebox/api/license_whitebox_query_signing_key_status_test.cc
Normal file
271
whitebox/api/license_whitebox_query_signing_key_status_test.cc
Normal file
@@ -0,0 +1,271 @@
|
||||
// Copyright 2021 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<LicenseParsing> {
|
||||
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
|
||||
@@ -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
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "api/test_public_key.h"
|
||||
#include "api/test_license_whitebox_keys.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
|
||||
@@ -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(
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
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);
|
||||
|
||||
if (settings.include_content_key_iv) {
|
||||
container->set_iv(key_iv);
|
||||
container->mutable_iv()->resize(settings.content_key_iv_size);
|
||||
}
|
||||
|
||||
if (settings.include_content_key_key) {
|
||||
std::vector<uint8_t> key(key_data.key.begin(), key_data.key.end());
|
||||
|
||||
auto padding = GetPadding(settings.padding);
|
||||
key.insert(key.end(), padding.begin(), padding.end());
|
||||
|
||||
container->set_key(Encrypt(container_key, key_iv, key));
|
||||
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) {
|
||||
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);
|
||||
|
||||
if (settings.include_signing_key_iv) {
|
||||
container->set_iv(key_iv);
|
||||
container->mutable_iv()->resize(settings.signing_key_iv_size);
|
||||
}
|
||||
|
||||
if (settings.include_signing_key_key) {
|
||||
std::vector<uint8_t> key(key_data.begin(), key_data.end());
|
||||
|
||||
auto padding = GetPadding(settings.padding);
|
||||
key.insert(key.end(), padding.begin(), padding.end());
|
||||
|
||||
container->set_key(Encrypt(container_key, key_iv, key));
|
||||
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(
|
||||
|
||||
@@ -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_; }
|
||||
|
||||
|
||||
149
whitebox/api/test_license_whitebox_keys.cc
Normal file
149
whitebox/api/test_license_whitebox_keys.cc
Normal file
@@ -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<uint8_t> GetLicensePrivateKey() {
|
||||
return std::vector<uint8_t>(kDevicePrivateKey,
|
||||
kDevicePrivateKey + sizeof(kDevicePrivateKey));
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetLicensePublicKey() {
|
||||
return std::vector<uint8_t>(kDevicePublicKey,
|
||||
kDevicePublicKey + sizeof(kDevicePublicKey));
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
24
whitebox/api/test_license_whitebox_keys.h
Normal file
24
whitebox/api/test_license_whitebox_keys.h
Normal file
@@ -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 <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
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<uint8_t> 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<uint8_t> GetLicensePublicKey();
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_API_TEST_LICENSE_WHITEBOX_KEYS_H_
|
||||
@@ -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 <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
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<uint8_t> GetLicensePublicKey();
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_API_TEST_PUBLIC_KEY_H_
|
||||
@@ -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",
|
||||
],
|
||||
|
||||
16
whitebox/external/odk.BUILD
vendored
16
whitebox/external/odk.BUILD
vendored
@@ -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,13 +56,13 @@ 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(
|
||||
|
||||
@@ -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"],
|
||||
)
|
||||
@@ -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 <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
namespace widevine {
|
||||
|
||||
// Return a RSA 2048-bit private key.
|
||||
std::vector<uint8_t> GetLicensePrivateKey();
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_IMPL_REFERENCE_LICENSE_PRIVATE_KEY_H_
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "api/test_public_key.h"
|
||||
#include "crypto_utils/rsa_test_keys.h"
|
||||
#include "impl/reference/license_private_key.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
std::vector<uint8_t> GetLicensePrivateKey() {
|
||||
widevine::RsaTestKeys key_generator;
|
||||
std::string init_data = key_generator.private_test_key_2_2048_bits();
|
||||
return std::vector<uint8_t>(init_data.begin(), init_data.end());
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GetLicensePublicKey() {
|
||||
widevine::RsaTestKeys key_generator;
|
||||
std::string init_data = key_generator.public_test_key_2_2048_bits();
|
||||
return std::vector<uint8_t>(init_data.begin(), init_data.end());
|
||||
}
|
||||
|
||||
} // namespace widevine
|
||||
@@ -1,115 +0,0 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "impl/reference/license_private_key.h"
|
||||
|
||||
namespace widevine {
|
||||
|
||||
std::vector<uint8_t> 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
|
||||
145
whitebox/reference/impl/BUILD
Normal file
145
whitebox/reference/impl/BUILD
Normal file
@@ -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",
|
||||
],
|
||||
)
|
||||
@@ -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"
|
||||
|
||||
43
whitebox/reference/impl/content_key.h
Normal file
43
whitebox/reference/impl/content_key.h
Normal file
@@ -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 <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#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<uint8_t, 16u> 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_
|
||||
81
whitebox/reference/impl/license_parser.cc
Normal file
81
whitebox/reference/impl/license_parser.cc
Normal file
@@ -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<const uint8_t*>(key.data()),
|
||||
key.size()));
|
||||
|
||||
return decryptor.Decrypt(
|
||||
reinterpret_cast<const uint8_t*>(iv.data()), iv.size(),
|
||||
reinterpret_cast<const uint8_t*>(encrypted.data()), encrypted.size(),
|
||||
reinterpret_cast<uint8_t*>(&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
|
||||
53
whitebox/reference/impl/license_parser.h
Normal file
53
whitebox/reference/impl/license_parser.h
Normal file
@@ -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 <map>
|
||||
#include <string>
|
||||
#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<std::string, ContentKey>& 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_
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
#include "api/license_whitebox.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<uint8_t> 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<const uint8_t*>(iv.data()), iv.size(),
|
||||
reinterpret_cast<const uint8_t*>(encrypted.data()), encrypted.size(),
|
||||
reinterpret_cast<uint8_t*>(&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<RsaPrivateKey> key;
|
||||
|
||||
// Keys used for license renewal.
|
||||
std::string server_signing_key;
|
||||
std::string client_signing_key;
|
||||
std::unique_ptr<widevine::RenewalKey> renewal_key;
|
||||
|
||||
std::map<std::string, ContentKey> content_keys;
|
||||
std::map<std::string, widevine::ContentKey> content_keys;
|
||||
};
|
||||
|
||||
namespace {
|
||||
@@ -237,7 +126,7 @@ std::vector<uint8_t> GetSecretStringFor(WB_CipherMode mode) {
|
||||
kCTRSecretStringPattern + sizeof(kCTRSecretStringPattern));
|
||||
}
|
||||
|
||||
const ContentKey* FindKey(const WB_License_Whitebox* whitebox,
|
||||
const widevine::ContentKey* FindKey(const WB_License_Whitebox* whitebox,
|
||||
const uint8_t* id,
|
||||
size_t id_size) {
|
||||
DCHECK(whitebox);
|
||||
@@ -249,7 +138,8 @@ const ContentKey* FindKey(const WB_License_Whitebox* whitebox,
|
||||
}
|
||||
|
||||
WB_Result DecryptBuffer(WB_CipherMode mode,
|
||||
const std::vector<uint8_t>& 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<const uint8_t*>(decryption_key.data()),
|
||||
decryption_key.size()));
|
||||
|
||||
std::map<std::string, ContentKey> content_keys;
|
||||
std::vector<std::string> server_renewal_keys;
|
||||
std::vector<std::string> client_renewal_keys;
|
||||
std::unique_ptr<widevine::LicenseParser> 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;
|
||||
parser.reset(new widevine::ProtobufLicenseParser);
|
||||
}
|
||||
|
||||
// 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);
|
||||
WB_Result result = parser->Parse(decryption_key, odk_context, message_str);
|
||||
|
||||
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;
|
||||
if (result != WB_RESULT_OK) {
|
||||
return result;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DCHECK_EQ(server_renewal_keys.size(), client_renewal_keys.size());
|
||||
if (server_renewal_keys.size() > 1) {
|
||||
return WB_RESULT_INVALID_PARAMETER;
|
||||
}
|
||||
// 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;
|
||||
@@ -1,6 +1,6 @@
|
||||
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "impl/reference/memory_util.h"
|
||||
#include "reference/impl/memory_util.h"
|
||||
|
||||
#include <string.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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -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_
|
||||
@@ -1,6 +1,6 @@
|
||||
// cOpyright 2021 Google LLC. All Rights Reserved.
|
||||
|
||||
#include "impl/reference/odk.h"
|
||||
#include "reference/impl/odk.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -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 <cstdint>
|
||||
#include <string>
|
||||
@@ -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_
|
||||
191
whitebox/reference/impl/odk_license_parser.cc
Normal file
191
whitebox/reference/impl/odk_license_parser.cc
Normal file
@@ -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<std::string, ContentKey>& 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
|
||||
36
whitebox/reference/impl/odk_license_parser.h
Normal file
36
whitebox/reference/impl/odk_license_parser.h
Normal file
@@ -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<std::string, ContentKey>& 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<widevine::RenewalKey> renewal_keys_;
|
||||
std::map<std::string, ContentKey> content_keys_;
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_REFERENCE_IMPL_ODK_LICENSE_PARSER_H_
|
||||
191
whitebox/reference/impl/protobuf_license_parser.cc
Normal file
191
whitebox/reference/impl/protobuf_license_parser.cc
Normal file
@@ -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<std::string, ContentKey>& 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
|
||||
37
whitebox/reference/impl/protobuf_license_parser.h
Normal file
37
whitebox/reference/impl/protobuf_license_parser.h
Normal file
@@ -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<std::string, ContentKey>& 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<widevine::RenewalKey> renewal_keys_;
|
||||
std::map<std::string, ContentKey> content_keys_;
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_REFERENCE_IMPL_LICENSE_PARSER_H_
|
||||
17
whitebox/reference/impl/renewal_key.h
Normal file
17
whitebox/reference/impl/renewal_key.h
Normal file
@@ -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<uint8_t, 32> server;
|
||||
std::array<uint8_t, 32> client;
|
||||
};
|
||||
|
||||
} // namespace widevine
|
||||
|
||||
#endif // WHITEBOX_REFERENCE_IMPL_RENEWAL_KEY_H_
|
||||
80
whitebox/reference/tests/BUILD
Normal file
80
whitebox/reference/tests/BUILD
Normal file
@@ -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",
|
||||
],
|
||||
)
|
||||
121
whitebox/resources/keygen.py
Executable file
121
whitebox/resources/keygen.py
Executable file
@@ -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()
|
||||
Reference in New Issue
Block a user