Source release 17.1.0

This commit is contained in:
John "Juce" Bruce
2022-07-07 17:14:31 -07:00
parent 8c17574083
commit 694cf6fb25
2233 changed files with 272026 additions and 223371 deletions

View File

@@ -1,5 +1,5 @@
# Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
# source code may only be used and distributed under the Widevine Master License
# source code may only be used and distributed under the Widevine License
# Agreement.
#
# Refer to the distribution package's integration guide
@@ -13,17 +13,16 @@
'core.gypi',
'util.gypi',
], # Get list of core source files.
'conditions': [
['protobuf_config=="source"', {
# Include protobuf targets used by protobuf_config=='source'
'includes': ['../third_party/protobuf.gypi'],
}],
], # conditions
'variables': {
'has_dual_key%': 'false',
},
'targets': [
{
'toolsets' : [ 'target' ],
'target_name': 'license_protocol',
'type': 'static_library',
'standalone_static_library': 1,
'hard_dependency': 1,
'includes': ['../third_party/protoc.gypi'],
'sources': [
'../core/src/license_protocol.proto',
@@ -33,9 +32,11 @@
},
},
{
'toolsets' : [ 'target' ],
'target_name': 'device_files',
'type': 'static_library',
'standalone_static_library': 1,
'hard_dependency': 1,
'includes': ['../third_party/protoc.gypi'],
'sources': ['../core/src/device_files.proto',],
'variables': {
@@ -43,9 +44,11 @@
},
},
{
'toolsets' : [ 'target' ],
'target_name': 'metrics_proto',
'type': 'static_library',
'standalone_static_library': 1,
'hard_dependency': 1,
'includes': ['../third_party/protoc.gypi'],
'sources': ['../metrics/src/wv_metrics.proto',],
'variables': {
@@ -53,17 +56,22 @@
},
},
{
'toolsets' : [ 'target' ],
'target_name': 'widevine_utils',
'type': 'static_library',
'standalone_static_library': 1,
'hard_dependency': 1,
'include_dirs': [
'../util/include',
],
'sources': [ '<@(wvutil_sources)'],
},
{
'toolsets' : [ 'target' ],
'target_name': 'widevine_cdm_core',
'type': 'static_library',
'standalone_static_library': 1,
'hard_dependency': 1,
'dependencies': [
'device_files',
'license_protocol',
@@ -104,6 +112,16 @@
'sources': [
'../core/src/privacy_crypto_<(privacy_crypto_impl).cpp',
],
'conditions': [
['privacy_crypto_impl=="apple"', {
'link_settings': {
'libraries': [
'$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
'$(SDKROOT)/System/Library/Frameworks/Security.framework',
],
},
}],
],
}], # end else
['oemcrypto_adapter_type=="dynamic"', {
'sources': [
@@ -112,14 +130,21 @@
}],
['oemcrypto_adapter_type=="static"', {
'sources': [
'../core/src/oemcrypto_adapter_static.cpp',
'../core/src/oemcrypto_adapter_static.cpp',
],
}],
# TODO(b/139814713): For testing and internal use only.
['oemcrypto_adapter_type=="static_v15"', {
['oemcrypto_adapter_type=="static_v16"', {
'sources': [
'../core/src/oemcrypto_adapter_static.cpp',
'../core/src/oemcrypto_adapter_static_v16.cpp',
'../core/src/oemcrypto_adapter_static_v17.cpp',
],
}],
['has_dual_key=="true"', {
'defines': ['HAS_DUAL_KEY'],
}],
['support_ota_keybox_functions=="false"', {
'sources': [
'../core/src/oemcrypto_ota_stubs.cpp',
],
}],
],
@@ -128,6 +153,7 @@
# This is the widevine_ce_cdm built as a static library. This name does
# not mean that it uses oemcrypto's static adapter. Control over which
# adapter is used comes from the settings file for the platform.
'toolsets' : [ 'target' ],
'target_name': 'widevine_ce_cdm_static',
'type': 'static_library',
'standalone_static_library': 1,
@@ -167,6 +193,7 @@
],
}, # widevine_cdm_static target
{
'toolsets' : [ 'target' ],
'target_name': 'widevine_ce_cdm_shared',
'type': 'shared_library',
'dependencies': [
@@ -179,6 +206,7 @@
},
},
{
'toolsets' : [ 'target' ],
'target_name': 'dummy',
'type': 'none',
},

145
cdm/cdm_reboot_tests.gyp Normal file
View File

@@ -0,0 +1,145 @@
# Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
# source code may only be used and distributed under the Widevine License
# Agreement.
#
# Any top-level targets in this file (and their dependencies) will be built by
# the CE CDM's ./build.py build system. Refer to the distribution package's
# README for details.
{
'includes': [
'platform_properties.gypi',
],
'variables': {
# Directory where OEMCrypto header, test, and reference files lives.
'oemcrypto_dir': '../oemcrypto',
# Directory where widevine utilities live.
'util_dir': '../util',
'metrics_target': 'cdm.gyp:metrics_proto',
'device_files_target': 'cdm.gyp:device_files',
# The path to the test device cert source file.
'device_cert_path%': 'test/device_cert.cpp',
},
'targets': [{
'toolsets' : [ 'target' ],
'target_name': 'widevine_ce_cdm_reboot_tests',
'sources': [
'test/cdm_reboot_test_main.cpp',
'test/cdm_test_runner.cpp',
'test/create_test_file_system.cpp',
'<(device_cert_path)',
'test/device_cert.h',
'test/test_host.cpp',
'test/test_host.h',
'../core/test/config_test_env.cpp',
'../core/test/fake_provisioning_server.cpp',
'../core/test/http_socket.cpp',
'../core/test/license_holder.cpp',
'../core/test/license_request.cpp',
'../core/test/reboot_test.cpp',
'../core/test/test_base.cpp',
'../core/test/test_printers.cpp',
'../core/test/url_request.cpp',
'../oemcrypto/test/oec_key_deriver.cpp',
'../oemcrypto/test/oec_device_features.cpp',
'../util/test/test_sleep.cpp',
],
'includes': [
'../oemcrypto/odk/src/kdo.gypi',
'../util/libssl_dependency.gypi',
],
'include_dirs': [
'../cdm/include',
'../core/include',
'../core/test',
'../metrics/include',
'../oemcrypto/test',
'../oemcrypto/include',
'../util/include',
'../util/test',
],
'dependencies': [
'cdm.gyp:widevine_ce_cdm_static',
'../oemcrypto/odk/src/odk.gyp:odk',
'../third_party/googletest.gyp:gmock',
'../third_party/googletest.gyp:gtest',
'<(metrics_target)',
'<(device_files_target)',
],
'defines': [
# The methods in util/ are marked as dllimport but in this case are
# being loaded in the static library. So define this so Windows
# doesn't look in a DLL for the implementations.
'CORE_UTIL_IMPLEMENTATION',
'UNIT_TEST',
'CDM_TESTS',
],
'msvs_settings': {
'VCLinkerTool': {
# Additionally, since they are loaded locally, suppress these
# warnings.
'AdditionalOptions': [
'/IGNORE:4049',
'/IGNORE:4217',
],
},
},
'conditions': [
['OS=="ios"', {
'type': 'loadable_module',
'mac_xctest_bundle': '1',
'defines': [
'GTEST_FILTER="<(gtest_filter)"',
],
'dependencies': [
'cdm_unittests.gyp:dummy_app',
],
'sources': [
'test/gtest_xctest_wrapper.mm',
],
'xcode_settings': {
'BUNDLE_LOADER': '$(TEST_HOST)',
'TEST_HOST': '<(PRODUCT_DIR)/dummy_app.app/dummy_app',
'WRAPPER_EXTENSION': 'xctest',
},
}, {
'type': 'executable',
}],
['oemcrypto_lib=="ref"', {
'dependencies': [
'oemcrypto_reference_implementation.gyp:oec_ref',
],
}],
# TODO(b/139814713): For testing and internal use only.
['oemcrypto_adapter_type=="static_v15"', {
'defines': [
# This is used by the unit tests to use some v15 functions.
'TEST_OEMCRYPTO_V15',
],
}],
['oemcrypto_lib=="level3"', {
'sources': [
# The test impl of OEMCrypto_Level3FileSystem and its factory.
'test/level3_file_system_ce_test.h',
'test/level3_file_system_ce_test.cpp',
'test/level3_file_system_ce_test_factory.cpp',
],
'conditions': [
['oemcrypto_adapter_type=="dynamic" ', {
'dependencies': [
'../oemcrypto/level3/oec_level3.gyp:oec_level3_dynamic',
],
}, {
'dependencies': [
'../oemcrypto/level3/oec_level3.gyp:oec_level3_static',
],
}],
],
}],
['oemcrypto_lib=="target"', {
'dependencies': [
'<(oemcrypto_gyp_target)',
],
}],
],
}],
}

View File

@@ -1,9 +1,10 @@
# Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
# source code may only be used and distributed under the Widevine Master License
# source code may only be used and distributed under the Widevine License
# Agreement.
#
# Builds under the CDM ./build.py (target platform) build system
# Refer to the distribution package's README for details.
# Any top-level targets in this file (and their dependencies) will be built by
# the CE CDM's ./build.py build system. Refer to the distribution package's
# README for details.
{
'includes': [
'platform_properties.gypi',
@@ -15,103 +16,262 @@
'util_dir': '../util',
'metrics_target': 'cdm.gyp:metrics_proto',
'device_files_target': 'cdm.gyp:device_files',
# The path to the test device cert source file.
'device_cert_path%': 'test/device_cert.cpp',
# The path to the prebuilt CDM to test against. (iOS only)
'prebuilt_cdm_path%': '',
'prebuilt_cdm_cert%': '',
'supports_dynamic_perf_test%': 0,
},
'targets': [
{
'target_name': 'widevine_ce_cdm_unittest',
'type': 'executable',
'sources': [
# The test runner and the testing device certificate.
'test/cdm_test_main.cpp',
'test/device_cert.cpp',
'test/device_cert.h',
# The test host, which is required for all test suites on CE.
'test/test_host.cpp',
'test/test_host.h',
'../util/test/test_sleep.cpp',
],
'includes': [
'../oemcrypto/test/oemcrypto_unittests.gypi',
'../oemcrypto/odk/src/kdo.gypi',
'cdm_unittests.gypi',
'core_unittests.gypi',
'util_unittests.gypi',
],
'dependencies': [
'cdm.gyp:widevine_ce_cdm_static',
'../third_party/gmock.gyp:gmock',
'../third_party/gmock.gyp:gtest',
],
'defines': [
# The methods in util/ are marked as dllimport but in this case are
# being loaded in the static library. So define this so Windows
# doesn't look in a DLL for the implementations.
'CORE_UTIL_IMPLEMENTATION',
],
'msvs_settings': {
'VCLinkerTool': {
# Additionally, since they are loaded locally, suppress these
# warnings.
'AdditionalOptions': [
'/IGNORE:4049',
'/IGNORE:4217',
],
},
'targets': [{
'toolsets' : [ 'target' ],
'target_name': 'widevine_ce_cdm_unittest',
'sources': [
'test/cdm_test_main.cpp',
'test/cdm_test_runner.cpp',
'test/create_test_file_system.cpp',
'<(device_cert_path)',
'test/device_cert.h',
# The test host, which is required for all test suites on CE.
'test/test_host.cpp',
'test/test_host.h',
'../util/test/test_sleep.cpp',
],
'includes': [
'../oemcrypto/test/oemcrypto_unittests.gypi',
'../oemcrypto/odk/src/kdo.gypi',
'../oemcrypto/odk/test/odk_test.gypi',
'../oemcrypto/util/oec_ref_util_unittests.gypi',
'cdm_unittests.gypi',
'core_unittests.gypi',
'util_unittests.gypi',
],
'dependencies': [
'cdm.gyp:widevine_ce_cdm_static',
'../oemcrypto/util/oec_ref_util.gyp:oec_ref_util',
'../third_party/googletest.gyp:gmock',
'../third_party/googletest.gyp:gtest',
],
'defines': [
# The methods in util/ are marked as dllimport but in this case are
# being loaded in the static library. So define this so Windows
# doesn't look in a DLL for the implementations.
'CORE_UTIL_IMPLEMENTATION',
],
'msvs_settings': {
'VCLinkerTool': {
# Additionally, since they are loaded locally, suppress these
# warnings.
'AdditionalOptions': [
'/IGNORE:4049',
'/IGNORE:4217',
],
},
'conditions': [
['oemcrypto_lib=="ref"', {
'dependencies': [
'oec_ref',
],
}],
# TODO(b/139814713): For testing and internal use only.
['oemcrypto_adapter_type=="static_v15"', {
'defines': [
# This is used by the unit tests to use some v15 functions.
'TEST_OEMCRYPTO_V15',
],
}],
['oemcrypto_lib=="level3"', {
'sources': [
# The test impl of OEMCrypto_Level3FileSystem and its factory.
'test/level3_file_system_ce_test.h',
'test/level3_file_system_ce_test.cpp',
'test/level3_file_system_ce_test_factory.cpp',
],
'conditions': [
['oemcrypto_adapter_type=="dynamic" ', {
'dependencies': [
'../oemcrypto/level3/oec_level3.gyp:oec_level3_dynamic',
],
}, {
'dependencies': [
'../oemcrypto/level3/oec_level3.gyp:oec_level3_static',
},
'conditions': [
['OS=="ios"', {
'type': 'loadable_module',
'mac_xctest_bundle': '1',
'defines': [
'GTEST_FILTER="<(gtest_filter)"',
],
'dependencies': [
'dummy_app',
],
'sources': [
'test/gtest_xctest_wrapper.mm',
],
'xcode_settings': {
'BUNDLE_LOADER': '$(TEST_HOST)',
'INFOPLIST_FILE': 'test/info.plist',
'PRODUCT_BUNDLE_IDENTIFIER': 'EQHXZ8M8AV.widevine_ce_cdm_unittest',
'TEST_HOST': '<(PRODUCT_DIR)/dummy_app.app/dummy_app',
'WRAPPER_EXTENSION': 'xctest',
},
}, {
'type': 'executable',
}],
['oemcrypto_lib=="ref"', {
'dependencies': [
'oemcrypto_reference_implementation.gyp:oec_ref',
],
}],
# TODO(b/139814713): For testing and internal use only.
['oemcrypto_adapter_type=="static_v15"', {
'defines': [
# This is used by the unit tests to use some v15 functions.
'TEST_OEMCRYPTO_V15',
],
}],
['oemcrypto_lib=="level3"', {
'sources': [
# The test impl of OEMCrypto_Level3FileSystem and its factory.
'test/level3_file_system_ce_test.h',
'test/level3_file_system_ce_test.cpp',
'test/level3_file_system_ce_test_factory.cpp',
],
'conditions': [
['oemcrypto_adapter_type=="dynamic" ', {
'dependencies': [
'../oemcrypto/level3/oec_level3.gyp:oec_level3_dynamic',
],
}, {
'dependencies': [
'../oemcrypto/level3/oec_level3.gyp:oec_level3_static',
],
}],
],
}],
['oemcrypto_lib=="target"', {
'dependencies': [
'<(oemcrypto_gyp_target)',
],
}],
],
}],
'conditions': [
['supports_dynamic_perf_test', {
'targets': [{
'target_name': 'widevine_perf_test_dynamic',
'type': 'executable',
'sources': [
'<(device_cert_path)',
'../core/test/config_test_env.cpp',
'../core/test/http_socket.cpp',
'../core/test/license_request.cpp',
'../core/test/url_request.cpp',
'../util/src/string_conversions.cpp',
'../util/test/test_sleep.cpp',
'src/log.cpp',
'test/perf_test.cpp',
'test/perf_test_dynamic.cpp',
'test/test_host.cpp',
],
'includes': [ '../util/libssl_dependency.gypi' ],
'include_dirs': [
'../core/include',
'../core/test',
'../util/include',
'../util/test',
'include',
],
'dependencies': [
'../third_party/googletest.gyp:gmock',
'../third_party/googletest.gyp:gtest',
],
'conditions': [
['OS=="linux"', {
'libraries': [ '-ldl' ],
}],
],
}],
}], # condition: supports_dynamic_perf_test
['OS=="ios"', {
'targets': [{
'toolsets' : [ 'target' ],
'target_name': 'dummy_app',
'type': 'executable',
'mac_bundle': '1',
'sources': [
'test/dummy_app.mm',
],
'libraries': [
'CoreFoundation.framework',
'UIKit.framework',
],
'xcode_settings': {
'INFOPLIST_FILE': 'test/info.plist',
},
'conditions': [
['prebuilt_cdm_path!=""', {
'copies': [{
'destination': '<(PRODUCT_DIR)/dummy_app.app/Frameworks',
'xcode_code_sign': 1,
'files': [
# GYP seems to always use relative paths here, even if they
# start with '/'.
'<!(realpath --relative-to=. "<(prebuilt_cdm_path)")',
],
}],
],
}],
['oemcrypto_lib=="target"', {
'dependencies': [
'<(oemcrypto_gyp_target)',
],
}],
}],
],
}],
'conditions': [
['supports_dynamic_perf_test', {
'targets': [{
'target_name': 'create_cert_cc',
'type': 'none',
'actions': [{
'action_name': 'gen_cert',
'message': 'Generating device_cert.cc',
'msvs_cygwin_shell': 0,
'inputs': [
'create_cert_cc.py',
'<(prebuilt_cdm_cert)',
],
'outputs': [
'<(INTERMEDIATE_DIR)/cert.cc',
],
'action': [
'python',
'create_cert_cc.py',
'<(prebuilt_cdm_cert)',
'<(INTERMEDIATE_DIR)/cert.cc',
],
}],
}, {
'target_name': 'widevine_perf_test_xctest',
'type': 'loadable_module',
'mac_xctest_bundle': '1',
'defines': [
'GTEST_FILTER="<(gtest_filter)"',
],
'xcode_settings': {
'BUNDLE_LOADER': '$(TEST_HOST)',
'INFOPLIST_FILE': 'test/info.plist',
'PRODUCT_BUNDLE_IDENTIFIER': 'EQHXZ8M8AV.widevine_perf_test_xctest',
'TEST_HOST': '<(PRODUCT_DIR)/dummy_app.app/dummy_app',
'WRAPPER_EXTENSION': 'xctest',
'OTHER_LDFLAGS': [
'-iframework',
'<(prebuilt_cdm_path)/..',
],
},
'libraries': [
'<(prebuilt_cdm_path)',
],
'sources': [
'<(INTERMEDIATE_DIR)/cert.cc',
'../core/test/config_test_env.cpp',
'../core/test/http_socket.cpp',
'../core/test/license_request.cpp',
'../core/test/url_request.cpp',
'../util/src/string_conversions.cpp',
'../util/test/test_clock.cpp',
'../util/test/test_sleep.cpp',
'src/log.cpp',
'test/perf_test.cpp',
'test/perf_test_xctest.mm',
'test/test_host.cpp',
],
'includes': [ '../util/libssl_dependency.gypi' ],
'include_dirs': [
'../core/include',
'../core/test',
'../util/include',
'../util/test',
'include',
],
'dependencies': [
'../third_party/googletest.gyp:gmock',
'../third_party/googletest.gyp:gtest',
'create_cert_cc',
'dummy_app',
],
}],
}], # condition: supports_dynamic_perf_test
],
},
{
'target_name': 'oec_ref',
'type': 'static_library',
'standalone_static_library': 1,
'hard_dependency': 1,
'includes': [
'../oemcrypto/ref/oec_ref.gypi',
],
},
{
'target_name': 'oec_ref_shared',
'type': 'shared_library',
'dependencies': [
'oec_ref'
],
},
}], # condition: OS=="ios"
],
}

View File

@@ -1,5 +1,5 @@
# Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
#source code may only be used and distributed under the Widevine Master License
#source code may only be used and distributed under the Widevine License
#Agreement.
#
# Include this in any custom unit test targets.
@@ -21,6 +21,13 @@
'../util/test/test_sleep.cpp',
],
'conditions': [
['support_ota_keybox_functions=="true"', {
'sources': [
'../core/test/keybox_ota_test.cpp',
],
}],
],
'include_dirs': [
'../core/test',
'../util/test',

View File

@@ -1,5 +1,5 @@
# Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
#source code may only be used and distributed under the Widevine Master License
#source code may only be used and distributed under the Widevine License
#Agreement.
#
# Include this in any custom unit test targets.
@@ -19,6 +19,7 @@
'../core/include/content_key_session.h',
'../core/include/crypto_key.h',
'../core/include/crypto_session.h',
'../core/include/crypto_wrapped_key.h',
'../core/include/device_files.h',
'../core/include/entitlement_key_session.h',
'../core/include/initialization_data.h',
@@ -26,6 +27,9 @@
'../core/include/license_key_status.h',
'../core/include/license.h',
'../core/include/oemcrypto_adapter.h',
'../core/include/okp_fallback_policy.h',
'../core/include/okp_info.h',
'../core/include/ota_keybox_provisioner.h',
'../core/include/policy_engine.h',
'../core/include/policy_timers.h',
'../core/include/policy_timers_v15.h',
@@ -51,13 +55,18 @@
'../core/src/initialization_data.cpp',
'../core/src/license.cpp',
'../core/src/license_key_status.cpp',
'../core/src/okp_fallback_policy.cpp',
'../core/src/okp_info.cpp',
'../core/src/ota_keybox_provisioner.cpp',
'../core/src/policy_engine.cpp',
'../core/src/policy_timers.cpp',
'../core/src/policy_timers_v15.cpp',
'../core/src/policy_timers_v16.cpp',
'../core/src/properties.cpp',
'../core/src/service_certificate.cpp',
'../core/src/system_id_extractor.cpp',
'../core/src/usage_table_header.cpp',
'../core/src/wv_cdm_types.cpp',
'../metrics/src/attribute_handler.cpp',
'../metrics/src/counter_metric.cpp',
'../metrics/src/distribution.cpp',

View File

@@ -1,5 +1,5 @@
# Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
#source code may only be used and distributed under the Widevine Master License
#source code may only be used and distributed under the Widevine License
#Agreement.
#
# Include this in any custom unit test targets.
@@ -14,19 +14,25 @@
'../core/test/certificate_provisioning_unittest.cpp',
'../core/test/crypto_session_unittest.cpp',
'../core/test/device_files_unittest.cpp',
'../core/test/duration_use_case_test.cpp',
'../core/test/fake_provisioning_server.cpp',
'../core/test/generic_crypto_unittest.cpp',
'../core/test/http_socket.cpp',
'../core/test/http_socket_test.cpp',
'../core/test/initialization_data_unittest.cpp',
'../core/test/license_holder.cpp',
'../core/test/license_keys_unittest.cpp',
'../core/test/license_request.cpp',
'../core/test/license_unittest.cpp',
'../core/test/okp_fallback_policy_test.cpp',
'../core/test/ota_keybox_provisioner_test.cpp',
'../core/test/parallel_operations_test.cpp',
'../core/test/policy_engine_constraints_unittest.cpp',
'../core/test/policy_engine_unittest.cpp',
'../core/test/policy_integration_test.cpp',
'../core/test/rw_lock_test.cpp',
'../core/test/service_certificate_unittest.cpp',
'../core/test/system_id_extractor_unittest.cpp',
'../core/test/test_base.cpp',
'../core/test/test_printers.cpp',
'../core/test/url_request.cpp',

30
cdm/create_cert_cc.py Executable file
View File

@@ -0,0 +1,30 @@
#!/usr/bin/python3
# Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
# source code may only be used and distributed under the Widevine License
# Agreement.
"""Creates a cert.cc file from a cert.bin file."""
import argparse
import pathlib
import sys
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('input')
parser.add_argument('output')
parser.add_argument('--variable', default='DeviceCert')
options = parser.parse_args(sys.argv[1:])
in_path = pathlib.Path(options.input)
dat = ', '.join(str(x) for x in in_path.read_bytes())
out_path = pathlib.Path(options.output)
out_path.write_text(f"""// Copyright 2021 Google LLC. All Rights Reserved.
#include <stdint.h>
#include <stddef.h>
extern const uint8_t k{options.variable}[];
const uint8_t k{options.variable}[] = {{ {dat} }};
extern const size_t k{options.variable}Size;
const size_t k{options.variable}Size = sizeof(k{options.variable});""")

View File

@@ -1,6 +1,6 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// source code may only be used and distributed under the Widevine License
// Agreement.
// Based on the EME draft spec from 2016 June 10.
// http://www.w3.org/TR/2016/WD-encrypted-media-20160610/"
#ifndef WVCDM_CDM_CDM_H_
@@ -31,12 +31,13 @@ namespace widevine {
class CDM_EXPORT ITimerClient {
public:
virtual ~ITimerClient() {}
// Called by ITimer when a timer expires.
virtual void onTimerExpired(void* context) = 0;
protected:
ITimerClient() {}
virtual ~ITimerClient() {}
};
class CDM_EXPORT Cdm : public ITimerClient {
@@ -194,6 +195,14 @@ class CDM_EXPORT Cdm : public ITimerClient {
kL3 = 3,
};
// Status code returned by getProvisioningStatus API.
enum ProvisioningStatus : int32_t {
kProvisioned = 0,
kUnknownProvisionStatus = 1,
kNeedsDrmCertProvisioning = 2,
kNeedsOemCertProvisioning = 3,
};
// A map of key statuses.
// See Cdm::getKeyStatuses().
typedef std::map<std::string, KeyStatus> KeyStatusMap;
@@ -203,6 +212,8 @@ class CDM_EXPORT Cdm : public ITimerClient {
// See Cdm::createSession().
class IEventListener {
public:
virtual ~IEventListener() {}
// A message (license request, renewal, etc.) to be dispatched to the
// application's license server.
// The response, if successful, should be provided back to the CDM via a
@@ -220,7 +231,6 @@ class CDM_EXPORT Cdm : public ITimerClient {
protected:
IEventListener() {}
virtual ~IEventListener() {}
};
// A storage interface provided by the application. This defines the "origin"
@@ -240,6 +250,8 @@ class CDM_EXPORT Cdm : public ITimerClient {
// See http://www.w3.org/TR/encrypted-media/#privacy-storedinfo.
class IStorage {
public:
virtual ~IStorage() {}
virtual bool read(const std::string& name, std::string* data) = 0;
virtual bool write(const std::string& name, const std::string& data) = 0;
virtual bool exists(const std::string& name) = 0;
@@ -256,7 +268,6 @@ class CDM_EXPORT Cdm : public ITimerClient {
protected:
IStorage() {}
virtual ~IStorage() {}
};
// A clock interface provided by the application, independent of CDM
@@ -264,12 +275,13 @@ class CDM_EXPORT Cdm : public ITimerClient {
// See Cdm::initialize().
class IClock {
public:
virtual ~IClock() {}
// Returns the current time in milliseconds since 1970 UTC.
virtual int64_t now() = 0;
protected:
IClock() {}
virtual ~IClock() {}
};
// A timer interface provided by the application, independent of CDM
@@ -284,6 +296,8 @@ class CDM_EXPORT Cdm : public ITimerClient {
// setTimeout() again inside the timeout callback.
class ITimer {
public:
virtual ~ITimer() {}
// This typedef is for backward compatibility with v3.0.0.
typedef ITimerClient IClient;
@@ -296,38 +310,40 @@ class CDM_EXPORT Cdm : public ITimerClient {
protected:
ITimer() {}
virtual ~ITimer() {}
};
// Client information, provided by the application, independent of CDM
// instances.
// See Cdm::initialize().
// These parameters end up as client identification in license requests.
// All fields may be used by a license server proxy to drive business logic.
// Some fields are required (indicated below), but please fill out as many
// as make sense for your application.
// No user-identifying information may be put in these fields!
// The CE CDM has various pieces of client information baked into it at
// compile-time. These can be retrieved at runtime by Cdm::getClientInfo(),
// which returns them in this struct.
// These parameters match the client identification in license requests.
struct ClientInfo {
// The name of the product or application, e.g. "TurtleTube"
// Required.
std::string product_name;
// The name of the company who makes the device, e.g. "Kubrick, Inc."
// Required.
// The name of the company who makes the client, e.g. "KubrickTech".
// This matches the "Make" field in the Widevine Integration Console.
std::string company_name;
// The name of the device, e.g. "HAL"
std::string device_name;
// The device model, e.g. "HAL 9000"
// Required.
// The client's model name, e.g. "HAL 9000".
// This matches the "Model" field in the Widevine Integration Console.
std::string model_name;
// The architecture of the device, e.g. "x86-64"
// The client's model year, e.g. "2001".
// Can be used to distinguish different devices with the same model name.
// This matches the "Year" field in the Widevine Integration Console.
std::string model_year;
// The name or codename of the product or application, e.g. "clarke".
// This may be the same as "model_name".
std::string product_name;
// The name or codename of the client, e.g. "HAL".
// This may be the same as "model_name" or "product_name".
std::string device_name;
// The CPU architecture of the client, e.g. "ARMv7".
std::string arch_name;
// Information about the build of the browser, application, or platform into
// which the CDM is integrated, e.g. "v2.71828, 2038-01-19-03:14:07"
// A string containing build information about the CE CDM and the client
// it's integrated into. Consists of multiple values separated by spaces
// and vertical pipes. (e.g. " | ")
std::string build_info;
};
@@ -338,21 +354,23 @@ class CDM_EXPORT Cdm : public ITimerClient {
// Logging is controlled by |verbosity|.
// Must be called and must return kSuccess before create() is called.
static Status initialize(SecureOutputType secure_output_type,
const ClientInfo& client_info, IStorage* storage,
IClock* clock, ITimer* timer, LogLevel verbosity);
IStorage* storage, IClock* clock, ITimer* timer,
LogLevel verbosity);
// This is a variant of the above function that allows the caller to pass a
// Sandbox ID. Platforms that use Sandbox IDs should use this initalize()
// function instead of the previous one. Platforms that do not use Sandbox IDs
// should not use this version of initialize().
static Status initialize(SecureOutputType secure_output_type,
const ClientInfo& client_info, IStorage* storage,
IClock* clock, ITimer* timer, LogLevel verbosity,
const std::string& sandbox_id);
IStorage* storage, IClock* clock, ITimer* timer,
LogLevel verbosity, const std::string& sandbox_id);
// Query the CDM library version.
static const char* version();
// Retrieves the client information for this CE CDM integration.
static Status getClientInfo(ClientInfo* client_info);
// Constructs a new CDM instance.
// initialize() must be called first and must return kSuccess before a CDM
// instance may be constructed.
@@ -387,7 +405,7 @@ class CDM_EXPORT Cdm : public ITimerClient {
static Cdm* create(IEventListener* listener, IStorage* storage,
bool privacy_mode, bool storage_is_read_only);
virtual ~Cdm() {}
~Cdm() override {}
// The following three methods relate to service certificates. A service
// certificate holds the RSA public key for a server, as well as other fields
@@ -449,6 +467,9 @@ class CDM_EXPORT Cdm : public ITimerClient {
// and the license service should be used to make security decisions.
virtual Status getRobustnessLevel(RobustnessLevel* level) = 0;
// Query the underlying OEMCrypto implementation's System ID.
virtual Status getSystemId(uint32_t* id) = 0;
// Returns the resource rating tier of the device, as reported by OEMCrypto.
virtual Status getResourceRatingTier(uint32_t* tier) = 0;
@@ -456,10 +477,10 @@ class CDM_EXPORT Cdm : public ITimerClient {
// implementation.
virtual Status getOemCryptoBuildInfo(std::string* build_info) = 0;
// Determine if the device has a Device Certificate (for the current origin).
// The Device Certificate is origin-specific, and the origin is
// dertermined by the CDM's current IStorage object.
virtual bool isProvisioned() = 0;
// Retrieves the current provisioning status. The Device Certificate is
// origin-specific, and the origin is determined by the CDM's current IStorage
// object.
virtual ProvisioningStatus getProvisioningStatus() = 0;
// Creates a Provisioning Request message.
// This is used to provision the device. The request should be sent to the

View File

@@ -1,5 +1,25 @@
// Copyright 2015 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#define CDM_VERSION_STR_(x, y, z, w) #x "." #y "." #z w
#define CDM_VERSION_STR(x, y, z, w) CDM_VERSION_STR_(x, y, z, w)
// Widevine CE CDM Version
#ifndef CDM_VERSION
# define CDM_VERSION "16.4.0"
#ifndef CDM_VERSION_MAJOR
# define CDM_VERSION_MAJOR 17
#endif
#ifndef CDM_VERSION_MINOR
# define CDM_VERSION_MINOR 1
#endif
#ifndef CDM_VERSION_PATCH
# define CDM_VERSION_PATCH 0
#endif
#ifndef CDM_VERSION_TAG
# define CDM_VERSION_TAG ""
#endif
#define CDM_VERSION \
CDM_VERSION_STR(CDM_VERSION_MAJOR, CDM_VERSION_MINOR, CDM_VERSION_PATCH, \
CDM_VERSION_TAG)
#define EME_VERSION "https://www.w3.org/TR/2017/REC-encrypted-media-20170918"

View File

@@ -0,0 +1,42 @@
// Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_CDM_COMPILER_DETECTION_H_
#define WVCDM_CDM_COMPILER_DETECTION_H_
// This file makes some best-effort attempts to detect details about the
// compilation environment used by CacheBuildInfo.
#if defined(CDM_DISABLE_LOGGING)
# define LOGGING_MESSAGE "Logging Disabled"
#else
# define LOGGING_MESSAGE "Logging Enabled"
#endif
#if defined(_DEBUG)
# define BUILD_FLAVOR_MESSAGE "Debug"
#elif defined(NDEBUG)
# define BUILD_FLAVOR_MESSAGE "Release"
#else
# define BUILD_FLAVOR_MESSAGE "Unknown"
#endif
#if defined(__x86_64__) || defined(_M_X64)
# define CPU_ARCH_MESSAGE "x86-64"
#elif defined(__i386__) || defined(_M_IX86)
# define CPU_ARCH_MESSAGE "i386"
#elif defined(__aarch64__) || defined(_M_ARM64)
# define CPU_ARCH_MESSAGE "AArch64"
#elif defined(__arm__) || defined(_M_ARM)
# define CPU_ARCH_MESSAGE "AArch32"
#elif defined(__mips__)
# define CPU_ARCH_MESSAGE "MIPS"
#elif defined(__powerpc64__)
# define CPU_ARCH_MESSAGE "64-bit PowerPC"
#elif defined(__powerpc__) || defined(_M_PPC)
# define CPU_ARCH_MESSAGE "32-bit PowerPC"
#else
# define CPU_ARCH_MESSAGE "Unrecognized CPU"
#endif
#endif // WVCDM_CDM_COMPILER_DETECTION_H_

View File

@@ -1,6 +1,6 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_CDM_PROPERTIES_CE_H_
#define WVCDM_CDM_PROPERTIES_CE_H_
@@ -14,7 +14,6 @@ namespace widevine {
class PropertiesCE {
public:
static Cdm::ClientInfo GetClientInfo();
static Cdm::SecureOutputType GetSecureOutputType();
static void SetProvisioningMessagesAreBinary(bool bin_prov);
// If this is set to an empty string, (which is the default) then PropertiesCE
@@ -23,7 +22,6 @@ class PropertiesCE {
private:
static void SetSecureOutputType(Cdm::SecureOutputType secure_output_type);
static void SetClientInfo(const Cdm::ClientInfo& client_info);
friend class Cdm;
#if defined(UNIT_TEST)

View File

@@ -0,0 +1,35 @@
# Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
# source code may only be used and distributed under the Widevine License
# Agreement.
{
'includes': [
'platform_properties.gypi',
],
'variables': {
# Directory where OEMCrypto header, test, and reference files lives.
'oemcrypto_dir': '../oemcrypto',
# Directory where widevine utilities live.
'util_dir': '../util',
},
'targets': [
{
'target_name': 'oec_ref',
'type': 'static_library',
'standalone_static_library': 1,
'hard_dependency': 1,
'includes': [
'../oemcrypto/ref/oec_ref.gypi',
],
'dependencies': [
'<(oemcrypto_dir)/util/oec_ref_util.gyp:oec_ref_util',
],
},
{
'target_name': 'oec_ref_shared',
'type': 'shared_library',
'dependencies': [
'oec_ref'
],
},
],
}

View File

@@ -1,6 +1,6 @@
# Copyright 2020 Google LLC. All Rights Reserved. This file and proprietary
# source code may only be used and distributed under the Widevine Master
# License Agreement.
# source code may only be used and distributed under the Widevine License
# Agreement.
# This file contains all the variables that Widevine exposes for integration
# partners to customize in their platforms' settings.gypi files. Partners will
@@ -16,6 +16,44 @@
# achieve.
{
'variables': {
# The following variables define the client info that is baked into the CDM
# binary at build-time. These parameters end up as client identification in
# license requests. All values may be used by a license server proxy to
# drive business logic. You *must* set meaningful values for all of these
# variables as part of defining your platform. Values may not contain
# double quotes, percent signs, or the substring " | ". (a vertical pipe
# surrounded by spaces)
# The name of the company who makes the client, e.g. "KubrickTech".
# This should match the "Make" field in the Widevine Integration Console.
'client_company_name%': '',
# The client's model name, e.g. "HAL 9000".
# This should match the "Model" field in the Widevine Integration Console.
'client_model_name%': '',
# The client's model year, e.g. "2001".
# Can be used to distinguish different devices with the same model name.
# This should match the "Year" field in the Widevine Integration Console.
'client_model_year%': '',
# The name or codename of the product or application, e.g. "clarke".
# This may be the same as "client_model_name".
'client_product_name%': '',
# The name or codename of the client, e.g. "HAL".
# This may be the same as "client_model_name" or "client_product_name".
'client_device_name%': '',
# The CPU architecture of the client, e.g. "ARMv7".
'client_arch_name%': '',
# The OS platform of the client, e.g. "Linux".
# This should match the "Platform" field in the Widevine Integration
# Console.
'client_platform%': '',
# The form factor of the client, e.g. "TV".
# This should match the "Type" field in the Widevine Integration Console.
'client_form_factor%': '',
# The version number of the client that the CE CDM is integrated into. This
# is the operating system or app version, not the CDM version.
'client_version%': '',
# Choose type of OEMCrypto library to compile in. Valid values are:
#
# 'vendor' - Production Level 1 systems should use 'vendor' to indicate that
@@ -47,6 +85,12 @@
# other values - for internal testing.
'oemcrypto_adapter_type%': 'static',
# Whether to include OTA Keybox functionality or not. This is an optional
# feature that is only used a few platforms. Generally, to qualify as an L1
# device, a keybox must be installed on the device in the factory.
# Valid values are 'true' or 'false'.
'support_ota_keybox_functions%': 'false',
# Override this to indicate what CPU architecture's assembly-language files
# should be used when building assembly language files. Or, set it to
# "none" to turn off the use of assembly language. The default is "none" for
@@ -58,7 +102,7 @@
#
# Valid values are:
# * x86
# * x86-64
# * x64 or x86-64
# * arm
# * arm64
# * ppc64
@@ -96,8 +140,8 @@
# When using BoringSSL, the Widevine CE CDM defaults to the copy of
# BoringSSL in third_party/boringssl/. If you have an alternative BoringSSL
# GYP build, you can override these variables to point to it instead.
'boringssl_libcrypto_path%': '../third_party/boringssl/boringssl.gyp:crypto',
'boringssl_libssl_path%': '../third_party/boringssl/boringssl.gyp:ssl',
'boringssl_libcrypto_path%': '<(DEPTH)/third_party/boringssl/boringssl.gyp:crypto',
'boringssl_libssl_path%': '<(DEPTH)/third_party/boringssl/boringssl.gyp:ssl',
# There are three protobuf configurations:
#
@@ -113,10 +157,29 @@
# Specify the path to protoc in protoc_bin.
#
# 3) protobuf_config == 'source' (default)
# Build protobuf and protoc from source.
# Specify the path to the protobuf source in protobuf_source.
# Build protobuf and protoc from the copy in third_party/protobuf.
'protobuf_config%': 'source',
'protobuf_source%': '../third_party/protobuf',
'protoc_host_target%': 'dummy',
'protoc_bin%': '',
'protoc_lib%': '',
'protoc_lib_target%': '',
'protoc_host_target%': '',
}, # variables
# This block defines preprocessor values that match the client information
# variables above. If you are writing your own build files to build CE CDM
# insted of using these GYP files, make sure to define these values, following
# the rules in the client info section above.
'target_defaults': {
'defines': [
'CLIENT_COMPANY_NAME="<(client_company_name)"',
'CLIENT_MODEL_NAME="<(client_model_name)"',
'CLIENT_MODEL_YEAR="<(client_model_year)"',
'CLIENT_PRODUCT_NAME="<(client_product_name)"',
'CLIENT_DEVICE_NAME="<(client_device_name)"',
'CLIENT_ARCH_NAME="<(client_arch_name)"',
'CLIENT_PLATFORM="<(client_platform)"',
'CLIENT_FORM_FACTOR="<(client_form_factor)"',
'CLIENT_VERSION="<(client_version)"',
],
},
}

View File

@@ -1,11 +1,11 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "cdm.h"
#include <assert.h>
#include <limits.h> // LLONG_MAX
#include <string.h> // memcpy
#include <string.h>
#include <algorithm>
#include <iterator>
@@ -37,13 +37,50 @@
namespace widevine {
using namespace wvcdm;
using namespace wvutil;
namespace {
constexpr const char* kNoSandboxId = "";
constexpr char kNoSandboxId[] = "";
const int64_t kPolicyTimerDurationMilliseconds = 5000;
void* const kPolicyTimerContext = nullptr;
#define STRINGIFY(PARAM) #PARAM
bool isClientInfoValid(const char* tag, const char* value) {
constexpr char kForbiddenSeparator[] = " | ";
constexpr char kForbiddenPercent[] = "%";
if (value[0] == '\0') {
LOGE("%s may not be empty, but it is.", tag);
return false;
}
if (strstr(value, kForbiddenSeparator) != nullptr) {
LOGE("%s may not contain \"%s\", but it's \"%s\"", tag, kForbiddenSeparator,
value);
return false;
}
if (strstr(value, kForbiddenPercent) != nullptr) {
LOGE("%s may not contain \"%s\", but it's \"%s\"", tag, kForbiddenPercent,
value);
return false;
}
return true;
}
bool clientInfoIsValid() {
return isClientInfoValid(STRINGIFY(CLIENT_COMPANY_NAME),
CLIENT_COMPANY_NAME) &&
isClientInfoValid(STRINGIFY(CLIENT_MODEL_NAME), CLIENT_MODEL_NAME) &&
isClientInfoValid(STRINGIFY(CLIENT_MODEL_YEAR), CLIENT_MODEL_YEAR) &&
isClientInfoValid(STRINGIFY(CLIENT_PRODUCT_NAME),
CLIENT_PRODUCT_NAME) &&
isClientInfoValid(STRINGIFY(CLIENT_DEVICE_NAME), CLIENT_DEVICE_NAME) &&
isClientInfoValid(STRINGIFY(CLIENT_ARCH_NAME), CLIENT_ARCH_NAME) &&
isClientInfoValid(STRINGIFY(CLIENT_PLATFORM), CLIENT_PLATFORM) &&
isClientInfoValid(STRINGIFY(CLIENT_FORM_FACTOR), CLIENT_FORM_FACTOR) &&
isClientInfoValid(STRINGIFY(CLIENT_VERSION), CLIENT_VERSION);
}
struct HostType {
Cdm::IStorage* storage;
Cdm::IClock* clock;
@@ -197,11 +234,13 @@ class CdmImpl final : public Cdm, public WvCdmEventListener {
Status getRobustnessLevel(RobustnessLevel* level) override;
Status getSystemId(uint32_t* id) override;
Status getResourceRatingTier(uint32_t* tier) override;
Status getOemCryptoBuildInfo(std::string* build_info) override;
bool isProvisioned() override;
ProvisioningStatus getProvisioningStatus() override;
Status getProvisioningRequest(std::string* request) override;
@@ -446,6 +485,27 @@ Cdm::Status CdmImpl::getRobustnessLevel(RobustnessLevel* level) {
return kSuccess;
}
Cdm::Status CdmImpl::getSystemId(uint32_t* id) {
if (id == nullptr) {
LOGE("Missing id parameter to receive system ID.");
return kTypeError;
}
std::string id_string;
const CdmResponseType result =
cdm_engine_->QueryStatus(kLevelDefault, QUERY_KEY_SYSTEM_ID, &id_string);
if (result == SYSTEM_INVALIDATED_ERROR) {
LOGE("System invalidated");
return kSystemStateLost;
} else if (result != NO_ERROR) {
LOGE("Unexpected error %d", static_cast<int>(result));
return kUnexpectedError;
}
*id = static_cast<uint32_t>(std::stoul(id_string));
return kSuccess;
}
Cdm::Status CdmImpl::getResourceRatingTier(uint32_t* tier) {
if (tier == nullptr) {
LOGE("Missing tier parameter to receive resource rating tier.");
@@ -492,8 +552,22 @@ Cdm::Status CdmImpl::getOemCryptoBuildInfo(std::string* build_info) {
return kSuccess;
}
bool CdmImpl::isProvisioned() {
return cdm_engine_->IsProvisioned(kSecurityLevelL1);
Cdm::ProvisioningStatus CdmImpl::getProvisioningStatus() {
const CdmProvisioningStatus status =
cdm_engine_->GetProvisioningStatus(kSecurityLevelL1);
switch (status) {
case CdmProvisioningStatus::kProvisioned:
return kProvisioned;
case CdmProvisioningStatus::kNeedsDrmCertProvisioning:
return kNeedsDrmCertProvisioning;
case CdmProvisioningStatus::kNeedsOemCertProvisioning:
return kNeedsOemCertProvisioning;
case CdmProvisioningStatus::kUnknownProvisionStatus:
LOGW("Unknown provisioning status");
return kUnknownProvisionStatus;
}
LOGE("Unexpected provisioning status %d", static_cast<int>(status));
return kUnknownProvisionStatus;
}
Cdm::Status CdmImpl::getProvisioningRequest(std::string* request) {
@@ -1654,17 +1728,16 @@ Cdm::Status CdmImpl::ConvertHdcpLevel(const std::string& query_value,
// static
Cdm::Status Cdm::initialize(SecureOutputType secure_output_type,
const ClientInfo& client_info, IStorage* storage,
IClock* clock, ITimer* timer, LogLevel verbosity) {
return initialize(secure_output_type, client_info, storage, clock, timer,
verbosity, kNoSandboxId);
IStorage* storage, IClock* clock, ITimer* timer,
LogLevel verbosity) {
return initialize(secure_output_type, storage, clock, timer, verbosity,
kNoSandboxId);
}
// static
Cdm::Status Cdm::initialize(SecureOutputType secure_output_type,
const ClientInfo& client_info, IStorage* storage,
IClock* clock, ITimer* timer, LogLevel verbosity,
const std::string& sandbox_id) {
IStorage* storage, IClock* clock, ITimer* timer,
LogLevel verbosity, const std::string& sandbox_id) {
// Specify the maximum severity of message that will be output to
// the console. See core/include/log.h for the valid priority values.
g_cutoff = static_cast<LogPriority>(verbosity);
@@ -1679,11 +1752,7 @@ Cdm::Status Cdm::initialize(SecureOutputType secure_output_type,
return kTypeError;
}
if (client_info.product_name.empty() || client_info.company_name.empty() ||
client_info.model_name.empty()) {
LOGE("Client info requires product_name, company_name, model_name!");
return kTypeError;
}
if (!clientInfoIsValid()) return kUnexpectedError;
if (!storage || !clock || !timer) {
LOGE("All interfaces are required!");
@@ -1691,7 +1760,6 @@ Cdm::Status Cdm::initialize(SecureOutputType secure_output_type,
}
PropertiesCE::SetSecureOutputType(secure_output_type);
PropertiesCE::SetClientInfo(client_info);
if (sandbox_id != kNoSandboxId) PropertiesCE::SetSandboxId(sandbox_id);
Properties::Init();
host.storage = storage;
@@ -1704,6 +1772,53 @@ Cdm::Status Cdm::initialize(SecureOutputType secure_output_type,
// static
const char* Cdm::version() { return CDM_VERSION; }
// static
Cdm::Status Cdm::getClientInfo(ClientInfo* client_info) {
if (client_info == nullptr) {
LOGE("Missing client_info parameter.");
return kTypeError;
}
if (!clientInfoIsValid()) return kUnexpectedError;
if (!Properties::GetCompanyName(&client_info->company_name)) {
LOGE("Unable to get Company Name.");
return kUnexpectedError;
}
if (!Properties::GetModelName(&client_info->model_name)) {
LOGE("Unable to get Model Name.");
return kUnexpectedError;
}
if (!Properties::GetModelYear(&client_info->model_year)) {
LOGE("Unable to get Model Year.");
return kUnexpectedError;
}
if (!Properties::GetDeviceName(&client_info->device_name)) {
LOGE("Unable to get Device Name.");
return kUnexpectedError;
}
if (!Properties::GetProductName(&client_info->product_name)) {
LOGE("Unable to get Product Name.");
return kUnexpectedError;
}
if (!Properties::GetArchitectureName(&client_info->arch_name)) {
LOGE("Unable to get Architecture Name.");
return kUnexpectedError;
}
if (!Properties::GetBuildInfo(&client_info->build_info)) {
LOGE("Unable to get Build Info.");
return kUnexpectedError;
}
return kSuccess;
}
// static
Cdm* Cdm::create(IEventListener* listener, IStorage* storage,
bool privacy_mode) {
@@ -1739,7 +1854,7 @@ Cdm* Cdm::create(IEventListener* listener, IStorage* storage, bool privacy_mode,
} // namespace widevine
// Missing symbols from core:
namespace wvcdm {
namespace wvutil {
using namespace widevine;
@@ -1840,4 +1955,4 @@ bool FileSystem::List(const std::string&,
return file_names && impl_->storage_->list(file_names);
}
} // namespace wvcdm
} // namespace wvutil

View File

@@ -1,6 +1,6 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// Log - implemented using stderr.
@@ -11,9 +11,9 @@
#include <stdlib.h>
#include <string.h>
namespace wvcdm {
namespace wvutil {
LogPriority g_cutoff = LOG_WARN;
LogPriority g_cutoff = CDM_LOG_WARN;
void InitLogging() {}
@@ -39,4 +39,4 @@ void Log(const char* file, const char* function, int line, LogPriority level,
fflush(stderr);
}
} // namespace wvcdm
} // namespace wvutil

View File

@@ -1,9 +1,15 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "properties_ce.h"
#include <stdio.h>
#include <memory>
#include <string>
#include "cdm_version.h"
#include "compiler_detection.h"
#include "log.h"
#include "properties.h"
@@ -19,16 +25,20 @@ std::string sandbox_id_;
widevine::Cdm::SecureOutputType secure_output_type_ =
widevine::Cdm::kNoSecureOutput;
widevine::Cdm::ClientInfo client_info_;
bool GetValue(const std::string& source, std::string* output) {
if (!output) {
bool GetValue(const char* source, std::string* output) {
if (!source || !output) {
return false;
}
*output = source;
return source.size() != 0;
return source[0] != '\0';
}
constexpr char kBuildInfo[] = CLIENT_VERSION
" | " CLIENT_PLATFORM " | " CLIENT_FORM_FACTOR " | " CLIENT_ARCH_NAME
" | CE CDM " CDM_VERSION " | " CPU_ARCH_MESSAGE " | " LOGGING_MESSAGE
" | " BUILD_FLAVOR_MESSAGE;
} // namespace
namespace widevine {
@@ -63,14 +73,6 @@ Cdm::SecureOutputType PropertiesCE::GetSecureOutputType() {
return secure_output_type_;
}
// static
void PropertiesCE::SetClientInfo(const Cdm::ClientInfo& client_info) {
client_info_ = client_info;
}
// static
Cdm::ClientInfo PropertiesCE::GetClientInfo() { return client_info_; }
// static
void PropertiesCE::SetProvisioningMessagesAreBinary(bool new_setting) {
set_provisioning_messages_to_binary_ = new_setting;
@@ -100,32 +102,37 @@ void Properties::InitOnce() {
// static
bool Properties::GetCompanyName(std::string* company_name) {
return GetValue(client_info_.company_name, company_name);
return GetValue(CLIENT_COMPANY_NAME, company_name);
}
// static
bool Properties::GetModelName(std::string* model_name) {
return GetValue(client_info_.model_name, model_name);
return GetValue(CLIENT_MODEL_NAME, model_name);
}
// static
bool Properties::GetModelYear(std::string* model_year) {
return GetValue(CLIENT_MODEL_YEAR, model_year);
}
// static
bool Properties::GetArchitectureName(std::string* arch_name) {
return GetValue(client_info_.arch_name, arch_name);
return GetValue(CLIENT_ARCH_NAME, arch_name);
}
// static
bool Properties::GetDeviceName(std::string* device_name) {
return GetValue(client_info_.device_name, device_name);
return GetValue(CLIENT_DEVICE_NAME, device_name);
}
// static
bool Properties::GetProductName(std::string* product_name) {
return GetValue(client_info_.product_name, product_name);
return GetValue(CLIENT_PRODUCT_NAME, product_name);
}
// static
bool Properties::GetBuildInfo(std::string* build_info) {
return GetValue(client_info_.build_info, build_info);
return GetValue(kBuildInfo, build_info);
}
// static
@@ -151,7 +158,13 @@ bool Properties::GetFactoryKeyboxPath(std::string*) {
// static
bool Properties::GetOEMCryptoPath(std::string* path) {
if (path == nullptr) return false;
*path = "liboemcrypto.so";
// Using an environment variable is useful for testing.
const char* env_path = getenv("LIBOEMCRYPTO_PATH");
if (env_path) {
*path = std::string(env_path);
} else {
*path = "liboemcrypto.so";
}
return true;
}

View File

@@ -0,0 +1,118 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include <assert.h>
#include <gtest/gtest.h>
#include <string.h>
#include <time.h>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#if defined(__linux__)
# include <sys/utsname.h>
#endif
#include "cdm.h"
#include "cdm_test_runner.h"
#include "clock.h"
#include "device_cert.h"
#include "log.h"
#include "reboot_test.h"
#include "test_base.h"
#include "test_host.h"
using namespace widevine;
// TODO(b/195338975): document how a partner should modify this so that they can
// use a real host.
TestHost* g_host = nullptr;
namespace {
constexpr char kGlobalDumpFileName[] = "dumped_global_filesystem.dat";
constexpr char kPerOriginDumpFileName[] = "dumped_per_origin_filesystem.dat";
using StorageMap = TestHost::Storage::StorageMap;
// Load a TestHost file system from the real file system.
bool ReloadFileSystem(const char* file_name, StorageMap* map) {
std::ifstream input(file_name);
if (input.fail()) {
// This is OK for first pass, but an error for later passes.
LOGD("Could not read %s", file_name);
return false;
}
std::string dumped_file_system((std::istreambuf_iterator<char>(input)),
std::istreambuf_iterator<char>());
if (!wvcdm::RebootTest::ParseDump(dumped_file_system, map)) {
LOGE("Could not parse %s", file_name);
return false;
}
return true;
}
bool ReloadFileSystems() {
StorageMap global_map;
StorageMap per_origin_map;
if (!ReloadFileSystem(kGlobalDumpFileName, &global_map) ||
!ReloadFileSystem(kPerOriginDumpFileName, &per_origin_map)) {
return false;
}
g_host->global_storage().ResetFiles(global_map);
g_host->per_origin_storage().ResetFiles(per_origin_map);
return true;
}
// Dump a TestHost file system to the real file system. If the dump file
// cannot be written, this raises an exception and crashes the program. Since
// we usually build with exceptions turned off, what this means is that the test
// executable will halt if the file cannot be written.
void DumpFileSystem(const char* file_name, const StorageMap& map) {
std::string dump = wvcdm::RebootTest::DumpData(map);
std::ofstream output(file_name);
output << dump;
output.close();
}
void DumpFileSystems() {
DumpFileSystem(kGlobalDumpFileName, g_host->global_storage().files());
DumpFileSystem(kPerOriginDumpFileName, g_host->per_origin_storage().files());
}
} // namespace
int main(int argc, char** argv) {
// Set up a Host and initialize the library. This makes these services
// available to the tests. We would do this in the test suite itself, but the
// core & OEMCrypto tests don't know they depend on this for storage.
g_host = new TestHost();
ReloadFileSystems();
// Partners will want to replace this with a real IStorage.
Cdm::IStorage* const storage = &g_host->global_storage();
// Partners may also want to replace this with real implementations of IClock
// and ITimer. If so, make not to set the command line argument
// "--fake_sleep".
Cdm::IClock* const clock = g_host;
Cdm::ITimer* const timer = g_host;
// If the tests need a spearate file system from that used by the host, that
// should be set up here. For the reference code, we use a default test file
// system, but save the data from the file system.
// A separate file system might be needed, for example, if the test data needs
// to be saved off-device. Or, for example, if the main file system limits the
// names and types of files it can save to certificates and offline licenses.
wvutil::FileSystem* file_system = nullptr;
wvcdm::RebootTest::set_file_system(file_system);
const int test_results = Main(storage, clock, timer, argc, argv);
DumpFileSystems();
// This is used by the test driver to know what time to use for initializing
// the fake clock for the next pass.
std::cout << "END_OF_TEST " << wvutil::Clock().GetCurrentTime() << "\n";
return test_results;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// source code may only be used and distributed under the Widevine License
// Agreement.
#include <assert.h>
#include <gtest/gtest.h>
@@ -10,11 +10,8 @@
#include <string>
#include <vector>
#if defined(__linux__)
# include <sys/utsname.h>
#endif
#include "cdm.h"
#include "cdm_test_runner.h"
#include "device_cert.h"
#include "log.h"
#include "test_base.h"
@@ -23,72 +20,22 @@
using namespace widevine;
TestHost* g_host = nullptr;
std::string g_sandbox_id = "";
namespace {
constexpr const char kSandboxIdParam[] = "--sandbox_id=";
// Following the pattern established by help text in test_base.cpp
constexpr const char kExtraHelpText[] =
" --sandbox_id=<sandbox_id>\n"
" Specifies the Sandbox ID that should be sent to OEMCrypto via\n"
" OEMCrypto_SetSandbox(). On most platforms, since Sandbox IDs are not\n"
" in use, this parameter should be omitted.\n";
} // namespace
int main(int argc, char** argv) {
// Find and filter out the Sandbox ID, if any.
std::vector<std::string> args(argv, argv + argc);
auto sandbox_id_iter = std::find_if(std::begin(args) + 1, std::end(args),
[](const std::string& elem) -> bool {
return elem.find(kSandboxIdParam) == 0;
});
if (sandbox_id_iter != std::end(args)) {
g_sandbox_id = sandbox_id_iter->substr(strlen(kSandboxIdParam));
args.erase(sandbox_id_iter);
}
// Set up a Host and initialize the library. This makes these services
// available to the tests. We would do this in the test suite itself, but the
// core & OEMCrypto tests don't know they depend on this for storage.
g_host = new TestHost();
Cdm::ClientInfo client_info;
// Set client info that denotes this as the test suite:
client_info.product_name = "CE cdm tests";
client_info.company_name = "www";
client_info.model_name = "www";
#if defined(__linux__)
client_info.device_name = "Linux";
{
struct utsname name;
if (uname(&name) == 0) {
client_info.arch_name = name.machine;
}
}
#else
client_info.device_name = "unknown";
#endif
client_info.build_info = __DATE__;
Cdm::Status status = Cdm::initialize(
Cdm::kNoSecureOutput, client_info, g_host, g_host, g_host,
static_cast<Cdm::LogLevel>(wvcdm::g_cutoff), g_sandbox_id);
(void)status; // status is now used when assertions are turned off.
assert(status == Cdm::kSuccess);
// TODO(b/195338975): document that using a real IStorage means you should not
// run the tests in $CDM_DIR/cdm/test/cdm_test.cpp
std::vector<const char*> new_argv(args.size());
std::transform(
std::begin(args), std::end(args), std::begin(new_argv),
[](const std::string& arg) -> const char* { return arg.c_str(); });
// This must take place after the call to Cdm::initialize() because it makes
// calls that are only valid after the library is initialized.
if (!wvcdm::WvCdmTestBase::Initialize(new_argv.size(), new_argv.data(),
kExtraHelpText)) {
return 0;
}
// Partners will want to replace this with a real IStorage.
Cdm::IStorage* const storage = &g_host->global_storage();
// Init gtest after oemcrypto and cdm host have been initialized.
::testing::InitGoogleTest(&argc, argv);
// Partners may also want to replace this with real implementations of IClock
// and ITimer. If so, make not to set the command line argument
// "--fake_sleep".
Cdm::IClock* const clock = g_host;
Cdm::ITimer* const timer = g_host;
return RUN_ALL_TESTS();
const int test_results = Main(storage, clock, timer, argc, argv);
return test_results;
}

View File

@@ -1,6 +1,6 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "cdm_test_printers.h"

View File

@@ -1,6 +1,6 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// source code may only be used and distributed under the Widevine License
// Agreement.
// This file adds some print methods so that when unit tests fail, the
// will print the name of an enumeration instead of the numeric value.

View File

@@ -0,0 +1,68 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include <assert.h>
#include <gtest/gtest.h>
#include <string.h>
#include <time.h>
#include <algorithm>
#include <string>
#include <vector>
#include "cdm.h"
#include "device_cert.h"
#include "log.h"
#include "test_base.h"
#include "test_host.h"
std::string g_sandbox_id = "";
namespace widevine {
namespace {
constexpr char kSandboxIdParam[] = "--sandbox_id=";
// Following the pattern established by help text in test_base.cpp
constexpr char kExtraHelpText[] =
" --sandbox_id=<sandbox_id>\n"
" Specifies the Sandbox ID that should be sent to OEMCrypto via\n"
" OEMCrypto_SetSandbox(). On most platforms, since Sandbox IDs are not\n"
" in use, this parameter should be omitted.\n";
} // namespace
int Main(Cdm::IStorage* storage, Cdm::IClock* clock, Cdm::ITimer* timer,
int argc, char** argv) {
// Find and filter out the Sandbox ID, if any.
std::vector<std::string> args(argv, argv + argc);
auto sandbox_id_iter = std::find_if(std::begin(args) + 1, std::end(args),
[](const std::string& elem) -> bool {
return elem.find(kSandboxIdParam) == 0;
});
if (sandbox_id_iter != std::end(args)) {
g_sandbox_id = sandbox_id_iter->substr(strlen(kSandboxIdParam));
args.erase(sandbox_id_iter);
}
Cdm::Status status = Cdm::initialize(
Cdm::kOpaqueHandle, storage, clock, timer,
static_cast<Cdm::LogLevel>(wvutil::g_cutoff), g_sandbox_id);
(void)status; // status is now used when assertions are turned off.
assert(status == Cdm::kSuccess);
std::vector<const char*> new_argv(args.size());
std::transform(
std::begin(args), std::end(args), std::begin(new_argv),
[](const std::string& arg) -> const char* { return arg.c_str(); });
// This must take place after the call to Cdm::initialize() because it makes
// calls that are only valid after the library is initialized.
if (!wvcdm::WvCdmTestBase::Initialize(static_cast<int>(new_argv.size()),
new_argv.data(), kExtraHelpText)) {
return 1;
}
// Init gtest after oemcrypto and cdm host have been initialized.
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
} // namespace widevine

View File

@@ -0,0 +1,20 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_CDM_TEST_CDM_TEST_RUNNER_H_
#define WVCDM_CDM_TEST_CDM_TEST_RUNNER_H_
#include <iostream>
#include "cdm.h"
namespace widevine {
// Run all CDM tests using the specified storage, clock, and timer. It parses
// standard command line arguments, sets some default test client info,
// initializes a CDM object, and then runs all the tests.
// Returns 0 on success.
int Main(Cdm::IStorage* storage, Cdm::IClock* clock, Cdm::ITimer* timer,
int argc, char** argv);
} // namespace widevine
#endif // WVCDM_CDM_TEST_CDM_TEST_RUNNER_H_

View File

@@ -0,0 +1,11 @@
// Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "create_test_file_system.h"
#include "test_host.h"
wvutil::FileSystem* CreateTestFileSystem() {
return new wvutil::FileSystem("", &g_host->per_origin_storage());
}

View File

@@ -1,6 +1,6 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// source code may only be used and distributed under the Widevine License
// Agreement.
//
// This source file contains the test data used to verify decryption behavior
// in cdm_test.cpp.

View File

@@ -1,6 +1,6 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "device_cert.h"

View File

@@ -1,6 +1,6 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_CDM_TEST_DEVICE_CERT_H_
#define WVCDM_CDM_TEST_DEVICE_CERT_H_

14
cdm/test/dummy_app.mm Normal file
View File

@@ -0,0 +1,14 @@
// Copyright 2019 Google LLC. All Rights Reserved.
#import <UIKit/UIKit.h>
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@end
@implementation AppDelegate {
}
@end
int main(int argc, char** argv) {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}

View File

@@ -0,0 +1,23 @@
// Copyright 2019 Google LLC. All Rights Reserved.
#import <XCTest/XCTest.h>
#include <gtest/gtest.h>
// Defined in cdm_test_main.cpp.
int main(int argc, char** argv);
@interface GtestTests : XCTestCase
@end
@implementation GtestTests
- (void)testAll {
testing::GTEST_FLAG(filter) = GTEST_FILTER;
char arg0[] = "tests";
char* argv[] = {arg0, nullptr};
XCTAssertEqual(main(1, argv), 0);
}
@end

25
cdm/test/info.plist Normal file
View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>EQHXZ8M8AV.$(PRODUCT_NAME:rfc1034identifier)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
</dict>
</plist>

View File

@@ -1,3 +1,13 @@
// Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
/**
The test file system for the Level 3 OEMCrypto sits on top of the host file
system. A real production version may either use the host file system or it
may write files directly to the file system.
*/
#include <algorithm>
#include <cstring>
#include <map>
@@ -5,14 +15,17 @@
#include "level3_file_system_ce_test.h"
namespace wvoec3 {
#include "test_host.h"
std::map<std::string, std::string> OEMCrypto_Level3CETestFileSystem::files_;
namespace wvoec3 {
ssize_t OEMCrypto_Level3CETestFileSystem::Read(const char* filename,
void* buffer, size_t size) {
if (!Exists(filename)) return 0;
std::string data = files_[std::string(filename)];
if (!g_host) return 0;
const std::string name(filename);
if (!g_host->global_storage().exists(name)) return 0;
std::string data;
if (!g_host->global_storage().read(name, &data)) return 0;
size_t bytes_read = std::min(size, data.size());
memcpy(buffer, data.data(), bytes_read);
return bytes_read;
@@ -21,24 +34,31 @@ ssize_t OEMCrypto_Level3CETestFileSystem::Read(const char* filename,
ssize_t OEMCrypto_Level3CETestFileSystem::Write(const char* filename,
const void* buffer,
size_t size) {
if (!g_host) return 0;
std::string data(static_cast<const char*>(buffer), size);
files_[std::string(filename)] = data;
const std::string name(filename);
if (!g_host->global_storage().write(name, data)) return 0;
return size;
}
bool OEMCrypto_Level3CETestFileSystem::Exists(const char* filename) {
return files_.find(std::string(filename)) != files_.end();
if (!g_host) return false;
const std::string name(filename);
return g_host->global_storage().exists(name);
}
ssize_t OEMCrypto_Level3CETestFileSystem::FileSize(const char* filename) {
if (!Exists(filename)) return -1;
return files_[std::string(filename)].size();
if (!g_host) return 0;
const std::string name(filename);
if (!g_host->global_storage().exists(name)) return 0;
return static_cast<ssize_t>(g_host->global_storage().size(name));
}
bool OEMCrypto_Level3CETestFileSystem::Remove(const char* filename) {
if (!Exists(filename)) return false;
files_.erase(std::string(filename));
return true;
if (!g_host) return false;
const std::string name(filename);
if (!g_host->global_storage().exists(name)) return false;
return g_host->global_storage().remove(name);
}
} // namespace wvoec3

View File

@@ -1,6 +1,6 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// source code may only be used and distributed under the Widevine License
// Agreement.
/*********************************************************************
* level3_file_system_ce_test.h
@@ -26,9 +26,6 @@ class OEMCrypto_Level3CETestFileSystem : public OEMCrypto_Level3FileSystem {
bool Exists(const char* filename) override;
ssize_t FileSize(const char* filename) override;
bool Remove(const char* filename) override;
private:
static std::map<std::string, std::string> files_;
};
} // namespace wvoec3

340
cdm/test/perf_test.cpp Normal file
View File

@@ -0,0 +1,340 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "perf_test.h"
#include <gtest/gtest.h>
#include <stdio.h>
#include <stdlib.h>
#include <chrono>
#include <cmath>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include "config_test_env.h"
#include "license_request.h"
#include "test_host.h"
#include "url_request.h"
#define ASSERT_SUCCESS(code) ASSERT_EQ(code, Cdm::kSuccess)
#define EXPECT_SUCCESS(code) EXPECT_EQ(code, Cdm::kSuccess)
#define WALL_NOW std::chrono::high_resolution_clock::now()
TestHost* g_host = nullptr;
namespace widevine {
namespace {
constexpr const size_t kTestCount = 50;
const wvcdm::ConfigTestEnv kTestData(wvcdm::kContentProtectionUatServer);
CreateFuncType create_func = nullptr;
using TimeType = std::chrono::duration<double, std::milli>;
struct PerfInfo {
double mean;
double min;
double max;
double std_dev;
template <size_t Size>
PerfInfo(const double (&values)[Size]) {
static_assert(Size > 0, "Must pass at least one value");
// First pass to calculate min/max/mean.
bool first = true;
double sum = 0;
for (auto v : values) {
sum += v;
if (first) {
min = max = v;
first = false;
} else {
if (v < min) min = v;
if (v > max) max = v;
}
}
mean = sum / Size;
// Second pass to calculate standard deviation.
sum = 0;
for (auto v : values) {
sum += (v - mean) * (v - mean);
}
std_dev = std::sqrt(sum / Size);
}
};
std::ostream& operator<<(std::ostream& os, const PerfInfo& info) {
// mean=12.33442, std-dev=1.44421, min=1.22431, max=244.1133144
return os << "mean=" << info.mean << ", std-dev=" << info.std_dev
<< ", min=" << info.min << ", max=" << info.max;
}
class PerfTracker {
public:
class Test {
public:
Test(PerfTracker* tracker)
: wall_start_(WALL_NOW), cpu_start_(std::clock()), tracker_(tracker) {}
~Test() {
tracker_->wall_times_[tracker_->index_] =
TimeType(WALL_NOW - wall_start_).count();
tracker_->cpu_times_[tracker_->index_] =
(std::clock() - cpu_start_) * 1000.0 / CLOCKS_PER_SEC;
tracker_->index_++;
}
private:
std::chrono::high_resolution_clock::time_point wall_start_;
std::clock_t cpu_start_;
PerfTracker* tracker_;
};
void Print(const std::string& name, size_t block_size_bytes = 0) {
PerfInfo wall_perf(wall_times_);
PerfInfo cpu_perf(cpu_times_);
std::cout << name << " (wall, ms): " << wall_perf << "\n";
std::cout << name << " (cpu, ms): " << cpu_perf << "\n";
if (block_size_bytes) {
// |mean| is in milliseconds.
std::cout << name << " (wall, MBit/sec): "
<< (block_size_bytes * 8 * 1000 / wall_perf.mean / 1024 / 1024)
<< "\n";
std::cout << name << " (cpu, MBit/sec): "
<< (block_size_bytes * 8 * 1000 / cpu_perf.mean / 1024 / 1024)
<< "\n";
}
}
private:
double wall_times_[kTestCount];
double cpu_times_[kTestCount];
size_t index_ = 0;
};
#define MEASURE_PERF(tracker, code) \
{ \
PerfTracker::Test test(&(tracker)); \
code; \
}
class EventListener : public Cdm::IEventListener {
public:
struct MessageInfo {
std::string session_id;
std::string message;
Cdm::MessageType message_type;
};
void onMessage(const std::string& session_id, Cdm::MessageType message_type,
const std::string& message) override {
messages.push_back({session_id, message, message_type});
}
void onKeyStatusesChange(const std::string& session_id,
bool has_new_usable_key) override {}
void onRemoveComplete(const std::string& session_id) override {}
std::vector<MessageInfo> messages;
};
bool SendPost(const std::string& message, std::string* response) {
wvcdm::UrlRequest req(kTestData.license_server());
std::string raw_response;
if (!req.is_connected() || !req.PostRequest(message) ||
!req.GetResponse(&raw_response)) {
return false;
}
wvcdm::LicenseRequest helper;
helper.GetDrmMessage(raw_response, *response);
return true;
}
std::unique_ptr<Cdm> CreateCdm(EventListener* event_listener) {
std::unique_ptr<Cdm> ret(
create_func(event_listener, &g_host->per_origin_storage(), true));
if (ret) {
EXPECT_SUCCESS(ret->setServiceCertificate(
Cdm::kProvisioningService,
kTestData.provisioning_service_certificate()));
EXPECT_SUCCESS(ret->setServiceCertificate(
Cdm::kLicensingService, kTestData.license_service_certificate()));
}
return ret;
}
class GlobalEnv : public testing::Environment {
public:
GlobalEnv(InitFuncType init_func, const std::string& cert)
: init_func_(init_func), cert_(cert) {}
void SetUp() override {
g_host = new TestHost;
if (!cert_.empty()) g_host->per_origin_storage().write("cert.bin", cert_);
Cdm::LogLevel log_level = Cdm::kErrors;
if (const char* verbose = getenv("VERBOSE_OUTPUT")) {
if (std::strcmp(verbose, "1") == 0) log_level = Cdm::kVerbose;
}
ASSERT_SUCCESS(init_func_(Cdm::kNoSecureOutput, &g_host->global_storage(),
g_host, g_host, log_level));
}
private:
const InitFuncType init_func_;
const std::string cert_;
};
} // namespace
class PerfTest : public testing::Test {};
TEST_F(PerfTest, LicenseExchange) {
EventListener event_listener;
auto cdm = CreateCdm(&event_listener);
ASSERT_TRUE(cdm);
ASSERT_EQ(cdm->getProvisioningStatus(), Cdm::kProvisioned);
PerfTracker create;
PerfTracker generate;
PerfTracker update;
PerfTracker close;
for (size_t i = 0; i < kTestCount; i++) {
std::string session_id;
MEASURE_PERF(create, ASSERT_SUCCESS(
cdm->createSession(Cdm::kTemporary, &session_id)));
MEASURE_PERF(
generate,
ASSERT_SUCCESS(cdm->generateRequest(
session_id, Cdm::kCenc,
wvcdm::ConfigTestEnv::GetInitData(wvcdm::kContentIdStreaming))));
std::string response;
ASSERT_TRUE(SendPost(event_listener.messages[0].message, &response));
MEASURE_PERF(update, ASSERT_SUCCESS(cdm->update(session_id, response)));
MEASURE_PERF(close, ASSERT_SUCCESS(cdm->close(session_id)));
event_listener.messages.pop_back();
}
create.Print("Create ");
generate.Print("Generate");
update.Print("Update ");
close.Print("Close ");
}
class DecryptPerfTest : public PerfTest,
public testing::WithParamInterface<bool> {};
TEST_P(DecryptPerfTest, Decrypt) {
EventListener event_listener;
auto cdm = CreateCdm(&event_listener);
ASSERT_TRUE(cdm);
ASSERT_EQ(cdm->getProvisioningStatus(), Cdm::kProvisioned);
std::string session_id;
ASSERT_SUCCESS(cdm->createSession(Cdm::kTemporary, &session_id));
ASSERT_SUCCESS(cdm->generateRequest(
session_id, Cdm::kCenc,
wvcdm::ConfigTestEnv::GetInitData(wvcdm::kContentIdStreaming)));
std::string response;
ASSERT_TRUE(SendPost(event_listener.messages[0].message, &response));
ASSERT_SUCCESS(cdm->update(session_id, response));
Cdm::KeyStatusMap statuses;
ASSERT_SUCCESS(cdm->getKeyStatuses(session_id, &statuses));
ASSERT_GT(statuses.size(), 0u);
const std::string key_id = statuses.begin()->first;
// Use in-place decrypt to avoid allocations. We don't care about the data,
// so we can just decrypt the same buffer again.
constexpr const size_t k16M = 16 * 1024 * 1024;
std::vector<uint8_t> buffer(k16M);
uint8_t iv[16];
for (auto& b : buffer) b = rand();
Cdm::DecryptionBatch batch;
batch.key_id = reinterpret_cast<const uint8_t*>(key_id.data());
batch.key_id_length = static_cast<uint32_t>(key_id.size());
if (GetParam()) {
batch.pattern.encrypted_blocks = batch.pattern.clear_blocks = 0;
} else {
batch.pattern.encrypted_blocks = 1;
batch.pattern.clear_blocks = 9;
}
batch.is_secure = false;
batch.encryption_scheme = GetParam() ? Cdm::kAesCtr : Cdm::kAesCbc;
batch.is_video = true;
Cdm::Subsample subsample;
subsample.clear_bytes = 0;
// subsample.encrypted_bytes set in the test.
Cdm::Sample sample;
sample.input.iv = iv;
sample.input.iv_length = 16;
sample.input.data = buffer.data();
// sample.data_length set in the test.
sample.input.subsamples = &subsample;
sample.input.subsamples_length = 1;
sample.output.data = buffer.data();
sample.output.data_offset = 0;
sample.output.data_length = static_cast<uint32_t>(buffer.size());
batch.samples = &sample;
batch.samples_length = 1;
constexpr const size_t block_sizes[] = {8 * 1024, 256 * 1024, k16M};
constexpr const size_t sizes_count =
sizeof(block_sizes) / sizeof(block_sizes[0]);
const std::string block_names[] = {" 8k", "256k", " 16M"};
PerfTracker perf[sizes_count];
for (size_t i = 0; i < sizes_count; i++) {
subsample.protected_bytes = sample.input.data_length =
sample.output.data_length = static_cast<uint32_t>(block_sizes[i]);
for (size_t j = 0; j < kTestCount; j++) {
MEASURE_PERF(perf[i], ASSERT_SUCCESS(cdm->decrypt(batch)));
}
}
for (size_t i = 0; i < sizes_count; i++) {
perf[i].Print("Decrypt " + block_names[i], block_sizes[i]);
}
}
std::string PrintDecryptParam(const testing::TestParamInfo<bool>& info) {
return info.param ? "CTR" : "CBC";
}
INSTANTIATE_TEST_SUITE_P(Decrypt, DecryptPerfTest, testing::Bool(),
PrintDecryptParam);
int PerfTestMain(InitFuncType init_func, CreateFuncType create,
const std::string& cert) {
#ifdef _DEBUG
// Don't use #error since we build all targets and we don't want to fail the
// debug build (and we can't have configuration-specific targets).
fprintf(stderr, "Don't run performance tests in Debug mode\n");
return 1;
#else
create_func = create;
testing::AddGlobalTestEnvironment(new GlobalEnv(init_func, cert));
return RUN_ALL_TESTS();
#endif
}
} // namespace widevine

23
cdm/test/perf_test.h Normal file
View File

@@ -0,0 +1,23 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_CDM_TEST_PERF_TEST_H_
#define WVCDM_CDM_TEST_PERF_TEST_H_
#include <string>
#include "cdm.h"
namespace widevine {
using InitFuncType = Cdm::Status (*)(Cdm::SecureOutputType, Cdm::IStorage*,
Cdm::IClock*, Cdm::ITimer*, Cdm::LogLevel);
using CreateFuncType = Cdm* (*)(Cdm::IEventListener*, Cdm::IStorage*, bool);
int PerfTestMain(InitFuncType init_func, CreateFuncType create_func,
const std::string& cert);
} // namespace widevine
#endif // WVCDM_CDM_TEST_PERF_TEST_H_

View File

@@ -0,0 +1,84 @@
// Copyright 2021 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
// This uses dlopen() which is only usable on POSIX platforms. The function
// names assume GCC/Clang.
#include <dlfcn.h>
#include <gtest/gtest.h>
#include <stdio.h>
#include <fstream>
#include <tuple>
#include "clock.h"
#include "perf_test.h"
#include "test_host.h"
namespace widevine {
constexpr char kInitName[] =
"_ZN8widevine3Cdm10initializeENS0_16SecureOutputTypeEPNS0_8IStorageEPNS0_"
"6IClockEPNS0_6ITimerENS0_8LogLevelE";
constexpr char kCreateName[] =
"_ZN8widevine3Cdm6createEPNS0_14IEventListenerEPNS0_8IStorageEb";
bool ReadFile(const std::string& path, std::string* output) {
constexpr size_t kReadSize = 8 * 1024;
std::ifstream fs(path, std::ios::in | std::ios::binary);
if (!fs) return false;
while (true) {
const size_t offset = output->size();
output->resize(output->size() + kReadSize);
fs.read(&output->at(offset), kReadSize);
if (fs.eof()) {
output->resize(offset + fs.gcount());
return true;
} else if (!fs) {
fprintf(stderr, "Error reading from cert file\n");
return false;
}
}
}
std::tuple<InitFuncType, CreateFuncType> LoadCdm(const char* path) {
// Note we will leak the library object; but this is just for tests and will
// be unloaded when we exit anyway.
auto dll = dlopen(path, RTLD_NOW);
if (!dll) {
fprintf(stderr, "Error loading so file: %s\n", dlerror());
exit(1);
}
auto init = reinterpret_cast<InitFuncType>(dlsym(dll, kInitName));
auto create = reinterpret_cast<CreateFuncType>(dlsym(dll, kCreateName));
if (!init || !create) {
fprintf(stderr, "Error finding CDM functions: %s\n", dlerror());
exit(1);
}
return std::make_tuple(init, create);
}
} // namespace widevine
namespace wvutil {
int64_t Clock::GetCurrentTime() { return g_host->now() / 1000; }
} // namespace wvutil
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
if (argc != 3) {
fprintf(stderr, "Usage: %s <CDM_SO_PATH> <CERT_PATH>\n", argv[0]);
return 1;
}
std::string cert;
if (!widevine::ReadFile(argv[2], &cert))
return 1;
auto funcs = widevine::LoadCdm(argv[1]);
return widevine::PerfTestMain(std::get<0>(funcs), std::get<1>(funcs), cert);
}

View File

@@ -0,0 +1,29 @@
// Copyright 2022 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine License
// Agreement.
#include <gtest/gtest.h>
#import <XCTest/XCTest.h>
#include "cdm.h"
#include "device_cert.h"
#include "perf_test.h"
@interface GtestTests : XCTestCase
@end
@implementation GtestTests
- (void)testAll {
testing::GTEST_FLAG(filter) = GTEST_FILTER;
char arg0[] = "tests";
char* argv[] = {arg0, nullptr};
int argc = 1;
testing::InitGoogleTest(&argc, argv);
std::string cert{kDeviceCert, kDeviceCert + kDeviceCertSize};
printf("Size 1: %zu, 2: %zu\n", kDeviceCertSize, cert.size());
XCTAssertEqual(widevine::PerfTestMain(&widevine::Cdm::initialize, &widevine::Cdm::create, cert), 0);
}
@end

View File

@@ -1,12 +1,15 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// source code may only be used and distributed under the Widevine License
// Agreement.
#include "test_host.h"
#include <gtest/gtest.h>
#include <chrono>
#include <unordered_set>
#include "cdm_version.h"
#include "device_cert.h"
#include "file_store.h"
#include "log.h"
using namespace widevine;
@@ -15,28 +18,40 @@ namespace {
constexpr char kCertificateFilename[] = "cert.bin";
// Some files are expected to go in global storage. All other files are expected
// to go in per-origin storage. To help us enforce this in tests, this set
// tracks the filenames that belong in global storage. TestHost::Storage will
// reject attempts to access these files via per-origin storage or to access
// files not in this list via global storage.
const std::unordered_set<std::string> kGlobalFilenames = {
"usgtable.bin",
"StoredUsageTime.dat",
"GenerationNumber.dat",
"persistent.dat",
"okp.bin",
"keybox.dat",
wvutil::kOemCertificateFileName,
};
} // namespace
TestHost::TestHost() { Reset(); }
TestHost::~TestHost() { wvcdm::TestSleep::set_callback(nullptr); }
TestHost::TestHost() : global_storage_(true), per_origin_storage_(false) {
Reset();
}
TestHost::~TestHost() { wvutil::TestSleep::set_callback(nullptr); }
void TestHost::Reset() {
auto now = std::chrono::steady_clock().now();
auto now = std::chrono::system_clock().now();
now_ = now.time_since_epoch() / std::chrono::milliseconds(1);
wvcdm::TestSleep::set_callback(this);
save_device_cert_ = false;
wvutil::TestSleep::set_callback(this);
// Surprisingly, std::priority_queue has no clear().
while (!timers_.empty()) {
timers_.pop();
}
files_.clear();
files_[kCertificateFilename] =
(device_cert_.size() > 0)
? device_cert_
: std::string((const char*)kDeviceCert, kDeviceCertSize);
global_storage_.Reset();
per_origin_storage_.Reset();
}
void TestHost::ElapseTime(int64_t milliseconds) {
@@ -64,58 +79,7 @@ void TestHost::ElapseTime(int64_t milliseconds) {
now_ = goal_time;
}
int TestHost::NumTimers() const { return timers_.size(); }
bool TestHost::read(const std::string& name, std::string* data) {
StorageMap::iterator it = files_.find(name);
bool ok = it != files_.end();
LOGV("read file: %s: %s", name.c_str(), ok ? "ok" : "fail");
if (!ok) return false;
*data = it->second;
return true;
}
bool TestHost::write(const std::string& name, const std::string& data) {
LOGV("write file: %s", name.c_str());
files_[name] = data;
if (save_device_cert_ && name.compare(kCertificateFilename) == 0) {
device_cert_ = data;
save_device_cert_ = false;
}
return true;
}
bool TestHost::exists(const std::string& name) {
StorageMap::iterator it = files_.find(name);
bool ok = it != files_.end();
LOGV("exists? %s: %s", name.c_str(), ok ? "true" : "false");
return ok;
}
bool TestHost::remove(const std::string& name) {
LOGV("remove: %s", name.c_str());
if (name.empty()) {
// If no name, delete all files (see DeviceFiles::DeleteAllFiles())
files_.clear();
return true;
}
return files_.erase(name) > 0;
}
int32_t TestHost::size(const std::string& name) {
StorageMap::iterator it = files_.find(name);
if (it == files_.end()) return -1;
return it->second.size();
}
bool TestHost::list(std::vector<std::string>* names) {
names->clear();
for (StorageMap::iterator it = files_.begin(); it != files_.end(); it++) {
names->push_back(it->first);
}
return true;
}
size_t TestHost::NumTimers() const { return timers_.size(); }
int64_t TestHost::now() { return now_; }
@@ -140,3 +104,77 @@ void TestHost::cancel(IClient* client) {
// Now swap the queues.
std::swap(timers_, others);
}
TestHost::Storage::Storage(bool is_global) : is_global_(is_global) { Reset(); }
void TestHost::Storage::Reset() {
files_.clear();
if (!is_global_) {
files_[kCertificateFilename] =
std::string((const char*)kDeviceCert, kDeviceCertSize);
}
}
bool TestHost::Storage::read(const std::string& name, std::string* data) {
StorageMap::iterator it = files_.find(name);
bool ok = it != files_.end();
LOGV("read file: %s: %s", name.c_str(), ok ? "ok" : "fail");
if (!CheckFilename(name) || !ok) return false;
*data = it->second;
return true;
}
bool TestHost::Storage::write(const std::string& name,
const std::string& data) {
LOGV("write file: %s", name.c_str());
if (!CheckFilename(name)) return false;
files_[name] = data;
return true;
}
bool TestHost::Storage::exists(const std::string& name) {
StorageMap::iterator it = files_.find(name);
bool ok = it != files_.end();
LOGV("exists? %s: %s", name.c_str(), ok ? "true" : "false");
if (!CheckFilename(name)) return false;
return ok;
}
bool TestHost::Storage::remove(const std::string& name) {
if (name.empty()) {
// If no name, delete all files (see DeviceFiles::DeleteAllFiles())
LOGV("remove all files");
files_.clear();
return true;
}
LOGV("remove: %s", name.c_str());
if (!CheckFilename(name)) return false;
return files_.erase(name) > 0;
}
int32_t TestHost::Storage::size(const std::string& name) {
StorageMap::iterator it = files_.find(name);
bool ok = (it != files_.end());
LOGV("size? %s: %s", name.c_str(), ok ? "ok" : "fail");
if (!CheckFilename(name) || !ok) return -1;
return static_cast<int32_t>(it->second.size());
}
bool TestHost::Storage::list(std::vector<std::string>* names) {
names->clear();
for (StorageMap::iterator it = files_.begin(); it != files_.end(); it++) {
names->push_back(it->first);
}
return true;
}
bool TestHost::Storage::CheckFilename(const std::string& name) {
const bool is_global_filename =
(kGlobalFilenames.find(name) != kGlobalFilenames.end());
if (is_global_ != is_global_filename) {
LOGE("Attempt to access %s in %s storage rejected.", name.c_str(),
is_global_ ? "global" : "per-origin");
return false;
}
return true;
}

View File

@@ -1,6 +1,6 @@
// Copyright 2018 Google LLC. All Rights Reserved. This file and proprietary
// source code may only be used and distributed under the Widevine Master
// License Agreement.
// source code may only be used and distributed under the Widevine License
// Agreement.
#ifndef WVCDM_CDM_TEST_TEST_HOST_H_
#define WVCDM_CDM_TEST_TEST_HOST_H_
@@ -15,30 +15,47 @@
// This provides a host environment for running CDM tests. It implements the
// IStorage, IClock and ITimer interfaces that a host would normally implement,
// while allowing them to be manipulated by the test.
class TestHost : public widevine::Cdm::IStorage,
public widevine::Cdm::IClock,
class TestHost : public widevine::Cdm::IClock,
public widevine::Cdm::ITimer,
public wvcdm::TestSleep::CallBack {
public wvutil::TestSleep::CallBack {
public:
class Storage : public widevine::Cdm::IStorage {
public:
typedef std::map<std::string, std::string> StorageMap;
explicit Storage(bool is_global);
~Storage() override {}
void Reset();
// Reset the file system to contain the specified files.
void ResetFiles(const StorageMap& files) { files_ = files; };
const StorageMap& files() const { return files_; }
// widevine::Cdm::IStorage
bool read(const std::string& name, std::string* data) override;
bool write(const std::string& name, const std::string& data) override;
bool exists(const std::string& name) override;
bool remove(const std::string& name) override;
int32_t size(const std::string& name) override;
bool list(std::vector<std::string>* names) override;
private:
bool is_global_;
StorageMap files_;
bool CheckFilename(const std::string& name);
};
TestHost();
~TestHost();
~TestHost() override;
void Reset();
// Used for manipulating and inspecting timer states during testing.
void ElapseTime(int64_t milliseconds) override;
int NumTimers() const;
size_t NumTimers() const;
// This should be called before trying to write the cert.bin file. This is
// used when testing device provisioning.
void SaveProvisioningInformation() { save_device_cert_ = true; }
// widevine::Cdm::IStorage
bool read(const std::string& name, std::string* data) override;
bool write(const std::string& name, const std::string& data) override;
bool exists(const std::string& name) override;
bool remove(const std::string& name) override;
int32_t size(const std::string& name) override;
bool list(std::vector<std::string>* names) override;
Storage& global_storage() { return global_storage_; }
Storage& per_origin_storage() { return per_origin_storage_; }
// widevine::Cdm::IClock
int64_t now() override;
@@ -71,11 +88,8 @@ class TestHost : public widevine::Cdm::IStorage,
int64_t now_;
std::priority_queue<Timer> timers_;
std::string device_cert_;
bool save_device_cert_;
typedef std::map<std::string, std::string> StorageMap;
StorageMap files_;
Storage global_storage_;
Storage per_origin_storage_;
};
// Owned and managed by the test runner.

View File

@@ -1,5 +1,5 @@
# Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
# source code may only be used and distributed under the Widevine Master License
# source code may only be used and distributed under the Widevine License
# Agreement.
{
'variables': {

View File

@@ -1,11 +1,11 @@
# Copyright 2019 Google LLC. All Rights Reserved. This file and proprietary
# source code may only be used and distributed under the Widevine Master License
# source code may only be used and distributed under the Widevine License
# Agreement.
{
'sources': [
'../util/test/base64_test.cpp',
'../util/test/cdm_random_unittest.cpp',
# TODO(b/119200528): Needs test vectors
# TODO(b/167126046): Needs test vectors
# '../util/test/file_store_unittest.cpp',
],
'include_dirs': [