diff --git a/CHANGELOG.md b/CHANGELOG.md index 61abc68b..a765b28d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,35 @@ +## 3.5.0 (2017-11-22) + +Features: + - Support OEMCrypto v13.2. + - Remove c++11-specific language features and library usages. Current + standard compliance is at gnu++98. + - Supply boringssl in third_party. This is the preferred SSL implementation, + although the gyp build scripts still allow an external boringssl or OpenSSL + library to be used. + - A number of gyp build rule changes have been made to improve how + dependencies are managed and how compile and link command switches are + applied. Compiler flags are used to restrictively detect and report + potential issues. + - Support for large Usage Tables (OEMCrypto v13 feature). + - Support for SRM enforcement and update (OEMCrypto v13 feature). + - Support for embedded licenses. + - Added support for OpenSSL 1.1 (OpenSSL API changes). Earlier versions of + OpenSSL 1.0 are still supported. The actual OpenSSL version being used is + checked at compile time. + - Begin migration to exclusively supporting BoringSSL. + - Add adapter and stubs for running CDM against OEMCrypto v12. Remove + the adapter for OEMCrypto v8. Currently adapters exist for OEMCrypto + versions 9 through 12. + - Add Fuzzing tests for OEMCrypto interface (work in progress). + +BugFixes: + - Numerous Usage Table fixes and improvements. + - Memory leak fixes. + - Handle non-aligned nonce pointer in RewrapDeviceRSAKey calls. + - Fix scoping errors in gyp build rules. + - Fixes to offline license handling. + ## 3.4.1 (2017-08-31) Features: @@ -6,7 +38,7 @@ Features: BugFixes: - Fixed build failure in protobuf host tools build (relaxed compiler warning checks). - - Enabled a number of more restrictive compiler checks, and fixed + - Enabled a number of more restrictive compiler checks, and fixed non-compliant code. - Mock OEMCrypto: handle case of non-aligned nonce pointer in OEMCrypto_RewrapDeviceRSAKey() and OEMCrypto_RewrapDevideRSAKey30() diff --git a/README.txt b/README.txt index 389fbf71..f9960eb7 100644 --- a/README.txt +++ b/README.txt @@ -1,4 +1,4 @@ -README for Widevine CE_CDM v3.4.x - 31 August, 2017 +README for Widevine CE_CDM v3.5.x - 22 November, 2017 Getting started @@ -7,7 +7,7 @@ This project contains the sources for building a Widevine CDM module. Read the following to learn more about the contents of this project and how to use them: -Widevine_CE_CDM_IntegrationGuide_v3.4.x.pdf +Widevine_CE_CDM_IntegrationGuide_v3.5.x.pdf Documents the CDM API and describes how to integrate the CDM into a system. diff --git a/Widevine_CE_CDM_IntegrationGuide_v3.4.x.pdf b/Widevine_CE_CDM_IntegrationGuide_v3.5.x.pdf similarity index 62% rename from Widevine_CE_CDM_IntegrationGuide_v3.4.x.pdf rename to Widevine_CE_CDM_IntegrationGuide_v3.5.x.pdf index 941a5a38..fd581afe 100644 Binary files a/Widevine_CE_CDM_IntegrationGuide_v3.4.x.pdf and b/Widevine_CE_CDM_IntegrationGuide_v3.5.x.pdf differ diff --git a/build.py b/build.py index 1f9e0358..bffdb4e5 100755 --- a/build.py +++ b/build.py @@ -58,7 +58,7 @@ def GetBuilder(generator): }.get(generator, RunUnknown) -def ImportPlatform(name, gyp_args): +def ImportPlatform(name, gyp_args, build_fuzz_tests): """Handles platform-specific setup for the named platform. Computes platform-specific paths, sets gyp arguments for platform-specific @@ -68,6 +68,7 @@ def ImportPlatform(name, gyp_args): Args: name: The name of the platform. gyp_args: An array of gyp arguments to which this function will append. + build_fuzz_tests: True when we are building OEMCrypto fuzz tests. Returns: The path to the root of the build output. @@ -81,6 +82,10 @@ def ImportPlatform(name, gyp_args): output_path = os.path.join(cdm_top, 'out', name) gyp_args.append('--include=%s' % platform_gypi_path) + if build_fuzz_tests: + fuzzer_settings_path = os.path.join(cdm_top, 'oemcrypto/test/fuzz_tests/platforms/x86-64') + fuzzer_settings_gypi_path = os.path.join(fuzzer_settings_path, 'fuzzer_settings.gypi') + gyp_args.append('--include=%s' % fuzzer_settings_gypi_path) gyp_args.append('-Goutput_dir=%s' % output_path) platform_environment_path = os.path.join(target_path, 'environment.py') @@ -97,8 +102,10 @@ def ImportPlatform(name, gyp_args): def main(args): if IsNinjaInstalled(): + print "ninja is installed - use ninja for generator." default_generator = 'ninja' else: + print "ninja is not installed - use make for generator." default_generator = 'make' parser = argparse.ArgumentParser() @@ -118,19 +125,26 @@ def main(args): 'Defaults to ninja when available, make otherwise.') parser.add_argument('-D', action='append', default=[], help='Pass variable definitions to gyp.') - parser.add_argument('--disable_cpp_11', dest='D', const='disable_cpp_11=1', - action='append_const', - help='Compile without support for C++11.') + parser.add_argument('-ft', '--fuzz_tests', default=False, + action='store_true', + help='Set this flag if you want to build fuzz tests.') options = parser.parse_args(args) gyp_args = [ - '--format=%s' % options.generator, - '--depth=%s' % cdm_top, - '%s/cdm/cdm_unittests.gyp' % cdm_top, - ] + [ '-D' + var for var in options.D ] + '--format=%s' % options.generator, + '--depth=%s' % cdm_top, + ] + if options.fuzz_tests: + gyp_args.append( + '%s/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gyp' % cdm_top) + else: + gyp_args.append('%s/cdm/cdm_unittests.gyp' % cdm_top) + for var in options.D: + gyp_args.append('-D' + var) - output_path = ImportPlatform(options.platform, gyp_args) + output_path = ImportPlatform( + options.platform, gyp_args, options.fuzz_tests) print ' Running: %s' % (['gyp'] + gyp_args) retval = gyp.main(gyp_args) diff --git a/cdm/cdm.gyp b/cdm/cdm.gyp index cdb86b13..02d2224a 100644 --- a/cdm/cdm.gyp +++ b/cdm/cdm.gyp @@ -8,7 +8,7 @@ { 'variables': { # Override if you intend to link against a different OEMCrypto API version. - 'oemcrypto_version%': 12, + 'oemcrypto_version%': 13, # Override if you can't depend on OpenSSL for privacy features. # If set to 'dummy', privacy mode in the CDM will fail. @@ -42,11 +42,11 @@ # 3) protobuf_config == 'source' (default) # Build protobuf and protoc from source. # Specify the path to the protobuf source in protobuf_source. - # Make sure that a valid config.h for your target is in the source tree. 'protobuf_config%': 'source', 'protobuf_source%': '../third_party/protobuf', 'protoc_host_target%': 'dummy', }, # variables + 'includes': ['core.gypi'], # Get list of core source files. 'conditions': [ ['protobuf_config=="source"', { # Include protobuf targets used by protobuf_config=='source' @@ -58,23 +58,33 @@ 'target_name': 'license_protocol', 'type': 'static_library', 'standalone_static_library': 1, + 'includes': ['../third_party/protoc.gypi'], 'sources': [ '../core/src/license_protocol.proto', ], 'variables': { 'proto_in_dir': '../core/src', }, - 'includes': ['../third_party/protoc.gypi'], }, { 'target_name': 'device_files', 'type': 'static_library', 'standalone_static_library': 1, + 'includes': ['../third_party/protoc.gypi'], 'sources': ['../core/src/device_files.proto',], 'variables': { 'proto_in_dir': '../core/src', }, + }, + { + 'target_name': 'metrics_proto', + 'type': 'static_library', + 'standalone_static_library': 1, 'includes': ['../third_party/protoc.gypi'], + 'sources': ['../metrics/src/metrics.proto',], + 'variables': { + 'proto_in_dir': '../metrics/src', + }, }, { 'target_name': 'widevine_cdm_core', @@ -83,71 +93,29 @@ 'dependencies': [ 'device_files', 'license_protocol', + 'metrics_proto', ], 'include_dirs': [ '../core/include', + '../metrics/include', '../oemcrypto/include', '../third_party/jsmn', ], 'direct_dependent_settings': { 'include_dirs': [ '../core/include', + '../metrics/include', '../oemcrypto/include', ], }, 'sources': [ - '../core/include/buffer_reader.h', - '../core/include/cdm_client_property_set.h', - '../core/include/cdm_engine.h', - '../core/include/cdm_session.h', - '../core/include/cdm_session_map.h', - '../core/include/certificate_provisioning.h', - '../core/include/clock.h', - '../core/include/crypto_key.h', - '../core/include/crypto_session.h', - '../core/include/device_files.h', - '../core/include/file_store.h', - '../core/include/initialization_data.h', - '../core/include/license_key_status.h', - '../core/include/license.h', - '../core/include/lock.h', - '../core/include/log.h', - '../core/include/oemcrypto_adapter.h', - '../core/include/policy_engine.h', - '../core/include/privacy_crypto.h', - '../core/include/properties.h', - '../core/include/scoped_ptr.h', - '../core/include/service_certificate.h', - '../core/include/string_conversions.h', - '../core/include/wv_cdm_constants.h', - '../core/include/wv_cdm_event_listener.h', - '../core/include/wv_cdm_types.h', - '../core/src/buffer_reader.cpp', - '../core/src/cdm_engine.cpp', - '../core/src/cdm_session.cpp', - '../core/src/cdm_session_map.cpp', - '../core/src/certificate_provisioning.cpp', - '../core/src/crypto_session.cpp', - '../core/src/device_files.cpp', - '../core/src/initialization_data.cpp', - '../core/src/license_key_status.cpp', - '../core/src/license.cpp', + '<@(wvcdm_sources)', '../core/src/oemcrypto_adapter_static.cpp', - '../core/src/policy_engine.cpp', '../core/src/privacy_crypto_<(privacy_crypto_impl).cpp', - '../core/src/properties.cpp', - '../core/src/service_certificate.cpp', - '../core/src/string_conversions.cpp', '../third_party/jsmn/jsmn.h', '../third_party/jsmn/jsmn.c', ], 'conditions': [ - ['oemcrypto_version < 9', { - 'sources': [ - # Include APIs introduced in v9. - '../core/src/oemcrypto_adapter_static_v9.cpp', - ], - }], ['oemcrypto_version < 10', { 'sources': [ # Include APIs introduced in v10. @@ -166,6 +134,12 @@ '../core/src/oemcrypto_adapter_static_v12.cpp', ], }], + ['oemcrypto_version < 13', { + 'sources': [ + # Include APIs introduced in v13. + '../core/src/oemcrypto_adapter_static_v13.cpp', + ], + }], ['privacy_crypto_impl=="openssl"', { 'conditions': [ ['openssl_config == "target"', { @@ -192,12 +166,14 @@ 'widevine_cdm_core', 'device_files', 'license_protocol', + 'metrics_proto', ], # Without this, library deps do not propagate from the protocol targets # up to the shared lib or executable above. 'export_dependent_settings': [ 'device_files', 'license_protocol', + 'metrics_proto', ], 'defines': ['CDM_IMPLEMENTATION'], 'include_dirs': [ diff --git a/cdm/cdm_unittests.gyp b/cdm/cdm_unittests.gyp index a5340c12..c06b9c6e 100644 --- a/cdm/cdm_unittests.gyp +++ b/cdm/cdm_unittests.gyp @@ -4,7 +4,6 @@ # Refer to the distribution package's README for details. { 'variables': { - 'oemcrypto_lib%': '', 'oemcrypto_stubs%': '', 'openssl_config%': 'system', 'openssl_target%': '', @@ -22,9 +21,6 @@ 'test/test_host.cpp', 'test/test_host.h', ], - 'variables': { - 'cdm_dir': '..', - }, 'includes': [ 'oemcrypto_unittests.gypi', 'core_unittests.gypi', @@ -35,6 +31,7 @@ ], 'dependencies': [ 'cdm.gyp:widevine_ce_cdm_shared', + 'cdm.gyp:widevine_cdm_core', '../third_party/gmock.gyp:gmock', '../third_party/gmock.gyp:gtest', ], @@ -45,13 +42,28 @@ ], }, { 'conditions': [ - ['oemcrypto_lib==""', { + ['oemcrypto_lib=="mock"', { 'dependencies': [ '../oemcrypto/mock/oec_mock.gyp:oec_mock', ], - }, { - 'libraries': [ - '<(oemcrypto_lib)', + }], + ['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_test_factory.cpp', + ], + 'conditions': [ + ['oemcrypto_adapter=="static"', { + 'dependencies': [ + '../oemcrypto/level3/oec_level3.gyp:oec_level3_static', + ], + }, { + 'dependencies': [ + '../oemcrypto/level3/oec_level3.gyp:oec_level3_dynamic', + ], + }], ], }], ], diff --git a/cdm/cdm_unittests.gypi b/cdm/cdm_unittests.gypi index 2ec26b1e..eb42673d 100644 --- a/cdm/cdm_unittests.gypi +++ b/cdm/cdm_unittests.gypi @@ -3,6 +3,9 @@ # Include this in any custom unit test targets. # Does not include the device certificate or the test runner main. { + 'cflags_cc+': [ + '-Wno-inconsistent-missing-override', + ], 'sources': [ '../core/test/http_socket.cpp', '../core/test/license_request.cpp', diff --git a/cdm/core.gypi b/cdm/core.gypi new file mode 100644 index 00000000..77a40e07 --- /dev/null +++ b/cdm/core.gypi @@ -0,0 +1,59 @@ +# Copyright 2017 Google Inc. All Rights Reserved. +# +# Include this in any custom unit test targets. +# Does not include the test runner main. +{ + 'variables': { + 'wvcdm_sources': [ + '../core/include/buffer_reader.h', + '../core/include/cdm_client_property_set.h', + '../core/include/cdm_engine.h', + '../core/include/cdm_session.h', + '../core/include/cdm_session_map.h', + '../core/include/certificate_provisioning.h', + '../core/include/clock.h', + '../core/include/crypto_key.h', + '../core/include/crypto_session.h', + '../core/include/device_files.h', + '../core/include/file_store.h', + '../core/include/initialization_data.h', + '../core/include/license_key_status.h', + '../core/include/license.h', + '../core/include/lock.h', + '../core/include/log.h', + '../core/include/oemcrypto_adapter.h', + '../core/include/policy_engine.h', + '../core/include/privacy_crypto.h', + '../core/include/properties.h', + '../core/include/scoped_ptr.h', + '../core/include/service_certificate.h', + '../core/include/string_conversions.h', + '../core/include/wv_cdm_constants.h', + '../core/include/wv_cdm_event_listener.h', + '../core/include/wv_cdm_types.h', + '../core/include/usage_table_header.h', + '../core/src/buffer_reader.cpp', + '../core/src/cdm_engine.cpp', + '../core/src/cdm_session.cpp', + '../core/src/cdm_session_map.cpp', + '../core/src/certificate_provisioning.cpp', + '../core/src/crypto_session.cpp', + '../core/src/device_files.cpp', + '../core/src/initialization_data.cpp', + '../core/src/license.cpp', + '../core/src/license_key_status.cpp', + '../core/src/policy_engine.cpp', + '../core/src/privacy_crypto_openssl.cpp', + '../core/src/properties.cpp', + '../core/src/service_certificate.cpp', + '../core/src/string_conversions.cpp', + '../core/src/usage_table_header.cpp', + '../metrics/src/counter_metric.cpp', + '../metrics/src/distribution.cpp', + '../metrics/src/event_metric.cpp', + '../metrics/src/metrics_collections.cpp', + '../metrics/src/timer_metric.cpp', + '../metrics/src/value_metric.cpp', + ], + }, +} diff --git a/cdm/core_unittests.gypi b/cdm/core_unittests.gypi index d8e76906..2c8c2593 100644 --- a/cdm/core_unittests.gypi +++ b/cdm/core_unittests.gypi @@ -5,6 +5,7 @@ { 'sources': [ '../core/test/base64_test.cpp', + '../core/test/buffer_reader_test.cpp', '../core/test/cdm_engine_test.cpp', '../core/test/cdm_session_unittest.cpp', '../core/test/config_test_env.cpp', @@ -12,28 +13,34 @@ # '../core/test/generic_crypto_unittest.cpp', # currently keybox only '../core/test/http_socket.cpp', '../core/test/initialization_data_unittest.cpp', + '../core/test/license_keys_unittest.cpp', '../core/test/license_request.cpp', '../core/test/license_unittest.cpp', - '../core/test/license_keys_unittest.cpp', - '../core/test/policy_engine_unittest.cpp', '../core/test/policy_engine_constraints_unittest.cpp', + '../core/test/policy_engine_unittest.cpp', + '../core/test/service_certificate_unittest.cpp', + '../core/test/shared_ptr_test.cpp', '../core/test/test_printers.cpp', '../core/test/url_request.cpp', + '../core/test/usage_table_header_unittest.cpp', + '../metrics/test/counter_metric_unittest.cpp', + '../metrics/test/distribution_unittest.cpp', + '../metrics/test/event_metric_unittest.cpp', + '../metrics/test/value_metric_unittest.cpp', ], 'include_dirs': [ + '../cdm/include', '../core/include', '../core/test', + '../metrics/include', ], 'defines': [ 'UNIT_TEST', 'CORE_TESTS', ], 'dependencies': [ - # This gypi may be included from outside this folder, and dependencies in - # a gypi are relative to the gyp file doing the including. - # cdm_dir is a variable the including file must set to help us find the - # correct path. - '<(cdm_dir)/cdm/cdm.gyp:license_protocol', + 'cdm.gyp:metrics_proto', + 'cdm.gyp:device_files', ], 'conditions': [ # OpenSSL needed for http_socket diff --git a/cdm/include/cdm_version.h b/cdm/include/cdm_version.h index 7f9fa78b..c5bbe8ff 100644 --- a/cdm/include/cdm_version.h +++ b/cdm/include/cdm_version.h @@ -1,2 +1,2 @@ // Widevine CE CDM Version -#define CDM_VERSION "v3.4.1-0-gb429ebdf-ce" +#define CDM_VERSION "v3.5.0-0-gc0ea5d51-ce" diff --git a/cdm/oemcrypto_unittests.gypi b/cdm/oemcrypto_unittests.gypi index 58d0dc22..08601072 100644 --- a/cdm/oemcrypto_unittests.gypi +++ b/cdm/oemcrypto_unittests.gypi @@ -3,16 +3,28 @@ # Include this in any custom unit test targets. # Does not include the test runner main. { + 'variables': { + # Label as 'static' or 'dynamic' to use the respective OEMCrypto adapter. + 'oemcrypto_adapter%': 'static', + # Choose OEMCrypto library to compile in (mock, level3, vendor). + 'oemcrypto_lib%': 'mock', + }, 'sources': [ '../oemcrypto/test/oec_device_features.cpp', '../oemcrypto/test/oec_session_util.cpp', + '../oemcrypto/test/oemcrypto_session_tests_helper.cpp', '../oemcrypto/test/oemcrypto_test.cpp', + '../test/auth/test_keybox.cpp', + '../test/auth/test_rsa_key.cpp', + '../test/auth/test_oem_cert.cpp', + '../test/auth/test_service_cert.cpp', ], 'include_dirs': [ '../core/include', # log.h '../oemcrypto/include', '../oemcrypto/mock/src', # oemcrypto_key_mock.h '../oemcrypto/test', + '../test/auth', ], 'defines': [ 'OEMCRYPTO_TESTS', diff --git a/cdm/src/cdm.cpp b/cdm/src/cdm.cpp index a1c0e073..67adc767 100644 --- a/cdm/src/cdm.cpp +++ b/cdm/src/cdm.cpp @@ -17,6 +17,7 @@ #include "license.h" #include "log.h" #include "properties.h" +#include "service_certificate.h" #include "string_conversions.h" #include "wv_cdm_constants.h" #include "wv_cdm_event_listener.h" @@ -212,7 +213,7 @@ class CdmImpl : public Cdm, virtual void OnSessionKeysChange(const CdmSessionId& session_id, const CdmKeyStatusMap& keys_status, - bool has_new_usable_key) OVERRIDE; + bool /* has_new_usable_key */) OVERRIDE; virtual void OnExpirationUpdate(const CdmSessionId& session_id, int64_t new_expiry_time_seconds) OVERRIDE; @@ -285,13 +286,16 @@ Cdm::Status CdmImpl::setServiceCertificate(const std::string& certificate) { return kTypeError; } - // Check for properly signed and well-formed certificate. - // Keep results. NOTE: an empty certificate should be accepted here. - CdmResponseType status = cdm_engine_.SetServiceCertificate(certificate); + // Verify that the certificate is properly signed and well-formed. + ServiceCertificate service_certificate; + CdmResponseType status = service_certificate.Init(certificate); if (status != NO_ERROR) { LOGE("Invalid service certificate! Error code = %d", status); return kTypeError; } + // TODO(gmorgan): remove when provisioning service certificate is added + cdm_engine_.SetServiceCertificate(certificate); + property_set_.set_service_certificate(certificate); return kSuccess; } @@ -301,8 +305,7 @@ Cdm::Status CdmImpl::getServiceCertificateRequest(std::string* message) { "string return parameter not supplied"); return kTypeError; } - CdmKeyMessage request_message; - if (!cdm_engine_.GetServiceCertificateRequest(message)) { + if (!ServiceCertificate::GetRequest(message)) { LOGE("Unable to return service certificate request!"); message->clear(); return kTypeError; @@ -317,13 +320,12 @@ Cdm::Status CdmImpl::parseServiceCertificateResponse( "string return parameter not supplied"); return kTypeError; } - if (cdm_engine_.ParseServiceCertificateResponse(response, certificate) != - NO_ERROR) { + if (ServiceCertificate::ParseResponse(response, certificate) != NO_ERROR) { LOGE("Failure parsing service certificate response!"); certificate->clear(); return kTypeError; } - return kSuccess; + return setServiceCertificate(*certificate); } bool CdmImpl::isProvisioned() { @@ -1017,7 +1019,7 @@ Cdm::Status CdmImpl::genericEncrypt( LOGE("No such session: %s", session_id.c_str()); return kSessionNotFound; } - if (result == KEY_NOT_FOUND_3 || result == KEY_ERROR_1) { + if (result == KEY_NOT_FOUND_3 || result == NEED_KEY) { LOGE("Key Error: %s", session_id.c_str()); return kNoKey; } @@ -1045,7 +1047,7 @@ Cdm::Status CdmImpl::genericDecrypt( LOGE("No such session: %s", session_id.c_str()); return kSessionNotFound; } - if (result == KEY_NOT_FOUND_4 || result == KEY_ERROR_2) { + if (result == KEY_NOT_FOUND_4 || result == NEED_KEY) { LOGE("Key Error: %s", session_id.c_str()); return kNoKey; } @@ -1073,7 +1075,7 @@ Cdm::Status CdmImpl::genericSign( LOGE("No such session: %s", session_id.c_str()); return kSessionNotFound; } - if (result == KEY_NOT_FOUND_5 || result == KEY_ERROR_3) { + if (result == KEY_NOT_FOUND_5 || result == NEED_KEY) { LOGE("Key Error: %s", session_id.c_str()); return kNoKey; } @@ -1101,7 +1103,7 @@ Cdm::Status CdmImpl::genericVerify( LOGE("No such session: %s", session_id.c_str()); return kSessionNotFound; } - if (result == KEY_NOT_FOUND_6 || result == KEY_ERROR_4) { + if (result == KEY_NOT_FOUND_6 || result == NEED_KEY) { LOGE("Key Error: %s", session_id.c_str()); return kNoKey; } @@ -1151,7 +1153,8 @@ void CdmImpl::OnSessionRenewalNeeded(const CdmSessionId& session_id) { } void CdmImpl::OnSessionKeysChange(const CdmSessionId& session_id, - const CdmKeyStatusMap& keys_status, bool) { + const CdmKeyStatusMap& keys_status, + bool /* has_new_usable_key */) { KeyStatusMap& map = sessions_[session_id].key_statuses; CdmKeyStatusMap::const_iterator it; @@ -1250,7 +1253,7 @@ CdmSigningAlgorithm CdmImpl::ConvertSigningAlgorithm( } bool VerifyL1() { - CryptoSession cs; + CryptoSession cs(NULL); return cs.GetSecurityLevel() == kSecurityLevelL1; } diff --git a/cdm/src/log.cpp b/cdm/src/log.cpp index cdfd5190..3c433736 100644 --- a/cdm/src/log.cpp +++ b/cdm/src/log.cpp @@ -15,7 +15,8 @@ LogPriority g_cutoff = LOG_WARN; void InitLogging() {} -void Log(const char* file, int line, LogPriority level, const char* fmt, ...) { +void Log(const char* file, const char* function, int line, LogPriority level, + const char* fmt, ...) { const char* severities[] = { "ERROR", "WARN", "INFO", "DEBUG", "VERBOSE" }; if (level >= sizeof(severities) / sizeof(*severities)) { fprintf(stderr, "[FATAL:%s(%d)] Invalid log priority level: %d\n", @@ -24,7 +25,7 @@ void Log(const char* file, int line, LogPriority level, const char* fmt, ...) { } if (level > g_cutoff) return; - fprintf(stderr, "[%s:%s(%d)] ", severities[level], file, line); + fprintf(stderr, "[%s:%s(%d):%s] ", severities[level], file, line, function); va_list ap; va_start(ap, fmt); diff --git a/cdm/src/properties_ce.cpp b/cdm/src/properties_ce.cpp index ee4f4d23..fbc9870c 100644 --- a/cdm/src/properties_ce.cpp +++ b/cdm/src/properties_ce.cpp @@ -84,9 +84,9 @@ void Properties::Init() { oem_crypto_use_secure_buffers_ = use_secure_buffers_; oem_crypto_use_fifo_ = use_fifo_; oem_crypto_use_userspace_buffers_ = use_userspace_buffers_; - use_certificates_as_identification_ = true; provisioning_messages_are_binary_ = set_provisioning_messages_to_binary_; security_level_path_backward_compatibility_support_ = false; + allow_service_certificate_requests_ = false; session_property_set_.reset(new CdmClientPropertySetMap()); } diff --git a/cdm/test/cdm_test.cpp b/cdm/test/cdm_test.cpp index 6332cc96..07423cee 100644 --- a/cdm/test/cdm_test.cpp +++ b/cdm/test/cdm_test.cpp @@ -16,6 +16,7 @@ #include "override.h" #include "properties_ce.h" #include "scoped_ptr.h" +#include "service_certificate.h" #include "string_conversions.h" #include "test_host.h" #include "url_request.h" @@ -130,7 +131,7 @@ class CdmTest : public Test, public Cdm::IEventListener { protected: virtual void SetUp() OVERRIDE { - ConfigTestEnv config(kContentProtectionStagingPlusProv30); + ConfigTestEnv config(kContentProtectionUatServer); g_provisioning_service_certificate.assign( config.provisioning_service_certificate()); @@ -143,7 +144,7 @@ class CdmTest : public Test, public Cdm::IEventListener { // Clear anything stored by OEMCrypto. ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); - int result = OEMCrypto_DeleteUsageTable(); + int result = OEMCrypto_DeleteOldUsageTable(); // TODO(fredgc): update this. // Don't fault OEMCrypto implementations without usage tables: if (result != OEMCrypto_ERROR_NOT_IMPLEMENTED) { EXPECT_EQ(OEMCrypto_SUCCESS, result); @@ -227,7 +228,28 @@ class CdmTest : public Test, public Cdm::IEventListener { int status_code; bool ok = Fetch(url, "", response, &status_code); ASSERT_TRUE(ok); - if (ok) ASSERT_EQ(kHttpOk, status_code); + if (ok) ASSERT_EQ(kHttpOk, status_code) + << "Error response: " << *response << "\n" + << "url: " << url; + } + + bool FetchServiceCertificate(const std::string& url, + std::string* response) { + std::string request; + int status; + if (!ServiceCertificate::GetRequest(&request)) { + LOGE("FAILED to generate service certificate request."); + return false; + } + if (!Fetch (url, request, response, &status)) { + LOGE("FAILED to get service certificate response: sts=%d", status); + return false; + } + LOGV("Reply body(hex): \n%s\n", b2a_hex(*response).c_str()); + LOGV("Reply body(b64): \n%s\n", + Base64SafeEncode(std::vector(response->begin(), + response->end())).c_str()); + return true; } void FetchLicense(const std::string& license_server, @@ -236,7 +258,9 @@ class CdmTest : public Test, public Cdm::IEventListener { int status_code; bool ok = Fetch(license_server, message, response, &status_code); ASSERT_TRUE(ok); - if (ok) ASSERT_EQ(kHttpOk, status_code); + if (ok) ASSERT_EQ(kHttpOk, status_code) + << "Error response: " << *response << "\n" + << "license_server: " << license_server; } void FetchLicenseFailure(const std::string& message, @@ -324,6 +348,24 @@ class CdmTest : public Test, public Cdm::IEventListener { Mock::VerifyAndClear(this); } + std::string GetProvisioningResponse(const std::string& message) { + std::string reply; + std::string uri = g_provisioning_server; + + LOGV("GetProvisioningResponse: URI: %s", uri.c_str()); + LOGV("GetProvisioningResponse: message:\n%s\n", b2a_hex(message).c_str()); + + uri += "&signedRequest=" + message; + FetchCertificate(uri, &reply); + if (HasFatalFailure()) { + LOGE("GetProvisioningResponse: Failed."); + return ""; + } + + LOGV("GetProvisioningResponse: response:\n%s\n", reply.c_str()); + return reply; + } + scoped_ptr cdm_; }; @@ -487,6 +529,51 @@ TEST_F(CdmTest, GetServiceCertificateRequest) { EXPECT_EQ(Cdm::kSuccess, status); } +TEST_F(CdmTest, ServiceCertificateRequestResponseUat) { + ConfigTestEnv uat_config(kContentProtectionUatServer); + + std::string response; + ASSERT_TRUE(FetchServiceCertificate(uat_config.license_server(), &response)); + LOGV("response size=%d", response.size()); +#if 0 // enable to extract the service certificate in byte form + size_t done = 0; + while (done < response.size()) { + for (int i = 0; i < 12; i++) { + if (done >= response.size()) { + break; + } + uint32_t x = static_cast(response.data()[done]); + printf("0x%02x, ", x); + done++; + } + printf ("\n"); + } +#endif +} + +TEST_F(CdmTest, ServiceCertificateRequestResponseStaging) { + ConfigTestEnv staging_config(kContentProtectionStagingServer); + + std::string response; + ASSERT_TRUE(FetchServiceCertificate(staging_config.license_server(), + &response)); + LOGV("response size=%d", response.size()); +#if 0 // enable to extract the service certificate in byte form + size_t done = 0; + while (done < response.size()) { + for (int i = 0; i < 12; i++) { + if (done >= response.size()) { + break; + } + uint32_t x = static_cast(response.data()[done]); + printf("0x%02x, ", x); + done++; + } + printf ("\n"); + } +#endif +} + TEST_F(CdmTest, SetServiceCertificate) { // Set a server certificate with privacy mode disabled - should work. ASSERT_NO_FATAL_FAILURE(RecreateCdm(false /* privacy_mode */)); @@ -504,6 +591,37 @@ TEST_F(CdmTest, SetServiceCertificate) { EXPECT_EQ(Cdm::kTypeError, status); } +TEST_F(CdmTest, Provision) { + // Clear any existing certificates. + g_host->remove("cert.bin"); + g_host->SaveProvisioningInformation(); + + // Creating a session should succeed. + std::string session_id; + Cdm::Status status; + status = cdm_->setServiceCertificate(g_license_service_certificate); + EXPECT_EQ(Cdm::kSuccess, status); + status = cdm_->createSession(Cdm::kTemporary, &session_id); + EXPECT_EQ(Cdm::kSuccess, status); + + // Should get an individualization request when we generate request. + std::string message; + EXPECT_CALL(*this, onDirectIndividualizationRequest(session_id, _)) + .WillOnce(SaveArg<1>(&message)); + status = cdm_->generateRequest(session_id, Cdm::kCenc, kCencInitData); + EXPECT_EQ(Cdm::kSuccess, status); + Mock::VerifyAndClear(this); + + // Complete the provisioning request. + std::string reply = GetProvisioningResponse(message); + ASSERT_FALSE(reply.empty()); + EXPECT_CALL(*this, onMessage(session_id, Cdm::kLicenseRequest, _)).Times(1); + EXPECT_CALL(*this, onDeferredComplete(_, _)).Times(0); + status = cdm_->update(session_id, reply); + ASSERT_EQ(Cdm::kSuccess, status); + Mock::VerifyAndClear(this); +} + TEST_F(CdmTest, CreateSession) { // Create a temporary session. std::string session_id; @@ -709,7 +827,8 @@ TEST_F(CdmTest, LoadTemporary) { EXPECT_EQ(Cdm::kSessionNotFound, status); } -TEST_F(CdmTest, LoadPersistent) { +// TODO(fredgc,rfrias): turn this on after big usage tables work. +TEST_F(CdmTest, DISABLED_LoadPersistent) { std::string session_id; std::string response; Cdm::Status status; @@ -748,7 +867,8 @@ TEST_F(CdmTest, LoadPersistent) { Mock::VerifyAndClear(this); } -TEST_F(CdmTest, LoadWillFireExpiration) { +// TODO(fredgc,rfrias): turn this on after big usage tables work. +TEST_F(CdmTest, DISABLED_LoadWillFireExpiration) { // There was a bug where calling load() would not start the PolicyEngine timer // because it was only started in update(). std::string session_id; @@ -769,7 +889,8 @@ TEST_F(CdmTest, LoadWillFireExpiration) { Mock::VerifyAndClear(this); } -TEST_F(CdmTest, PerOriginLoadPersistent) { +// TODO(fredgc,rfrias): turn this on after big usage tables work. +TEST_F(CdmTest, DISABLED_PerOriginLoadPersistent) { std::string session_id; std::string response; ASSERT_NO_FATAL_FAILURE(CreateSessionAndFetchLicense( @@ -801,7 +922,8 @@ TEST_F(CdmTest, PerOriginLoadPersistent) { Mock::VerifyAndClear(this); } -TEST_F(CdmTest, LoadUsageRecord) { +// TODO(fredgc,rfrias): turn this on after big usage tables work. +TEST_F(CdmTest, DISABLED_LoadUsageRecord) { std::string session_id; std::string response; @@ -844,7 +966,8 @@ TEST_F(CdmTest, LoadUsageRecord) { Mock::VerifyAndClear(this); } -TEST_F(CdmTest, DestroyUsageRecord) { +// TODO(gmorgan): temporarily disabled - pending cdm_partner_3.2 merges +TEST_F(CdmTest, DISABLED_DestroyUsageRecord) { std::string session_id; std::string response; ASSERT_NO_FATAL_FAILURE(CreateSessionAndFetchLicense( @@ -878,7 +1001,8 @@ TEST_F(CdmTest, DestroyUsageRecord) { Mock::VerifyAndClear(this); } -TEST_F(CdmTest, DestroyAllUsageRecords) { +// TODO(gmorgan): temporarily disabled - pending cdm_partner_3.2 merges +TEST_F(CdmTest, DISABLED_DestroyAllUsageRecords) { std::string session_id; std::string response; ASSERT_NO_FATAL_FAILURE(CreateSessionAndFetchLicense( @@ -912,7 +1036,8 @@ TEST_F(CdmTest, DestroyAllUsageRecords) { Mock::VerifyAndClear(this); } -TEST_F(CdmTest, ListUsageRecords) { +// TODO(gmorgan): temporarily disabled - pending cdm_partner_3.2 merges +TEST_F(CdmTest, DISABLED_ListUsageRecords) { std::string session_id; std::string response; ASSERT_NO_FATAL_FAILURE(CreateSessionAndFetchLicense( @@ -1026,7 +1151,8 @@ TEST_F(CdmTest, GetExpiration) { ASSERT_EQ(Cdm::kSessionNotFound, status); } -TEST_F(CdmTest, Remove) { +// TODO(fredgc,rfrias): turn this on after big usage tables work. +TEST_F(CdmTest, DISABLED_Remove) { std::string session_id; ASSERT_NO_FATAL_FAILURE(CreateSessionAndUpdate( Cdm::kPersistentLicense, Cdm::kCenc, &session_id)); @@ -1080,7 +1206,8 @@ TEST_F(CdmTest, Remove) { EXPECT_EQ(Cdm::kRangeError, status); } -TEST_F(CdmTest, RemoveUsageRecord) { +// TODO(fredgc,rfrias): turn this on after big usage tables work. +TEST_F(CdmTest, DISABLED_RemoveUsageRecord) { std::string session_id; ASSERT_NO_FATAL_FAILURE(CreateSessionAndUpdate( Cdm::kPersistentUsageRecord, Cdm::kCenc, &session_id)); @@ -1116,7 +1243,8 @@ TEST_F(CdmTest, RemoveUsageRecord) { ASSERT_EQ(Cdm::kSessionNotFound, status); } -TEST_F(CdmTest, RemoveIncomplete) { +// TODO(fredgc,rfrias): turn this on after big usage tables work. +TEST_F(CdmTest, DISABLED_RemoveIncomplete) { std::string session_id; ASSERT_NO_FATAL_FAILURE(CreateSessionAndUpdate( Cdm::kPersistentLicense, Cdm::kCenc, &session_id)); @@ -1179,15 +1307,8 @@ TEST_F(CdmTest, RemoveIncomplete) { ASSERT_EQ(Cdm::kSessionNotFound, status); } -TEST_F(CdmTest, RemoveUsageTable) { - Cdm::Status status; - status = cdm_->removeUsageTable(); - ASSERT_EQ(Cdm::kSuccess, status); - status = cdm_->removeUsageTable(); - ASSERT_EQ(Cdm::kSuccess, status); -} - -TEST_F(CdmTest, RemoveUsageRecordIncomplete) { +// TODO(fredgc,rfrias): turn this on after big usage tables work. +TEST_F(CdmTest, DISABLED_RemoveUsageRecordIncomplete) { std::string session_id; ASSERT_NO_FATAL_FAILURE(CreateSessionAndUpdate( Cdm::kPersistentUsageRecord, Cdm::kCenc, &session_id)); @@ -1250,7 +1371,8 @@ TEST_F(CdmTest, RemoveUsageRecordIncomplete) { ASSERT_EQ(Cdm::kSessionNotFound, status); } -TEST_F(CdmTest, RemoveNotLoaded) { +// TODO(fredgc,rfrias): turn this on after big usage tables work. +TEST_F(CdmTest, DISABLED_RemoveNotLoaded) { // Create a persistent session and then close it. std::string session_id; ASSERT_NO_FATAL_FAILURE(CreateSessionAndUpdate( @@ -1285,7 +1407,8 @@ TEST_F(CdmTest, RequestPersistentLicenseWithWrongInitData) { FetchLicenseFailure(message, 500); } -TEST_F(CdmTest, RequestTemporaryLicenseWithWrongInitData) { +// TODO(fredgc,rfrias): turn this on after big usage tables work. +TEST_F(CdmTest, DISABLED_RequestTemporaryLicenseWithWrongInitData) { // Generate a request for a temporary license using persistent init data. std::string session_id; Cdm::Status status; @@ -1460,6 +1583,7 @@ TEST_F(CdmTest, SetVideoResolutionOverflow) { cdm_->close(session_id); } +// TODO(http://b/37286053): Fix this test. TEST_P(CdmTestWithDecryptParam, DecryptToClearBuffer) { DecryptParam param = GetParam(); @@ -1630,6 +1754,7 @@ TEST_F(CdmIndividualizationTest, IsProvisioned) { EXPECT_TRUE(cdm_->isProvisioned()); } +// TODO(fredgc,rfrias): turn this on after big usage tables work. TEST_F(CdmIndividualizationTest, RemoveProvisioning) { if (!CheckProvisioningSupport()) return; @@ -1810,7 +1935,8 @@ TEST_F(CdmIndividualizationTest, OnlyPropagatesErrorsForThisSession) { Mock::VerifyAndClear(this); } -TEST_F(CdmIndividualizationTest, WorksWithLoad) { +// TODO(fredgc,rfrias): turn this on after big usage tables work. +TEST_F(CdmIndividualizationTest, DISABLED_WorksWithLoad) { if (!CheckProvisioningSupport()) return; // Create an offline session to load. diff --git a/cdm/test/cdm_test_main.cpp b/cdm/test/cdm_test_main.cpp index 2ee5e45a..9ffa31c8 100644 --- a/cdm/test/cdm_test_main.cpp +++ b/cdm/test/cdm_test_main.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #if defined(__linux__) #include @@ -13,6 +14,7 @@ #include "device_cert.h" #include "override.h" #include "test_host.h" +#include "test_keybox.h" #if defined(OEMCRYPTO_TESTS) # include "oec_device_features.h" @@ -45,6 +47,23 @@ int main(int argc, char** argv) { return 0; } + { + FILE* outfile; + outfile = fopen("danger_do_not_use.bin", "w"); + if (outfile == NULL) { return 0; } + if (fwrite(wvcdm_test_auth::kKeybox, 1, + wvcdm_test_auth::kKeyboxSize, outfile) != 128) { + fclose(outfile); + return 0; + } + if (fwrite(wvcdm_test_auth::kKeyboxValid02, 1, + wvcdm_test_auth::kKeyboxSize, outfile) != 128) { + fclose(outfile); + return 0; + } + fclose(outfile); + } + // Set up a Host so that tests 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 @@ -76,7 +95,6 @@ int main(int argc, char** argv) { #if defined(OEMCRYPTO_TESTS) // Set up the OEMCrypto test harness. - // NOTE: This creates a temporary OEMCrypto "instance". wvoec::global_features.Initialize(false /* is_cast_receiver */, false /* force_load_test_keybox */); ::testing::GTEST_FLAG(filter) diff --git a/cdm/test/level3_file_system_ce_test.cpp b/cdm/test/level3_file_system_ce_test.cpp new file mode 100644 index 00000000..d2d2ddae --- /dev/null +++ b/cdm/test/level3_file_system_ce_test.cpp @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +#include "level3_file_system_ce_test.h" + +namespace wvoec3 { + +std::map OEMCrypto_Level3CETestFileSystem::files_; + +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)]; + size_t bytes_read = std::min(size, data.size()); + memcpy(buffer, data.data(), bytes_read); + return bytes_read; +} + +ssize_t OEMCrypto_Level3CETestFileSystem::Write(const char *filename, + const void *buffer, + size_t size) { + std::string data(static_cast(buffer), size); + files_[std::string(filename)] = data; + return size; +} + +bool OEMCrypto_Level3CETestFileSystem::Exists(const char *filename) { + return files_.find(std::string(filename)) != files_.end(); +} + +ssize_t OEMCrypto_Level3CETestFileSystem::FileSize(const char *filename) { + if (!Exists(filename)) return -1; + return files_[std::string(filename)].size(); +} + +bool OEMCrypto_Level3CETestFileSystem::Remove(const char *filename) { + if (!Exists(filename)) return false; + files_.erase(std::string(filename)); + return true; +} + +} // namespace wvoec3 diff --git a/cdm/test/level3_file_system_ce_test.h b/cdm/test/level3_file_system_ce_test.h new file mode 100644 index 00000000..214a04c4 --- /dev/null +++ b/cdm/test/level3_file_system_ce_test.h @@ -0,0 +1,34 @@ +// Copyright 2017 Google Inc. All Rights Reserved + +/********************************************************************* + * level3_file_system_ce_test.h + * + * Test file system for CE CDM for OEMCrypto Level3 File Operations. + *********************************************************************/ + +#ifndef LEVEL3_FILE_SYSTEM_CE_TEST_H_ +#define LEVEL3_FILE_SYSTEM_CE_TEST_H_ + +#include "cdm.h" +#include "file_store.h" +#include "level3_file_system.h" + +namespace wvoec3 { + +class OEMCrypto_Level3CETestFileSystem : public OEMCrypto_Level3FileSystem { + public: + OEMCrypto_Level3CETestFileSystem() {}; + ~OEMCrypto_Level3CETestFileSystem() override {}; + ssize_t Read(const char *filename, void *buffer, size_t size) override; + ssize_t Write(const char *filename, const void *buffer, size_t size) override; + bool Exists(const char *filename) override; + ssize_t FileSize(const char *filename) override; + bool Remove(const char *filename) override; + + private: + static std::map files_; +}; + +} // namespace wvoec3 + +#endif \ No newline at end of file diff --git a/cdm/test/level3_file_system_test_factory.cpp b/cdm/test/level3_file_system_test_factory.cpp new file mode 100644 index 00000000..83d70be7 --- /dev/null +++ b/cdm/test/level3_file_system_test_factory.cpp @@ -0,0 +1,16 @@ +#include "level3_file_system_ce_test.h" +#include "level3_file_system_factory.h" + +namespace wvoec3 { + +OEMCrypto_Level3FileSystem* createLevel3FileSystem() { + return new OEMCrypto_Level3CETestFileSystem(); +} + +void deleteLevel3FileSystem(OEMCrypto_Level3FileSystem* file_system) { + if (file_system) { + delete file_system; + } +} + +} \ No newline at end of file diff --git a/cdm/test/test_host.cpp b/cdm/test/test_host.cpp index 45600759..a063aae3 100644 --- a/cdm/test/test_host.cpp +++ b/cdm/test/test_host.cpp @@ -9,6 +9,10 @@ using namespace widevine; +namespace { + const std::string kCertificateFilename = "cert.bin"; +} // namespace + TestHost::TestHost() { Reset(); } @@ -19,14 +23,17 @@ void TestHost::Reset() { gettimeofday(&tv, NULL); now_ = (tv.tv_sec * 1000LL) + (tv.tv_usec / 1000LL); + save_device_cert_ = false; + // Surprisingly, std::priority_queue has no clear(). while (!timers_.empty()) { timers_.pop(); } files_.clear(); - files_["cert.bin"] = - std::string((const char*)kDeviceCert, kDeviceCertSize); + files_[kCertificateFilename.c_str()] = (device_cert_.size() > 0) + ? device_cert_ + : std::string((const char*)kDeviceCert, kDeviceCertSize); } void TestHost::ElapseTime(int64_t milliseconds) { @@ -37,9 +44,9 @@ void TestHost::ElapseTime(int64_t milliseconds) { } else { Timer t = timers_.top(); timers_.pop(); - ASSERT_GE(t.expiry_time, now_); - now_ = t.expiry_time; - t.client->onTimerExpired(t.context); + ASSERT_GE(t.expiry_time(), now_); + now_ = t.expiry_time(); + t.client()->onTimerExpired(t.context()); } } } @@ -60,6 +67,10 @@ bool TestHost::write(const std::string& name, const std::string& data) { LOGD("write file: %s", name.c_str()); files_[name] = data; + if (save_device_cert_ && kCertificateFilename.compare(name) == 0) { + device_cert_ = data; + save_device_cert_ = false; + } return true; } @@ -114,7 +125,7 @@ void TestHost::cancel(IClient* client) { Timer t = timers_.top(); timers_.pop(); - if (t.client != client) { + if (t.client() != client) { others.push(t); } } diff --git a/cdm/test/test_host.h b/cdm/test/test_host.h index e3522a30..7917a94a 100644 --- a/cdm/test/test_host.h +++ b/cdm/test/test_host.h @@ -18,6 +18,8 @@ class TestHost : public widevine::Cdm::IStorage, void ElapseTime(int64_t milliseconds); int NumTimers() const; + void SaveProvisioningInformation() { save_device_cert_ = true; } + virtual bool read(const std::string& name, std::string* data) OVERRIDE; virtual bool write(const std::string& name, @@ -36,23 +38,31 @@ class TestHost : public widevine::Cdm::IStorage, private: struct Timer { - Timer(int64_t expiry_time_, IClient* client_, void* context_) - : expiry_time(expiry_time_), client(client_), context(context_) {} + Timer(int64_t expiry_time, IClient* client, void* context) + : expiry_time_(expiry_time), client_(client), context_(context) {} bool operator<(const Timer& other) const { // We want to reverse the order so that the smallest expiry times go to // the top of the priority queue. - return expiry_time > other.expiry_time; + return expiry_time_ > other.expiry_time_; } - int64_t expiry_time; - IClient* client; - void* context; + int64_t expiry_time() { return expiry_time_; } + IClient* client() { return client_; } + void* context() { return context_; } + + private: + int64_t expiry_time_; + IClient* client_; + void* context_; }; int64_t now_; std::priority_queue timers_; + std::string device_cert_; + bool save_device_cert_; + typedef std::map StorageMap; StorageMap files_; }; diff --git a/core/include/cdm_engine.h b/core/include/cdm_engine.h index 51d3ddda..9780c3e7 100644 --- a/core/include/cdm_engine.h +++ b/core/include/cdm_engine.h @@ -14,9 +14,11 @@ #include "file_store.h" #include "initialization_data.h" #include "lock.h" +#include "metrics_collections.h" #include "oemcrypto_adapter.h" #include "scoped_ptr.h" #include "service_certificate.h" +#include "timer_metric.h" #include "wv_cdm_constants.h" #include "wv_cdm_types.h" @@ -47,17 +49,6 @@ class CdmEngine { // Report whether the service certificate has been set. virtual bool HasServiceCertificate(); - // Generate and return a Service Certificate Request message. - // This message can be sent to the License Server to get a service - // certificate. - virtual bool GetServiceCertificateRequest(CdmKeyMessage* request); - - // Parse the message returned by the License Server in response to a - // Service Certificate Request message. Return the service certificate - // from the parsed response. - virtual CdmResponseType ParseServiceCertificateResponse( - const std::string& response, std::string* certificate); - // Session related methods virtual CdmResponseType OpenSession( const CdmKeySystem& key_system, CdmClientPropertySet* property_set, @@ -103,7 +94,26 @@ class CdmEngine { const CdmSessionId& session_id, const CdmKeySetId& key_set_id, const InitializationData& init_data, const CdmLicenseType license_type, CdmAppParameterMap& app_parameters, CdmKeyRequest* key_request); - // Accept license response and extract key info. + // This API may + // (a) accept license response, extract key info and load keys. + // (b) accept a renewal response and update license policy information. + // (c) accept a release response and release an offline license or secure + // stop. + // (d) accept a service certificate and cache that information for the + // the lifetime of the session. + // + // |session_id| identifies the session that generated the request and can + // process the response. Should be empty if a release response. + // |key_data| is the license, renewal, release response or service + // certificate response. + // |key_set_id| should be non-null and specified if license release. + // If offline license or streaming license associated with + // a secure stop, |key_set_id| should be non-null and will + // be filled in on return. Use the |key_set_id| with + // RestoreKeys (to reload offline session) or + // GenerateKeyRequest (to release offline session/secure stop). + // |key_set_id| will be cleared if release or streaming + // (not associated with a secure stop). virtual CdmResponseType AddKey(const CdmSessionId& session_id, const CdmKeyResponse& key_data, CdmKeySetId* key_set_id); @@ -167,8 +177,8 @@ class CdmEngine { // (origin-specific) file system. virtual bool IsProvisioned(CdmSecurityLevel security_level); - // Remove provisioning-related from the current (origin-specific) file system. - // This will force the device to reprovision itself. + // Remove device DRM certificate from the current (origin-specific) file + // system. This will force the device to reprovision itself. virtual CdmResponseType Unprovision(CdmSecurityLevel security_level); // Delete OEMCrypto usage tables. Used by Unprovision(). @@ -270,6 +280,8 @@ class CdmEngine { // dead lock. virtual void OnTimerEvent(); + virtual metrics::EngineMetrics* GetMetrics() { return &metrics_; } + private: // private methods CdmResponseType OpenSession( @@ -290,6 +302,17 @@ class CdmEngine { void CloseExpiredReleaseSessions(); // instance variables + + /* + * The metrics group must be the first variable declared to ensure + * that it is the last member destroyed so that no child members + * try to use a reference to it after it is destroyed. This will + * ensure that all data has been properly recorded in the group before + * it is published. + */ + metrics::EngineMetrics metrics_; + metrics::TimerMetric life_span_; + CdmSessionMap session_map_; CdmReleaseKeySetMap release_key_sets_; scoped_ptr cert_provisioning_; diff --git a/core/include/cdm_session.h b/core/include/cdm_session.h index 5bf5ebae..e14cc797 100644 --- a/core/include/cdm_session.h +++ b/core/include/cdm_session.h @@ -12,9 +12,11 @@ #include "file_store.h" #include "initialization_data.h" #include "license.h" +#include "metrics_collections.h" #include "oemcrypto_adapter.h" #include "policy_engine.h" #include "scoped_ptr.h" +#include "timer_metric.h" #include "wv_cdm_types.h" namespace wvcdm { @@ -22,25 +24,42 @@ namespace wvcdm { class CdmClientPropertySet; class ServiceCertificate; class WvCdmEventListener; +class UsageTableHeader; class CdmSession { public: - CdmSession(FileSystem* file_system); + // Creates a new instance of the CdmSession with the given |file_system| + // and |metrics| parameters. Both parameters are owned by the caller and + // must remain in scope througout the scope of the new instance. |metrics| + // must not be null. + CdmSession(FileSystem* file_system, metrics::SessionMetrics* metrics); virtual ~CdmSession(); void Close() { closed_ = true; } bool IsClosed() { return closed_; } + // Initializes this instance of CdmSession with the given property set. + // |cdm_client_property_set| MAY be null, is owned by the caller, + // and must remain in scope throughout the scope of this session. virtual CdmResponseType Init(CdmClientPropertySet* cdm_client_property_set); - virtual CdmResponseType Init(ServiceCertificate* service_certificate, - CdmClientPropertySet* cdm_client_property_set, + + // Initializes this instance of CdmSession with the given parmeters. + // All parameters are owned by the caller. + // |service_certificate| is caller owned, cannot be null, and must be in + // scope as long as the session is in scope. + // |cdm_client_property_set| is caller owned, may be null, but must be + // in scope as long as the session is in scope. + // |forced_session_id| is caller owned and may be null. + // |event_listener| is caller owned, may be null, but must be in scope + // as long as the session is in scope. + virtual CdmResponseType Init(CdmClientPropertySet* cdm_client_property_set, const CdmSessionId* forced_session_id, WvCdmEventListener* event_listener); virtual CdmResponseType RestoreOfflineSession( const CdmKeySetId& key_set_id, const CdmLicenseType license_type); virtual CdmResponseType RestoreUsageSession( - const CdmKeyMessage& key_request, const CdmKeyResponse& key_response); + const DeviceFiles::CdmUsageData& usage_data); virtual const CdmSessionId& session_id() { return session_id_; } virtual const CdmKeySetId& key_set_id() { return key_set_id_; } @@ -84,6 +103,8 @@ class CdmSession { // ReleaseKey() - Accept response and release key. virtual CdmResponseType ReleaseKey(const CdmKeyResponse& key_response); + virtual CdmResponseType DeleteUsageEntry(uint32_t usage_entry_number); + virtual bool IsKeyLoaded(const KeyId& key_id); virtual int64_t GetDurationRemaining(); @@ -102,7 +123,8 @@ class CdmSession { // Delete usage information for the list of tokens, |provider_session_tokens|. virtual CdmResponseType DeleteMultipleUsageInformation( const std::vector& provider_session_tokens); - virtual CdmResponseType UpdateUsageInformation(); + virtual CdmResponseType UpdateUsageTableInformation(); + virtual CdmResponseType UpdateUsageEntryInformation(); virtual bool is_initial_usage_update() { return is_initial_usage_update_; } virtual bool is_usage_update_needed() { return is_usage_update_needed_; } @@ -115,6 +137,13 @@ class CdmSession { virtual bool is_offline() { return is_offline_; } virtual bool is_temporary() { return is_temporary_; } virtual bool license_received() { return license_received_; } + virtual bool has_provider_session_token() { + return (license_parser_.get() != NULL && + license_parser_->provider_session_token().size() > 0); + } + + virtual CdmUsageSupportType get_usage_support_type() + { return usage_support_type_; } // ReleaseCrypto() - Closes the underlying crypto session but leaves this // object alive. It is invalid to call any method that requires a crypto @@ -158,6 +187,8 @@ class CdmSession { CdmSigningAlgorithm algorithm, const std::string& signature); + virtual metrics::SessionMetrics* GetMetrics() { return metrics_; } + private: friend class CdmSessionTest; @@ -166,6 +197,8 @@ class CdmSession { CdmResponseType StoreLicense(); bool StoreLicense(DeviceFiles::LicenseState state); + bool UpdateUsageInfo(); + // These setters are for testing only. Takes ownership of the pointers. void set_license_parser(CdmLicense* license_parser); void set_crypto_session(CryptoSession* crypto_session); @@ -173,9 +206,14 @@ class CdmSession { void set_file_handle(DeviceFiles* file_handle); // instance variables + metrics::SessionMetrics* metrics_; + metrics::CryptoMetrics* crypto_metrics_; + metrics::TimerMetric life_span_; + bool initialized_; bool closed_; // Session closed, but final shared_ptr has not been released. CdmSessionId session_id_; + FileSystem* file_system_; scoped_ptr license_parser_; scoped_ptr crypto_session_; scoped_ptr policy_engine_; @@ -188,11 +226,18 @@ class CdmSession { SecurityLevel requested_security_level_; CdmAppParameterMap app_parameters_; - // decryption and usage flags + // decryption flags bool is_initial_decryption_; bool has_decrypted_since_last_report_; // ... last report to policy engine. + + // Usage related flags and data bool is_initial_usage_update_; bool is_usage_update_needed_; + CdmUsageSupportType usage_support_type_; + UsageTableHeader* usage_table_header_; + uint32_t usage_entry_number_; + CdmUsageEntry usage_entry_; + std::string usage_provider_session_token_; // information useful for offline and usage scenarios CdmKeyMessage key_request_; diff --git a/core/include/cdm_session_map.h b/core/include/cdm_session_map.h index a26a6a66..0d7f1471 100644 --- a/core/include/cdm_session_map.h +++ b/core/include/cdm_session_map.h @@ -7,14 +7,14 @@ #include #include +#include "cdm_session.h" #include "lock.h" +#include "shared_ptr.h" #include "wv_cdm_types.h" namespace wvcdm { -class CdmSession; - -typedef std::list > CdmSessionList; +typedef std::list > CdmSessionList; class CdmSessionMap { public: @@ -30,16 +30,16 @@ class CdmSessionMap { size_t Size() const { return sessions_.size(); } bool FindSession(const CdmSessionId& id, - std::shared_ptr& session); + shared_ptr* session); void GetSessionList(CdmSessionList& sessions); private: - typedef std::map > + typedef std::map > CdmIdToSessionMap; bool FindSessionNoLock(const CdmSessionId& session_id, - std::shared_ptr& session); + shared_ptr* session); Lock lock_; CdmIdToSessionMap sessions_; diff --git a/core/include/certificate_provisioning.h b/core/include/certificate_provisioning.h index ded7903c..784f8921 100644 --- a/core/include/certificate_provisioning.h +++ b/core/include/certificate_provisioning.h @@ -7,6 +7,7 @@ #include "crypto_session.h" #include "license_protocol.pb.h" +#include "metrics_collections.h" #include "oemcrypto_adapter.h" #include "scoped_ptr.h" #include "wv_cdm_types.h" @@ -20,7 +21,9 @@ class ServiceCertificate; class CertificateProvisioning { public: - explicit CertificateProvisioning(ServiceCertificate* service_certificate) : + CertificateProvisioning(metrics::CryptoMetrics* metrics, + ServiceCertificate* service_certificate) : + crypto_session_(metrics), cert_type_(kCertificateWidevine), service_certificate_(service_certificate) {} diff --git a/core/include/crypto_session.h b/core/include/crypto_session.h index 89ee4943..ae84948f 100644 --- a/core/include/crypto_session.h +++ b/core/include/crypto_session.h @@ -7,21 +7,26 @@ #include #include +#include "OEMCryptoCENC.h" #include "lock.h" +#include "metrics_collections.h" #include "oemcrypto_adapter.h" #include "OEMCryptoCENC.h" #include "scoped_ptr.h" +#include "timer_metric.h" #include "wv_cdm_types.h" namespace wvcdm { class CryptoKey; -typedef std::map CryptoKeyMap; +class UsageTableHeader; + +typedef std::map CryptoKeyMap; typedef std::map SubLicenseSessionMap; class KeySession { protected: - KeySession() {} + KeySession(metrics::CryptoMetrics* metrics) : metrics_(metrics) {} public: typedef enum { kDefault, kSubLicense } KeySessionType; @@ -36,12 +41,16 @@ class KeySession { const std::string& mac_key, const std::vector& keys, const std::string& provider_session_token, - CdmCipherMode* cipher_mode) = 0; + CdmCipherMode* cipher_mode, + const std::string& srm_requirement) = 0; virtual OEMCryptoResult SelectKey(const std::string& key_id) = 0; virtual OEMCryptoResult Decrypt( const CdmDecryptionParameters& params, OEMCrypto_DestBufferDesc& buffer_descriptor, OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) = 0; + + protected: + metrics::CryptoMetrics* metrics_; }; class CryptoSession { @@ -53,7 +62,16 @@ class CryptoSession { kUsageDurationsValid = 2, } UsageDurationStatus; - CryptoSession(); + struct SupportedCertificateTypes { + bool rsa_2048_bit; + bool rsa_3072_bit; + bool rsa_cast; + }; + + // Creates an instance of CryptoSession with the given |crypto_metrics|. + // |crypto_metrics| is owned by the caller, must NOT be null, and must + // exist as long as the new CryptoSession exists. + explicit CryptoSession(metrics::CryptoMetrics* crypto_metrics); virtual ~CryptoSession(); virtual bool GetClientToken(std::string* client_token); @@ -62,7 +80,8 @@ class CryptoSession { return pre_provision_token_type_; } virtual CdmSecurityLevel GetSecurityLevel(); - virtual bool GetDeviceUniqueId(std::string* device_id); + virtual bool GetInternalDeviceUniqueId(std::string* device_id); + virtual bool GetExternalDeviceUniqueId(std::string* device_id); virtual bool GetApiVersion(uint32_t* version); virtual bool GetSystemId(uint32_t* system_id); virtual bool GetProvisioningId(std::string* provisioning_id); @@ -86,7 +105,8 @@ class CryptoSession { const std::string& mac_key_iv, const std::string& mac_key, const std::vector& key_array, - const std::string& provider_session_token); + const std::string& provider_session_token, + const std::string& srm_requirement); virtual bool LoadCertificatePrivateKey(std::string& wrapped_key); virtual bool RefreshKeys(const std::string& message, const std::string& signature, int num_keys, @@ -95,8 +115,6 @@ class CryptoSession { virtual bool GenerateDerivedKeys(const std::string& message); virtual bool GenerateDerivedKeys(const std::string& message, const std::string& session_key); - - virtual bool RewrapCertificate(const std::string& signed_message, const std::string& signature, const std::string& nonce, @@ -110,7 +128,7 @@ class CryptoSession { // Usage related methods virtual bool UsageInformationSupport(bool* has_support); - virtual CdmResponseType UpdateUsageInformation(); + virtual CdmResponseType UpdateUsageInformation(); // only for OEMCrypto v9-12 virtual CdmResponseType DeactivateUsageInformation( const std::string& provider_session_token); virtual CdmResponseType GenerateUsageReport( @@ -126,17 +144,22 @@ class CryptoSession { const std::string& provider_session_token); // Delete usage information for a list of tokens. This does not require // a signed message from the server. - virtual CdmResponseType DeleteMultipleUsageInformation( + virtual CdmResponseType DeleteMultipleUsageInformation( const std::vector& provider_session_tokens); virtual CdmResponseType DeleteAllUsageReports(); virtual bool IsAntiRollbackHwPresent(); virtual bool GetHdcpCapabilities(HdcpCapability* current, HdcpCapability* max); + virtual bool GetSupportedCertificateTypes(SupportedCertificateTypes* support); virtual bool GetRandom(size_t data_length, uint8_t* random_data); virtual bool GetNumberOfOpenSessions(size_t* count); virtual bool GetMaxNumberOfSessions(size_t* max); + virtual bool GetSrmVersion(uint16_t* srm_version); + virtual bool IsSrmUpdateSupported(); + virtual bool LoadSrm(const std::string& srm); + virtual CdmResponseType GenericEncrypt(const std::string& in_buffer, const std::string& key_id, const std::string& iv, @@ -156,14 +179,45 @@ class CryptoSession { CdmSigningAlgorithm algorithm, const std::string& signature); - virtual CdmResponseType AddSubSession(const std::string& sub_session_key_id); + // Usage table header and usage entry related methods + virtual UsageTableHeader* GetUsageTableHeader() { + return usage_table_header_; + } + virtual CdmResponseType GetUsageSupportType(CdmUsageSupportType* type); + virtual CdmResponseType CreateUsageTableHeader( + CdmUsageTableHeader* usage_table_header); + virtual CdmResponseType LoadUsageTableHeader( + const CdmUsageTableHeader& usage_table_header); + virtual CdmResponseType CreateUsageEntry(uint32_t* entry_number); + virtual CdmResponseType LoadUsageEntry(uint32_t entry_number, + const CdmUsageEntry& usage_entry); + virtual CdmResponseType UpdateUsageEntry( + CdmUsageTableHeader* usage_table_header, + CdmUsageEntry* usage_entry); + virtual CdmResponseType ShrinkUsageTableHeader( + uint32_t new_entry_count, CdmUsageTableHeader* usage_table_header); + virtual CdmResponseType MoveUsageEntry(uint32_t new_entry_number); + virtual bool CreateOldUsageEntry( + uint64_t time_since_license_received, + uint64_t time_since_first_decrypt, + uint64_t time_since_last_decrypt, + UsageDurationStatus status, + const std::string& server_mac_key, + const std::string& client_mac_key, + const std::string& provider_session_token); + virtual CdmResponseType CopyOldUsageEntry( + const std::string& provider_session_token); + virtual metrics::CryptoMetrics* GetCryptoMetrics() { return metrics_; } + + virtual CdmResponseType AddSubSession(const std::string& sub_session_key_id, + const std::string& group_master_key_id); // TODO(jfore): exists is set based on whether a sub session exists. For now, // that is not assumed to be an error. virtual bool GenerateSubSessionNonce(const std::string& sub_session_key_id, bool* exists, uint32_t* nonce); private: - bool GetProvisioningMethod(CdmClientTokenType& token_type); + bool GetProvisioningMethod(CdmClientTokenType* token_type); void Init(); void Terminate(); bool GetTokenFromKeybox(std::string* token); @@ -211,9 +265,12 @@ class CryptoSession { static bool initialized_; static int session_count_; + metrics::CryptoMetrics* metrics_; + metrics::TimerMetric life_span_; + bool open_; CdmClientTokenType pre_provision_token_type_; - std::string oem_token_; // Cached OEMCrypto Public Key + std::string oem_token_; // Cached OEMCrypto Public Key bool update_usage_table_after_close_session_; CryptoSessionId oec_session_id_; SubLicenseSessionMap sub_license_oec_sessions_; @@ -227,6 +284,12 @@ class CryptoSession { KeyId cached_key_id_; + bool is_usage_support_type_valid_; + CdmUsageSupportType usage_support_type_; + UsageTableHeader* usage_table_header_; + static UsageTableHeader* usage_table_header_l1_; + static UsageTableHeader* usage_table_header_l3_; + uint64_t request_id_base_; static uint64_t request_id_index_; diff --git a/core/include/device_files.h b/core/include/device_files.h index 287cdc95..13aad015 100644 --- a/core/include/device_files.h +++ b/core/include/device_files.h @@ -29,6 +29,15 @@ class DeviceFiles { kLicenseStateUnknown, } LicenseState; + struct CdmUsageData { + std::string provider_session_token; + CdmKeyMessage license_request; + CdmKeyResponse license; + std::string key_set_id; + CdmUsageEntry usage_entry; + uint32_t usage_entry_number; + }; + DeviceFiles(FileSystem*); virtual ~DeviceFiles(); @@ -57,14 +66,17 @@ class DeviceFiles { int64_t playback_start_time, int64_t last_playback_time, int64_t grace_period_end_time, - const CdmAppParameterMap& app_parameters); + const CdmAppParameterMap& app_parameters, + const CdmUsageEntry& usage_entry, + uint32_t usage_entry_number); virtual bool RetrieveLicense( const std::string& key_set_id, LicenseState* state, CdmInitData* pssh_data, CdmKeyMessage* key_request, CdmKeyResponse* key_response, CdmKeyMessage* key_renewal_request, CdmKeyResponse* key_renewal_response, std::string* release_server_url, int64_t* playback_start_time, int64_t* last_playback_time, - int64_t* grace_period_end_time, CdmAppParameterMap* app_parameters); + int64_t* grace_period_end_time, CdmAppParameterMap* app_parameters, + CdmUsageEntry* usage_entry, uint32_t* usage_entry_number); virtual bool DeleteLicense(const std::string& key_set_id); virtual bool ListLicenses(std::vector* key_set_ids); virtual bool DeleteAllFiles(); @@ -73,51 +85,84 @@ class DeviceFiles { virtual bool ReserveLicenseId(const std::string& key_set_id); virtual bool UnreserveLicenseId(const std::string& key_set_id); - // Store a usage record to the set of usage information on the file system. + // Use this method to create a |usage_info_file_name| from an |app_id| + static std::string GetUsageInfoFileName(const std::string& app_id); + + // The UsageInfo methods have been revised to use |usage_info_file_name| + // rather than |app_id| as a parameter. Use the helper method above to + // translate. + // OEMCrypto API 13 introduced big usage tables which required + // migration from usage tables stored by the TEE to usage table + // header+usage entries stored in unsecured persistent storage. The upgrade + // required creation of reverse lookup tables (CdmUsageEntryInfo). + // |app_id| however was hashed and unextractable, and necessitated the + // switch to |usage_info_file_name| virtual bool StoreUsageInfo(const std::string& provider_session_token, const CdmKeyMessage& key_request, const CdmKeyResponse& key_response, - const std::string& app_id, - const std::string& key_set_id); + const std::string& usage_info_file_name, + const std::string& key_set_id, + const CdmUsageEntry& usage_entry, + uint32_t usage_entry_number); // Extract KSIDs from usage information on the file system. virtual bool ListUsageRecords(const std::string& app_id, std::vector* ksids); - // Get the provider token for the given key_set_id. - virtual bool GetProviderToken(const std::string& app_id, - const std::string& key_set_id, - std::string* provider_session_token); + // Get the provider session token for the given key_set_id. + virtual bool GetProviderSessionToken(const std::string& app_id, + const std::string& key_set_id, + std::string* provider_session_token); - // Delete the usage record for the given PST. - virtual bool DeleteUsageInfo(const std::string& app_id, + virtual bool DeleteUsageInfo(const std::string& usage_info_file_name, const std::string& provider_session_token); // Delete usage information from the file system. Puts a list of all the // psts that were deleted from the file into |provider_session_tokens|. virtual bool DeleteAllUsageInfoForApp( - const std::string& app_id, + const std::string& usage_info_file_name, std::vector* provider_session_tokens); // Retrieve one usage info from the file. Subsequent calls will retrieve // subsequent entries in the table for this app_id. virtual bool RetrieveUsageInfo( - const std::string& app_id, + const std::string& usage_info_file_name, std::vector >* usage_info); // Retrieve the usage info entry specified by |provider_session_token|. // Returns false if the entry could not be found. - virtual bool RetrieveUsageInfo(const std::string& app_id, + virtual bool RetrieveUsageInfo(const std::string& usage_info_file_name, const std::string& provider_session_token, CdmKeyMessage* license_request, - CdmKeyResponse* license_response); - + CdmKeyResponse* license_response, + CdmUsageEntry* usage_entry, + uint32_t* usage_entry_number); // Retrieve the usage info entry specified by |key_set_id|. // Returns false if the entry could not be found. - virtual bool RetrieveUsageInfoByKeySetId(const std::string& app_id, - const std::string& key_set_id, - CdmKeyMessage* license_request, - CdmKeyResponse* license_response); + virtual bool RetrieveUsageInfoByKeySetId( + const std::string& usage_info_file_name, + const std::string& key_set_id, + std::string* provider_session_token, + CdmKeyMessage* license_request, + CdmKeyResponse* license_response, + CdmUsageEntry* usage_entry, + uint32_t* usage_entry_number); + + // These APIs support upgrading from usage tables to usage tabler header + + // entries introduced in OEMCrypto V13. + + virtual bool ListUsageInfoFiles(std::vector* usage_file_names); + virtual bool RetrieveUsageInfo(const std::string& usage_info_file_name, + std::vector* usage_data); + virtual bool RetrieveUsageInfo(const std::string& usage_info_file_name, + const std::string& provider_session_token, + CdmUsageData* usage_data); + // This method overwrites rather than appends data to the usage file + virtual bool StoreUsageInfo(const std::string& usage_info_file_name, + const std::vector& usage_data); + virtual bool UpdateUsageInfo(const std::string& usage_info_file_name, + const std::string& provider_session_token, + const CdmUsageData& usage_data); virtual bool StoreHlsAttributes(const std::string& key_set_id, const CdmHlsMethod method, @@ -127,6 +172,14 @@ class DeviceFiles { std::vector* media_segment_iv); virtual bool DeleteHlsAttributes(const std::string& key_set_id); + virtual bool StoreUsageTableInfo( + const CdmUsageTableHeader& usage_table_header, + const std::vector& usage_entry_info); + + virtual bool RetrieveUsageTableInfo( + CdmUsageTableHeader* usage_table_header, + std::vector* usage_entry_info); + private: // Extract serial number and system ID from DRM Device certificate bool ExtractDeviceInfo(const std::string& device_certificate, @@ -149,7 +202,7 @@ class DeviceFiles { static std::string GetCertificateFileName(); static std::string GetHlsAttributesFileNameExtension(); static std::string GetLicenseFileNameExtension(); - static std::string GetUsageInfoFileName(const std::string& app_id); + static std::string GetUsageTableFileName(); static std::string GetFileNameSafeHash(const std::string& input); #if defined(UNIT_TEST) @@ -171,6 +224,8 @@ class DeviceFiles { FRIEND_TEST(DeviceFilesUsageInfoTest, DeleteAll); FRIEND_TEST(DeviceFilesUsageInfoTest, Read); FRIEND_TEST(DeviceFilesUsageInfoTest, Store); + FRIEND_TEST(DeviceFilesUsageTableTest, Read); + FRIEND_TEST(DeviceFilesUsageTableTest, Store); FRIEND_TEST(WvCdmRequestLicenseTest, UnprovisionTest); FRIEND_TEST(WvCdmRequestLicenseTest, ForceL3Test); FRIEND_TEST(WvCdmRequestLicenseTest, UsageInfoRetryTest); diff --git a/core/include/initialization_data.h b/core/include/initialization_data.h index 8a9b42af..53e8402f 100644 --- a/core/include/initialization_data.h +++ b/core/include/initialization_data.h @@ -29,6 +29,7 @@ class InitializationData { std::vector hls_iv() const { return hls_iv_; } CdmHlsMethod hls_method() const { return hls_method_; } std::vector ExtractEmbeddedKeys() const; + const std::string ExtractGroupMasterKeyId() const; private: // Parse a blob of multiple concatenated PSSH atoms to extract the first diff --git a/core/include/license.h b/core/include/license.h index 14d36797..805c2332 100644 --- a/core/include/license.h +++ b/core/include/license.h @@ -8,6 +8,7 @@ #include "initialization_data.h" #include "license_protocol.pb.h" #include "scoped_ptr.h" +#include "service_certificate.h" #include "wv_cdm_types.h" namespace video_widevine { @@ -20,7 +21,7 @@ namespace wvcdm { class Clock; class CryptoSession; class PolicyEngine; -class ServiceCertificate; +class CdmSession; class CryptoKey; class CdmLicense { @@ -29,9 +30,10 @@ class CdmLicense { virtual ~CdmLicense(); virtual bool Init( - ServiceCertificate* service_certificate, const std::string& client_token, - CdmClientTokenType client_token_type, const std::string& device_id, - CryptoSession* session, PolicyEngine* policy_engine); + const std::string& client_token, CdmClientTokenType client_token_type, + const std::string& device_id, bool use_privacy_mode, + const std::string& signed_service_certificate, CryptoSession* session, + PolicyEngine* policy_engine); virtual CdmResponseType PrepareKeyRequest( const InitializationData& init_data, CdmLicenseType license_type, @@ -39,7 +41,8 @@ class CdmLicense { std::string* server_url); virtual CdmResponseType PrepareKeyUpdateRequest( bool is_renewal, const CdmAppParameterMap& app_parameters, - CdmKeyMessage* signed_request, std::string* server_url); + CdmSession* cdm_session, CdmKeyMessage* signed_request, + std::string* server_url); virtual CdmResponseType HandleKeyResponse( const CdmKeyResponse& license_response); virtual CdmResponseType HandleKeyUpdateResponse( @@ -51,9 +54,10 @@ class CdmLicense { const CdmKeyResponse& license_response, const CdmKeyResponse& license_renewal_response, int64_t playback_start_time, int64_t last_playback_time, - int64_t grace_period_end_time); + int64_t grace_period_end_time, CdmSession* cdm_session); virtual bool RestoreLicenseForRelease(const CdmKeyMessage& license_request, const CdmKeyResponse& license_response); + virtual bool HasInitData() { return stored_init_data_.get(); } virtual bool IsKeyLoaded(const KeyId& key_id); virtual std::string provider_session_token() { @@ -64,6 +68,10 @@ class CdmLicense { return is_offline_; } + static bool ExtractProviderSessionToken( + const CdmKeyResponse& license_response, + std::string* provider_session_token); + private: CdmResponseType HandleKeyErrorResponse( @@ -92,14 +100,16 @@ class CdmLicense { CdmClientTokenType client_token_type_; std::string device_id_; const CdmSessionId session_id_; + scoped_ptr stored_init_data_; bool initialized_; std::set loaded_keys_; std::string provider_session_token_; bool renew_with_client_id_; bool is_offline_; - // Used to encrypt ClientIdentification message - ServiceCertificate* service_certificate_; + // Associated with ClientIdentification encryption + bool use_privacy_mode_; + ServiceCertificate service_certificate_; // Used for certificate based licensing CdmKeyMessage key_request_; diff --git a/core/include/log.h b/core/include/log.h index d03ef873..3ef15223 100644 --- a/core/include/log.h +++ b/core/include/log.h @@ -25,15 +25,20 @@ extern LogPriority g_cutoff; // unit tests. void InitLogging(); -void Log(const char* file, int line, LogPriority level, const char* fmt, ...); +void Log(const char* file, const char* function, int line, LogPriority level, + const char* fmt, ...); // Log APIs -#define LOGE(...) Log(__FILE__, __LINE__, wvcdm::LOG_ERROR, __VA_ARGS__) -#define LOGW(...) Log(__FILE__, __LINE__, wvcdm::LOG_WARN, __VA_ARGS__) -#define LOGI(...) Log(__FILE__, __LINE__, wvcdm::LOG_INFO, __VA_ARGS__) -#define LOGD(...) Log(__FILE__, __LINE__, wvcdm::LOG_DEBUG, __VA_ARGS__) -#define LOGV(...) Log(__FILE__, __LINE__, wvcdm::LOG_VERBOSE, __VA_ARGS__) - +#define LOGE(...) Log(__FILE__, __func__, __LINE__, \ + wvcdm::LOG_ERROR, __VA_ARGS__) +#define LOGW(...) Log(__FILE__, __func__, __LINE__, \ + wvcdm::LOG_WARN, __VA_ARGS__) +#define LOGI(...) Log(__FILE__, __func__, __LINE__, \ + wvcdm::LOG_INFO, __VA_ARGS__) +#define LOGD(...) Log(__FILE__, __func__, __LINE__, \ + wvcdm::LOG_DEBUG, __VA_ARGS__) +#define LOGV(...) Log(__FILE__, __func__, __LINE__, \ + wvcdm::LOG_VERBOSE, __VA_ARGS__) } // namespace wvcdm #endif // WVCDM_CORE_LOG_H_ diff --git a/core/include/oemcrypto_adapter.h b/core/include/oemcrypto_adapter.h index cbff211d..19d78542 100644 --- a/core/include/oemcrypto_adapter.h +++ b/core/include/oemcrypto_adapter.h @@ -37,6 +37,49 @@ OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(SecurityLevel level, uint8_t OEMCrypto_Security_Patch_Level(SecurityLevel level); OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod( SecurityLevel level); +uint32_t OEMCrypto_SupportedCertificates(SecurityLevel level); +OEMCryptoResult OEMCrypto_CreateUsageTableHeader(SecurityLevel level, + uint8_t* header_buffer, + size_t* header_buffer_length); +OEMCryptoResult OEMCrypto_LoadUsageTableHeader(SecurityLevel level, + const uint8_t* buffer, + size_t buffer_length); +OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(SecurityLevel level, + uint32_t new_table_size, + uint8_t* header_buffer, + size_t* header_buffer_length); +OEMCryptoResult OEMCrypto_CreateOldUsageEntry(SecurityLevel level, + uint64_t time_since_license_received, uint64_t time_since_first_decrypt, + uint64_t time_since_last_decrypt, OEMCrypto_Usage_Entry_Status status, + uint8_t* server_mac_key, uint8_t* client_mac_key, const uint8_t* pst, + size_t pst_length); + } // namespace wvcdm +/* The following functions are deprecated in OEMCrypto v13. They are defined + * here so that core cdm code may be backwards compatible with an OEMCrypto + * v12. + */ +extern "C" { + +OEMCryptoResult OEMCrypto_LoadKeys_V11_or_V12( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, + const uint8_t* enc_mac_keys_iv, const uint8_t* enc_mac_keys, + size_t num_keys, const OEMCrypto_KeyObject* key_array, const uint8_t* pst, + size_t pst_length); + +OEMCryptoResult OEMCrypto_UpdateUsageTable(); + +OEMCryptoResult OEMCrypto_DeactivateUsageEntry_V12(const uint8_t* pst, + size_t pst_length); +OEMCryptoResult OEMCrypto_DeleteUsageEntry( + OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length, + const uint8_t* message, size_t message_length, const uint8_t* signature, + size_t signature_length); + +OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(const uint8_t* pst, + size_t pst_length); +} // extern "C" + #endif // WVCDM_CORE_OEMCRYPTO_ADAPTER_H_ diff --git a/cdm/include/override.h b/core/include/override.h similarity index 90% rename from cdm/include/override.h rename to core/include/override.h index 33fb4c93..a206d166 100644 --- a/cdm/include/override.h +++ b/core/include/override.h @@ -8,7 +8,7 @@ (__GNUC__ == 4 && __GNUC_MINOR__ >= 7) \ ) -#if !defined(DISABLE_CPP_11) && \ +#if !defined(DISABLE_OVERRIDE_KEYWORD) && \ (defined(COMPILER_MSVC) || defined(__clang__) || GCC_HAS_OVERRIDE) #define OVERRIDE override #else diff --git a/core/include/policy_engine.h b/core/include/policy_engine.h index 73daea13..113253af 100644 --- a/core/include/policy_engine.h +++ b/core/include/policy_engine.h @@ -106,6 +106,10 @@ class PolicyEngine { bool CanRenew() { return policy_.can_renew(); } + bool IsSufficientOutputProtection(const KeyId& key_id) { + return license_keys_->MeetsConstraints(key_id); + } + private: friend class PolicyEngineTest; friend class PolicyEngineConstraintsTest; @@ -132,7 +136,8 @@ class PolicyEngine { int64_t GetRentalExpiryTime(); // Gets the clock time that the license expires based on whether we have // started playing. This takes into account GetHardLicenseExpiryTime. - int64_t GetExpiryTime(int64_t current_time, bool is_load); + int64_t GetExpiryTime(int64_t current_time, + bool ignore_soft_enforce_playback_duration); int64_t GetLicenseOrRentalDurationRemaining(int64_t current_time); int64_t GetPlaybackDurationRemaining(int64_t current_time); @@ -151,6 +156,9 @@ class PolicyEngine { // expiry time changes. void NotifyExpirationUpdate(int64_t current_time); + // Guard against clock rollbacks + int64_t GetCurrentTime(); + // set_clock() is for testing only. It alters ownership of the // passed-in pointer. void set_clock(Clock* clock); @@ -182,6 +190,9 @@ class PolicyEngine { // calculate the time where renewal retries should occur. int64_t next_renewal_time_; + // to assist in clock rollback checks + int64_t last_recorded_current_time_; + // Used to dispatch CDM events. CdmSessionId session_id_; WvCdmEventListener* event_listener_; diff --git a/core/include/properties.h b/core/include/properties.h index 9b14187d..214d9e13 100644 --- a/core/include/properties.h +++ b/core/include/properties.h @@ -12,7 +12,7 @@ #include "wv_cdm_types.h" #if defined(UNIT_TEST) -# include +#include #endif namespace wvcdm { @@ -37,12 +37,15 @@ class Properties { static inline bool oem_crypto_use_userspace_buffers() { return oem_crypto_use_userspace_buffers_; } - static inline bool use_certificates_as_identification() { - return use_certificates_as_identification_; - } static inline bool provisioning_messages_are_binary() { return provisioning_messages_are_binary_; } + static inline bool allow_service_certificate_requests() { + return allow_service_certificate_requests_; + } + static void set_provisioning_messages_are_binary(bool flag) { + provisioning_messages_are_binary_ = flag; + } static inline bool security_level_path_backward_compatibility_support() { return security_level_path_backward_compatibility_support_; } @@ -93,9 +96,6 @@ class Properties { static void set_use_certificates_as_identification(bool flag) { use_certificates_as_identification_ = flag; } - static void set_provisioning_messages_are_binary(bool flag) { - provisioning_messages_are_binary_ = flag; - } static void set_security_level_path_backward_compatibility_support( bool flag) { security_level_path_backward_compatibility_support_ = flag; @@ -119,6 +119,7 @@ class Properties { static bool use_certificates_as_identification_; static bool security_level_path_backward_compatibility_support_; static bool provisioning_messages_are_binary_; + static bool allow_service_certificate_requests_; static scoped_ptr session_property_set_; CORE_DISALLOW_COPY_AND_ASSIGN(Properties); diff --git a/core/include/service_certificate.h b/core/include/service_certificate.h index e9f0e88b..d672c3ac 100644 --- a/core/include/service_certificate.h +++ b/core/include/service_certificate.h @@ -49,8 +49,19 @@ class ServiceCertificate { const video_widevine::ClientIdentification* clear_client_id, video_widevine::EncryptedClientIdentification* encrypted_client_id); + // Helper methods + static bool GetRequest(CdmKeyMessage* request); + static CdmResponseType ParseResponse(const std::string& response, + std::string* signed_certificate); private: + // Encrypt data using RSA with OAEP padding. + // |plaintext| is the data to be encrypted. |ciphertext| is a pointer to a + // string to contain the decrypted data on return, and may not be null. + // returns NO_ERROR if successful or an appropriate error code otherwise. + virtual CdmResponseType EncryptRsaOaep(const std::string& plaintext, + std::string* ciphertext); + // Track whether object holds valid certificate bool has_certificate_; @@ -64,7 +75,7 @@ class ServiceCertificate { std::string provider_id_; // Public key. - std::unique_ptr public_key_; + std::auto_ptr public_key_; CORE_DISALLOW_COPY_AND_ASSIGN(ServiceCertificate); }; diff --git a/core/include/shared_ptr.h b/core/include/shared_ptr.h new file mode 100644 index 00000000..1d9b8f91 --- /dev/null +++ b/core/include/shared_ptr.h @@ -0,0 +1,222 @@ +// Copyright 2017 Google Inc. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// from google3/util/gtl/shared_ptr.h +// from protobuf/src/google/protobuf/stubs/shared_ptr.h + +#ifndef WVCDM_CORE_SHARED_PTR_H__ +#define WVCDM_CORE_SHARED_PTR_H__ + +#include // for swap +#include +#include + +#include "wv_cdm_types.h" + +namespace { +bool Barrier_AtomicIncrement(volatile uint32_t* ptr, uint32_t value) { + *ptr += value; + return *ptr; +} +bool NoBarrier_AtomicIncrement(volatile uint32_t* ptr, uint32_t value) { + *ptr += value; + return *ptr; +} + +inline bool RefCountDec(volatile uint32_t *ptr) { + return Barrier_AtomicIncrement(ptr, -1) != 0; +} + +inline void RefCountInc(volatile uint32_t *ptr) { + NoBarrier_AtomicIncrement(ptr, 1); +} + +} // namespace + +namespace wvcdm { + +template class shared_ptr; + +// This class is an internal implementation detail for shared_ptr. If two +// shared_ptrs point to the same object, they also share a control block. +// An "empty" shared_pointer refers to NULL and also has a NULL control block. +// It contains all of the state that's needed for reference counting or any +// other kind of resource management. In this implementation the control block +// consists of a single reference count (the number of shared_ptrs that +// share ownership of the object). +class SharedPtrControlBlock { + template friend class shared_ptr; + private: + SharedPtrControlBlock() : refcount_(1) {} + uint32_t refcount_; +}; + +// Forward declaration. The class is defined below. +template class enable_shared_from_this; + +template +class shared_ptr { + public: + typedef T element_type; + + shared_ptr() : ptr_(NULL), control_block_(NULL) {} + + explicit shared_ptr(T* ptr) + : ptr_(ptr), + control_block_(ptr != NULL ? new SharedPtrControlBlock : NULL) { + } + + // Copy constructor: makes this object a copy of ptr, and increments + // the reference count. + template + shared_ptr(const shared_ptr& ptr) + : ptr_(NULL), + control_block_(NULL) { + Initialize(ptr); + } + // Need non-templated version to prevent the compiler-generated default + shared_ptr(const shared_ptr& ptr) + : ptr_(NULL), + control_block_(NULL) { + Initialize(ptr); + } + + // Assignment operator. Replaces the existing shared_ptr with ptr. + // Increment ptr's reference count and decrement the one being replaced. + template + shared_ptr& operator=(const shared_ptr& ptr) { + if (ptr_ != ptr.ptr_) { + shared_ptr me(ptr); // will hold our previous state to be destroyed. + swap(me); + } + return *this; + } + + // Need non-templated version to prevent the compiler-generated default + shared_ptr& operator=(const shared_ptr& ptr) { + if (ptr_ != ptr.ptr_) { + shared_ptr me(ptr); // will hold our previous state to be destroyed. + swap(me); + } + return *this; + } + + ~shared_ptr() { + if (ptr_ != NULL) { + if (!RefCountDec(&control_block_->refcount_)) { + delete ptr_; + } + } + } + + // Replaces underlying raw pointer with the one passed in. The reference + // count is set to one (or zero if the pointer is NULL) for the pointer + // being passed in and decremented for the one being replaced. + // + // If you have a compilation error with this code, make sure you aren't + // passing NULL, nullptr, or 0 to this function. Call reset without an + // argument to reset to a null ptr. + template + void reset(Y* p) { + if (p != ptr_) { + shared_ptr tmp(p); + tmp.swap(*this); + } + } + + void reset() { + reset(static_cast(NULL)); + } + + // Exchanges the contents of this with the contents of r. This function + // supports more efficient swapping since it eliminates the need for a + // temporary shared_ptr object. + void swap(shared_ptr& r) { + using std::swap; // http://go/using-std-swap + swap(ptr_, r.ptr_); + swap(control_block_, r.control_block_); + } + + // The following function is useful for gaining access to the underlying + // pointer when a shared_ptr remains in scope so the reference-count is + // known to be > 0 (e.g. for parameter passing). + T* get() const { + return ptr_; + } + + T& operator*() const { + return *ptr_; + } + + T* operator->() const { + return ptr_; + } + + long use_count() const { + return control_block_ ? control_block_->refcount_ : 1; + } + + bool unique() const { + return use_count() == 1; + } + + private: + // If r is non-empty, initialize *this to share ownership with r, + // increasing the underlying reference count. + // If r is empty, *this remains empty. + // Requires: this is empty, namely this->ptr_ == NULL. + template + void Initialize(const shared_ptr& r) { + // This performs a static_cast on r.ptr_ to U*, which is a no-op since it + // is already a U*. So initialization here requires that r.ptr_ is + // implicitly convertible to T*. + InitializeWithStaticCast(r); + } + + // Initializes *this as described in Initialize, but additionally performs a + // static_cast from r.ptr_ (V*) to U*. + // NOTE(gfc): We'd need a more general form to support const_pointer_cast and + // dynamic_pointer_cast, but those operations are sufficiently discouraged + // that supporting static_pointer_cast is sufficient. + template + void InitializeWithStaticCast(const shared_ptr& r) { + if (r.control_block_ != NULL) { + RefCountInc(&r.control_block_->refcount_); + + ptr_ = static_cast(r.ptr_); + control_block_ = r.control_block_; + } + } + + T* ptr_; + SharedPtrControlBlock* control_block_; +}; + +} // namespace wvcdm + +#endif // WVCDM_CORE_SHARED_PTR_H__ diff --git a/core/include/usage_table_header.h b/core/include/usage_table_header.h new file mode 100644 index 00000000..78f3b9ec --- /dev/null +++ b/core/include/usage_table_header.h @@ -0,0 +1,136 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +#ifndef WVCDM_CORE_USAGE_TABLE_HEADER_H_ +#define WVCDM_CORE_USAGE_TABLE_HEADER_H_ + +#include +#include + +#include "device_files.h" +#include "file_store.h" +#include "lock.h" +#include "metrics_collections.h" +#include "scoped_ptr.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +class CryptoSession; + +// Offline licenses/secure stops may be securely tracked using usage +// tables (OEMCrypto v9-12) or usage table headers+usage entries +// (OEMCrypto v13+). This class assists with the latter, synchronizing +// access to usage table header and associated data-structures and controlling +// when they are read in or written out to non-secure persistent storage. +// +// Each OEMCrypto (for each security level) will maintain its own usage table +// header. Each license will have an associated usage entry that is also +// stored in persistent memory and is noted in the usage table header. +// Usage entry information will be verified when licenses are loaded. +// +// OEMCrypto for each security level have their own usage table +// headers. They are loaded on initialization and written out periodically. +// The lifecycle of this class is tied to when OEMCrypto is +// initialized/terminated. +// +// Sessions and licenses are however handled by CdmSession and so most +// calls to maniplate the usage table header related to usage entries +// are by CdmSession. +// +// Upgrades from a fixed size usage table (supported by previous +// versions of the OEMCrypto API v9-12) are handled by this class. +// |usage_entry| and |usage_entry_number|s need to be saved in the license +// and usage info records by the caller. +class UsageTableHeader { + public: + UsageTableHeader(); + virtual ~UsageTableHeader() {} + + // |crypto_session| is used to create or load a usage master table and + // not cached beyound this call. + bool Init(CdmSecurityLevel security_level, CryptoSession* crypto_session); + + // |persistent_license| false indicates usage info record + CdmResponseType AddEntry(CryptoSession* crypto_session, + bool persistent_license, + const CdmKeySetId& key_set_id, + const std::string& usage_info_filename, + uint32_t* usage_entry_number); + CdmResponseType LoadEntry(CryptoSession* crypto_session, + const CdmUsageEntry& usage_entry, + uint32_t usage_entry_number); + CdmResponseType UpdateEntry(CryptoSession* crypto_session, + CdmUsageEntry* usage_entry); + + // The licenses or usage info records specified by |usage_entry_number| + // should not be in use by any open CryptoSession objects when calls + // to DeleteEntry and MoveEntry are made. + CdmResponseType DeleteEntry(uint32_t usage_entry_number, DeviceFiles* handle, + metrics::CryptoMetrics* metrics); + + private: + CdmResponseType MoveEntry(uint32_t from /* usage entry number */, + const CdmUsageEntry& from_usage_entry, + uint32_t to /* usage entry number */, + DeviceFiles* handle, + metrics::CryptoMetrics* metrics); + + CdmResponseType GetEntry(uint32_t usage_entry_number, DeviceFiles* handle, + CdmUsageEntry* usage_entry); + CdmResponseType StoreEntry(uint32_t usage_entry_number, DeviceFiles* handle, + const CdmUsageEntry& usage_entry); + + CdmResponseType Shrink(metrics::CryptoMetrics* metrics, + uint32_t number_of_usage_entries_to_delete); + + CdmResponseType UpgradeFromUsageTable(DeviceFiles* handle, + metrics::CryptoMetrics* metrics); + bool UpgradeLicensesFromUsageTable(DeviceFiles* handle, + metrics::CryptoMetrics* metrics); + bool UpgradeUsageInfoFromUsageTable(DeviceFiles* handle, + metrics::CryptoMetrics* metrics); + + virtual bool is_inited() { return is_inited_; } + + virtual bool CreateDummyOldUsageEntry(CryptoSession* crypto_session); + + // This handle and file system is only to be used when accessing + // usage_table_header. Usage entries should use the file system provided + // by CdmSession. + scoped_ptr file_handle_; + scoped_ptr file_system_; + CdmSecurityLevel security_level_; + SecurityLevel requested_security_level_; + + CdmUsageTableHeader usage_table_header_; + std::vector usage_entry_info_; + + // Lock to ensure that a single object is created for each security level + // and data member to represent whether an object has been correctly + // initialized. + bool is_inited_; + + // Synchonizes access to the Usage Table Header and bookkeeping + // data-structures + Lock usage_table_header_lock_; + + // Test related declarations + friend class UsageTableHeaderTest; + + // These setters are for testing only. Takes ownership of the pointers. + void SetDeviceFiles(DeviceFiles* device_files) { + file_handle_.reset(device_files); + } + void SetCryptoSession(CryptoSession* crypto_session) { + test_crypto_session_.reset(crypto_session); + } + + // Test related data members + scoped_ptr test_crypto_session_; + + CORE_DISALLOW_COPY_AND_ASSIGN(UsageTableHeader); +}; + +} // namespace wvcdm + +#endif // WVCDM_CORE_USAGE_TABLE_HEADER_H_ diff --git a/core/include/wv_cdm_constants.h b/core/include/wv_cdm_constants.h index 985cf09e..ae3bd2dd 100644 --- a/core/include/wv_cdm_constants.h +++ b/core/include/wv_cdm_constants.h @@ -13,6 +13,7 @@ static const size_t KEY_PAD_SIZE = 16; static const size_t KEY_SIZE = 16; static const size_t MAC_KEY_SIZE = 32; static const size_t KEYBOX_KEY_DATA_SIZE = 72; +static const size_t SRM_REQUIREMENT_SIZE = 12; // Initial estimate of certificate size. Code that // uses this estimate should be able to adapt to a larger or smaller size. @@ -63,6 +64,10 @@ static const std::string QUERY_KEY_MAX_NUMBER_OF_SESSIONS = "MaxNumberOfSessions"; static const std::string QUERY_KEY_OEMCRYPTO_API_VERSION = "OemCryptoApiVersion"; +static const std::string QUERY_KEY_CURRENT_SRM_VERSION = "CurrentSRMVersion"; +static const std::string QUERY_KEY_SRM_UPDATE_SUPPORT = "SRMUpdateSupport"; + // whether OEM supports SRM update +static const std::string QUERY_KEY_WVCDM_VERSION = "WidevineCdmVersion"; static const std::string QUERY_VALUE_TRUE = "True"; static const std::string QUERY_VALUE_FALSE = "False"; @@ -72,6 +77,7 @@ static const std::string QUERY_VALUE_SECURITY_LEVEL_L1 = "L1"; static const std::string QUERY_VALUE_SECURITY_LEVEL_L2 = "L2"; static const std::string QUERY_VALUE_SECURITY_LEVEL_L3 = "L3"; static const std::string QUERY_VALUE_SECURITY_LEVEL_UNKNOWN = "Unknown"; +static const std::string QUERY_VALUE_SECURITY_LEVEL_DEFAULT = "Default"; static const std::string QUERY_VALUE_DISCONNECTED = "Disconnected"; static const std::string QUERY_VALUE_UNPROTECTED = "Unprotected"; static const std::string QUERY_VALUE_HDCP_V1 = "HDCP-1.x"; @@ -103,6 +109,7 @@ static const char EMPTY_SPOID[] = ""; //Policy engine HDCP enforcement static const uint32_t HDCP_UNSPECIFIED_VIDEO_RESOLUTION = 0; static const int64_t HDCP_DEVICE_CHECK_INTERVAL = 10; +static const char EMPTY_APP_PACKAGE_NAME[] = ""; } // namespace wvcdm #endif // WVCDM_CORE_WV_CDM_CONSTANTS_H_ diff --git a/core/include/wv_cdm_types.h b/core/include/wv_cdm_types.h index 37fee292..4ae91b94 100644 --- a/core/include/wv_cdm_types.h +++ b/core/include/wv_cdm_types.h @@ -21,13 +21,14 @@ typedef std::string CdmKeySetId; typedef std::string RequestId; typedef uint32_t CryptoResult; typedef uint32_t CryptoSessionId; -typedef std::string CryptoKeyId; typedef std::map CdmAppParameterMap; typedef std::map CdmQueryMap; typedef std::vector CdmUsageInfo; typedef std::string CdmUsageInfoReleaseMessage; typedef std::string CdmProvisioningRequest; typedef std::string CdmProvisioningResponse; +typedef std::string CdmUsageTableHeader; +typedef std::string CdmUsageEntry; enum CdmKeyRequestType { kKeyRequestTypeUnknown, @@ -52,7 +53,7 @@ enum CdmResponseType { CERT_PROVISIONING_GET_KEYBOX_ERROR_2, CERT_PROVISIONING_INVALID_CERT_TYPE, CERT_PROVISIONING_REQUEST_ERROR_1, - CERT_PROVISIONING_REQUEST_ERROR_2, /* 15 */ + CERT_PROVISIONING_NONCE_GENERATION_ERROR, /* 15 */ CERT_PROVISIONING_REQUEST_ERROR_3, CERT_PROVISIONING_REQUEST_ERROR_4, CERT_PROVISIONING_RESPONSE_ERROR_1, @@ -195,8 +196,7 @@ enum CdmResponseType { UNUSED_2, /* previously INVALID_PARAMETERS_LIC_5 */ INVALID_PARAMETERS_LIC_6, INVALID_PARAMETERS_LIC_7, /* 155 */ - UNUSED_9, - /* UNUSED_9 previously LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR */ + LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR, CENC_INIT_DATA_UNAVAILABLE, PREPARE_CENC_CONTENT_ID_FAILED, WEBM_INIT_DATA_UNAVAILABLE, @@ -229,55 +229,95 @@ enum CdmResponseType { INVALID_PARAMETERS_ENG_8, INVALID_PARAMETERS_ENG_9, INVALID_PARAMETERS_ENG_10, - INVALID_PARAMETERS_ENG_11, /* 190 */ - INVALID_PARAMETERS_ENG_12, + INVALID_PARAMETERS_ENG_11, + INVALID_PARAMETERS_ENG_12, /* 190 */ SESSION_NOT_FOUND_13, SESSION_NOT_FOUND_14, SESSION_NOT_FOUND_15, - SESSION_NOT_FOUND_16, /* 195 */ - KEY_NOT_FOUND_3, + SESSION_NOT_FOUND_16, + KEY_NOT_FOUND_3, /* 195 */ KEY_NOT_FOUND_4, KEY_NOT_FOUND_5, KEY_NOT_FOUND_6, - KEY_ERROR_1, /* 200 */ - KEY_ERROR_2, - KEY_ERROR_3, - KEY_ERROR_4, + INVALID_SESSION_1, + NO_DEVICE_KEY_1, /* 200 */ + NO_CONTENT_KEY_2, + INSUFFICIENT_CRYPTO_RESOURCES_2, INVALID_PARAMETERS_ENG_13, - INVALID_PARAMETERS_ENG_14, /* 205 */ - INVALID_PARAMETERS_ENG_15, + INVALID_PARAMETERS_ENG_14, + INVALID_PARAMETERS_ENG_15, /* 205 */ INVALID_PARAMETERS_ENG_16, UNUSED_7, /* previously DEVICE_CERTIFICATE_ERROR_5 */ CERT_PROVISIONING_CLIENT_TOKEN_ERROR_1, - CERT_PROVISIONING_CLIENT_TOKEN_ERROR_2, /* 210 */ - LICENSING_CLIENT_TOKEN_ERROR_1, - INVALID_PARAMETERS_ENG_17, - INVALID_PARAMETERS_ENG_18, - LIST_LICENSE_ERROR_1, - LIST_LICENSE_ERROR_2, /* 215 */ - LIST_USAGE_ERROR_1, - LIST_USAGE_ERROR_2, - DELETE_USAGE_ERROR_1, - DELETE_USAGE_ERROR_2, - DELETE_USAGE_ERROR_3, /* 220 */ - RELEASE_ALL_USAGE_INFO_ERROR_3, - RELEASE_ALL_USAGE_INFO_ERROR_4, - PRIVACY_MODE_ERROR_1, - PRIVACY_MODE_ERROR_2, - PRIVACY_MODE_ERROR_3, /* 225 */ - EMPTY_RESPONSE_ERROR_1, - INVALID_PARAMETERS_ENG_19, - PARSE_RESPONSE_ERROR_1, - PARSE_RESPONSE_ERROR_2, - PARSE_RESPONSE_ERROR_3, /* 230 */ - PARSE_RESPONSE_ERROR_4, - LICENSE_REQUEST_INVALID_SUBLICENSE, - INVALID_SESSION_1, - NO_DEVICE_KEY_1, - NO_CONTENT_KEY_2, /* 235 */ - INSUFFICIENT_CRYPTO_RESOURCES_2, + CERT_PROVISIONING_CLIENT_TOKEN_ERROR_2, + LICENSING_CLIENT_TOKEN_ERROR_1, /* 210 */ + ANALOG_OUTPUT_ERROR, UNKNOWN_SELECT_KEY_ERROR_1, UNKNOWN_SELECT_KEY_ERROR_2, + CREATE_USAGE_TABLE_ERROR, + LOAD_USAGE_HEADER_GENERATION_SKEW, /* 215 */ + LOAD_USAGE_HEADER_SIGNATURE_FAILURE, + LOAD_USAGE_HEADER_BAD_MAGIC, + LOAD_USAGE_HEADER_UNKNOWN_ERROR, + INVALID_PARAMETERS_ENG_17, + INVALID_PARAMETERS_ENG_18, /* 220 */ + INSUFFICIENT_CRYPTO_RESOURCES_3, + CREATE_USAGE_ENTRY_UNKNOWN_ERROR, + LOAD_USAGE_ENTRY_GENERATION_SKEW, + LOAD_USAGE_ENTRY_SIGNATURE_FAILURE, + LOAD_USAGE_ENTRY_UNKNOWN_ERROR, /* 225 */ + INVALID_PARAMETERS_ENG_19, + INVALID_PARAMETERS_ENG_20, + UPDATE_USAGE_ENTRY_UNKNOWN_ERROR, + INVALID_PARAMETERS_ENG_21, + SHRINK_USAGE_TABLER_HEADER_UNKNOWN_ERROR, /* 230 */ + MOVE_USAGE_ENTRY_UNKNOWN_ERROR, + COPY_OLD_USAGE_ENTRY_UNKNOWN_ERROR, + INVALID_PARAMETERS_ENG_22, + LIST_LICENSE_ERROR_1, + LIST_LICENSE_ERROR_2, /* 235 */ + INVALID_PARAMETERS_ENG_23, + USAGE_INFORMATION_SUPPORT_FAILED, + USAGE_SUPPORT_GET_API_FAILED, + UNEXPECTED_EMPTY_USAGE_ENTRY, + INVALID_USAGE_ENTRY_NUMBER_MODIFICATION, /* 240 */ + USAGE_INVALID_NEW_ENTRY, + USAGE_INVALID_PARAMETERS_1, + USAGE_GET_ENTRY_RETRIEVE_LICENSE_FAILED, + USAGE_GET_ENTRY_RETRIEVE_USAGE_INFO_FAILED, + USAGE_GET_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE, /* 245 */ + USAGE_ENTRY_NUMBER_MISMATCH, + USAGE_STORE_LICENSE_FAILED, + USAGE_STORE_USAGE_INFO_FAILED, + USAGE_INVALID_LOAD_ENTRY, + RELEASE_ALL_USAGE_INFO_ERROR_4, /* 250 */ + RELEASE_ALL_USAGE_INFO_ERROR_5, + RELEASE_USAGE_INFO_FAILED, + INCORRECT_USAGE_SUPPORT_TYPE_1, + INCORRECT_USAGE_SUPPORT_TYPE_2, + KEY_PROHIBITED_FOR_SECURITY_LEVEL, /* 255 */ + KEY_NOT_FOUND_IN_SESSION, + NO_USAGE_ENTRIES, + LIST_USAGE_ERROR_1, + LIST_USAGE_ERROR_2, + DELETE_USAGE_ERROR_1, /* 260 */ + DELETE_USAGE_ERROR_2, + DELETE_USAGE_ERROR_3, + PRIVACY_MODE_ERROR_1, + PRIVACY_MODE_ERROR_2, + PRIVACY_MODE_ERROR_3, /* 265 */ + EMPTY_RESPONSE_ERROR_1, + INVALID_PARAMETERS_ENG_24, + PARSE_RESPONSE_ERROR_1, + PARSE_RESPONSE_ERROR_2, + PARSE_RESPONSE_ERROR_3, /* 270 */ + PARSE_RESPONSE_ERROR_4, + USAGE_STORE_ENTRY_RETRIEVE_LICENSE_FAILED, + USAGE_STORE_ENTRY_RETRIEVE_USAGE_INFO_FAILED, + USAGE_STORE_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE, + RELEASE_ALL_USAGE_INFO_ERROR_6, /* 275 */ + RELEASE_ALL_USAGE_INFO_ERROR_7, + LICENSE_REQUEST_INVALID_SUBLICENSE, }; enum CdmKeyStatus { @@ -349,6 +389,39 @@ enum CdmClientTokenType { kClientTokenOemCert }; +// kNonSecureUsageSupport - TEE does not provide any support for usage +// information. +// kUsageTableSupport - TEE persists usage information securely in a fixed +// size table, commonly 50 entries. (OEMCrypto v9+) +// kUsageEntrySupport - usage information (table headers and entries) are +// persisted in non-secure storage but are loaded and unloaded from +// the TEE during use (OEMCrypto v13+) +// kUnknownUsageSupport - usage support type is uninitialized or unavailable +enum CdmUsageSupportType { + kNonSecureUsageSupport, + kUsageTableSupport, + kUsageEntrySupport, + kUnknownUsageSupport, +}; + +enum CdmUsageEntryStorageType { + kStorageLicense, + kStorageUsageInfo, + kStorageTypeUnknown, +}; + +struct CdmUsageEntryInfo { + CdmUsageEntryStorageType storage_type; + CdmKeySetId key_set_id; + std::string usage_info_file_name; + bool operator==(const CdmUsageEntryInfo& other) const { + return storage_type == other.storage_type && + key_set_id == other.key_set_id && + (storage_type != kStorageUsageInfo || + usage_info_file_name == other.usage_info_file_name); + } +}; + class CdmKeyAllowedUsage { public: CdmKeyAllowedUsage() { diff --git a/core/src/cdm_engine.cpp b/core/src/cdm_engine.cpp index 24efc54b..3cc5ace6 100644 --- a/core/src/cdm_engine.cpp +++ b/core/src/cdm_engine.cpp @@ -14,7 +14,6 @@ #include "clock.h" #include "device_files.h" #include "file_store.h" -#include "license_protocol.pb.h" #include "log.h" #include "properties.h" #include "string_conversions.h" @@ -29,9 +28,6 @@ const size_t kUsageReportsPerRequest = 1; namespace wvcdm { -using video_widevine::SignedMessage; -using video_widevine::LicenseError; - class UsagePropertySet : public CdmClientPropertySet { public: UsagePropertySet() {} @@ -68,14 +64,27 @@ CdmEngine::CdmEngine(FileSystem* file_system, const std::string& spoid) usage_session_(NULL), last_usage_information_update_time_(0) { assert(file_system); - Properties::Init(); if (!seeded_) { + Properties::Init(); srand(clock_.GetCurrentTime()); seeded_ = true; } + + life_span_.Start(); + metrics_.cdm_engine_creation_time_millis_.Record(clock_.GetCurrentTime()); + + std::string cdm_version; + if(Properties::GetWVCdmVersion(&cdm_version)) { + metrics_.cdm_engine_cdm_version_.Record(cdm_version); + } else { + // Set error "false", the return value of GetWVCdmVersion. + metrics_.cdm_engine_cdm_version_.SetError(false); + } } -CdmEngine::~CdmEngine() {} +CdmEngine::~CdmEngine() { + M_RECORD(&metrics_, cdm_engine_life_span_, life_span_.AsMs()); +} CdmResponseType CdmEngine::SetServiceCertificate( const std::string& certificate) { @@ -86,66 +95,6 @@ bool CdmEngine::HasServiceCertificate() { return service_certificate_.has_certificate(); } -bool CdmEngine::GetServiceCertificateRequest(CdmKeyMessage* request) { - if (!request) { - LOGE("ServiceCertificate::PrepareRequest: no request parameter provided"); - return false; - } - SignedMessage message; - message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST); - message.SerializeToString(request); - return true; -} - -CdmResponseType CdmEngine::ParseServiceCertificateResponse( - const std::string& response, std::string* certificate) { - if (response.empty()) { - LOGE("CdmEngine::ParseServiceCertificateResponse: empty response"); - return EMPTY_RESPONSE_ERROR_1; - } - if (!certificate) { - LOGE("CdmEngine::ParseServiceCertificateResponse: null return parameter"); - return INVALID_PARAMETERS_ENG_19; - } - - SignedMessage signed_response; - if (!signed_response.ParseFromString(response)) { - LOGE( - "CdmEngine::ParseServiceCertificateResponse: cannot parse response"); - return PARSE_RESPONSE_ERROR_1; - } - if (signed_response.type() == SignedMessage::SERVICE_CERTIFICATE) { - - CdmResponseType status; - status = service_certificate_.Init(signed_response.msg()); - if (status != NO_ERROR) { - LOGE( - "CdmEngine::ParseServiceCertificateResponse: certificate handling " - "failure, status=%d", status); - return PARSE_SERVICE_CERTIFICATE_ERROR; - } - certificate->assign(signed_response.msg()); - - } else if (signed_response.type() == SignedMessage::ERROR_RESPONSE) { - - LicenseError license_error; - if (!license_error.ParseFromString(signed_response.msg())) { - LOGE("CdmEngine::ParseServiceCertificateResponse: cannot parse " - "license error"); - return PARSE_RESPONSE_ERROR_2; - } - LOGE("CdmEngine::ParseServiceCertificateResponse: server returned error:" - "error code = %d", license_error.error_code()); - return PARSE_RESPONSE_ERROR_3; - } else { - LOGE( - "CdmEngine::ParseServiceCertificateResponse: response (%d) is " - "wrong type", signed_response.type()); - return PARSE_RESPONSE_ERROR_4; - } - return NO_ERROR; -} - CdmResponseType CdmEngine::OpenSession( const CdmKeySystem& key_system, CdmClientPropertySet* property_set, const CdmSessionId& forced_session_id, WvCdmEventListener* event_listener) { @@ -183,9 +132,10 @@ CdmResponseType CdmEngine::OpenSession( CloseExpiredReleaseSessions(); - scoped_ptr new_session(new CdmSession(file_system_)); - CdmResponseType sts = new_session->Init(&service_certificate_, property_set, - forced_session_id, event_listener); + scoped_ptr new_session(new CdmSession(file_system_, + metrics_.AddSession())); + CdmResponseType sts = new_session->Init(property_set, forced_session_id, + event_listener); if (sts != NO_ERROR) { if (sts == NEED_PROVISIONING) { cert_provisioning_requested_security_level_ = @@ -240,7 +190,7 @@ CdmResponseType CdmEngine::OpenKeySetSession( } CdmResponseType CdmEngine::CloseSession(const CdmSessionId& session_id) { - LOGI("CdmEngine::CloseSession"); + LOGV("CdmEngine::CloseSession: %s", session_id.c_str()); if (!session_map_.CloseSession(session_id)) { LOGE("CdmEngine::CloseSession: session not found = %s", session_id.c_str()); return SESSION_NOT_FOUND_1; @@ -312,8 +262,8 @@ CdmResponseType CdmEngine::GenerateKeyRequest( id = iter->second.first; } - std::shared_ptr session; - if (!session_map_.FindSession(id, session)) { + shared_ptr session; + if (!session_map_.FindSession(id, &session)) { LOGE("CdmEngine::GenerateKeyRequest: session_id not found = %s", id.c_str()); return SESSION_NOT_FOUND_2; @@ -329,6 +279,7 @@ CdmResponseType CdmEngine::GenerateKeyRequest( if (license_type == kLicenseTypeRelease && !session->license_received()) { sts = session->RestoreOfflineSession(key_set_id, kLicenseTypeRelease); + session->GetMetrics()->cdm_session_restore_offline_session_.Increment(sts); if (sts != KEY_ADDED) { LOGE("CdmEngine::GenerateKeyRequest: key release restoration failed," "sts = %d", static_cast(sts)); @@ -385,8 +336,8 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id, id = iter->second.first; } - std::shared_ptr session; - if (!session_map_.FindSession(id, session)) { + shared_ptr session; + if (!session_map_.FindSession(id, &session)) { LOGE("CdmEngine::AddKey: session id not found = %s", id.c_str()); return SESSION_NOT_FOUND_3; } @@ -398,7 +349,12 @@ CdmResponseType CdmEngine::AddKey(const CdmSessionId& session_id, CdmResponseType sts = session->AddKey(key_data); if (key_set_id) { - *key_set_id = session->key_set_id(); + if ((session->is_offline() || + session->has_provider_session_token()) && !license_type_release) { + *key_set_id = session->key_set_id(); + } else { + key_set_id->clear(); + } } switch (sts) { @@ -424,15 +380,16 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id, return EMPTY_KEYSET_ID_ENG_4; } - std::shared_ptr session; - if (!session_map_.FindSession(session_id, session)) { + shared_ptr session; + if (!session_map_.FindSession(session_id, &session)) { LOGE("CdmEngine::RestoreKey: session_id not found = %s ", session_id.c_str()); return SESSION_NOT_FOUND_4; } - CdmResponseType sts = - session->RestoreOfflineSession(key_set_id, kLicenseTypeOffline); + CdmResponseType sts; + sts = session->RestoreOfflineSession(key_set_id, kLicenseTypeOffline); + session->GetMetrics()->cdm_session_restore_offline_session_.Increment(sts); if (sts == NEED_PROVISIONING) { cert_provisioning_requested_security_level_ = session->GetRequestedSecurityLevel(); @@ -446,14 +403,15 @@ CdmResponseType CdmEngine::RestoreKey(const CdmSessionId& session_id, CdmResponseType CdmEngine::RemoveKeys(const CdmSessionId& session_id) { LOGI("CdmEngine::RemoveKeys"); - std::shared_ptr session; - if (!session_map_.FindSession(session_id, session)) { + shared_ptr session; + if (!session_map_.FindSession(session_id, &session)) { LOGE("CdmEngine::RemoveKeys: session_id not found = %s", session_id.c_str()); return SESSION_NOT_FOUND_5; } session->ReleaseCrypto(); + return NO_ERROR; } @@ -461,8 +419,8 @@ CdmResponseType CdmEngine::GenerateRenewalRequest( const CdmSessionId& session_id, CdmKeyRequest* key_request) { LOGI("CdmEngine::GenerateRenewalRequest"); - std::shared_ptr session; - if (!session_map_.FindSession(session_id, session)) { + shared_ptr session; + if (!session_map_.FindSession(session_id, &session)) { LOGE("CdmEngine::GenerateRenewalRequest: session_id not found = %s", session_id.c_str()); return SESSION_NOT_FOUND_6; @@ -490,8 +448,8 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id, const CdmKeyResponse& key_data) { LOGI("CdmEngine::RenewKey"); - std::shared_ptr session; - if (!session_map_.FindSession(session_id, session)) { + shared_ptr session; + if (!session_map_.FindSession(session_id, &session)) { LOGE("CdmEngine::RenewKey: session_id not found = %s", session_id.c_str()); return SESSION_NOT_FOUND_7; } @@ -501,7 +459,14 @@ CdmResponseType CdmEngine::RenewKey(const CdmSessionId& session_id, return EMPTY_KEY_DATA_2; } - CdmResponseType sts = session->RenewKey(key_data); + CdmResponseType sts; + M_TIME( + sts = session->RenewKey( + key_data), + session->GetMetrics(), + cdm_session_renew_key_, + sts); + if (KEY_ADDED != sts) { LOGE("CdmEngine::RenewKey: keys not added, sts=%d", static_cast(sts)); return sts; @@ -514,9 +479,16 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, const std::string& query_token, std::string* query_response) { LOGI("CdmEngine::QueryStatus"); - CryptoSession crypto_session; + CryptoSession crypto_session(metrics_.GetCryptoMetrics()); if (security_level == kLevel3) { - CdmResponseType status = crypto_session.Open(kLevel3); + CdmResponseType status; + M_TIME( + status = crypto_session.Open( + kLevel3), + metrics_.GetCryptoMetrics(), + crypto_session_open_, + status, + kLevel3); if (NO_ERROR != status) return INVALID_QUERY_STATUS; } @@ -526,8 +498,9 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, } if (query_token == QUERY_KEY_SECURITY_LEVEL) { - CdmSecurityLevel level = crypto_session.GetSecurityLevel(); - switch (level) { + CdmSecurityLevel found_security_level = + crypto_session.GetSecurityLevel(); + switch (found_security_level) { case kSecurityLevelL1: *query_response = QUERY_VALUE_SECURITY_LEVEL_L1; break; @@ -542,12 +515,16 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, *query_response = QUERY_VALUE_SECURITY_LEVEL_UNKNOWN; break; default: - LOGW("CdmEngine::QueryStatus: Unknown security level: %d", level); + LOGW("CdmEngine::QueryStatus: Unknown security level: %d", + found_security_level); return UNKNOWN_ERROR; } } else if (query_token == QUERY_KEY_DEVICE_ID) { std::string deviceId; - if (!crypto_session.GetDeviceUniqueId(&deviceId)) { + bool got_id = crypto_session.GetExternalDeviceUniqueId(&deviceId); + metrics_.GetCryptoMetrics()->crypto_session_get_device_unique_id_ + .Increment(got_id); + if (!got_id) { LOGW("CdmEngine::QueryStatus: QUERY_KEY_DEVICE_ID unknown failure"); return UNKNOWN_ERROR; } @@ -555,7 +532,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, *query_response = deviceId; } else if (query_token == QUERY_KEY_SYSTEM_ID) { uint32_t system_id; - if (!crypto_session.GetSystemId(&system_id)) { + bool got_id = crypto_session.GetSystemId(&system_id); + if (!got_id) { LOGW("CdmEngine::QueryStatus: QUERY_KEY_SYSTEM_ID unknown failure"); return UNKNOWN_ERROR; } @@ -584,10 +562,17 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, current_hdcp : max_hdcp); } else if (query_token == QUERY_KEY_USAGE_SUPPORT) { bool supports_usage_reporting; - if (!crypto_session.UsageInformationSupport(&supports_usage_reporting)) { + bool got_info = crypto_session.UsageInformationSupport( + &supports_usage_reporting); + + if (!got_info) { LOGW("CdmEngine::QueryStatus: UsageInformationSupport failed"); + metrics_.GetCryptoMetrics()->crypto_session_usage_information_support_ + .SetError(got_info); return UNKNOWN_ERROR; } + metrics_.GetCryptoMetrics()->crypto_session_usage_information_support_ + .Record(supports_usage_reporting); *query_response = supports_usage_reporting ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; @@ -613,7 +598,7 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, *query_response = max_sessions_stream.str(); } else if (query_token == QUERY_KEY_OEMCRYPTO_API_VERSION) { uint32_t api_version; - if (!crypto_session.GetApiVersion(&api_version)) { + if (crypto_session.GetApiVersion(&api_version)) { LOGW("CdmEngine::QueryStatus: GetApiVersion failed"); return UNKNOWN_ERROR; } @@ -621,6 +606,28 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, std::ostringstream api_version_stream; api_version_stream << api_version; *query_response = api_version_stream.str(); + } else if (query_token == QUERY_KEY_CURRENT_SRM_VERSION) { + uint16_t current_srm_version; + if (!crypto_session.GetSrmVersion(¤t_srm_version)) { + LOGW("CdmEngine::QueryStatus: GetCurrentSRMVersion failed"); + return UNKNOWN_ERROR; + } + + std::ostringstream current_srm_version_stream; + current_srm_version_stream << current_srm_version; + *query_response = current_srm_version_stream.str(); + } else if (query_token == QUERY_KEY_SRM_UPDATE_SUPPORT) { + bool is_srm_update_supported = crypto_session.IsSrmUpdateSupported(); + *query_response = + is_srm_update_supported ? QUERY_VALUE_TRUE : QUERY_VALUE_FALSE; + } else if (query_token == QUERY_KEY_WVCDM_VERSION) { + std::string cdm_version; + if (!Properties::GetWVCdmVersion(&cdm_version)) { + LOGW("CdmEngine::QueryStatus: GetWVCdmVersion failed"); + return UNKNOWN_ERROR; + } + + *query_response = cdm_version; } else { LOGW("CdmEngine::QueryStatus: Unknown status requested, token = %s", query_token.c_str()); @@ -633,8 +640,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level, CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id, CdmQueryMap* query_response) { LOGI("CdmEngine::QuerySessionStatus"); - std::shared_ptr session; - if (!session_map_.FindSession(session_id, session)) { + shared_ptr session; + if (!session_map_.FindSession(session_id, &session)) { LOGE("CdmEngine::QuerySessionStatus: session_id not found = %s", session_id.c_str()); return SESSION_NOT_FOUND_8; @@ -644,8 +651,8 @@ CdmResponseType CdmEngine::QuerySessionStatus(const CdmSessionId& session_id, bool CdmEngine::IsReleaseSession(const CdmSessionId& session_id) { LOGI("CdmEngine::IsReleaseSession"); - std::shared_ptr session; - if (!session_map_.FindSession(session_id, session)) { + shared_ptr session; + if (!session_map_.FindSession(session_id, &session)) { LOGE("CdmEngine::IsReleaseSession: session_id not found = %s", session_id.c_str()); return false; @@ -655,8 +662,8 @@ bool CdmEngine::IsReleaseSession(const CdmSessionId& session_id) { bool CdmEngine::IsOfflineSession(const CdmSessionId& session_id) { LOGI("CdmEngine::IsOfflineSession"); - std::shared_ptr session; - if (!session_map_.FindSession(session_id, session)) { + shared_ptr session; + if (!session_map_.FindSession(session_id, &session)) { LOGE("CdmEngine::IsOfflineSession: session_id not found = %s", session_id.c_str()); return false; @@ -667,8 +674,8 @@ bool CdmEngine::IsOfflineSession(const CdmSessionId& session_id) { CdmResponseType CdmEngine::QueryKeyStatus(const CdmSessionId& session_id, CdmQueryMap* query_response) { LOGI("CdmEngine::QueryKeyStatus"); - std::shared_ptr session; - if (!session_map_.FindSession(session_id, session)) { + shared_ptr session; + if (!session_map_.FindSession(session_id, &session)) { LOGE("CdmEngine::QueryKeyStatus: session_id not found = %s", session_id.c_str()); return SESSION_NOT_FOUND_9; @@ -684,8 +691,8 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const CdmSessionId& session_id, LOGE("CdmEngine::QueryKeyAllowedUsage: no response destination"); return INVALID_PARAMETERS_ENG_12; } - std::shared_ptr session; - if (!session_map_.FindSession(session_id, session)) { + shared_ptr session; + if (!session_map_.FindSession(session_id, &session)) { LOGE("CdmEngine::QueryKeyAllowedUsage: session_id not found = %s", session_id.c_str()); return SESSION_NOT_FOUND_12; @@ -710,9 +717,6 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id, for (CdmSessionList::iterator iter = sessions.begin(); iter != sessions.end(); ++iter) { - if ((*iter)->IsClosed()) { - continue; - } session_sts = (*iter)->QueryKeyAllowedUsage(key_id, &found_in_this_session); if (session_sts == NO_ERROR) { if (found) { @@ -738,8 +742,8 @@ CdmResponseType CdmEngine::QueryKeyAllowedUsage(const std::string& key_id, CdmResponseType CdmEngine::QueryOemCryptoSessionId( const CdmSessionId& session_id, CdmQueryMap* query_response) { LOGI("CdmEngine::QueryOemCryptoSessionId"); - std::shared_ptr session; - if (!session_map_.FindSession(session_id, session)) { + shared_ptr session; + if (!session_map_.FindSession(session_id, &session)) { LOGE("CdmEngine::QueryOemCryptoSessionId: session_id not found = %s", session_id.c_str()); return SESSION_NOT_FOUND_10; @@ -770,7 +774,8 @@ CdmResponseType CdmEngine::GetProvisioningRequest( if (NULL == cert_provisioning_.get()) { cert_provisioning_.reset( - new CertificateProvisioning(&service_certificate_)); + new CertificateProvisioning(metrics_.GetCryptoMetrics(), + &service_certificate_)); } CdmResponseType ret = cert_provisioning_->GetProvisioningRequest( cert_provisioning_requested_security_level_, cert_type, cert_authority, @@ -812,9 +817,15 @@ CdmResponseType CdmEngine::HandleProvisioningResponse( if (NULL == cert_provisioning_.get()) { // Certificate provisioning object has been released. Check if a concurrent // provisioning attempt has succeeded before declaring failure. - CryptoSession crypto_session; - CdmResponseType status = - crypto_session.Open(cert_provisioning_requested_security_level_); + CryptoSession crypto_session(metrics_.GetCryptoMetrics()); + CdmResponseType status; + M_TIME( + status = crypto_session.Open( + cert_provisioning_requested_security_level_), + metrics_.GetCryptoMetrics(), + crypto_session_open_, + status, + cert_provisioning_requested_security_level_); if (NO_ERROR != status) { LOGE( "CdmEngine::HandleProvisioningResponse: provisioning object " @@ -873,17 +884,27 @@ CdmResponseType CdmEngine::Unprovision(CdmSecurityLevel security_level) { } CdmResponseType CdmEngine::DeleteUsageTable(CdmSecurityLevel security_level) { - scoped_ptr crypto_session(new CryptoSession()); - CdmResponseType status = crypto_session->Open( + CryptoSession crypto_session(metrics_.GetCryptoMetrics()); + CdmResponseType status; + M_TIME( + status = crypto_session.Open( + security_level == kSecurityLevelL3 ? + kLevel3 : + kLevelDefault), + metrics_.GetCryptoMetrics(), + crypto_session_open_, + status, security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault); if (NO_ERROR != status) { LOGE("CdmEngine::DeleteUsageTable: error opening crypto session: %d", - status); + status); return UNPROVISION_ERROR_4; } - status = crypto_session->DeleteAllUsageReports(); + status = crypto_session.DeleteAllUsageReports(); + metrics_.GetCryptoMetrics()->crypto_session_delete_all_usage_reports_ + .Increment(status); if (status != NO_ERROR) { - LOGE("CdmEngine::DeleteUsageTable: error deleting usage reports: %d", + LOGE("CdmEngine::DeleteUsageTable: error deleteing usage reports: %d", status); } return status; @@ -894,7 +915,7 @@ CdmResponseType CdmEngine::ListStoredLicenses( DeviceFiles handle(file_system_); if (!key_set_ids) { LOGE("CdmEngine::ListStoredLicenses: no response destination"); - return INVALID_PARAMETERS_ENG_17; + return INVALID_PARAMETERS_ENG_22; } if (!handle.Init(security_level)) { LOGE("CdmEngine::ListStoredLicenses: unable to initialize device files"); @@ -913,7 +934,7 @@ CdmResponseType CdmEngine::ListUsageRecords(const std::string& app_id, DeviceFiles handle(file_system_); if (!ksids) { LOGE("CdmEngine::ListUsageRecords: no response destination"); - return INVALID_PARAMETERS_ENG_18; + return INVALID_PARAMETERS_ENG_23; } if (!handle.Init(security_level)) { LOGE("CdmEngine::ListUsageRecords: unable to initialize device files"); @@ -936,13 +957,15 @@ CdmResponseType CdmEngine::DeleteUsageRecord(const std::string& app_id, LOGE("CdmEngine::DeleteUsageRecord: unable to initialize device files"); return DELETE_USAGE_ERROR_1; } - if (!handle.GetProviderToken(app_id, key_set_id, &provider_session_token)) { - LOGE("CdmEngine::DeleteUsageRecord: GetProviderToken failed"); + if (!handle.GetProviderSessionToken(app_id, key_set_id, + &provider_session_token)) { + LOGE("CdmEngine::DeleteUsageRecord: GetProviderSessionToken failed"); return DELETE_USAGE_ERROR_2; } // Got provider token. Remove from OEMCrypto. - scoped_ptr crypto_session(new CryptoSession()); + scoped_ptr crypto_session( + new CryptoSession(metrics_.GetCryptoMetrics())); CdmResponseType status = crypto_session->Open( security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault); if (status == NO_ERROR) { @@ -972,10 +995,10 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, } usage_property_set_->set_security_level(kLevelDefault); usage_property_set_->set_app_id(app_id); - usage_session_.reset(new CdmSession(file_system_)); + usage_session_.reset(new CdmSession(file_system_, metrics_.AddSession())); CdmResponseType status = usage_session_->Init(usage_property_set_.get()); if (NO_ERROR != status) { - LOGE("CdmEngine::GetUsageInfo: session init error"); + LOGE("CdmEngine::GetUsageInfo: session init error: %d", status); return status; } DeviceFiles handle(file_system_); @@ -986,11 +1009,13 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, CdmKeyMessage license_request; CdmKeyResponse license_response; - if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request, - &license_response)) { + std::string usage_entry; + DeviceFiles::CdmUsageData usage_data; + if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id), + ssid, &usage_data)) { usage_property_set_->set_security_level(kLevel3); usage_property_set_->set_app_id(app_id); - usage_session_.reset(new CdmSession(file_system_)); + usage_session_.reset(new CdmSession(file_system_, metrics_.AddSession())); status = usage_session_->Init(usage_property_set_.get()); if (NO_ERROR != status) { LOGE("CdmEngine::GetUsageInfo: session init error"); @@ -1000,15 +1025,15 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, LOGE("CdmEngine::GetUsageInfo: device file init error"); return GET_USAGE_INFO_ERROR_2; } - if (!handle.RetrieveUsageInfo(app_id, ssid, &license_request, - &license_response)) { + if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id), + ssid, &usage_data)) { // No entry found for that ssid. return USAGE_INFO_NOT_FOUND; } } status = - usage_session_->RestoreUsageSession(license_request,license_response); + usage_session_->RestoreUsageSession(usage_data); if (KEY_ADDED != status) { LOGE("CdmEngine::GetUsageInfo: restore usage session error %d", status); @@ -1067,7 +1092,7 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, usage_property_set_->set_security_level(requested_security_level); usage_property_set_->set_app_id(app_id); - usage_session_.reset(new CdmSession(file_system_)); + usage_session_.reset(new CdmSession(file_system_, metrics_.AddSession())); CdmResponseType status = usage_session_->Init(usage_property_set_.get()); if (NO_ERROR != status) { @@ -1081,8 +1106,9 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, return GET_USAGE_INFO_ERROR_3; } - std::vector > license_info; - if (!handle.RetrieveUsageInfo(app_id, &license_info)) { + std::vector usage_data; + if (!handle.RetrieveUsageInfo(DeviceFiles::GetUsageInfoFileName(app_id), + &usage_data)) { LOGE("CdmEngine::GetUsageInfo: unable to read usage information"); return GET_USAGE_INFO_ERROR_4; } @@ -1091,16 +1117,15 @@ CdmResponseType CdmEngine::GetUsageInfo(const std::string& app_id, LOGE("CdmEngine::GetUsageInfo: no usage info destination"); return INVALID_PARAMETERS_ENG_10; } - if (0 == license_info.size()) { + if (0 == usage_data.size()) { usage_info->resize(0); return NO_ERROR; } usage_info->resize(kUsageReportsPerRequest); - uint32_t index = rand() % license_info.size(); - status = usage_session_->RestoreUsageSession(license_info[index].first, - license_info[index].second); + uint32_t index = rand() % usage_data.size(); + status = usage_session_->RestoreUsageSession(usage_data[index]); if (KEY_ADDED != status) { LOGE("CdmEngine::GetUsageInfo: restore usage session (%d) error %ld", index, status); @@ -1135,12 +1160,12 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo( DeviceFiles handle(file_system_); if (!handle.Init(security_level)) { LOGE("CdmEngine::ReleaseAllUsageInfo: unable to initialize device files"); - return RELEASE_ALL_USAGE_INFO_ERROR_3; + return RELEASE_ALL_USAGE_INFO_ERROR_6; } std::vector provider_session_tokens; if (!handle.DeleteAllUsageInfoForApp(app_id, &provider_session_tokens)) { LOGE("CdmEngine::ReleaseAllUsageInfo: failed to delete usage records"); - return RELEASE_ALL_USAGE_INFO_ERROR_4; + return RELEASE_ALL_USAGE_INFO_ERROR_7; } if (provider_session_tokens.size() == 0UL) { @@ -1148,7 +1173,8 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo( } // Got at least one provider token. Remove from OEMCrypto. - scoped_ptr crypto_session(new CryptoSession()); + scoped_ptr crypto_session( + new CryptoSession(metrics_.GetCryptoMetrics())); CdmResponseType status = crypto_session->Open( security_level == kSecurityLevelL3 ? kLevel3 : kLevelDefault); if (status == NO_ERROR) { @@ -1156,7 +1182,7 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo( DeleteMultipleUsageInformation(provider_session_tokens); } if (status != NO_ERROR) { - LOGE("CdmEngine::DeleteUsageRecord: CryptoSession failure"); + LOGE("CdmEngine::ReleaseAllUsageInfo: CryptoSession failure"); } return status; } @@ -1171,24 +1197,69 @@ CdmResponseType CdmEngine::ReleaseAllUsageInfo(const std::string& app_id) { for (int j = kSecurityLevelL1; j < kSecurityLevelUnknown; ++j) { DeviceFiles handle(file_system_); if (handle.Init(static_cast(j))) { - std::vector provider_session_tokens; - if (!handle.DeleteAllUsageInfoForApp(app_id, &provider_session_tokens)) { - LOGE("CdmEngine::ReleaseAllUsageInfo: failed to delete L%d secure" - "stops", j); - status = RELEASE_ALL_USAGE_INFO_ERROR_1; - } else { - SecurityLevel security_level = - static_cast(j) == kSecurityLevelL3 - ? kLevel3 - : kLevelDefault; - usage_property_set_->set_security_level(security_level); - usage_session_.reset(new CdmSession(file_system_)); - usage_session_->Init(usage_property_set_.get()); - CdmResponseType status2 = usage_session_-> - DeleteMultipleUsageInformation(provider_session_tokens); - if (status2 != NO_ERROR) { - status = status2; + SecurityLevel security_level = + static_cast(j) == kSecurityLevelL3 + ? kLevel3 + : kLevelDefault; + usage_property_set_->set_security_level(security_level); + usage_session_.reset(new CdmSession(file_system_, metrics_.AddSession())); + usage_session_->Init(usage_property_set_.get()); + + switch (usage_session_->get_usage_support_type()) { + case kUsageEntrySupport: { + std::vector usage_data; + // Retrieve all usage information but delete only one before + // refetching. This is because deleting the usage entry + // might cause other entries to be shifted and information updated. + do { + if (!handle.RetrieveUsageInfo( + DeviceFiles::GetUsageInfoFileName(app_id), + &usage_data)) { + status = RELEASE_ALL_USAGE_INFO_ERROR_4; + break; + } + + if (usage_data.empty()) break; + + status = usage_session_->DeleteUsageEntry( + usage_data[0].usage_entry_number); + + if (status != NO_ERROR) break; + + if (!handle.DeleteUsageInfo( + DeviceFiles::GetUsageInfoFileName(app_id), + usage_data[0].provider_session_token)) { + status = RELEASE_ALL_USAGE_INFO_ERROR_6; + break; + } + } while (status == NO_ERROR && !usage_data.empty()); + + std::vector provider_session_tokens; + if (!handle.DeleteAllUsageInfoForApp( + DeviceFiles::GetUsageInfoFileName(app_id), + &provider_session_tokens)) { + status = RELEASE_ALL_USAGE_INFO_ERROR_5; + } + break; } + case kUsageTableSupport: { + std::vector provider_session_tokens; + if (!handle.DeleteAllUsageInfoForApp( + DeviceFiles::GetUsageInfoFileName(app_id), + &provider_session_tokens)) { + LOGE("CdmEngine::ReleaseAllUsageInfo: failed to delete %d secure" + "stops", j); + status = RELEASE_ALL_USAGE_INFO_ERROR_1; + } else { + CdmResponseType status2 = usage_session_-> + DeleteMultipleUsageInformation(provider_session_tokens); + if (status2 != NO_ERROR) status = status2; + } + break; + } + default: + // Ignore + break; } } else { LOGE("CdmEngine::ReleaseAllUsageInfo: failed to initialize L%d device" @@ -1227,8 +1298,8 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id, return EMPTY_KEYSET_ID_ENG_5; } - std::shared_ptr session; - if (!session_map_.FindSession(key_set_id, session)) { + shared_ptr session; + if (!session_map_.FindSession(key_set_id, &session)) { LOGE("CdmEngine::LoadUsageSession: session_id not found = %s ", key_set_id.c_str()); return SESSION_NOT_FOUND_11; @@ -1248,16 +1319,19 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id, std::string app_id; session->GetApplicationId(&app_id); - CdmKeyMessage key_message; - CdmKeyResponse key_response; - if (!handle.RetrieveUsageInfoByKeySetId(app_id, key_set_id, &key_message, - &key_response)) { + DeviceFiles::CdmUsageData usage_data; + if (!handle.RetrieveUsageInfoByKeySetId( + DeviceFiles::GetUsageInfoFileName(app_id), key_set_id, + &(usage_data.provider_session_token), + &(usage_data.license_request), + &(usage_data.license), &(usage_data.usage_entry), + &(usage_data.usage_entry_number))) { LOGE("CdmEngine::LoadUsageSession: unable to find usage information"); return LOAD_USAGE_INFO_MISSING; } - CdmResponseType status = session->RestoreUsageSession(key_message, - key_response); + CdmResponseType status = session->RestoreUsageSession(usage_data); + session->GetMetrics()->cdm_session_restore_usage_session_.Increment(status); if (KEY_ADDED != status) { LOGE("CdmEngine::LoadUsageSession: usage session error %ld", status); return status; @@ -1265,13 +1339,15 @@ CdmResponseType CdmEngine::LoadUsageSession(const CdmKeySetId& key_set_id, CdmKeyRequest request; status = session->GenerateReleaseRequest(&request); + *release_message = request.message; switch (status) { case KEY_MESSAGE: break; - case KEY_CANCELED: // usage information not present in - session->DeleteLicense(); // OEMCrypto, delete and try again + case KEY_CANCELED: + // usage information not present in OEMCrypto, delete and try again + session->DeleteLicense(); break; default: LOGE("CdmEngine::LoadUsageSession: generate release request error: %d", @@ -1307,7 +1383,7 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id, // else we must be level 1 direct and we don't need to return a buffer. } - std::shared_ptr session; + shared_ptr session; if (session_id.empty()) { CdmSessionList sessions; session_map_.GetSessionList(sessions); @@ -1317,9 +1393,6 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id, int64_t seconds_remaining = 0; for (CdmSessionList::iterator iter = sessions.begin(); iter != sessions.end(); ++iter) { - if ((*iter)->IsClosed()) { - continue; - } if ((*iter)->IsKeyLoaded(*parameters.key_id)) { int64_t duration = (*iter)->GetDurationRemaining(); if (duration > seconds_remaining) { @@ -1328,18 +1401,16 @@ CdmResponseType CdmEngine::Decrypt(const CdmSessionId& session_id, } } } - } else { - session_map_.FindSession(session_id, session); - } - - if (session.get() == NULL) { - if (session_id.empty()) { + if (session.get() == NULL) { LOGE("CdmEngine::Decrypt: session not found: Empty session ID"); - } else { + return SESSION_NOT_FOUND_FOR_DECRYPT; + } + } else { + if (!session_map_.FindSession(session_id, &session)) { LOGE("CdmEngine::Decrypt: session not found: id=%s, id size=%d", session_id.c_str(), session_id.size()); + return SESSION_NOT_FOUND_FOR_DECRYPT; } - return SESSION_NOT_FOUND_FOR_DECRYPT; } return session->Decrypt(parameters); @@ -1349,8 +1420,8 @@ CdmResponseType CdmEngine::GenericEncrypt( const std::string& session_id, const std::string& in_buffer, const std::string& key_id, const std::string& iv, CdmEncryptionAlgorithm algorithm, std::string* out_buffer) { - std::shared_ptr session; - if (!session_map_.FindSession(session_id, session)) { + shared_ptr session; + if (!session_map_.FindSession(session_id, &session)) { LOGE("CdmEngine::GenericEncrypt: session_id not found = %s ", session_id.c_str()); return SESSION_NOT_FOUND_13; @@ -1363,8 +1434,8 @@ CdmResponseType CdmEngine::GenericDecrypt( const std::string& key_id, const std::string& iv, CdmEncryptionAlgorithm algorithm, std::string* out_buffer) { - std::shared_ptr session; - if (!session_map_.FindSession(session_id, session)) { + shared_ptr session; + if (!session_map_.FindSession(session_id, &session)) { LOGE("CdmEngine::GenericDecrypt: session_id not found = %s ", session_id.c_str()); return SESSION_NOT_FOUND_14; @@ -1376,8 +1447,8 @@ CdmResponseType CdmEngine::GenericSign( const std::string& session_id, const std::string& message, const std::string& key_id, CdmSigningAlgorithm algorithm, std::string* signature) { - std::shared_ptr session; - if (!session_map_.FindSession(session_id, session)) { + shared_ptr session; + if (!session_map_.FindSession(session_id, &session)) { LOGE("CdmEngine::GenericSign: session_id not found = %s ", session_id.c_str()); return SESSION_NOT_FOUND_15; @@ -1389,8 +1460,8 @@ CdmResponseType CdmEngine::GenericVerify( const std::string& session_id, const std::string& message, const std::string& key_id, CdmSigningAlgorithm algorithm, const std::string& signature) { - std::shared_ptr session; - if (!session_map_.FindSession(session_id, session)) { + shared_ptr session; + if (!session_map_.FindSession(session_id, &session)) { LOGE("CdmEngine::GenericVerify: session_id not found = %s ", session_id.c_str()); return SESSION_NOT_FOUND_16; @@ -1404,9 +1475,6 @@ bool CdmEngine::IsKeyLoaded(const KeyId& key_id) { session_map_.GetSessionList(sessions); for (CdmSessionList::iterator iter = sessions.begin(); iter != sessions.end(); ++iter) { - if ((*iter)->IsClosed()) { - continue; - } if ((*iter)->IsKeyLoaded(key_id)) { return true; } @@ -1431,9 +1499,6 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id, int64_t seconds_remaining = 0; for (CdmSessionList::iterator iter = sessions.begin(); iter != sessions.end(); ++iter) { - if ((*iter)->IsClosed()) { - continue; - } CdmSessionId id = (*iter)->session_id(); if (Properties::GetSessionSharingId(id) == session_sharing_id) { if ((*iter)->IsKeyLoaded(key_id)) { @@ -1455,8 +1520,8 @@ bool CdmEngine::FindSessionForKey(const KeyId& key_id, bool CdmEngine::NotifyResolution(const CdmSessionId& session_id, uint32_t width, uint32_t height) { - std::shared_ptr session; - if (session_map_.FindSession(session_id, session)) { + shared_ptr session; + if (session_map_.FindSession(session_id, &session)) { session->NotifyResolution(width, height); return true; } @@ -1490,9 +1555,7 @@ void CdmEngine::OnTimerEvent() { is_usage_update_needed = is_usage_update_needed || sessions.front()->is_usage_update_needed(); - if (!sessions.front()->IsClosed()) { - sessions.front()->OnTimerEvent(usage_update_period_expired); - } + sessions.front()->OnTimerEvent(usage_update_period_expired); sessions.pop_front(); } @@ -1505,19 +1568,26 @@ void CdmEngine::OnTimerEvent() { for (CdmSessionList::iterator iter = sessions.begin(); iter != sessions.end(); ++iter) { - if ((*iter)->IsClosed()) { - continue; - } (*iter)->reset_usage_flags(); - if (!has_usage_been_updated) { - // usage is updated for all sessions so this needs to be - // called only once per update usage information period - CdmResponseType status = (*iter)->UpdateUsageInformation(); - if (NO_ERROR != status) { - LOGW("Update usage information failed: %d", status); - } else { - has_usage_been_updated = true; - } + switch ((*iter)->get_usage_support_type()) { + case kUsageEntrySupport: + (*iter)->UpdateUsageEntryInformation(); + break; + case kUsageTableSupport: + if (!has_usage_been_updated) { + // usage is updated for all sessions so this needs to be + // called only once per update usage information period + CdmResponseType status = (*iter)->UpdateUsageTableInformation(); + if (NO_ERROR != status) { + LOGW("Update usage information failed: %d", status); + } else { + has_usage_been_updated = true; + } + } + break; + default: + // Ignore + break; } } } @@ -1529,9 +1599,7 @@ void CdmEngine::OnKeyReleaseEvent(const CdmKeySetId& key_set_id) { session_map_.GetSessionList(sessions); while (!sessions.empty()) { - if (!sessions.front()->IsClosed()) { - sessions.front()->OnKeyReleaseEvent(key_set_id); - } + sessions.front()->OnKeyReleaseEvent(key_set_id); sessions.pop_front(); } } @@ -1588,11 +1656,20 @@ void CdmEngine::DeleteAllUsageReportsUponFactoryReset() { if (!file_system_->Exists(device_base_path_level1) && !file_system_->Exists(device_base_path_level3)) { - scoped_ptr crypto_session(new CryptoSession()); - CdmResponseType status = crypto_session->Open( + scoped_ptr crypto_session( + new CryptoSession(metrics_.GetCryptoMetrics())); + CdmResponseType status; + M_TIME( + status = crypto_session->Open( + cert_provisioning_requested_security_level_), + metrics_.GetCryptoMetrics(), + crypto_session_open_, + status, cert_provisioning_requested_security_level_); if (NO_ERROR == status) { status = crypto_session->DeleteAllUsageReports(); + metrics_.GetCryptoMetrics()->crypto_session_delete_all_usage_reports_ + .Increment(status); if (NO_ERROR != status) { LOGW( "CdmEngine::DeleteAllUsageReportsUponFactoryReset: " diff --git a/core/src/cdm_session.cpp b/core/src/cdm_session.cpp index ab8c97f8..64875f1d 100644 --- a/core/src/cdm_session.cpp +++ b/core/src/cdm_session.cpp @@ -16,6 +16,7 @@ #include "string_conversions.h" #include "wv_cdm_constants.h" #include "wv_cdm_event_listener.h" +#include "usage_table_header.h" namespace { const size_t kKeySetIdLength = 14; @@ -23,10 +24,11 @@ const size_t kKeySetIdLength = 14; namespace wvcdm { -CdmSession::CdmSession(FileSystem* file_system) : +CdmSession::CdmSession(FileSystem* file_system, + metrics::SessionMetrics* metrics) : + metrics_(metrics), initialized_(false), - closed_(false), - crypto_session_(new CryptoSession), + closed_(true), file_handle_(new DeviceFiles(file_system)), license_received_(false), is_offline_(false), @@ -38,8 +40,16 @@ CdmSession::CdmSession(FileSystem* file_system) : has_decrypted_since_last_report_(false), is_initial_usage_update_(true), is_usage_update_needed_(false), + usage_support_type_(kNonSecureUsageSupport), + usage_table_header_(NULL), + usage_entry_number_(0), mock_license_parser_in_use_(false), - mock_policy_engine_in_use_(false) {} + mock_policy_engine_in_use_(false) { + assert(metrics_); // metrics_ must not be null. + crypto_metrics_ = metrics_->GetCryptoMetrics(); + crypto_session_.reset(new CryptoSession(crypto_metrics_)); + life_span_.Start(); +} CdmSession::~CdmSession() { if (!key_set_id_.empty()) { @@ -47,15 +57,19 @@ CdmSession::~CdmSession() { file_handle_->UnreserveLicenseId(key_set_id_); } Properties::RemoveSessionPropertySet(session_id_); + + if (metrics_) { + M_RECORD(metrics_, cdm_session_life_span_, life_span_.AsMs()); + metrics_->SetCompleted(); + } } CdmResponseType CdmSession::Init( CdmClientPropertySet* cdm_client_property_set) { - return Init(NULL, cdm_client_property_set, NULL, NULL); + return Init(cdm_client_property_set, NULL, NULL); } CdmResponseType CdmSession::Init( - ServiceCertificate* service_certificate, CdmClientPropertySet* cdm_client_property_set, const CdmSessionId* forced_session_id, WvCdmEventListener* event_listener) { if (initialized_) { @@ -69,15 +83,30 @@ CdmResponseType CdmSession::Init( requested_security_level_ = kLevel3; security_level_ = kSecurityLevelL3; } - CdmResponseType sts = crypto_session_->Open(requested_security_level_); + CdmResponseType sts; + M_TIME( + sts = crypto_session_->Open(requested_security_level_), + crypto_metrics_, + crypto_session_open_, + sts, + requested_security_level_); if (NO_ERROR != sts) return sts; + security_level_ = crypto_session_->GetSecurityLevel(); + crypto_metrics_->crypto_session_security_level_.Record(security_level_); if (!file_handle_->Init(security_level_)) { LOGE("CdmSession::Init: Unable to initialize file handle"); return SESSION_FILE_HANDLE_INIT_ERROR; } + if (crypto_session_->GetUsageSupportType(&usage_support_type_) == NO_ERROR) { + if (usage_support_type_ == kUsageEntrySupport) + usage_table_header_ = crypto_session_->GetUsageTableHeader(); + } else { + usage_support_type_ = kNonSecureUsageSupport; + } + // Device Provisioning state is not yet known. // If not using certificates, then Keybox is client token for license // requests. @@ -90,25 +119,25 @@ CdmResponseType CdmSession::Init( std::string serial_number; CdmClientTokenType client_token_type = crypto_session_->GetPreProvisionTokenType(); - if ((client_token_type == kClientTokenKeybox) && - !Properties::use_certificates_as_identification()) { - // Keybox is client token. - LOGW("CdmSession::Init: Properties::use_certificates_as_identification() " - "is not set - using Keybox for license requests (not recommended)."); - if (!crypto_session_->GetClientToken(&client_token)) { - return SESSION_INIT_ERROR_1; - } - } else { - // License server client ID token is a stored certificate. Stage it or - // indicate that provisioning is needed. Get token from stored certificate - std::string wrapped_key; - if (!file_handle_->RetrieveCertificate(&client_token, &wrapped_key, - &serial_number, nullptr) || - !crypto_session_->LoadCertificatePrivateKey(wrapped_key)) { - return NEED_PROVISIONING; - } - client_token_type = kClientTokenDrmCert; + + // License server client ID token is a stored certificate. Stage it or + // indicate that provisioning is needed. Get token from stored certificate + std::string wrapped_key; + if (!file_handle_->RetrieveCertificate(&client_token, &wrapped_key, + &serial_number, NULL)) { + return NEED_PROVISIONING; } + bool load_cert_sts; + M_TIME( + load_cert_sts = crypto_session_->LoadCertificatePrivateKey( + wrapped_key), + crypto_metrics_, + crypto_session_load_certificate_private_key_, + load_cert_sts); + if(!load_cert_sts) { + return NEED_PROVISIONING; + } + client_token_type = kClientTokenDrmCert; // Session is provisioned with certificate needed to construct // license request (or with keybox). @@ -123,6 +152,7 @@ CdmResponseType CdmSession::Init( session_id_ = Properties::AlwaysUseKeySetIds() ? key_set_id_ : GenerateSessionId(); + metrics_->SetSessionId(session_id_); if (session_id_.empty()) { LOGE("CdmSession::Init: empty session ID"); @@ -137,19 +167,28 @@ CdmResponseType CdmSession::Init( policy_engine_.reset(new PolicyEngine( session_id_, event_listener, crypto_session_.get())); + std::string service_certificate; + if (!Properties::GetServiceCertificate(session_id_, &service_certificate)) + service_certificate.clear(); + if (!license_parser_->Init( - service_certificate, client_token, client_token_type, - serial_number, crypto_session_.get(), policy_engine_.get())) + client_token, client_token_type, serial_number, + Properties::UsePrivacyMode(session_id_), service_certificate, + crypto_session_.get(), policy_engine_.get())) return LICENSE_PARSER_INIT_ERROR; license_received_ = false; is_initial_decryption_ = true; initialized_ = true; + closed_ = false; return NO_ERROR; } CdmResponseType CdmSession::RestoreOfflineSession( const CdmKeySetId& key_set_id, const CdmLicenseType license_type) { + if (!key_set_id_.empty()) { + file_handle_->UnreserveLicenseId(key_set_id_); + } key_set_id_ = key_set_id; DeviceFiles::LicenseState license_state; @@ -162,20 +201,38 @@ CdmResponseType CdmSession::RestoreOfflineSession( &key_response_, &offline_key_renewal_request_, &offline_key_renewal_response_, &offline_release_server_url_, &playback_start_time, &last_playback_time, &grace_period_end_time, - &app_parameters_)) { - LOGE("CdmSession::Init failed to retrieve license. key set id = %s", - key_set_id.c_str()); + &app_parameters_, &usage_entry_, &usage_entry_number_)) { + LOGE("CdmSession::RestoreOfflineSession: failed to retrieve license. " + "key set id = %s", key_set_id.c_str()); return GET_LICENSE_ERROR; } // Do not restore a released offline license, unless a release retry if (!(license_type == kLicenseTypeRelease || license_state == DeviceFiles::kLicenseStateActive)) { - LOGE("CdmSession::Init invalid offline license state = %d, type = %d", - license_state, license_type); + LOGE("CdmSession::RestoreOfflineSession: invalid offline license state = " + "%d, type = %d", license_state, license_type); return GET_RELEASED_LICENSE_ERROR; } + std::string provider_session_token; + if (usage_support_type_ == kUsageEntrySupport) { + if (!license_parser_->ExtractProviderSessionToken( + key_response_, &provider_session_token) || + usage_table_header_ == NULL) { + provider_session_token.clear(); + } else { + CdmResponseType sts = + usage_table_header_->LoadEntry(crypto_session_.get(), usage_entry_, + usage_entry_number_); + if (sts != NO_ERROR) { + LOGE("CdmSession::RestoreOfflineSession: failed to load usage entry = " + "%d", sts); + return sts; + } + } + } + if (license_type == kLicenseTypeRelease) { if (!license_parser_->RestoreLicenseForRelease(key_request_, key_response_)) { @@ -184,11 +241,27 @@ CdmResponseType CdmSession::RestoreOfflineSession( } else { if (!license_parser_->RestoreOfflineLicense( key_request_, key_response_, offline_key_renewal_response_, - playback_start_time, last_playback_time, grace_period_end_time)) { + playback_start_time, last_playback_time, grace_period_end_time, + this)) { return RESTORE_OFFLINE_LICENSE_ERROR_2; } } + if (usage_support_type_ == kUsageEntrySupport && + !provider_session_token.empty() && usage_table_header_ != NULL) { + CdmResponseType sts = + usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_); + if (sts != NO_ERROR) { + LOGE("CdmSession::RestoreOfflineSession failed to update usage entry = " + "%d", sts); + return sts; + } + if (!StoreLicense(license_state)) { + LOGW("CdmSession::RestoreUsageSession: unable to save updated usage " + "info"); + } + } + license_received_ = true; is_offline_ = true; is_release_ = license_type == kLicenseTypeRelease; @@ -196,14 +269,47 @@ CdmResponseType CdmSession::RestoreOfflineSession( } CdmResponseType CdmSession::RestoreUsageSession( - const CdmKeyMessage& key_request, const CdmKeyResponse& key_response) { - key_request_ = key_request; - key_response_ = key_response; + const DeviceFiles::CdmUsageData& usage_data) { + if (!key_set_id_.empty()) { + file_handle_->UnreserveLicenseId(key_set_id_); + } + key_set_id_ = usage_data.key_set_id; + key_request_ = usage_data.license_request; + key_response_ = usage_data.license; + usage_entry_ = usage_data.usage_entry; + usage_entry_number_ = usage_data.usage_entry_number; + usage_provider_session_token_ = usage_data.provider_session_token; + + if (usage_support_type_ == kUsageEntrySupport && + usage_table_header_ != NULL) { + CdmResponseType sts = usage_table_header_->LoadEntry( + crypto_session_.get(), usage_entry_, usage_entry_number_); + if (sts != NO_ERROR) { + LOGE("CdmSession::RestoreUsageSession: failed to load usage entry = %d", + sts); + return sts; + } + } if (!license_parser_->RestoreLicenseForRelease(key_request_, key_response_)) { return RELEASE_LICENSE_ERROR_2; } + if (usage_support_type_ == kUsageEntrySupport && + usage_table_header_ != NULL) { + CdmResponseType sts = + usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_); + if (sts != NO_ERROR) { + LOGE("CdmSession::RestoreUsageSession: failed to update usage entry: %d", + sts); + return sts; + } + if (!UpdateUsageInfo()) { + LOGW("CdmSession::RestoreUsageSession: unable to save updated usage " + "info"); + } + } + license_received_ = true; is_offline_ = false; is_release_ = true; @@ -274,9 +380,10 @@ CdmResponseType CdmSession::GenerateKeyRequest( std::vector embedded_key_data = init_data.ExtractEmbeddedKeys(); - for (int i = 0; i < embedded_key_data.size(); ++i) { + for (size_t i = 0; i < embedded_key_data.size(); ++i) { CdmResponseType sts = crypto_session_->AddSubSession( - embedded_key_data[i].sub_session_key_id()); + embedded_key_data[i].sub_session_key_id(), + init_data.ExtractGroupMasterKeyId()); if (NO_ERROR != sts) { LOGE("CdmSession::GenerateKeyRequest: Unable to generate sub session"); return sts; @@ -318,7 +425,41 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) { } else if (license_received_) { // renewal return RenewKey(key_response); } else { - CdmResponseType sts = license_parser_->HandleKeyResponse(key_response); + // If usage table header+entries are supported, preprocess the license + // to see if it has a provider session token. If so a new entry needs + // to be created. + CdmResponseType sts; + std::string provider_session_token; + if (usage_support_type_ == kUsageEntrySupport && + usage_table_header_ != NULL) { + if (license_parser_->ExtractProviderSessionToken( + key_response, &provider_session_token) && + !provider_session_token.empty()) { + std::string app_id; + GetApplicationId(&app_id); + sts = usage_table_header_->AddEntry( + crypto_session_.get(), is_offline_, key_set_id_, + DeviceFiles::GetUsageInfoFileName(app_id), &usage_entry_number_); + if (sts != NO_ERROR) return sts; + } + } + sts = license_parser_->HandleKeyResponse(key_response); + + // Update or delete entry if usage table header+entries are supported + if (usage_support_type_ == kUsageEntrySupport && + !provider_session_token.empty() && + usage_table_header_ != NULL) { + if (sts != KEY_ADDED) { + CdmResponseType delete_sts = + usage_table_header_->DeleteEntry(usage_entry_number_, + file_handle_.get(), + crypto_metrics_); + if (delete_sts != NO_ERROR) { + LOGW("CdmSession::AddKey: Delete usage entry failed = %d", + delete_sts); + } + } + } if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? ADD_KEY_ERROR : sts; @@ -329,7 +470,17 @@ CdmResponseType CdmSession::AddKey(const CdmKeyResponse& key_response) { license_parser_->provider_session_token().size(), license_parser_->provider_session_token().c_str()); - if (is_offline_ || !license_parser_->provider_session_token().empty()) { + if (is_offline_ || has_provider_session_token()) { + if (has_provider_session_token() && + usage_support_type_ == kUsageEntrySupport && + usage_table_header_ != NULL) { + usage_table_header_->UpdateEntry(crypto_session_.get(), &usage_entry_); + } + + if (!is_offline_) + usage_provider_session_token_ = + license_parser_->provider_session_token(); + sts = StoreLicense(); if (sts != NO_ERROR) return sts; } @@ -409,18 +560,18 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) { if (!crypto_session_->IsOpen()) { LOGW("CdmSession::Decrypt: Crypto session not open"); - return CRYPTO_SESSION_OPEN_ERROR_5; + return DECRYPT_NOT_READY; } // Playback may not begin until either the start time passes or the license // is updated, so we treat this Decrypt call as invalid. if (params.is_encrypted && !policy_engine_->CanDecryptContent(*params.key_id)) { - if (policy_engine_->GetKeyStatus(*params.key_id) == - kKeyStatusOutputNotAllowed) { + if (policy_engine_->IsLicenseForFuture()) + return DECRYPT_NOT_READY; + if (!policy_engine_->IsSufficientOutputProtection(*params.key_id)) return INSUFFICIENT_OUTPUT_PROTECTION; - } - return policy_engine_->IsLicenseForFuture() ? DECRYPT_NOT_READY : NEED_KEY; + return NEED_KEY; } CdmResponseType status = crypto_session_->Decrypt(params); @@ -432,8 +583,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) { } has_decrypted_since_last_report_ = true; if (!is_usage_update_needed_) { - is_usage_update_needed_ = - !license_parser_->provider_session_token().empty(); + is_usage_update_needed_ = has_provider_session_token(); } } else { Clock clock; @@ -452,7 +602,7 @@ CdmResponseType CdmSession::Decrypt(const CdmDecryptionParameters& params) { CdmResponseType CdmSession::GenerateRenewalRequest( CdmKeyRequest* key_request) { CdmResponseType status = license_parser_->PrepareKeyUpdateRequest( - true, app_parameters_, &key_request->message, &key_request->url); + true, app_parameters_, NULL, &key_request->message, &key_request->url); key_request->type = kKeyRequestTypeRenewal; @@ -482,16 +632,32 @@ CdmResponseType CdmSession::GenerateReleaseRequest( CdmKeyRequest* key_request) { is_release_ = true; CdmResponseType status = license_parser_->PrepareKeyUpdateRequest( - false, app_parameters_, &key_request->message, - &key_request->url); + false, app_parameters_, usage_table_header_ == NULL ? NULL : this, + &key_request->message, &key_request->url); key_request->type = kKeyRequestTypeRelease; if (KEY_MESSAGE != status) return status; + if (has_provider_session_token() && + usage_support_type_ == kUsageEntrySupport) { + status = usage_table_header_->UpdateEntry(crypto_session_.get(), + &usage_entry_); + if (status != NO_ERROR) { + LOGE("CdmSession::GenerateReleaseRequest: Update usage entry failed = " + "%d", status); + return status; + } + } + if (is_offline_) { // Mark license as being released if (!StoreLicense(DeviceFiles::kLicenseStateReleasing)) return RELEASE_KEY_REQUEST_ERROR; + } else if (!usage_provider_session_token_.empty()) { + if (usage_support_type_ == kUsageEntrySupport) { + if (!UpdateUsageInfo()) + return RELEASE_USAGE_INFO_FAILED; + } } return KEY_MESSAGE; } @@ -502,12 +668,56 @@ CdmResponseType CdmSession::ReleaseKey(const CdmKeyResponse& key_response) { license_parser_->HandleKeyUpdateResponse(false, key_response); if (sts != KEY_ADDED) return (sts == KEY_ERROR) ? RELEASE_KEY_ERROR : sts; - if (is_offline_ || !license_parser_->provider_session_token().empty()) { + if (is_offline_ || has_provider_session_token()) { DeleteLicense(); + + if (usage_support_type_ == kUsageEntrySupport && + has_provider_session_token()) { + sts = DeleteUsageEntry(usage_entry_number_); + if (NO_ERROR != sts) return sts; + } } return NO_ERROR; } +CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_number) { + if (usage_support_type_ != kUsageEntrySupport) { + LOGE("CdmSession::DeleteUsageEntry: Unexpected usage type supported: %d", + usage_support_type_); + return INCORRECT_USAGE_SUPPORT_TYPE_1; + } + + // The usage entry cannot be deleted if it has a crypto session handling + // it, so close and reopen session. + CdmResponseType sts; + crypto_session_->Close(); + crypto_session_.reset(new CryptoSession(crypto_metrics_)); + M_TIME( + sts = crypto_session_->Open(requested_security_level_), + crypto_metrics_, + crypto_session_open_, + sts, + requested_security_level_); + if (sts != NO_ERROR) return sts; + + usage_table_header_ = NULL; + if (crypto_session_->GetUsageSupportType(&usage_support_type_) == NO_ERROR) { + if (usage_support_type_ == kUsageEntrySupport) + usage_table_header_ = crypto_session_->GetUsageTableHeader(); + } else { + usage_support_type_ = kNonSecureUsageSupport; + } + + if (usage_table_header_ == NULL) { + LOGE("CdmSession::DeleteUsageEntry: Usage table header unavailable"); + return INCORRECT_USAGE_SUPPORT_TYPE_1; + } + + return usage_table_header_->DeleteEntry(usage_entry_number, + file_handle_.get(), + crypto_metrics_); +} + bool CdmSession::IsKeyLoaded(const KeyId& key_id) { return license_parser_->IsKeyLoaded(key_id); } @@ -532,8 +742,9 @@ bool CdmSession::GenerateKeySetId(CdmKeySetId* key_set_id) { (kKeySetIdLength - sizeof(KEY_SET_ID_PREFIX)) / 2, 0); while (key_set_id->empty()) { - if (!crypto_session_->GetRandom(random_data.size(), &random_data[0])) + if (!crypto_session_->GetRandom(random_data.size(), &random_data[0])) { return false; + } *key_set_id = KEY_SET_ID_PREFIX + b2a_hex(random_data); @@ -581,7 +792,10 @@ CdmResponseType CdmSession::StoreLicense() { std::string app_id; GetApplicationId(&app_id); if (!file_handle_->StoreUsageInfo(provider_session_token, key_request_, - key_response_, app_id, key_set_id_)) { + key_response_, + DeviceFiles::GetUsageInfoFileName(app_id), + key_set_id_, usage_entry_, + usage_entry_number_)) { LOGE("CdmSession::StoreLicense: Unable to store usage info"); return STORE_USAGE_INFO_ERROR; } @@ -594,7 +808,8 @@ bool CdmSession::StoreLicense(DeviceFiles::LicenseState state) { offline_key_renewal_request_, offline_key_renewal_response_, offline_release_server_url_, policy_engine_->GetPlaybackStartTime(), policy_engine_->GetLastPlaybackTime(), - policy_engine_->GetGracePeriodEndTime(), app_parameters_); + policy_engine_->GetGracePeriodEndTime(), app_parameters_, usage_entry_, + usage_entry_number_); } CdmResponseType CdmSession::ReleaseCrypto() { @@ -603,7 +818,7 @@ CdmResponseType CdmSession::ReleaseCrypto() { } bool CdmSession::DeleteLicense() { - if (!is_offline_ && license_parser_->provider_session_token().empty()) + if (!is_offline_ && !has_provider_session_token()) return false; if (is_offline_) { @@ -612,7 +827,8 @@ bool CdmSession::DeleteLicense() { std::string app_id; GetApplicationId(&app_id); return file_handle_->DeleteUsageInfo( - app_id, license_parser_->provider_session_token()); + DeviceFiles::GetUsageInfoFileName(app_id), + license_parser_->provider_session_token()); } } @@ -645,12 +861,55 @@ void CdmSession::GetApplicationId(std::string* app_id) { CdmResponseType CdmSession::DeleteMultipleUsageInformation( const std::vector& provider_session_tokens) { - return crypto_session_->DeleteMultipleUsageInformation( + CdmResponseType sts = crypto_session_->DeleteMultipleUsageInformation( provider_session_tokens); + crypto_metrics_->crypto_session_delete_multiple_usage_information_ + .Increment(sts); + return sts; } -CdmResponseType CdmSession::UpdateUsageInformation() { - return crypto_session_->UpdateUsageInformation(); +CdmResponseType CdmSession::UpdateUsageTableInformation() { + CdmUsageSupportType usage_support_type; + CdmResponseType sts = + crypto_session_->GetUsageSupportType(&usage_support_type); + + if (sts == NO_ERROR && usage_support_type == kUsageTableSupport) { + M_TIME( + sts = crypto_session_->UpdateUsageInformation(), + crypto_metrics_, + crypto_session_update_usage_information_, + sts); + return sts; + } + + return NO_ERROR; // Ignore +} + +CdmResponseType CdmSession::UpdateUsageEntryInformation() { + if (usage_support_type_ != kUsageEntrySupport || + !has_provider_session_token() || + usage_table_header_ == NULL) { + LOGE("CdmSession::UpdateUsageEntryInformation: Unexpected state, " + "usage support type: %d, PST present: %s, usage table header available" + ": %s", usage_support_type_, + has_provider_session_token() ? "yes" : "no", + usage_table_header_ == NULL ? "no" : "yes"); + return INCORRECT_USAGE_SUPPORT_TYPE_2; + } + + CdmResponseType sts = usage_table_header_->UpdateEntry(crypto_session_.get(), + &usage_entry_); + + if (sts != NO_ERROR) return sts; + + if (is_offline_) + StoreLicense(is_release_ + ? DeviceFiles::kLicenseStateReleasing + : DeviceFiles::kLicenseStateActive); + else if (!usage_provider_session_token_.empty()) + UpdateUsageInfo(); + + return NO_ERROR; } CdmResponseType CdmSession::GenericEncrypt(const std::string& in_buffer, @@ -662,8 +921,20 @@ CdmResponseType CdmSession::GenericEncrypt(const std::string& in_buffer, LOGE("CdmSession::GenericEncrypt: No output destination provided"); return INVALID_PARAMETERS_ENG_6; } - return crypto_session_->GenericEncrypt(in_buffer, key_id, iv, algorithm, - out_buffer); + CdmResponseType sts; + M_TIME( + sts = crypto_session_->GenericEncrypt( + in_buffer, + key_id, + iv, + algorithm, + out_buffer), + crypto_metrics_, + crypto_session_generic_encrypt_, + sts, + metrics::Pow2Bucket(in_buffer.size()), + algorithm); + return sts; } CdmResponseType CdmSession::GenericDecrypt(const std::string& in_buffer, @@ -675,8 +946,20 @@ CdmResponseType CdmSession::GenericDecrypt(const std::string& in_buffer, LOGE("CdmSession::GenericDecrypt: No output destination provided"); return INVALID_PARAMETERS_ENG_7; } - return crypto_session_->GenericDecrypt(in_buffer, key_id, iv, algorithm, - out_buffer); + CdmResponseType sts; + M_TIME( + sts = crypto_session_->GenericDecrypt( + in_buffer, + key_id, + iv, + algorithm, + out_buffer), + crypto_metrics_, + crypto_session_generic_decrypt_, + sts, + metrics::Pow2Bucket(in_buffer.size()), + algorithm); + return sts; } CdmResponseType CdmSession::GenericSign(const std::string& message, @@ -687,14 +970,56 @@ CdmResponseType CdmSession::GenericSign(const std::string& message, LOGE("CdmSession::GenericSign: No output destination provided"); return INVALID_PARAMETERS_ENG_8; } - return crypto_session_->GenericSign(message, key_id, algorithm, signature); + CdmResponseType sts; + M_TIME( + sts = crypto_session_->GenericSign( + message, + key_id, + algorithm, + signature), + crypto_metrics_, + crypto_session_generic_sign_, + sts, + metrics::Pow2Bucket(message.size()), + algorithm); + return sts; } CdmResponseType CdmSession::GenericVerify(const std::string& message, const std::string& key_id, CdmSigningAlgorithm algorithm, const std::string& signature) { - return crypto_session_->GenericVerify(message, key_id, algorithm, signature); + CdmResponseType sts; + M_TIME( + sts = crypto_session_->GenericVerify( + message, + key_id, + algorithm, + signature), + crypto_metrics_, + crypto_session_generic_verify_, + sts, + metrics::Pow2Bucket(message.size()), + algorithm); + return sts; +} + +bool CdmSession::UpdateUsageInfo() { + std::string app_id; + GetApplicationId(&app_id); + + DeviceFiles::CdmUsageData usage_data; + usage_data.provider_session_token = usage_provider_session_token_; + usage_data.license_request = key_request_; + usage_data.license = key_response_; + usage_data.key_set_id = key_set_id_; + usage_data.usage_entry = usage_entry_; + usage_data.usage_entry_number = usage_entry_number_; + + return file_handle_->UpdateUsageInfo( + DeviceFiles::GetUsageInfoFileName(app_id), + usage_provider_session_token_, + usage_data); } // For testing only - takes ownership of pointers diff --git a/core/src/cdm_session_map.cpp b/core/src/cdm_session_map.cpp index 84a8d0a8..c577a26a 100644 --- a/core/src/cdm_session_map.cpp +++ b/core/src/cdm_session_map.cpp @@ -26,8 +26,8 @@ void CdmSessionMap::Add(const std::string& id, CdmSession* session) { bool CdmSessionMap::CloseSession(const std::string& id) { AutoLock lock(lock_); - std::shared_ptr session; - if (!FindSessionNoLock(id, session)) { + shared_ptr session; + if (!FindSessionNoLock(id, &session)) { return false; } session->Close(); @@ -41,30 +41,30 @@ bool CdmSessionMap::Exists(const std::string& id) { } bool CdmSessionMap::FindSession(const CdmSessionId& id, - std::shared_ptr& session) { + shared_ptr* session) { AutoLock lock(lock_); return FindSessionNoLock(id, session); } bool CdmSessionMap::FindSessionNoLock(const CdmSessionId& session_id, - std::shared_ptr& session) { + shared_ptr* session) { CdmIdToSessionMap::iterator iter = sessions_.find(session_id); if (iter == sessions_.end()) { return false; } - session = iter->second; - assert(session.get() != NULL); + *session = iter->second; + assert(session->get() != NULL); return true; } -typedef std::list > CdmSessionList; - void CdmSessionMap::GetSessionList(CdmSessionList& sessions) { sessions.clear(); AutoLock lock(lock_); for (CdmIdToSessionMap::iterator iter = sessions_.begin(); iter != sessions_.end(); ++iter) { - sessions.push_back(iter->second); + if (!(iter->second)->IsClosed()) { + sessions.push_back(iter->second); + } } } diff --git a/core/src/certificate_provisioning.cpp b/core/src/certificate_provisioning.cpp index 25c3e6f3..0484fef6 100644 --- a/core/src/certificate_provisioning.cpp +++ b/core/src/certificate_provisioning.cpp @@ -66,7 +66,7 @@ void ExtractAndDecodeSignedMessage(const std::string& provisioning_response, result->assign(decoded_message.begin(), decoded_message.end()); } -} +} // namespace namespace wvcdm { // Protobuf generated classes. @@ -83,7 +83,8 @@ using video_widevine::SignedProvisioningMessage; */ bool CertificateProvisioning::GetProvisioningTokenType( ClientIdentification::TokenType* token_type) { - switch (crypto_session_.GetPreProvisionTokenType()) { + CdmClientTokenType token = crypto_session_.GetPreProvisionTokenType(); + switch (token) { case kClientTokenKeybox: *token_type = ClientIdentification::KEYBOX; return true; @@ -93,6 +94,8 @@ bool CertificateProvisioning::GetProvisioningTokenType( case kClientTokenDrmCert: default: // shouldn't happen + LOGE("CertificateProvisioning::GetProvisioningTokenType: unexpected " + "provisioning type: %d", token); return false; } } @@ -107,7 +110,7 @@ bool CertificateProvisioning::SetSpoidParameter( const std::string& origin, const std::string& spoid, ProvisioningRequest* request) { if (!request) { - LOGE("CertificateProvisioning::SetSpoidParameter : No request buffer " + LOGE("CertificateProvisioning::SetSpoidParameter: No request buffer " "passed to method."); return false; } @@ -125,7 +128,7 @@ bool CertificateProvisioning::SetSpoidParameter( } else if (origin != EMPTY_ORIGIN) { // Legacy behavior - Concatenate Unique ID with Origin std::string device_unique_id; - if (!crypto_session_.GetDeviceUniqueId(&device_unique_id)) { + if (!crypto_session_.GetInternalDeviceUniqueId(&device_unique_id)) { LOGE("CertificateProvisioning::SetSpoidParameter: Failure getting " "device unique ID"); return false; @@ -208,7 +211,7 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest( uint32_t nonce; if (!crypto_session_.GenerateNonce(&nonce)) { LOGE("GetProvisioningRequest: fails to generate a nonce"); - return CERT_PROVISIONING_REQUEST_ERROR_2; + return CERT_PROVISIONING_NONCE_GENERATION_ERROR; } // The provisioning server does not convert the nonce to uint32_t, it just @@ -280,29 +283,33 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest( * Returns NO_ERROR for success and CERT_PROVISIONING_RESPONSE_ERROR_? if fails. */ CdmResponseType CertificateProvisioning::HandleProvisioningResponse( - FileSystem* file_system, const CdmProvisioningResponse& response, + FileSystem* file_system, const CdmProvisioningResponse& response_message, std::string* cert, std::string* wrapped_key) { - std::string raw_string; - if (!wvcdm::Properties::provisioning_messages_are_binary()) { - // The response is base64 encoded in a JSON wrapper. - // Extract it and decode it. If errors, return an empty string. - ExtractAndDecodeSignedMessage(response, &raw_string); - } else { - raw_string.assign(response); + if (response_message.empty()) { + LOGE("HandleProvisioningResponse: response message is empty."); + return CERT_PROVISIONING_RESPONSE_ERROR_1; } - if (raw_string.empty()) { - LOGE("HandleProvisioningResponse: response message is empty or " - "an invalid JSON/base64 string."); - return CERT_PROVISIONING_RESPONSE_ERROR_1; + std::string response; + if (wvcdm::Properties::provisioning_messages_are_binary()) { + response.assign(response_message); + } else { + // The response is base64 encoded in a JSON wrapper. + // Extract it and decode it. On error return an empty string. + ExtractAndDecodeSignedMessage(response_message, &response); + if (response.empty()) { + LOGE("HandleProvisioningResponse: response message is " + "an invalid JSON/base64 string."); + return CERT_PROVISIONING_RESPONSE_ERROR_1; + } } // Authenticates provisioning response using D1s (server key derived from // the provisioing request's input). Validate provisioning response and // stores private device RSA key and certificate. SignedProvisioningMessage signed_response; - if (!signed_response.ParseFromString(raw_string)) { + if (!signed_response.ParseFromString(response)) { LOGE("HandleProvisioningResponse: fails to parse signed response"); return CERT_PROVISIONING_RESPONSE_ERROR_2; } diff --git a/core/src/crypto_session.cpp b/core/src/crypto_session.cpp index 9f736baa..23d855d2 100644 --- a/core/src/crypto_session.cpp +++ b/core/src/crypto_session.cpp @@ -12,8 +12,11 @@ #include "crypto_key.h" #include "log.h" +#include "openssl/sha.h" #include "properties.h" +#include "pst_report.h" #include "string_conversions.h" +#include "usage_table_header.h" #include "wv_cdm_constants.h" namespace { @@ -69,6 +72,8 @@ void GenerateEncryptContext(const std::string& input_context, } const uint32_t kRsaSignatureLength = 256; const size_t kMaximumChunkSize = 100 * 1024; // 100 KiB +const size_t kEstimatedInitialUsageTableHeader = 40; +const size_t kOemCryptoApiVersionSupportsBigUsageTables = 13; } namespace wvcdm { @@ -77,14 +82,22 @@ Lock CryptoSession::crypto_lock_; bool CryptoSession::initialized_ = false; int CryptoSession::session_count_ = 0; uint64_t CryptoSession::request_id_index_ = 0; +UsageTableHeader* CryptoSession::usage_table_header_l1_ = NULL; +UsageTableHeader* CryptoSession::usage_table_header_l3_ = NULL; +// TODO(jfore, rfrias, gmorgan): This object should be split off into it's own +// module or refactored out. class DefaultKeySession : public KeySession { public: - explicit DefaultKeySession(CryptoSessionId oec_session_id) - : oec_session_id_(oec_session_id) {} + DefaultKeySession(CryptoSessionId oec_session_id, + metrics::CryptoMetrics* metrics) + : KeySession(metrics), + oec_session_id_(oec_session_id) {} virtual ~DefaultKeySession() {} KeySessionType Type() { return kDefault; } + + // Generate Derived Keys for DefaultKeySession bool GenerateDerivedKeys(const std::string& message) { std::string mac_deriv_message; std::string enc_deriv_message; @@ -92,12 +105,17 @@ class DefaultKeySession : public KeySession { GenerateEncryptContext(message, &enc_deriv_message); LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_); - OEMCryptoResult sts = OEMCrypto_GenerateDerivedKeys( - oec_session_id_, - reinterpret_cast(mac_deriv_message.data()), - mac_deriv_message.size(), - reinterpret_cast(enc_deriv_message.data()), - enc_deriv_message.size()); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_GenerateDerivedKeys( + oec_session_id_, + reinterpret_cast(mac_deriv_message.data()), + mac_deriv_message.size(), + reinterpret_cast(enc_deriv_message.data()), + enc_deriv_message.size()), + metrics_, + oemcrypto_generate_derived_keys_, + sts); if (OEMCrypto_SUCCESS != sts) { LOGE("GenerateDerivedKeys: OEMCrypto_GenerateDerivedKeys error=%d", sts); return false; @@ -106,6 +124,7 @@ class DefaultKeySession : public KeySession { return true; } + // Generate Derived Keys (from session key) for DefaultKeySession bool GenerateDerivedKeys(const std::string& message, const std::string& session_key) { std::string mac_deriv_message; @@ -114,14 +133,20 @@ class DefaultKeySession : public KeySession { GenerateEncryptContext(message, &enc_deriv_message); LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)oec_session_id_); - OEMCryptoResult sts = OEMCrypto_DeriveKeysFromSessionKey( - oec_session_id_, - reinterpret_cast(session_key.data()), - session_key.size(), - reinterpret_cast(mac_deriv_message.data()), - mac_deriv_message.size(), - reinterpret_cast(enc_deriv_message.data()), - enc_deriv_message.size()); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_DeriveKeysFromSessionKey( + oec_session_id_, + reinterpret_cast(session_key.data()), + session_key.size(), + reinterpret_cast(mac_deriv_message.data()), + mac_deriv_message.size(), + reinterpret_cast(enc_deriv_message.data()), + enc_deriv_message.size()), + metrics_, + oemcrypto_derive_keys_from_session_key_, + sts); + if (OEMCrypto_SUCCESS != sts) { LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", sts); @@ -131,13 +156,15 @@ class DefaultKeySession : public KeySession { return true; } + // Load Keys for DefaultKeySession OEMCryptoResult LoadKeys(const std::string& message, const std::string& signature, const std::string& mac_key_iv, const std::string& mac_key, const std::vector& keys, const std::string& provider_session_token, - CdmCipherMode* cipher_mode) { + CdmCipherMode* cipher_mode, + const std::string& srm_requirement) { const uint8_t* msg = reinterpret_cast(message.data()); const uint8_t* enc_mac_key = NULL; const uint8_t* enc_mac_key_iv = NULL; @@ -145,7 +172,7 @@ class DefaultKeySession : public KeySession { enc_mac_key = msg + GetOffset(message, mac_key); enc_mac_key_iv = msg + GetOffset(message, mac_key_iv); } else { - LOGV("CryptoSession::LoadKeys: enc_mac_key not set"); + LOGV("DefaultKeySession::LoadKeys: enc_mac_key not set"); } std::vector load_keys(keys.size()); for (size_t i = 0; i < keys.size(); ++i) { @@ -170,20 +197,35 @@ class DefaultKeySession : public KeySession { : OEMCrypto_CipherMode_CTR; *cipher_mode = ki->cipher_mode(); } + uint8_t* pst = NULL; if (!provider_session_token.empty()) { pst = const_cast(msg) + GetOffset(message, provider_session_token); } + uint8_t* srm_req = NULL; + if (!srm_requirement.empty()) { + srm_req = const_cast(msg) + + GetOffset(message, srm_requirement); + } + LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_); - return OEMCrypto_LoadKeys( - oec_session_id_, msg, message.size(), - reinterpret_cast(signature.data()), - signature.size(), enc_mac_key_iv, enc_mac_key, keys.size(), - &load_keys[0], pst, provider_session_token.length()); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_LoadKeys( + oec_session_id_, msg, message.size(), + reinterpret_cast(signature.data()), + signature.size(), enc_mac_key_iv, enc_mac_key, keys.size(), + &load_keys[0], pst, provider_session_token.length(), + srm_req), + metrics_, + oemcrypto_load_keys_, + sts); + return sts; } + // Select Key for DefaultKeySession OEMCryptoResult SelectKey(const std::string& key_id) { // Crypto session lock already locked. if (!cached_key_id_.empty() && cached_key_id_ == key_id) { @@ -196,8 +238,15 @@ class DefaultKeySession : public KeySession { const uint8_t* key_id_string = reinterpret_cast(cached_key_id_.data()); - OEMCryptoResult sts = OEMCrypto_SelectKey(oec_session_id_, key_id_string, - cached_key_id_.size()); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_SelectKey( + oec_session_id_, + key_id_string, + cached_key_id_.size()), + metrics_, + oemcrypto_select_key_, + sts); if (OEMCrypto_SUCCESS != sts) { cached_key_id_.clear(); @@ -205,15 +254,28 @@ class DefaultKeySession : public KeySession { return sts; } + // Decrypt for DefaultKeySession OEMCryptoResult Decrypt( const CdmDecryptionParameters& params, OEMCrypto_DestBufferDesc& buffer_descriptor, OEMCrypto_CENCEncryptPatternDesc& pattern_descriptor) { - - return OEMCrypto_DecryptCENC( - oec_session_id_, params.encrypt_buffer, params.encrypt_length, - params.is_encrypted, &(*params.iv).front(), params.block_offset, - &buffer_descriptor, &pattern_descriptor, params.subsample_flags); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_DecryptCENC( + oec_session_id_, + params.encrypt_buffer, + params.encrypt_length, + params.is_encrypted, + &(*params.iv).front(), + params.block_offset, + &buffer_descriptor, + &pattern_descriptor, + params.subsample_flags), + metrics_, + oemcrypto_decrypt_cenc_, + sts, + metrics::Pow2Bucket(params.encrypt_length)); + return sts; } private: @@ -221,7 +283,8 @@ class DefaultKeySession : public KeySession { KeyId cached_key_id_; }; -// TODO(jfore): Move this class into it's own module. +// TODO(jfore, rfrias, gmorgan): This object should be split off into it's own +// module or refactored out. class SubLicenseKeySession : public KeySession { typedef enum { kInitializing, @@ -231,18 +294,32 @@ class SubLicenseKeySession : public KeySession { public: SubLicenseKeySession(SubLicenseSessionMap& sub_license_oec_sessions, - std::string& wrapped_private_device_key, - SecurityLevel requested_security_level) - : state_(kInitializing), + metrics::CryptoMetrics* metrics, + const std::string& wrapped_private_device_key, + SecurityLevel requested_security_level, + const std::string& group_master_key_id) + : KeySession(metrics), + state_(kInitializing), wrapped_private_device_key_(wrapped_private_device_key), sub_license_oec_sessions_(sub_license_oec_sessions), - requested_security_level_(requested_security_level) {} - virtual ~SubLicenseKeySession() {} + requested_security_level_(requested_security_level), + group_master_key_id_(group_master_key_id) {} + + virtual ~SubLicenseKeySession() { + for (SubLicenseSessionMap::iterator oec_session = + sub_license_oec_sessions_.begin(); + oec_session != sub_license_oec_sessions_.end(); oec_session++) { + metrics_->oemcrypto_close_session_.Increment( + OEMCrypto_CloseSession(oec_session->second)); + } + sub_license_oec_sessions_.clear(); + } KeySessionType Type() { return kSubLicense; } + // This version of GenerateDerivedKeys is for devices using keyboxes. It is // not supported using sub licenses. - bool GenerateDerivedKeys(const std::string& message) { + bool GenerateDerivedKeys(const std::string&) { return false; } @@ -258,13 +335,19 @@ class SubLicenseKeySession : public KeySession { for (SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.begin(); it != sub_license_oec_sessions_.end(); it++) { LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)it->second); - OEMCryptoResult sts = OEMCrypto_DeriveKeysFromSessionKey( - it->second, reinterpret_cast(session_key.data()), - session_key.size(), - reinterpret_cast(mac_deriv_message.data()), - mac_deriv_message.size(), - reinterpret_cast(enc_deriv_message.data()), - enc_deriv_message.size()); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_DeriveKeysFromSessionKey( + it->second, + reinterpret_cast(session_key.data()), + session_key.size(), + reinterpret_cast(mac_deriv_message.data()), + mac_deriv_message.size(), + reinterpret_cast(enc_deriv_message.data()), + enc_deriv_message.size()), + metrics_, + oemcrypto_derive_keys_from_session_key_, + sts); if (OEMCrypto_SUCCESS != sts) { LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", @@ -283,7 +366,8 @@ class SubLicenseKeySession : public KeySession { const std::string& mac_key, const std::vector& keys, const std::string& provider_session_token, - CdmCipherMode* cipher_mode) { + CdmCipherMode* cipher_mode, + const std::string& srm_requirement) { if (state_ == kInitializing) { state_ = kInitialLicenseLoaded; keys_ = keys; @@ -293,7 +377,8 @@ class SubLicenseKeySession : public KeySession { mac_key, keys, provider_session_token, - cipher_mode); + cipher_mode, + srm_requirement); if (OEMCrypto_SUCCESS != sts) { state_ = kInitialLicenseFailed; } @@ -301,13 +386,13 @@ class SubLicenseKeySession : public KeySession { } return DoSubLicenseLoadKeys(message, signature, mac_key_iv, mac_key, keys[0], provider_session_token, - cipher_mode); + cipher_mode, srm_requirement); } // Each oemcrypto session contains a single key. Find the right sub session // and save it's id as the selected oemcrypto session. OEMCryptoResult SelectKey(const std::string& key_id) { - for (int i = 0; i < keys_.size(); ++i) { + for (size_t i = 0; i < keys_.size(); ++i) { if (keys_[i].key_id() == key_id) { cached_sub_session_key_id_ = keys_[i].sub_session_key_id(); } @@ -326,11 +411,23 @@ class SubLicenseKeySession : public KeySession { if (it == sub_license_oec_sessions_.end()) { return OEMCrypto_ERROR_INVALID_SESSION; } - - return OEMCrypto_DecryptCENC( - it->second, params.encrypt_buffer, params.encrypt_length, - params.is_encrypted, &(*params.iv).front(), params.block_offset, - &buffer_descriptor, &pattern_descriptor, params.subsample_flags); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_DecryptCENC( + it->second, + params.encrypt_buffer, + params.encrypt_length, + params.is_encrypted, + &(*params.iv).front(), + params.block_offset, + &buffer_descriptor, + &pattern_descriptor, + params.subsample_flags), + metrics_, + oemcrypto_decrypt_cenc_, + sts, + metrics::Pow2Bucket(params.encrypt_length)); + return sts; } private: @@ -338,8 +435,8 @@ class SubLicenseKeySession : public KeySession { OEMCryptoResult ResetCryptoSessions() { for (SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.begin(); it != sub_license_oec_sessions_.end(); it++) { - OEMCryptoResult sts; - sts = OEMCrypto_CloseSession(it->second); + OEMCryptoResult sts = OEMCrypto_CloseSession(it->second); + metrics_->oemcrypto_close_session_.Increment(sts); if (OEMCrypto_SUCCESS != sts) { return sts; } @@ -347,10 +444,13 @@ class SubLicenseKeySession : public KeySession { if (OEMCrypto_SUCCESS != sts) { return sts; } - sts = OEMCrypto_LoadDeviceRSAKey( - it->second, - reinterpret_cast(wrapped_private_device_key_.data()), - wrapped_private_device_key_.size()); + M_TIME( + sts = OEMCrypto_LoadDeviceRSAKey( + it->second, + reinterpret_cast( + wrapped_private_device_key_.data()), + wrapped_private_device_key_.size()), + metrics_, oemcrypto_load_device_rsa_key_, sts); if (OEMCrypto_SUCCESS != sts) { return sts; } @@ -365,7 +465,8 @@ class SubLicenseKeySession : public KeySession { const std::string& mac_key, const std::vector& keys, const std::string& provider_session_token, - CdmCipherMode* cipher_mode) { + CdmCipherMode* cipher_mode, + const std::string& srm_requirement) { const uint8_t* msg = reinterpret_cast(message.data()); const uint8_t* enc_mac_key = NULL; const uint8_t* enc_mac_key_iv = NULL; @@ -375,14 +476,21 @@ class SubLicenseKeySession : public KeySession { } else { LOGV("CryptoSession::LoadKeys: enc_mac_key not set"); } + uint8_t* pst = NULL; if (!provider_session_token.empty()) { pst = const_cast(msg) + - GetOffset(message, provider_session_token); + GetOffset(message, provider_session_token); + } + + uint8_t* srm_req = NULL; + if (!srm_requirement.empty()) { + srm_req = const_cast(msg) + + GetOffset(message, srm_requirement); } // TODO(jfore): Use C++ 11 range loop? - for (int i = 0; i < keys.size(); i++) { + for (size_t i = 0; i < keys.size(); i++) { OEMCrypto_KeyObject key_object; const CryptoKey& key_data = keys[i]; key_object.key_id = msg + GetOffset(message, key_data.key_id()); @@ -405,6 +513,7 @@ class SubLicenseKeySession : public KeySession { ? OEMCrypto_CipherMode_CBC : OEMCrypto_CipherMode_CTR; *cipher_mode = key_data.cipher_mode(); + SubLicenseSessionMap::iterator oec_session_id = sub_license_oec_sessions_.find(key_data.sub_session_key_id()); if (oec_session_id == sub_license_oec_sessions_.end()) { @@ -414,19 +523,32 @@ class SubLicenseKeySession : public KeySession { } OEMCryptoResult sts; - sts = OEMCrypto_LoadKeys( - oec_session_id->second, msg, message.size(), - reinterpret_cast(signature.data()), - signature.size(), enc_mac_key_iv, enc_mac_key, 1, &key_object, - pst, provider_session_token.length()); + M_TIME( + sts = OEMCrypto_LoadKeys( + oec_session_id->second, msg, message.size(), + reinterpret_cast(signature.data()), + signature.size(), enc_mac_key_iv, enc_mac_key, 1, &key_object, + pst, provider_session_token.length(), srm_req), + metrics_, + oemcrypto_load_keys_, + sts); + if (sts != OEMCrypto_SUCCESS) { return sts; } - sts = OEMCrypto_SelectKey( - oec_session_id->second, - reinterpret_cast(key_data.key_id().data()), - key_data.key_id().size()); + M_TIME( + sts = OEMCrypto_SelectKey( + oec_session_id->second, + reinterpret_cast(key_data.key_id().data()), + key_data.key_id().size()), + metrics_, + oemcrypto_select_key_, + sts); + + if (sts != OEMCrypto_SUCCESS) { + return sts; + } } keys_ = keys; return OEMCrypto_SUCCESS; @@ -437,9 +559,10 @@ class SubLicenseKeySession : public KeySession { const std::string& message, const std::string& signature, const std::string& mac_key_iv, const std::string& mac_key, const CryptoKey& key, const std::string& provider_session_token, - CdmCipherMode* cipher_mode) { + CdmCipherMode*, const std::string& srm_requirement) { + SubLicenseSessionMap::iterator it = sub_license_oec_sessions_.end(); - int key_index = 0; + size_t key_index = 0; for (; key_index < keys_.size(); key_index++) { if (keys_[key_index].track_label() == key.track_label()) { it = sub_license_oec_sessions_.find( @@ -452,14 +575,17 @@ class SubLicenseKeySession : public KeySession { } } if (it == sub_license_oec_sessions_.end()) { - return OEMCrypto_ERROR_INVALID_SESSION; + return OEMCrypto_SUCCESS; } + LOGV("GenerateDerivedKeys: id=%ld", (uint32_t)it->second); std::string mac_deriv_message; std::string enc_deriv_message; - GenerateMacContext(key.track_label(), &mac_deriv_message); - GenerateEncryptContext(key.track_label(), &enc_deriv_message); + GenerateMacContext(group_master_key_id_ + + key.track_label(), &mac_deriv_message); + GenerateEncryptContext(group_master_key_id_ + + key.track_label(), &enc_deriv_message); const uint8_t* msg = reinterpret_cast(message.data()); const uint8_t* enc_mac_key = NULL; @@ -470,22 +596,35 @@ class SubLicenseKeySession : public KeySession { } else { LOGV("CryptoSession::LoadKeys: enc_mac_key not set"); } + uint8_t* pst = NULL; if (!provider_session_token.empty()) { pst = const_cast(msg) + GetOffset(message, provider_session_token); } + uint8_t* srm_req = NULL; + if (!srm_requirement.empty()) { + srm_req = const_cast(msg) + + GetOffset(message, srm_requirement); + } + OEMCryptoResult sts; const std::string& sub_session_key = keys_[key_index].sub_session_key(); LOGE("ssksize = %d", sub_session_key.size()); - sts = OEMCrypto_DeriveKeysFromSessionKey( - it->second, reinterpret_cast(sub_session_key.data()), - sub_session_key.size(), - reinterpret_cast(mac_deriv_message.data()), - mac_deriv_message.size(), - reinterpret_cast(enc_deriv_message.data()), - enc_deriv_message.size()); + + M_TIME( + sts = OEMCrypto_DeriveKeysFromSessionKey( + it->second, + reinterpret_cast(sub_session_key.data()), + sub_session_key.size(), + reinterpret_cast(mac_deriv_message.data()), + mac_deriv_message.size(), + reinterpret_cast(enc_deriv_message.data()), + enc_deriv_message.size()), + metrics_, + oemcrypto_derive_keys_from_session_key_, + sts); if (OEMCrypto_SUCCESS != sts) { LOGE("GenerateDerivedKeys: OEMCrypto_DeriveKeysFromSessionKey err=%d", @@ -506,23 +645,33 @@ class SubLicenseKeySession : public KeySession { key_object.key_control = msg + GetOffset( message, keys_[key_index].key_control()); } - key_object.cipher_mode = keys_[key_index].cipher_mode() == kCipherModeCbc - ? OEMCrypto_CipherMode_CBC - : OEMCrypto_CipherMode_CTR; + key_object.cipher_mode = + (keys_[key_index].cipher_mode() == kCipherModeCbc) ? + OEMCrypto_CipherMode_CBC : OEMCrypto_CipherMode_CTR; + + M_TIME( + sts = OEMCrypto_LoadKeys( + it->second, msg, message.size(), + reinterpret_cast(signature.data()), + signature.size(), enc_mac_key_iv, enc_mac_key, 1, &key_object, + pst, provider_session_token.length(), srm_req), + metrics_, + oemcrypto_load_keys_, + sts); - sts = OEMCrypto_LoadKeys( - it->second, msg, message.size(), - reinterpret_cast(signature.data()), - signature.size(), enc_mac_key_iv, enc_mac_key, 1, &key_object, - pst, provider_session_token.length()); if (sts != OEMCrypto_SUCCESS) { return sts; } - sts = OEMCrypto_SelectKey( - it->second, - reinterpret_cast(keys_[key_index].key_id().data()), - keys_[key_index].key_id().size()); + M_TIME( + sts = OEMCrypto_SelectKey( + it->second, + reinterpret_cast(keys_[key_index].key_id().data()), + keys_[key_index].key_id().size()), + metrics_, + oemcrypto_select_key_, + sts); + return sts; } @@ -534,16 +683,23 @@ class SubLicenseKeySession : public KeySession { std::vector keys_; SubLicenseSessionMap& sub_license_oec_sessions_; SecurityLevel requested_security_level_; + KeyId group_master_key_id_; }; -CryptoSession::CryptoSession() - : open_(false), +CryptoSession::CryptoSession(metrics::CryptoMetrics* metrics) + : metrics_(metrics), + open_(false), update_usage_table_after_close_session_(false), is_destination_buffer_type_valid_(false), requested_security_level_(kLevelDefault), + is_usage_support_type_valid_(false), + usage_support_type_(kNonSecureUsageSupport), + usage_table_header_(NULL), request_id_base_(0), cipher_mode_(kCipherModeCtr) { + assert(metrics); Init(); + life_span_.Start(); } CryptoSession::~CryptoSession() { @@ -551,25 +707,28 @@ CryptoSession::~CryptoSession() { Close(); } Terminate(); + M_RECORD(metrics_, crypto_session_life_span_, life_span_.AsMs()); } -bool CryptoSession::GetProvisioningMethod(CdmClientTokenType& token_type) { +bool CryptoSession::GetProvisioningMethod(CdmClientTokenType* token_type) { OEMCrypto_ProvisioningMethod method; + CdmClientTokenType type; switch (method = OEMCrypto_GetProvisioningMethod(requested_security_level_)) { case OEMCrypto_OEMCertificate: - token_type = kClientTokenOemCert; + type = kClientTokenOemCert; break; case OEMCrypto_Keybox: - token_type = kClientTokenKeybox; + type = kClientTokenKeybox; break; case OEMCrypto_DrmCertificate: - token_type = kClientTokenDrmCert; + type = kClientTokenDrmCert; break; case OEMCrypto_ProvisioningError: default: LOGE("OEMCrypto_GetProvisioningMethod failed", method); return false; } + *token_type = type; return true; } @@ -578,20 +737,25 @@ void CryptoSession::Init() { AutoLock auto_lock(crypto_lock_); session_count_ += 1; if (!initialized_) { - OEMCryptoResult sts = OEMCrypto_Initialize(); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_Initialize(), + metrics_, + oemcrypto_initialize_, + sts); if (OEMCrypto_SUCCESS != sts) { LOGE("OEMCrypto_Initialize failed: %d", sts); return; } initialized_ = true; } - if (!GetProvisioningMethod(pre_provision_token_type_)) { + if (!GetProvisioningMethod(&pre_provision_token_type_)) { initialized_ = false; } } void CryptoSession::Terminate() { - LOGE("CryptoSession::Terminate: initialized_=%d, session_count_=%d", + LOGV("CryptoSession::Terminate: initialized_=%d, session_count_=%d", initialized_, session_count_); AutoLock auto_lock(crypto_lock_); if (session_count_ > 0) { @@ -604,17 +768,36 @@ void CryptoSession::Terminate() { if (OEMCrypto_SUCCESS != sts) { LOGE("OEMCrypto_Terminate failed: %d", sts); } + + if (usage_table_header_l1_ != NULL) { + delete usage_table_header_l1_; + usage_table_header_l1_ = NULL; + } + if (usage_table_header_l3_ != NULL) { + delete usage_table_header_l3_; + usage_table_header_l3_ = NULL; + } + initialized_ = false; } bool CryptoSession::GetTokenFromKeybox(std::string* token) { - OEMCryptoResult status; std::string temp_buffer(KEYBOX_KEY_DATA_SIZE, '\0'); // lock is held by caller size_t buf_size = temp_buffer.size(); uint8_t* buf = reinterpret_cast(&temp_buffer[0]); - status = OEMCrypto_GetKeyData(buf, &buf_size, requested_security_level_); - if (status == OEMCrypto_SUCCESS) { + + OEMCryptoResult status; + M_TIME( + status = OEMCrypto_GetKeyData( + buf, + &buf_size, + requested_security_level_), + metrics_, + oemcrypto_get_key_data_, + status, + metrics::Pow2Bucket(buf_size)); + if (OEMCrypto_SUCCESS == status) { token->swap(temp_buffer); return true; } @@ -623,6 +806,10 @@ bool CryptoSession::GetTokenFromKeybox(std::string* token) { } bool CryptoSession::GetTokenFromOemCert(std::string* token) { + if (token == NULL) { + LOGE("CryptoSession::GetTokenFromOemCert: token not provided "); + return false; + } OEMCryptoResult status; if (!oem_token_.empty()) { token->assign(oem_token_); @@ -638,7 +825,7 @@ bool CryptoSession::GetTokenFromOemCert(std::string* token) { if (OEMCrypto_SUCCESS == status) { temp_buffer.resize(buf_size); oem_token_.assign(temp_buffer); - token->swap(temp_buffer); + token->assign(temp_buffer); return true; } if (OEMCrypto_ERROR_SHORT_BUFFER && !retrying) { @@ -661,7 +848,6 @@ bool CryptoSession::GetClientToken(std::string* token) { if (!initialized_) { return false; } - // Only keybox is used for client token. All other cases use DRM Cert. if (pre_provision_token_type_ != kClientTokenKeybox) { return false; @@ -691,8 +877,7 @@ bool CryptoSession::GetProvisioningToken(std::string* token) { } CdmSecurityLevel CryptoSession::GetSecurityLevel() { - LOGV("CryptoSession::GetSecurityLevel: Lock"); - AutoLock auto_lock(crypto_lock_); + LOGV("CryptoSession::GetSecurityLevel"); if (!initialized_) { return kSecurityLevelUninitialized; } @@ -704,27 +889,33 @@ CdmSecurityLevel CryptoSession::GetSecurityLevel() { return kSecurityLevelUnknown; } + CdmSecurityLevel cdm_security_level; switch (security_level.at(1)) { case '1': - return kSecurityLevelL1; + cdm_security_level = kSecurityLevelL1; + break; case '2': - return kSecurityLevelL2; + cdm_security_level = kSecurityLevelL2; + break; case '3': - return kSecurityLevelL3; + cdm_security_level = kSecurityLevelL3; + break; default: - return kSecurityLevelUnknown; + cdm_security_level = kSecurityLevelUnknown; + break; } - return kSecurityLevelUnknown; + return cdm_security_level; } -bool CryptoSession::GetDeviceUniqueId(std::string* device_id) { +bool CryptoSession::GetInternalDeviceUniqueId(std::string* device_id) { if (!device_id) { - LOGE("CryptoSession::GetDeviceUniqueId : No buffer passed to method."); + LOGE("CryptoSession::GetInternalDeviceUniqueId : No buffer passed to " + "method."); return false; } - LOGV("CryptoSession::GetDeviceUniqueId: Lock"); + LOGV("CryptoSession::GetInternalDeviceUniqueId: Lock"); AutoLock auto_lock(crypto_lock_); if (!initialized_) { return false; @@ -739,8 +930,10 @@ bool CryptoSession::GetDeviceUniqueId(std::string* device_id) { size_t id_length = 32; id.resize(id_length); - OEMCryptoResult sts = - OEMCrypto_GetDeviceID(&id[0], &id_length, requested_security_level_); + OEMCryptoResult sts = OEMCrypto_GetDeviceID(&id[0], &id_length, + requested_security_level_); + // Increment the count of times this method was called. + metrics_->oemcrypto_get_device_id_.Increment(sts); if (OEMCrypto_SUCCESS != sts) { return false; @@ -751,6 +944,26 @@ bool CryptoSession::GetDeviceUniqueId(std::string* device_id) { } } +bool CryptoSession::GetExternalDeviceUniqueId(std::string* device_id) { + std::string temp; + if (!GetInternalDeviceUniqueId(&temp)) return false; + + if (pre_provision_token_type_ == kClientTokenOemCert) { + // To keep the size of the value passed back to the application down, hash + // the large OEM Public Cert to a smaller value. + uint8_t hash[SHA256_DIGEST_LENGTH]; + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, temp.data(), temp.length()); + SHA256_Final(hash, &ctx); + + temp.assign(reinterpret_cast(hash), SHA256_DIGEST_LENGTH); + } + + *device_id = temp; + return true; +} + bool CryptoSession::GetApiVersion(uint32_t* version) { if (!version) { LOGE("CryptoSession::GetApiVersion: No buffer passed to method."); @@ -760,7 +973,11 @@ bool CryptoSession::GetApiVersion(uint32_t* version) { if (!initialized_) { return false; } + *version = OEMCrypto_APIVersion(requested_security_level_); + // Record the version into the metrics. + metrics_->oemcrypto_api_version_.Record(*version); + return true; } @@ -778,8 +995,16 @@ bool CryptoSession::GetSystemId(uint32_t* system_id) { if (!initialized_) { return false; } - OEMCryptoResult sts = - OEMCrypto_GetKeyData(buf, &buf_size, requested_security_level_); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_GetKeyData( + buf, + &buf_size, + requested_security_level_), + metrics_, + oemcrypto_get_key_data_, + sts, + metrics::Pow2Bucket(buf_size)); if (OEMCrypto_SUCCESS != sts) { return false; @@ -807,23 +1032,50 @@ bool CryptoSession::GetProvisioningId(std::string* provisioning_id) { if (!initialized_) { return false; } - OEMCryptoResult sts = - OEMCrypto_GetKeyData(buf, &buf_size, requested_security_level_); - if (OEMCrypto_SUCCESS != sts) { - return false; + if (pre_provision_token_type_ == kClientTokenOemCert) { + // OEM Cert devices have no provisioning-unique ID embedded in them, so we + // synthesize one by using the External Device-Unique ID and inverting all + // the bits. + if (!GetExternalDeviceUniqueId(provisioning_id)) return false; + + for (size_t i = 0; i < provisioning_id->size(); ++i) { + char value = (*provisioning_id)[i]; + (*provisioning_id)[i] = ~value; + } + + return true; + } else { + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_GetKeyData( + buf, + &buf_size, + requested_security_level_), + metrics_, + oemcrypto_get_key_data_, + sts, + metrics::Pow2Bucket(buf_size)); + if (OEMCrypto_SUCCESS != sts) { + return false; + } + + provisioning_id->assign(reinterpret_cast(&buf[8]), 16); + return true; } - - provisioning_id->assign(reinterpret_cast(&buf[8]), 16); - return true; } uint8_t CryptoSession::GetSecurityPatchLevel() { - return OEMCrypto_Security_Patch_Level(requested_security_level_); + uint8_t patch = OEMCrypto_Security_Patch_Level(requested_security_level_); + metrics_->oemcrypto_security_patch_level_.Record(patch); + return patch; } CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) { - LOGV("CryptoSession::Open: Lock"); + LOGD("CryptoSession::Open: Lock: requested_security_level: %s", + requested_security_level == kLevel3 + ? QUERY_VALUE_SECURITY_LEVEL_L3.c_str() + : QUERY_VALUE_SECURITY_LEVEL_DEFAULT.c_str()); AutoLock auto_lock(crypto_lock_); if (!initialized_) return UNKNOWN_ERROR; if (open_) return NO_ERROR; @@ -845,25 +1097,66 @@ CdmResponseType CryptoSession::Open(SecurityLevel requested_security_level) { sts, session_count_, (int)initialized_); return UNKNOWN_ERROR; } - OEMCrypto_GetRandom(reinterpret_cast(&request_id_base_), - sizeof(request_id_base_)); + OEMCryptoResult random_sts = OEMCrypto_GetRandom( + reinterpret_cast(&request_id_base_), + sizeof(request_id_base_)); + metrics_->oemcrypto_get_random_.Increment(random_sts); ++request_id_index_; - key_session_.reset(new DefaultKeySession(oec_session_id_)); + + CdmUsageSupportType usage_support_type; + if (GetUsageSupportType(&usage_support_type) == NO_ERROR) { + if (usage_support_type == kUsageEntrySupport) { + CdmSecurityLevel security_level = GetSecurityLevel(); + if (security_level == kSecurityLevelL1 || + security_level == kSecurityLevelL3) { + UsageTableHeader** header = security_level == kSecurityLevelL1 ? + &usage_table_header_l1_ : &usage_table_header_l3_; + if (*header == NULL) { + *header = new UsageTableHeader(); + // Ignore errors since we do not know when a session is opened, + // if it is intended to be used for offline/usage session related + // or otherwise. + crypto_lock_.Release(); + bool is_usage_table_header_inited = + (*header)->Init(security_level, this); + crypto_lock_.Acquire(); + if (!is_usage_table_header_inited) { + delete *header; + *header = NULL; + usage_table_header_ = NULL; + return NO_ERROR; + } + } + usage_table_header_ = *header; + } + } + } + + // TODO(gmorgan, jfore): resolve handling of usage records in sublicenses + key_session_.reset(new DefaultKeySession(oec_session_id_, metrics_)); + return NO_ERROR; } void CryptoSession::Close() { LOGV("CloseSession: id=%ld open=%s", (uint32_t)oec_session_id_, open_ ? "true" : "false"); - AutoLock auto_lock(crypto_lock_); - if (!open_) return; - if (OEMCrypto_SUCCESS == OEMCrypto_CloseSession(oec_session_id_)) { - open_ = false; - if (update_usage_table_after_close_session_) { - OEMCryptoResult sts = OEMCrypto_UpdateUsageTable(); - if (sts != OEMCrypto_SUCCESS) - LOGW("CryptoSession::Close: OEMCrypto_UpdateUsageTable error=%ld", sts); - } + + OEMCryptoResult close_sts; + bool update_usage_table = false; + { + AutoLock auto_lock(crypto_lock_); + if (!open_) return; + + close_sts = OEMCrypto_CloseSession(oec_session_id_); + metrics_->oemcrypto_close_session_.Increment(close_sts); + if (OEMCrypto_SUCCESS == close_sts) + open_ = false; + update_usage_table = update_usage_table_after_close_session_; + } + if (close_sts == OEMCrypto_SUCCESS && update_usage_table && + usage_support_type_ == kUsageTableSupport) { + UpdateUsageInformation(); } } @@ -893,8 +1186,7 @@ bool CryptoSession::PrepareRequest(const std::string& message, return false; } - if (!Properties::use_certificates_as_identification() || - (is_provisioning && (pre_provision_token_type_ == kClientTokenKeybox))) { + if (is_provisioning && (pre_provision_token_type_ == kClientTokenKeybox)) { if (!GenerateDerivedKeys(message)) return false; if (!GenerateSignature(message, signature)) return false; @@ -928,31 +1220,41 @@ CdmResponseType CryptoSession::LoadKeys( const std::string& message, const std::string& signature, const std::string& mac_key_iv, const std::string& mac_key, const std::vector& keys, - const std::string& provider_session_token) { + const std::string& provider_session_token, + const std::string& srm_requirement) { LOGV("CryptoSession::LoadKeys: Lock"); AutoLock auto_lock(crypto_lock_); + LOGV("LoadKeys: id=%ld", (uint32_t)oec_session_id_); OEMCryptoResult sts = key_session_->LoadKeys( message, signature, mac_key_iv, mac_key, keys, provider_session_token, - &cipher_mode_); + &cipher_mode_, srm_requirement); + CdmResponseType result = KEY_ADDED; if (OEMCrypto_SUCCESS == sts) { - if (!provider_session_token.empty()) { + if (!provider_session_token.empty()) update_usage_table_after_close_session_ = true; - sts = OEMCrypto_UpdateUsageTable(); - if (sts != OEMCrypto_SUCCESS) { - LOGW("CryptoSession::LoadKeys: OEMCrypto_UpdateUsageTable error=%ld", - sts); - } - } - return KEY_ADDED; + result = KEY_ADDED; } else if (OEMCrypto_ERROR_TOO_MANY_KEYS == sts) { LOGE("CryptoSession::LoadKeys: OEMCrypto_LoadKeys error=%d", sts); - return INSUFFICIENT_CRYPTO_RESOURCES; + result = INSUFFICIENT_CRYPTO_RESOURCES; + } else if (OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE == sts) { + // Handle vendor specific error + LOGE("CryptoSession::LoadKeys: OEMCrypto_LoadKeys error=%d", sts); + result = NEED_PROVISIONING; } else { LOGE("CryptoSession::LoadKeys: OEMCrypto_LoadKeys error=%d", sts); - return LOAD_KEY_ERROR; + result = LOAD_KEY_ERROR; } + + // Leaving critical section + crypto_lock_.Release(); + + if (!provider_session_token.empty() && + usage_support_type_ == kUsageTableSupport) { + UpdateUsageInformation(); + } + return result; } bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) { @@ -968,9 +1270,14 @@ bool CryptoSession::LoadCertificatePrivateKey(std::string& wrapped_key) { OEMCrypto_GetOEMPublicCertificate(oec_session_id_, buf, &buf_size); LOGV("LoadDeviceRSAKey: id=%ld", (uint32_t)oec_session_id_); - sts = OEMCrypto_LoadDeviceRSAKey( - oec_session_id_, reinterpret_cast(wrapped_key.data()), - wrapped_key.size()); + M_TIME( + sts = OEMCrypto_LoadDeviceRSAKey( + oec_session_id_, + reinterpret_cast(wrapped_key.data()), + wrapped_key.size()), + metrics_, + oemcrypto_load_device_rsa_key_, + sts); if (OEMCrypto_SUCCESS != sts) { LOGE("LoadCertificatePrivateKey: OEMCrypto_LoadDeviceRSAKey error=%d", sts); @@ -1010,14 +1317,25 @@ bool CryptoSession::RefreshKeys(const std::string& message, } } LOGV("RefreshKeys: id=%ld", static_cast(oec_session_id_)); - OEMCryptoResult sts = OEMCrypto_RefreshKeys( - oec_session_id_, msg, message.size(), - reinterpret_cast(signature.data()), - signature.size(), num_keys, &load_key_array[0]); - return sts == OEMCrypto_SUCCESS; + OEMCryptoResult refresh_sts; + M_TIME( + refresh_sts = OEMCrypto_RefreshKeys( + oec_session_id_, + msg, + message.size(), + reinterpret_cast(signature.data()), + signature.size(), + num_keys, + &load_key_array[0]), + metrics_, + oemcrypto_refresh_keys_, + refresh_sts); + return OEMCrypto_SUCCESS == refresh_sts; } CdmResponseType CryptoSession::SelectKey(const std::string& key_id) { + // Crypto session lock already locked. + OEMCryptoResult sts = key_session_->SelectKey(key_id); switch (sts) { @@ -1027,6 +1345,8 @@ CdmResponseType CryptoSession::SelectKey(const std::string& key_id) { return NEED_KEY; case OEMCrypto_ERROR_INSUFFICIENT_HDCP: return INSUFFICIENT_OUTPUT_PROTECTION; + case OEMCrypto_ERROR_ANALOG_OUTPUT: + return ANALOG_OUTPUT_ERROR; case OEMCrypto_ERROR_INVALID_SESSION: return INVALID_SESSION_1; case OEMCrypto_ERROR_NO_DEVICE_KEY: @@ -1067,12 +1387,17 @@ bool CryptoSession::GenerateSignature(const std::string& message, // At most two attempts. // The first attempt may fail due to buffer too short for (int i = 0; i < 2; ++i) { - sts = OEMCrypto_GenerateSignature( - oec_session_id_, reinterpret_cast(message.data()), - message.size(), - reinterpret_cast(const_cast(signature->data())), - &length); - + M_TIME( + sts = OEMCrypto_GenerateSignature( + oec_session_id_, + reinterpret_cast(message.data()), + message.size(), + reinterpret_cast(const_cast(signature->data())), + &length), + metrics_, + oemcrypto_generate_signature_, + sts, + metrics::Pow2Bucket(length)); if (OEMCrypto_SUCCESS == sts) { // Trim signature buffer and done signature->resize(length); @@ -1105,11 +1430,18 @@ bool CryptoSession::GenerateRsaSignature(const std::string& message, // At most two attempts. // The first attempt may fail due to buffer too short for (int i = 0; i < 2; ++i) { - sts = OEMCrypto_GenerateRSASignature( - oec_session_id_, reinterpret_cast(message.data()), - message.size(), - reinterpret_cast(const_cast(signature->data())), - &length, kSign_RSASSA_PSS); + M_TIME( + sts = OEMCrypto_GenerateRSASignature( + oec_session_id_, + reinterpret_cast(message.data()), + message.size(), + reinterpret_cast(const_cast(signature->data())), + &length, + kSign_RSASSA_PSS), + metrics_, + oemcrypto_generate_rsa_signature_, + sts, + metrics::Pow2Bucket(length)); if (OEMCrypto_SUCCESS == sts) { // Trim signature buffer and done @@ -1165,9 +1497,18 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) { if (!params.is_encrypted && params.subsample_flags == (OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)) { - sts = OEMCrypto_CopyBuffer(requested_security_level_, - params.encrypt_buffer, params.encrypt_length, - &buffer_descriptor, params.subsample_flags); + + M_TIME( + sts = OEMCrypto_CopyBuffer( + requested_security_level_, + params.encrypt_buffer, + params.encrypt_length, + &buffer_descriptor, + params.subsample_flags), + metrics_, + oemcrypto_copy_buffer_, + sts, + metrics::Pow2Bucket(params.encrypt_length)); if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE && params.encrypt_length > kMaximumChunkSize) { @@ -1187,14 +1528,9 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) { AutoLock auto_lock(crypto_lock_); // Check if key needs to be selected if (params.is_encrypted) { - if (NO_ERROR != SelectKey(*params.key_id)) { - return NEED_KEY; - } + CdmResponseType result = SelectKey(*params.key_id); + if (result != NO_ERROR) return result; } - sts = OEMCrypto_DecryptCENC( - oec_session_id_, params.encrypt_buffer, params.encrypt_length, - params.is_encrypted, &(*params.iv).front(), params.block_offset, - &buffer_descriptor, &pattern_descriptor, params.subsample_flags); sts = key_session_->Decrypt(params, buffer_descriptor, pattern_descriptor); @@ -1232,6 +1568,8 @@ CdmResponseType CryptoSession::Decrypt(const CdmDecryptionParameters& params) { return DECRYPT_ERROR; case OEMCrypto_ERROR_INSUFFICIENT_HDCP: return INSUFFICIENT_OUTPUT_PROTECTION; + case OEMCrypto_ERROR_ANALOG_OUTPUT: + return ANALOG_OUTPUT_ERROR; default: return UNKNOWN_ERROR; } @@ -1246,13 +1584,19 @@ bool CryptoSession::UsageInformationSupport(bool* has_support) { } CdmResponseType CryptoSession::UpdateUsageInformation() { - LOGV("UpdateUsageInformation: id=%ld", (uint32_t)oec_session_id_); + LOGV("CryptoSession::UpdateUsageInformation: id=%ld", (uint32_t)oec_session_id_); AutoLock auto_lock(crypto_lock_); if (!initialized_) return UNKNOWN_ERROR; + if (usage_table_header_ != NULL) { + LOGV("UpdateUsageInformation: deprecated for OEMCrypto v13+"); + return NO_ERROR; + } + OEMCryptoResult status = OEMCrypto_UpdateUsageTable(); + metrics_->oemcrypto_update_usage_table_.Increment(status); if (status != OEMCrypto_SUCCESS) { - LOGE("CryptoSession::UsageUsageInformation: error=%ld", status); + LOGE("CryptoSession::UpdateUsageInformation: error=%ld", status); return UNKNOWN_ERROR; } return NO_ERROR; @@ -1266,8 +1610,10 @@ CdmResponseType CryptoSession::DeactivateUsageInformation( uint8_t* pst = reinterpret_cast( const_cast(provider_session_token.data())); - OEMCryptoResult status = - OEMCrypto_DeactivateUsageEntry(pst, provider_session_token.length()); + // TODO(fredgc or rfrias): make sure oec_session_id_ is valid. + OEMCryptoResult status = OEMCrypto_DeactivateUsageEntry( + (uint32_t)oec_session_id_, pst, provider_session_token.length()); + metrics_->oemcrypto_deactivate_usage_entry_.Increment(status); switch (status) { case OEMCrypto_SUCCESS: @@ -1298,8 +1644,9 @@ CdmResponseType CryptoSession::GenerateUsageReport( size_t usage_length = 0; OEMCryptoResult status = OEMCrypto_ReportUsage( - oec_session_id_, pst, provider_session_token.length(), NULL, - &usage_length); + oec_session_id_, pst, provider_session_token.length(), + NULL, &usage_length); + metrics_->oemcrypto_report_usage_.Increment(status); if (OEMCrypto_SUCCESS != status) { if (OEMCrypto_ERROR_SHORT_BUFFER != status) { @@ -1309,61 +1656,70 @@ CdmResponseType CryptoSession::GenerateUsageReport( } } - usage_report->resize(usage_length); - OEMCrypto_PST_Report* report = reinterpret_cast( - const_cast(usage_report->data())); - status = OEMCrypto_ReportUsage(oec_session_id_, pst, - provider_session_token.length(), report, - &usage_length); + std::vector buffer(usage_length); + + status = OEMCrypto_ReportUsage( + oec_session_id_, + pst, + provider_session_token.length(), + &buffer[0], + &usage_length); + metrics_->oemcrypto_report_usage_.Increment(status); if (OEMCrypto_SUCCESS != status) { LOGE("CryptoSession::GenerateUsageReport: Report Usage error=%ld", status); return UNKNOWN_ERROR; } - if (usage_length != usage_report->length()) { - usage_report->resize(usage_length); + if (usage_length != buffer.size()) { + buffer.resize(usage_length); } + (*usage_report) = std::string(reinterpret_cast(&buffer[0]), + buffer.size()); - OEMCrypto_PST_Report pst_report; + Unpacked_PST_Report pst_report(&buffer[0]); *usage_duration_status = kUsageDurationsInvalid; - if (usage_length < sizeof(pst_report)) { + if (usage_length < pst_report.report_size()) { LOGE("CryptoSession::GenerateUsageReport: usage report too small=%ld", usage_length); return NO_ERROR; // usage report available but no duration information } - memcpy(&pst_report, usage_report->data(), sizeof(pst_report)); - if (kUnused == pst_report.status) { + if (kUnused == pst_report.status()) { *usage_duration_status = kUsageDurationPlaybackNotBegun; return NO_ERROR; } - LOGV("OEMCrypto_PST_Report.status: %d\n", pst_report.status); + LOGV("OEMCrypto_PST_Report.status: %d\n", pst_report.status()); LOGV("OEMCrypto_PST_Report.clock_security_level: %d\n", - pst_report.clock_security_level); - LOGV("OEMCrypto_PST_Report.pst_length: %d\n", pst_report.pst_length); - LOGV("OEMCrypto_PST_Report.padding: %d\n", pst_report.padding); + pst_report.clock_security_level()); + LOGV("OEMCrypto_PST_Report.pst_length: %d\n", + pst_report.pst_length()); + LOGV("OEMCrypto_PST_Report.padding: %d\n", pst_report.padding()); LOGV("OEMCrypto_PST_Report.seconds_since_license_received: %lld\n", - ntohll64(pst_report.seconds_since_license_received)); + pst_report.seconds_since_license_received()); LOGV("OEMCrypto_PST_Report.seconds_since_first_decrypt: %lld\n", - ntohll64(pst_report.seconds_since_first_decrypt)); + pst_report.seconds_since_first_decrypt()); LOGV("OEMCrypto_PST_Report.seconds_since_last_decrypt: %lld\n", - ntohll64(pst_report.seconds_since_last_decrypt)); + pst_report.seconds_since_last_decrypt()); LOGV("OEMCrypto_PST_Report: %s\n", b2a_hex(*usage_report).c_str()); - // When usage report state is inactive, we have to deduce whether the - // license was ever used. - if (kInactive == pst_report.status && - (0 > ntohll64(pst_report.seconds_since_first_decrypt) || - ntohll64(pst_report.seconds_since_license_received) < - ntohll64(pst_report.seconds_since_first_decrypt))) { + if (kInactiveUnused == pst_report.status()) { + *usage_duration_status = kUsageDurationPlaybackNotBegun; + return NO_ERROR; + } + // Before OEMCrypto v13, When usage report state is inactive, we have to + // deduce whether the license was ever used. + if (kInactive == pst_report.status() && + (0 > pst_report.seconds_since_first_decrypt() || + pst_report.seconds_since_license_received() < + pst_report.seconds_since_first_decrypt())) { *usage_duration_status = kUsageDurationPlaybackNotBegun; return NO_ERROR; } *usage_duration_status = kUsageDurationsValid; - *seconds_since_started = ntohll64(pst_report.seconds_since_first_decrypt); - *seconds_since_last_played = ntohll64(pst_report.seconds_since_last_decrypt); + *seconds_since_started = pst_report.seconds_since_first_decrypt(); + *seconds_since_last_played = pst_report.seconds_since_last_decrypt(); return NO_ERROR; } @@ -1371,26 +1727,31 @@ CdmResponseType CryptoSession::ReleaseUsageInformation( const std::string& message, const std::string& signature, const std::string& provider_session_token) { LOGV("ReleaseUsageInformation: id=%ld", (uint32_t)oec_session_id_); - AutoLock auto_lock(crypto_lock_); - const uint8_t* msg = reinterpret_cast(message.data()); - const uint8_t* sig = reinterpret_cast(signature.data()); - const uint8_t* pst = msg + GetOffset(message, provider_session_token); + { + AutoLock auto_lock(crypto_lock_); + if (usage_table_header_ != NULL) { + LOGW("ReleaseUsageInformation: deprecated for OEMCrypto v13+"); + return NO_ERROR; + } - OEMCryptoResult status = OEMCrypto_DeleteUsageEntry( - oec_session_id_, pst, provider_session_token.length(), msg, - message.length(), sig, signature.length()); + const uint8_t* msg = reinterpret_cast(message.data()); + const uint8_t* sig = reinterpret_cast(signature.data()); + const uint8_t* pst = msg + GetOffset(message, provider_session_token); - if (OEMCrypto_SUCCESS != status) { - LOGE("CryptoSession::ReleaseUsageInformation: Report Usage error=%ld", - status); - return UNKNOWN_ERROR; - } - status = OEMCrypto_UpdateUsageTable(); - if (status != OEMCrypto_SUCCESS) { - LOGW("CryptoSession::ReleaseUsageInformation: update table error=%ld", - status); + OEMCryptoResult status = OEMCrypto_DeleteUsageEntry( + oec_session_id_, pst, provider_session_token.length(), + msg, message.length(), sig, signature.length()); + metrics_->oemcrypto_delete_usage_entry_.Increment(status); + + if (OEMCrypto_SUCCESS != status) { + LOGE("CryptoSession::ReleaseUsageInformation: Report Usage error=%ld", + status); + return UNKNOWN_ERROR; + } } + if (usage_support_type_ == kUsageTableSupport) + UpdateUsageInformation(); return NO_ERROR; } @@ -1398,22 +1759,21 @@ CdmResponseType CryptoSession::DeleteUsageInformation( const std::string& provider_session_token) { CdmResponseType response = NO_ERROR; LOGV("CryptoSession::DeleteUsageInformation"); - OEMCryptoResult status = OEMCrypto_ForceDeleteUsageEntry( - reinterpret_cast(provider_session_token.c_str()), - provider_session_token.length()); - if (OEMCrypto_SUCCESS != status) { - LOGE( - "CryptoSession::DeleteUsageInformation: Delete Usage Table error " - "=%ld", - status); - response = UNKNOWN_ERROR; - } - status = OEMCrypto_UpdateUsageTable(); - if (status != OEMCrypto_SUCCESS) { - LOGE("CryptoSession::DeleteUsageInformation: update table error=%ld", - status); - response = UNKNOWN_ERROR; + OEMCryptoResult status; + { + AutoLock auto_lock(crypto_lock_); + status = OEMCrypto_ForceDeleteUsageEntry( + reinterpret_cast(provider_session_token.c_str()), + provider_session_token.length()); + metrics_->oemcrypto_force_delete_usage_entry_.Increment(status); + if (OEMCrypto_SUCCESS != status) { + LOGE("CryptoSession::DeleteUsageInformation: Delete Usage Table error " + "= %ld", status); + response = UNKNOWN_ERROR; + } } + if (usage_support_type_ == kUsageTableSupport) + UpdateUsageInformation(); return response; } @@ -1421,45 +1781,48 @@ CdmResponseType CryptoSession::DeleteMultipleUsageInformation( const std::vector& provider_session_tokens) { LOGV("CryptoSession::DeleteMultipleUsageInformation"); CdmResponseType response = NO_ERROR; - for (size_t i=0; i < provider_session_tokens.size(); ++i) { - OEMCryptoResult status = OEMCrypto_ForceDeleteUsageEntry( - reinterpret_cast(provider_session_tokens[i].c_str()), - provider_session_tokens[i].length()); - if (OEMCrypto_SUCCESS != status) { - LOGW("CryptoSession::DeleteMultipleUsageInformation: " - "Delete Usage Table error =%ld", status); - response = UNKNOWN_ERROR; + { + AutoLock auto_lock(crypto_lock_); + for (size_t i=0; i < provider_session_tokens.size(); ++i) { + OEMCryptoResult status = OEMCrypto_ForceDeleteUsageEntry( + reinterpret_cast(provider_session_tokens[i].c_str()), + provider_session_tokens[i].length()); + metrics_->oemcrypto_force_delete_usage_entry_.Increment(status); + if (OEMCrypto_SUCCESS != status) { + LOGW("CryptoSession::DeleteMultipleUsageInformation: " + "Delete Usage Table error =%ld", status); + response = UNKNOWN_ERROR; + } } } - OEMCryptoResult status = OEMCrypto_UpdateUsageTable(); - if (status != OEMCrypto_SUCCESS) { - LOGE("CryptoSession::DeleteMultipleUsageInformation: update table error=%ld", - status); - response = UNKNOWN_ERROR; - } + if (usage_support_type_ == kUsageTableSupport) + UpdateUsageInformation(); return response; } CdmResponseType CryptoSession::DeleteAllUsageReports() { LOGV("DeleteAllUsageReports"); - OEMCryptoResult status = OEMCrypto_DeleteUsageTable(); - - if (OEMCrypto_SUCCESS != status) { - LOGE("CryptoSession::DeleteAllUsageReports: Delete Usage Table error =%ld", - status); + OEMCryptoResult status; + { + AutoLock auto_lock(crypto_lock_); + status = OEMCrypto_DeleteOldUsageTable(); + metrics_->oemcrypto_delete_usage_table_.Increment(status); + if (OEMCrypto_SUCCESS != status) { + LOGE("CryptoSession::DeleteAllUsageReports: Delete Usage Table error " + "=%ld", status); + } } - status = OEMCrypto_UpdateUsageTable(); - if (status != OEMCrypto_SUCCESS) { - LOGE("CryptoSession::DeletaAllUsageReports: update table error=%ld", - status); - return UNKNOWN_ERROR; - } + if (usage_support_type_ == kUsageTableSupport) + UpdateUsageInformation(); return NO_ERROR; } bool CryptoSession::IsAntiRollbackHwPresent() { - return OEMCrypto_IsAntiRollbackHwPresent(requested_security_level_); + bool is_present = + OEMCrypto_IsAntiRollbackHwPresent(requested_security_level_); + metrics_->oemcrypto_is_anti_rollback_hw_present_.Record(is_present); + return is_present; } bool CryptoSession::GenerateNonce(uint32_t* nonce) { @@ -1471,7 +1834,9 @@ bool CryptoSession::GenerateNonce(uint32_t* nonce) { LOGV("CryptoSession::GenerateNonce: Lock"); AutoLock auto_lock(crypto_lock_); - return (OEMCrypto_SUCCESS == OEMCrypto_GenerateNonce(oec_session_id_, nonce)); + OEMCryptoResult result = OEMCrypto_GenerateNonce(oec_session_id_, nonce); + metrics_->oemcrypto_generate_nonce_.Increment(result); + return OEMCrypto_SUCCESS == result; } bool CryptoSession::SetDestinationBufferType() { @@ -1544,23 +1909,45 @@ bool CryptoSession::RewrapDeviceRSAKey(const std::string& message, // Gets wrapped_rsa_key_length by passing NULL as uint8_t* wrapped_rsa_key // and 0 as wrapped_rsa_key_length. size_t wrapped_rsa_key_length = 0; - OEMCryptoResult status = OEMCrypto_RewrapDeviceRSAKey( - oec_session_id_, signed_msg, message.size(), - reinterpret_cast(signature.data()), signature.size(), - msg_nonce, msg_rsa_key, enc_rsa_key.size(), msg_rsa_key_iv, NULL, - &wrapped_rsa_key_length); + OEMCryptoResult status; + M_TIME( + status = OEMCrypto_RewrapDeviceRSAKey( + oec_session_id_, + signed_msg, + message.size(), + reinterpret_cast(signature.data()), + signature.size(), + msg_nonce, + msg_rsa_key, + enc_rsa_key.size(), + msg_rsa_key_iv, + NULL, + &wrapped_rsa_key_length), + metrics_, + oemcrypto_rewrap_device_rsa_key_, + status); if (status != OEMCrypto_ERROR_SHORT_BUFFER) { LOGE("OEMCrypto_RewrapDeviceRSAKey fails to get wrapped_rsa_key_length"); return false; } wrapped_rsa_key->resize(wrapped_rsa_key_length); - status = OEMCrypto_RewrapDeviceRSAKey( - oec_session_id_, signed_msg, message.size(), - reinterpret_cast(signature.data()), signature.size(), - msg_nonce, msg_rsa_key, enc_rsa_key.size(), msg_rsa_key_iv, - reinterpret_cast(&(*wrapped_rsa_key)[0]), - &wrapped_rsa_key_length); + M_TIME( + status = OEMCrypto_RewrapDeviceRSAKey( + oec_session_id_, + signed_msg, + message.size(), + reinterpret_cast(signature.data()), + signature.size(), + msg_nonce, + msg_rsa_key, + enc_rsa_key.size(), + msg_rsa_key_iv, + reinterpret_cast(&(*wrapped_rsa_key)[0]), + &wrapped_rsa_key_length), + metrics_, + oemcrypto_rewrap_device_rsa_key_, + status); wrapped_rsa_key->resize(wrapped_rsa_key_length); @@ -1617,7 +2004,7 @@ bool CryptoSession::RewrapDeviceRSAKey30(const std::string& message, wrapped_private_key->resize(wrapped_private_key_length); if (OEMCrypto_SUCCESS != status) { - LOGE("OEMCrypto_RewrapDeviceRSAKey fails with %d", status); + LOGE("OEMCrypto_RewrapDeviceRSAKey30 fails with %d", status); return false; } return true; @@ -1635,10 +2022,33 @@ bool CryptoSession::GetHdcpCapabilities(HdcpCapability* current, } OEMCryptoResult status = OEMCrypto_GetHDCPCapability( requested_security_level_, current, max); + if (OEMCrypto_SUCCESS != status) { + metrics_->oemcrypto_current_hdcp_capability_.SetError(status); + metrics_->oemcrypto_max_hdcp_capability_.SetError(status); LOGW("OEMCrypto_GetHDCPCapability fails with %d", status); return false; } + metrics_->oemcrypto_current_hdcp_capability_.Record(*current); + metrics_->oemcrypto_max_hdcp_capability_.Record(*max); + return true; +} + +bool CryptoSession::GetSupportedCertificateTypes( + SupportedCertificateTypes* support) { + LOGV("GetSupportedCertificateTypes: id=%ld", (uint32_t)oec_session_id_); + if (!initialized_) return false; + if (support == NULL) { + LOGE("CryptoSession::GetSupportedCertificateTypes: |support| cannot be " + "NULL"); + return false; + } + + uint32_t oec_support = + OEMCrypto_SupportedCertificates(requested_security_level_); + support->rsa_2048_bit = oec_support & OEMCrypto_Supports_RSA_2048bit; + support->rsa_3072_bit = oec_support & OEMCrypto_Supports_RSA_3072bit; + support->rsa_cast = oec_support & OEMCrypto_Supports_RSA_CAST; return true; } @@ -1648,6 +2058,7 @@ bool CryptoSession::GetRandom(size_t data_length, uint8_t* random_data) { return false; } OEMCryptoResult sts = OEMCrypto_GetRandom(random_data, data_length); + metrics_->oemcrypto_get_random_.Increment(sts); if (sts != OEMCrypto_SUCCESS) { LOGE("OEMCrypto_GetRandom fails with %d", sts); @@ -1668,10 +2079,14 @@ bool CryptoSession::GetNumberOfOpenSessions(size_t* count) { size_t sessions_count; OEMCryptoResult status = OEMCrypto_GetNumberOfOpenSessions( requested_security_level_, &sessions_count); + if (OEMCrypto_SUCCESS != status) { LOGW("OEMCrypto_GetNumberOfOpenSessions fails with %d", status); + metrics_->oemcrypto_number_of_open_sessions_.SetError(status); return false; } + + metrics_->oemcrypto_number_of_open_sessions_.Record(sessions_count); *count = sessions_count; return true; } @@ -1689,12 +2104,58 @@ bool CryptoSession::GetMaxNumberOfSessions(size_t* max) { requested_security_level_, &max_sessions); if (OEMCrypto_SUCCESS != status) { LOGW("OEMCrypto_GetMaxNumberOfSessions fails with %d", status); + metrics_->oemcrypto_max_number_of_sessions_.SetError(status); return false; } + + metrics_->oemcrypto_max_number_of_sessions_.Record(max_sessions); *max = max_sessions; return true; } +bool CryptoSession::GetSrmVersion(uint16_t* srm_version) { + LOGV("GetSrmVersion"); + if (!initialized_) return false; + if (srm_version == NULL) { + LOGE("CryptoSession::GetSrmVersion: |srm_version| cannot be NULL"); + return false; + } + + OEMCryptoResult status = OEMCrypto_GetCurrentSRMVersion(srm_version); + switch (status) { + case OEMCrypto_SUCCESS: + return true; + case OEMCrypto_ERROR_NOT_IMPLEMENTED: + return false; + default: + LOGW("OEMCrypto_GetCurrentSRMVersion fails with %d", status); + return false; + } +} + +bool CryptoSession::IsSrmUpdateSupported() { + LOGV("IsSrmUpdateSupported"); + if (!initialized_) return false; + return OEMCrypto_IsSRMUpdateSupported(); +} + +bool CryptoSession::LoadSrm(const std::string& srm) { + LOGV("LoadSrm"); + if (!initialized_) return false; + if (srm.empty()) { + LOGE("CryptoSession::LoadSrm: |srm| cannot be empty"); + return false; + } + + OEMCryptoResult status = OEMCrypto_LoadSRM( + reinterpret_cast(srm.data()), srm.size()); + if (OEMCrypto_SUCCESS != status) { + LOGW("OEMCrypto_LoadSRM fails with %d", status); + return false; + } + return true; +} + CdmResponseType CryptoSession::GenericEncrypt(const std::string& in_buffer, const std::string& key_id, const std::string& iv, @@ -1714,20 +2175,29 @@ CdmResponseType CryptoSession::GenericEncrypt(const std::string& in_buffer, } AutoLock auto_lock(crypto_lock_); - if (NO_ERROR != SelectKey(key_id)) { - return KEY_ERROR_1; - } + CdmResponseType result = SelectKey(key_id); + if (result != NO_ERROR) return result; - OEMCryptoResult sts = OEMCrypto_Generic_Encrypt( - oec_session_id_, reinterpret_cast(in_buffer.data()), - in_buffer.size(), reinterpret_cast(iv.data()), - oec_algorithm, - reinterpret_cast(const_cast(out_buffer->data()))); + OEMCryptoResult sts; + + M_TIME( + sts = OEMCrypto_Generic_Encrypt( + oec_session_id_, + reinterpret_cast(in_buffer.data()), + in_buffer.size(), + reinterpret_cast(iv.data()), + oec_algorithm, + reinterpret_cast(const_cast(out_buffer->data()))), + metrics_, + oemcrypto_generic_encrypt_, + sts, + metrics::Pow2Bucket(in_buffer.size())); if (OEMCrypto_SUCCESS != sts) { LOGE("GenericEncrypt: OEMCrypto_Generic_Encrypt err=%d", sts); - if (OEMCrypto_ERROR_KEY_EXPIRED == sts || - OEMCrypto_ERROR_NO_CONTENT_KEY == sts) { + if (OEMCrypto_ERROR_KEY_EXPIRED == sts) { + return NEED_KEY; + } else if (OEMCrypto_ERROR_NO_CONTENT_KEY == sts) { return KEY_NOT_FOUND_3; } else { return UNKNOWN_ERROR; @@ -1755,20 +2225,29 @@ CdmResponseType CryptoSession::GenericDecrypt(const std::string& in_buffer, } AutoLock auto_lock(crypto_lock_); - if (NO_ERROR != SelectKey(key_id)) { - return KEY_ERROR_2; - } + CdmResponseType result = SelectKey(key_id); + if (result != NO_ERROR) return result; - OEMCryptoResult sts = OEMCrypto_Generic_Decrypt( - oec_session_id_, reinterpret_cast(in_buffer.data()), - in_buffer.size(), reinterpret_cast(iv.data()), - oec_algorithm, - reinterpret_cast(const_cast(out_buffer->data()))); + OEMCryptoResult sts; + + M_TIME( + sts = OEMCrypto_Generic_Decrypt( + oec_session_id_, + reinterpret_cast(in_buffer.data()), + in_buffer.size(), + reinterpret_cast(iv.data()), + oec_algorithm, + reinterpret_cast(const_cast(out_buffer->data()))), + metrics_, + oemcrypto_generic_decrypt_, + sts, + metrics::Pow2Bucket(in_buffer.size())); if (OEMCrypto_SUCCESS != sts) { LOGE("GenericDecrypt: OEMCrypto_Generic_Decrypt err=%d", sts); - if (OEMCrypto_ERROR_KEY_EXPIRED == sts || - OEMCrypto_ERROR_NO_CONTENT_KEY == sts) { + if (OEMCrypto_ERROR_KEY_EXPIRED == sts) { + return NEED_KEY; + } else if (OEMCrypto_ERROR_NO_CONTENT_KEY == sts) { return KEY_NOT_FOUND_4; } else { return UNKNOWN_ERROR; @@ -1796,18 +2275,24 @@ CdmResponseType CryptoSession::GenericSign(const std::string& message, size_t length = signature->size(); AutoLock auto_lock(crypto_lock_); - if (NO_ERROR != SelectKey(key_id)) { - return KEY_ERROR_3; - } + CdmResponseType result = SelectKey(key_id); + if (result != NO_ERROR) return result; // At most two attempts. // The first attempt may fail due to buffer too short for (int i = 0; i < 2; ++i) { - sts = OEMCrypto_Generic_Sign( - oec_session_id_, reinterpret_cast(message.data()), - message.size(), oec_algorithm, - reinterpret_cast(const_cast(signature->data())), - &length); + M_TIME( + sts = OEMCrypto_Generic_Sign( + oec_session_id_, + reinterpret_cast(message.data()), + message.size(), + oec_algorithm, + reinterpret_cast(const_cast(signature->data())), + &length), + metrics_, + oemcrypto_generic_sign_, + sts, + metrics::Pow2Bucket(message.size())); if (OEMCrypto_SUCCESS == sts) { // Trim signature buffer and done @@ -1823,8 +2308,9 @@ CdmResponseType CryptoSession::GenericSign(const std::string& message, } LOGE("GenericSign: OEMCrypto_Generic_Sign err=%d", sts); - if (OEMCrypto_ERROR_KEY_EXPIRED == sts || - OEMCrypto_ERROR_NO_CONTENT_KEY == sts) { + if (OEMCrypto_ERROR_KEY_EXPIRED == sts) { + return NEED_KEY; + } else if (OEMCrypto_ERROR_NO_CONTENT_KEY == sts) { return KEY_NOT_FOUND_5; } else { return UNKNOWN_ERROR; @@ -1843,19 +2329,28 @@ CdmResponseType CryptoSession::GenericVerify(const std::string& message, } AutoLock auto_lock(crypto_lock_); - if (NO_ERROR != SelectKey(key_id)) { - return KEY_ERROR_4; - } + CdmResponseType result = SelectKey(key_id); + if (result != NO_ERROR) return result; - OEMCryptoResult sts = OEMCrypto_Generic_Verify( - oec_session_id_, reinterpret_cast(message.data()), - message.size(), oec_algorithm, - reinterpret_cast(signature.data()), signature.size()); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_Generic_Verify( + oec_session_id_, + reinterpret_cast(message.data()), + message.size(), + oec_algorithm, + reinterpret_cast(signature.data()), + signature.size()), + metrics_, + oemcrypto_generic_verify_, + sts, + metrics::Pow2Bucket(signature.size())); if (OEMCrypto_SUCCESS != sts) { LOGE("GenericVerify: OEMCrypto_Generic_Verify err=%d", sts); - if (OEMCrypto_ERROR_KEY_EXPIRED == sts || - OEMCrypto_ERROR_NO_CONTENT_KEY == sts) { + if (OEMCrypto_ERROR_KEY_EXPIRED == sts) { + return NEED_KEY; + } else if (OEMCrypto_ERROR_NO_CONTENT_KEY == sts) { return KEY_NOT_FOUND_6; } else { return UNKNOWN_ERROR; @@ -1864,8 +2359,332 @@ CdmResponseType CryptoSession::GenericVerify(const std::string& message, return NO_ERROR; } +CdmResponseType CryptoSession::GetUsageSupportType( + CdmUsageSupportType* usage_support_type) { + LOGV("GetUsageSupportType: id=%ld", (uint32_t)oec_session_id_); + + if (usage_support_type == NULL) { + LOGE("GetUsageSupportType: usage_support_type param not provided"); + return INVALID_PARAMETERS_ENG_23; + } + + if (usage_support_type_ != kUnknownUsageSupport) { + *usage_support_type = usage_support_type_; + return NO_ERROR; + } + + bool has_support = false; + if (!UsageInformationSupport(&has_support)) { + LOGE("GetUsageSupportType: UsageInformationSupport failed"); + return USAGE_INFORMATION_SUPPORT_FAILED; + } + + if (!has_support) { + *usage_support_type = usage_support_type_ = kNonSecureUsageSupport; + return NO_ERROR; + } + + uint32_t api_version = 0; + if (!GetApiVersion(&api_version)) { + LOGE("GetUsageSupportType: GetApiVersion failed"); + return USAGE_SUPPORT_GET_API_FAILED; + } + + *usage_support_type = usage_support_type_ = + (api_version >= kOemCryptoApiVersionSupportsBigUsageTables) ? + kUsageEntrySupport : kUsageTableSupport; + return NO_ERROR; +} + +CdmResponseType CryptoSession::CreateUsageTableHeader( + CdmUsageTableHeader* usage_table_header) { + LOGV("CreateUsageTableHeader: id=%ld", (uint32_t)oec_session_id_); + + if (usage_table_header == NULL) { + LOGE("CreateUsageTableHeader: usage_table_header param not provided"); + return INVALID_PARAMETERS_ENG_17; + } + + usage_table_header->resize(kEstimatedInitialUsageTableHeader); + + size_t usage_table_header_size = usage_table_header->size(); + OEMCryptoResult result = OEMCrypto_CreateUsageTableHeader( + requested_security_level_, + reinterpret_cast(const_cast(usage_table_header->data())), + &usage_table_header_size); + + if (result == OEMCrypto_ERROR_SHORT_BUFFER) { + usage_table_header->resize(usage_table_header_size); + result = OEMCrypto_CreateUsageTableHeader( + requested_security_level_, + reinterpret_cast( + const_cast(usage_table_header->data())), + &usage_table_header_size); + } + + if (result != OEMCrypto_SUCCESS) { + LOGE("CreateUsageTableHeader: usage table header creation failed: %d", + result); + return CREATE_USAGE_TABLE_ERROR; + } + + usage_table_header->resize(usage_table_header_size); + return NO_ERROR; +} + +CdmResponseType CryptoSession::LoadUsageTableHeader( + const CdmUsageTableHeader& usage_table_header) { + LOGV("LoadUsageTableHeader: id=%ld", (uint32_t)oec_session_id_); + + OEMCryptoResult result = OEMCrypto_LoadUsageTableHeader( + requested_security_level_, + reinterpret_cast(usage_table_header.data()), + usage_table_header.size()); + + if (result != OEMCrypto_SUCCESS) { + if (result == OEMCrypto_WARNING_GENERATION_SKEW) { + LOGW( + "LoadUsageTableHeader: OEMCrypto_LoadUsageTableHeader warning: " + "generation skew"); + } else { + LOGE("LoadUsageTableHeader: OEMCrypto_LoadUsageTableHeader error: %d", + result); + } + } + + switch (result) { + case OEMCrypto_SUCCESS: + case OEMCrypto_WARNING_GENERATION_SKEW: + return NO_ERROR; + case OEMCrypto_ERROR_GENERATION_SKEW: + return LOAD_USAGE_HEADER_GENERATION_SKEW; + case OEMCrypto_ERROR_SIGNATURE_FAILURE: + return LOAD_USAGE_HEADER_SIGNATURE_FAILURE; + case OEMCrypto_ERROR_BAD_MAGIC: + return LOAD_USAGE_HEADER_BAD_MAGIC; + case OEMCrypto_ERROR_UNKNOWN_FAILURE: + default: + return LOAD_USAGE_HEADER_UNKNOWN_ERROR; + } +} + +CdmResponseType CryptoSession::CreateUsageEntry(uint32_t* entry_number) { + LOGV("CreateUsageEntry: id=%ld", (uint32_t)oec_session_id_); + + if (entry_number == NULL) { + LOGE("CreateUsageEntry: entry_number param not provided"); + return INVALID_PARAMETERS_ENG_18; + } + + OEMCryptoResult result = + OEMCrypto_CreateNewUsageEntry(oec_session_id_, entry_number); + + switch (result) { + case OEMCrypto_SUCCESS: + return NO_ERROR; + case OEMCrypto_ERROR_INSUFFICIENT_RESOURCES: + LOGE( + "CreateUsageEntry: OEMCrypto_CreateNewUsageEntry error: " + "Insufficient resources"); + return INSUFFICIENT_CRYPTO_RESOURCES_3; + default: + LOGE("CreateUsageEntry: OEMCrypto_CreateNewUsageEntry error: %d", result); + return CREATE_USAGE_ENTRY_UNKNOWN_ERROR; + } +} + +CdmResponseType CryptoSession::LoadUsageEntry( + uint32_t entry_number, + const CdmUsageEntry& usage_entry) { + LOGV("LoadUsageEntry: id=%ld", (uint32_t)oec_session_id_); + + OEMCryptoResult result = OEMCrypto_LoadUsageEntry( + oec_session_id_, entry_number, + reinterpret_cast(usage_entry.data()), usage_entry.size()); + + if (result != OEMCrypto_SUCCESS) { + if (result == OEMCrypto_WARNING_GENERATION_SKEW) { + LOGW("LoadUsageEntry: OEMCrypto_LoadUsageEntry warning: generation skew"); + } else { + LOGE("LoadUsageTableHeader: OEMCrypto_LoadUsageEntry error: %d", result); + } + } + + switch (result) { + case OEMCrypto_SUCCESS: + case OEMCrypto_WARNING_GENERATION_SKEW: + return NO_ERROR; + case OEMCrypto_ERROR_GENERATION_SKEW: + return LOAD_USAGE_ENTRY_GENERATION_SKEW; + case OEMCrypto_ERROR_SIGNATURE_FAILURE: + return LOAD_USAGE_ENTRY_SIGNATURE_FAILURE; + default: + return LOAD_USAGE_ENTRY_UNKNOWN_ERROR; + } +} + +CdmResponseType CryptoSession::UpdateUsageEntry( + CdmUsageTableHeader* usage_table_header, + CdmUsageEntry* usage_entry) { + LOGV("UpdateUsageEntry: id=%ld", (uint32_t)oec_session_id_); + + if (usage_table_header == NULL) { + LOGE("UpdateUsageEntry: usage_table_header param not provided"); + return INVALID_PARAMETERS_ENG_19; + } + + if (usage_entry == NULL) { + LOGE("UpdateUsageEntry: usage_entry param not provided"); + return INVALID_PARAMETERS_ENG_20; + } + + size_t usage_table_header_len = 0; + size_t usage_entry_len = 0; + OEMCryptoResult result = OEMCrypto_UpdateUsageEntry( + oec_session_id_, NULL, &usage_table_header_len, NULL, &usage_entry_len); + + if (result == OEMCrypto_ERROR_SHORT_BUFFER) { + usage_table_header->resize(usage_table_header_len); + usage_entry->resize(usage_entry_len); + + result = OEMCrypto_UpdateUsageEntry( + oec_session_id_, + reinterpret_cast( + const_cast(usage_table_header->data())), + &usage_table_header_len, + reinterpret_cast(const_cast(usage_entry->data())), + &usage_entry_len); + } + + if (result != OEMCrypto_SUCCESS) { + LOGE("UpdateUsageEntry: OEMCrypto_UpdateUsageEntry error: %d", result); + return UPDATE_USAGE_ENTRY_UNKNOWN_ERROR; + } + + usage_table_header->resize(usage_table_header_len); + usage_entry->resize(usage_entry_len); + return NO_ERROR; +} + +CdmResponseType CryptoSession::ShrinkUsageTableHeader( + uint32_t new_entry_count, CdmUsageTableHeader* usage_table_header) { + LOGV("ShrinkUsageTableHeader: id=%ld", (uint32_t)oec_session_id_); + + if (usage_table_header == NULL) { + LOGE( + "ShrinkUsageTableHeader: usage_table_header param not provided"); + return INVALID_PARAMETERS_ENG_21; + } + + size_t usage_table_header_len = 0; + OEMCryptoResult result = OEMCrypto_ShrinkUsageTableHeader( + requested_security_level_, new_entry_count, NULL, + &usage_table_header_len); + + if (result == OEMCrypto_ERROR_SHORT_BUFFER) { + usage_table_header->resize(usage_table_header_len); + + result = OEMCrypto_ShrinkUsageTableHeader( + requested_security_level_, new_entry_count, + reinterpret_cast( + const_cast(usage_table_header->data())), + &usage_table_header_len); + } + + if (result != OEMCrypto_SUCCESS) { + LOGE( + "ShrinkUsageTableHeader: OEMCrypto_ShrinkUsageTableHeader error: %d", + result); + return SHRINK_USAGE_TABLER_HEADER_UNKNOWN_ERROR; + } + + usage_table_header->resize(usage_table_header_len); + return NO_ERROR; +} + +CdmResponseType CryptoSession::MoveUsageEntry(uint32_t new_entry_number) { + LOGV("MoveUsageEntry: id=%ld", (uint32_t)oec_session_id_); + + OEMCryptoResult result = + OEMCrypto_MoveEntry(oec_session_id_, new_entry_number); + + if (result != OEMCrypto_SUCCESS) { + LOGE("MoveUsageEntry: OEMCrypto_MoveEntry error: %d", result); + return MOVE_USAGE_ENTRY_UNKNOWN_ERROR; + } + + return NO_ERROR; +} + +bool CryptoSession::CreateOldUsageEntry( + uint64_t time_since_license_received, + uint64_t time_since_first_decrypt, + uint64_t time_since_last_decrypt, + UsageDurationStatus usage_duration_status, + const std::string& server_mac_key, + const std::string& client_mac_key, + const std::string& provider_session_token) { + LOGV("CreateOldUsageEntry: Lock"); + AutoLock auto_lock(crypto_lock_); + + if (server_mac_key.size() < MAC_KEY_SIZE || + client_mac_key.size() < MAC_KEY_SIZE) { + LOGE("CreateOldUsageEntry: Invalid mac key size: server mac key size %d, " + "client mac key size: %d", server_mac_key.size(), + client_mac_key.size()); + return false; + } + + OEMCrypto_Usage_Entry_Status status = kUnused; + switch (usage_duration_status) { + case kUsageDurationsInvalid: status = kUnused; break; + case kUsageDurationPlaybackNotBegun: status = kInactiveUnused; break; + case kUsageDurationsValid: status = kActive; break; + default: + LOGE("CreateOldUsageEntry: Unrecognized usage duration status: %d", + usage_duration_status); + return false; + } + + OEMCryptoResult result = + OEMCrypto_CreateOldUsageEntry( + requested_security_level_, time_since_license_received, + time_since_first_decrypt, time_since_last_decrypt, status, + reinterpret_cast( + const_cast(server_mac_key.data())), + reinterpret_cast( + const_cast(client_mac_key.data())), + reinterpret_cast(provider_session_token.data()), + provider_session_token.size()); + + if (result != OEMCrypto_SUCCESS) { + LOGE("CreateOldUsageEntry: OEMCrypto_CreateOldUsageEntry error: %d", + result); + return false; + } + return true; +} + +CdmResponseType CryptoSession::CopyOldUsageEntry( + const std::string& provider_session_token) { + LOGV("CopyOldUsageEntry: id=%ld", (uint32_t)oec_session_id_); + + OEMCryptoResult result = OEMCrypto_CopyOldUsageEntry( + oec_session_id_, + reinterpret_cast(provider_session_token.data()), + provider_session_token.size()); + + if (result != OEMCrypto_SUCCESS) { + LOGE("CopyOldUsageEntry: OEMCrypto_CopyOldUsageEntry error: %d", result); + return COPY_OLD_USAGE_ENTRY_UNKNOWN_ERROR; + } + + return NO_ERROR; +} + CdmResponseType CryptoSession::AddSubSession( - const std::string& sub_session_key_id) { + const std::string& sub_session_key_id, + const std::string& group_master_key_id) { size_t exists = sub_license_oec_sessions_.count(sub_session_key_id); if (exists > 0) { // TODO(jfore): Should this be an error if the key exists? If so add a new @@ -1887,14 +2706,26 @@ CdmResponseType CryptoSession::AddSubSession( return UNKNOWN_ERROR; } - sts = OEMCrypto_LoadDeviceRSAKey( - sid, reinterpret_cast(wrapped_key_.data()), - wrapped_key_.size()); + M_TIME( + sts = OEMCrypto_LoadDeviceRSAKey( + sid, + reinterpret_cast(wrapped_key_.data()), + wrapped_key_.size()), + metrics_, + oemcrypto_load_device_rsa_key_, + sts); + + if (OEMCrypto_SUCCESS != sts) { + LOGE("LoadDeviceRSAKey failed: %d", sts); + return NEED_PROVISIONING; + } sub_license_oec_sessions_[sub_session_key_id] = sid; if (key_session_->Type() != KeySession::kSubLicense) { - key_session_.reset(new SubLicenseKeySession( - sub_license_oec_sessions_, wrapped_key_, requested_security_level_)); + key_session_.reset( + new SubLicenseKeySession(sub_license_oec_sessions_, metrics_, + wrapped_key_, requested_security_level_, + group_master_key_id)); } return NO_ERROR; } @@ -1914,7 +2745,7 @@ bool CryptoSession::GenerateSubSessionNonce( if (it == sub_license_oec_sessions_.end()) { // A subsession does not exist. Indicate that and return success. *exists = false; - return true; + return false; } *exists = true; @@ -1992,9 +2823,19 @@ OEMCryptoResult CryptoSession::CopyBufferInChunks( subsample_flags |= OEMCrypto_LastSubsample; } - OEMCryptoResult sts = OEMCrypto_CopyBuffer( - requested_security_level_, params.encrypt_buffer + additional_offset, - chunk_size, &buffer_descriptor, subsample_flags); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_CopyBuffer( + requested_security_level_, + params.encrypt_buffer + additional_offset, + chunk_size, + &buffer_descriptor, + subsample_flags), + metrics_, + oemcrypto_copy_buffer_, + sts, + metrics::Pow2Bucket(chunk_size)); + if (sts != OEMCrypto_SUCCESS) { return sts; } @@ -2061,10 +2902,23 @@ OEMCryptoResult CryptoSession::DecryptInChunks( // max_chunk_size is guaranteed to be an even multiple of the // pattern length long, which is also guaranteed to be an exact number // of AES blocks long. - OEMCryptoResult sts = OEMCrypto_DecryptCENC( - oec_session_id_, params.encrypt_buffer + additional_offset, - chunk_size, params.is_encrypted, &iv.front(), params.block_offset, - &buffer_descriptor, &pattern_descriptor, subsample_flags); + OEMCryptoResult sts; + M_TIME( + sts = OEMCrypto_DecryptCENC( + oec_session_id_, + params.encrypt_buffer + additional_offset, + chunk_size, + params.is_encrypted, + &iv.front(), + params.block_offset, + &buffer_descriptor, + &pattern_descriptor, + subsample_flags), + metrics_, + oemcrypto_decrypt_cenc_, + sts, + metrics::Pow2Bucket(chunk_size)); + if (sts != OEMCrypto_SUCCESS) { return sts; } diff --git a/core/src/device_files.cpp b/core/src/device_files.cpp index 43dbf977..4db8d3de 100644 --- a/core/src/device_files.cpp +++ b/core/src/device_files.cpp @@ -35,6 +35,14 @@ using video_widevine_client::sdk::License_LicenseState_RELEASING; using video_widevine_client::sdk::NameValue; using video_widevine_client::sdk::UsageInfo; using video_widevine_client::sdk::UsageInfo_ProviderSession; +using video_widevine_client::sdk::UsageTableInfo; +using video_widevine_client::sdk::UsageTableInfo_UsageEntryInfo; +using video_widevine_client::sdk:: + UsageTableInfo_UsageEntryInfo_UsageEntryStorage_LICENSE; +using video_widevine_client::sdk:: + UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO; +using video_widevine_client::sdk:: + UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN; using video_widevine::SignedDrmDeviceCertificate; using video_widevine::DrmDeviceCertificate; @@ -47,6 +55,7 @@ const char kUsageInfoFileNamePrefix[] = "usage"; const char kUsageInfoFileNameExt[] = ".bin"; const char kLicenseFileNameExt[] = ".lic"; const char kEmptyFileName[] = ""; +const char kUsageTableFileName[] = "usgtable.bin"; const char kWildcard[] = "*"; bool Hash(const std::string& data, std::string* hash) { @@ -154,7 +163,7 @@ bool DeviceFiles::ExtractDeviceInfo(const std::string& device_certificate, uint32_t* system_id) { LOGI("ExtractDeviceInfo Entry"); if (!serial_number && !system_id) { - LOGE("Invalid paramters to DeviceFiles::ExtractDeviceInfo"); + LOGE("DeviceFiles::ExtractDeviceInfo: invalid parameter."); return false; } @@ -210,7 +219,9 @@ bool DeviceFiles::StoreLicense( const CdmKeyResponse& license_renewal, const std::string& release_server_url, int64_t playback_start_time, int64_t last_playback_time, int64_t grace_period_end_time, - const CdmAppParameterMap& app_parameters) { + const CdmAppParameterMap& app_parameters, + const CdmUsageEntry& usage_entry, + const uint32_t usage_entry_number) { if (!initialized_) { LOGW("DeviceFiles::StoreLicense: not initialized"); return false; @@ -251,6 +262,8 @@ bool DeviceFiles::StoreLicense( app_params->set_name(iter->first); app_params->set_value(iter->second); } + license->set_usage_entry(usage_entry); + license->set_usage_entry_number(usage_entry_number); std::string serialized_file; file.SerializeToString(&serialized_file); @@ -265,7 +278,8 @@ bool DeviceFiles::RetrieveLicense( CdmKeyMessage* license_renewal_request, CdmKeyResponse* license_renewal, std::string* release_server_url, int64_t* playback_start_time, int64_t* last_playback_time, int64_t* grace_period_end_time, - CdmAppParameterMap* app_parameters) { + CdmAppParameterMap* app_parameters, CdmUsageEntry* usage_entry, + uint32_t* usage_entry_number) { if (!initialized_) { LOGW("DeviceFiles::RetrieveLicense: not initialized"); return false; @@ -319,6 +333,8 @@ bool DeviceFiles::RetrieveLicense( (*app_parameters)[license.app_parameters(i).name()] = license.app_parameters(i).value(); } + *usage_entry = license.usage_entry(); + *usage_entry_number = license.usage_entry_number(); return true; } @@ -336,6 +352,11 @@ bool DeviceFiles::ListLicenses(std::vector* key_set_ids) { return false; } + if (key_set_ids == NULL) { + LOGW("DeviceFiles::ListLicenses: key_set_ids parameter not provided"); + return false; + } + // Get list of filenames std::vector filenames; if (!ListFiles(&filenames)) { @@ -407,20 +428,21 @@ bool DeviceFiles::UnreserveLicenseId(const std::string& key_set_id) { bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token, const CdmKeyMessage& key_request, const CdmKeyResponse& key_response, - const std::string& app_id, - const std::string& key_set_id) { + const std::string& usage_info_file_name, + const std::string& key_set_id, + const std::string& usage_entry, + uint32_t usage_entry_number) { if (!initialized_) { LOGW("DeviceFiles::StoreUsageInfo: not initialized"); return false; } video_widevine_client::sdk::File file; - std::string file_name = GetUsageInfoFileName(app_id); - if (!FileExists(file_name)) { + if (!FileExists(usage_info_file_name)) { file.set_type(video_widevine_client::sdk::File::USAGE_INFO); file.set_version(video_widevine_client::sdk::File::VERSION_1); } else { - if (!RetrieveHashedFile(file_name, &file)) { + if (!RetrieveHashedFile(usage_info_file_name, &file)) { LOGW("DeviceFiles::StoreUsageInfo: Unable to parse file"); return false; } @@ -434,10 +456,12 @@ bool DeviceFiles::StoreUsageInfo(const std::string& provider_session_token, provider_session->set_license_request(key_request.data(), key_request.size()); provider_session->set_license(key_response.data(), key_response.size()); provider_session->set_key_set_id(key_set_id.data(), key_set_id.size()); + provider_session->set_usage_entry(usage_entry); + provider_session->set_usage_entry_number(usage_entry_number); std::string serialized_file; file.SerializeToString(&serialized_file); - return StoreFileWithHash(file_name, serialized_file); + return StoreFileWithHash(usage_info_file_name, serialized_file); } bool DeviceFiles::ListUsageRecords(const std::string& app_id, @@ -476,28 +500,28 @@ bool DeviceFiles::ListUsageRecords(const std::string& app_id, return true; } -bool DeviceFiles::GetProviderToken(const std::string& app_id, - const std::string& key_set_id, - std::string* provider_session_token) { +bool DeviceFiles::GetProviderSessionToken(const std::string& app_id, + const std::string& key_set_id, + std::string* provider_session_token) { if (!initialized_) { - LOGW("DeviceFiles::GetProviderToken: not initialized"); + LOGW("DeviceFiles::GetProviderSessionToken: not initialized"); return false; } if (provider_session_token == NULL) { - LOGW("DeviceFiles::GetProviderToken: NULL return argument pointer"); + LOGW("DeviceFiles::GetProviderSessionToken: NULL return argument pointer"); return false; } std::string file_name = GetUsageInfoFileName(app_id); if (!FileExists(file_name) || GetFileSize(file_name) == 0) { - LOGW("DeviceFiles::GetProviderToken: empty file"); + LOGW("DeviceFiles::GetProviderSessionToken: empty file"); return false; } video_widevine_client::sdk::File file; if (!RetrieveHashedFile(file_name, &file)) { - LOGW("DeviceFiles::GetProviderToken: unable to parse file"); + LOGW("DeviceFiles::GetProviderSessionToken: unable to parse file"); return false; } @@ -511,15 +535,14 @@ bool DeviceFiles::GetProviderToken(const std::string& app_id, return false; } -bool DeviceFiles::DeleteUsageInfo(const std::string& app_id, +bool DeviceFiles::DeleteUsageInfo(const std::string& usage_info_file_name, const std::string& provider_session_token) { if (!initialized_) { LOGW("DeviceFiles::DeleteUsageInfo: not initialized"); return false; } video_widevine_client::sdk::File file; - std::string file_name = GetUsageInfoFileName(app_id); - if (!RetrieveHashedFile(file_name, &file)) return false; + if (!RetrieveHashedFile(usage_info_file_name, &file)) return false; UsageInfo* usage_info = file.mutable_usage_info(); int index = 0; @@ -548,11 +571,11 @@ bool DeviceFiles::DeleteUsageInfo(const std::string& app_id, std::string serialized_file; file.SerializeToString(&serialized_file); - return StoreFileWithHash(file_name, serialized_file); + return StoreFileWithHash(usage_info_file_name, serialized_file); } bool DeviceFiles::DeleteAllUsageInfoForApp( - const std::string& app_id, + const std::string& usage_info_file_name, std::vector* provider_session_tokens) { if (!initialized_) { LOGW("DeviceFiles::DeleteAllUsageInfoForApp: not initialized"); @@ -564,22 +587,21 @@ bool DeviceFiles::DeleteAllUsageInfoForApp( } provider_session_tokens->clear(); - std::string file_name = GetUsageInfoFileName(app_id); - if (!FileExists(file_name)) return true; + if (!FileExists(usage_info_file_name)) return true; video_widevine_client::sdk::File file; - if (RetrieveHashedFile(file_name, &file)) { + if (RetrieveHashedFile(usage_info_file_name, &file)) { for (int i = 0; i < file.usage_info().sessions_size(); ++i) { provider_session_tokens->push_back(file.usage_info().sessions(i).token()); } } else { LOGW("DeviceFiles::DeleteAllUsageInfoForApp: Unable to retrieve file"); } - return RemoveFile(file_name); + return RemoveFile(usage_info_file_name); } bool DeviceFiles::RetrieveUsageInfo( - const std::string& app_id, + const std::string& usage_info_file_name, std::vector >* usage_info) { if (!initialized_) { LOGW("DeviceFiles::RetrieveUsageInfo: not initialized"); @@ -593,14 +615,14 @@ bool DeviceFiles::RetrieveUsageInfo( return false; } - std::string file_name = GetUsageInfoFileName(app_id); - if (!FileExists(file_name) || GetFileSize(file_name) == 0) { + if (!FileExists(usage_info_file_name) || + GetFileSize(usage_info_file_name) == 0) { usage_info->resize(0); return true; } video_widevine_client::sdk::File file; - if (!RetrieveHashedFile(file_name, &file)) { + if (!RetrieveHashedFile(usage_info_file_name, &file)) { LOGW("DeviceFiles::RetrieveUsageInfo: Unable to parse file"); return false; } @@ -615,18 +637,19 @@ bool DeviceFiles::RetrieveUsageInfo( return true; } -bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id, +bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name, const std::string& provider_session_token, CdmKeyMessage* license_request, - CdmKeyResponse* license_response) { + CdmKeyResponse* license_response, + std::string* usage_entry, + uint32_t* usage_entry_number) { if (!initialized_) { LOGW("DeviceFiles::RetrieveUsageInfo: not initialized"); return false; } - std::string file_name = GetUsageInfoFileName(app_id); video_widevine_client::sdk::File file; - if (!RetrieveHashedFile(file_name, &file)) { + if (!RetrieveHashedFile(usage_info_file_name, &file)) { return false; } @@ -635,6 +658,9 @@ bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id, if (file.usage_info().sessions(index).token() == provider_session_token) { *license_request = file.usage_info().sessions(index).license_request(); *license_response = file.usage_info().sessions(index).license(); + *usage_entry = file.usage_info().sessions(index).usage_entry(); + *usage_entry_number = + file.usage_info().sessions(index).usage_entry_number(); return true; } } @@ -643,26 +669,32 @@ bool DeviceFiles::RetrieveUsageInfo(const std::string& app_id, } bool DeviceFiles::RetrieveUsageInfoByKeySetId( - const std::string& app_id, + const std::string& usage_info_file_name, const std::string& key_set_id, + std::string* provider_session_token, CdmKeyMessage* license_request, - CdmKeyResponse* license_response) { + CdmKeyResponse* license_response, + std::string* usage_entry, + uint32_t* usage_entry_number) { if (!initialized_) { LOGW("DeviceFiles::RetrieveUsageInfoByKeySetId: not initialized"); return false; } - std::string file_name = GetUsageInfoFileName(app_id); video_widevine_client::sdk::File file; - if (!RetrieveHashedFile(file_name, &file)) { + if (!RetrieveHashedFile(usage_info_file_name, &file)) { return false; } int index = 0; for (; index < file.usage_info().sessions_size(); ++index) { if (file.usage_info().sessions(index).key_set_id() == key_set_id) { + *provider_session_token = file.usage_info().sessions(index).token(); *license_request = file.usage_info().sessions(index).license_request(); *license_response = file.usage_info().sessions(index).license(); + *usage_entry = file.usage_info().sessions(index).usage_entry(); + *usage_entry_number = + file.usage_info().sessions(index).usage_entry_number(); return true; } } @@ -670,6 +702,190 @@ bool DeviceFiles::RetrieveUsageInfoByKeySetId( return false; } +bool DeviceFiles::StoreUsageInfo(const std::string& usage_info_file_name, + const std::vector& usage_data) { + if (!initialized_) { + LOGW("DeviceFiles::StoreUsageInfo: not initialized"); + return false; + } + + video_widevine_client::sdk::File file; + file.set_type(video_widevine_client::sdk::File::USAGE_INFO); + file.set_version(video_widevine_client::sdk::File::VERSION_1); + + UsageInfo* usage_info = file.mutable_usage_info(); + for (size_t i = 0; i < usage_data.size(); ++i) { + UsageInfo_ProviderSession* provider_session = usage_info->add_sessions(); + + provider_session->set_token(usage_data[i].provider_session_token.data(), + usage_data[i].provider_session_token.size()); + provider_session->set_license_request(usage_data[i].license_request.data(), + usage_data[i].license_request.size()); + provider_session->set_license(usage_data[i].license.data(), + usage_data[i].license.size()); + provider_session->set_key_set_id(usage_data[i].key_set_id.data(), + usage_data[i].key_set_id.size()); + provider_session->set_usage_entry(usage_data[i].usage_entry); + provider_session->set_usage_entry_number(usage_data[i].usage_entry_number); + } + + std::string serialized_file; + file.SerializeToString(&serialized_file); + return StoreFileWithHash(usage_info_file_name, serialized_file); +} + +bool DeviceFiles::UpdateUsageInfo(const std::string& usage_info_file_name, + const std::string& provider_session_token, + const CdmUsageData& usage_data) { + if (!initialized_) { + LOGW("DeviceFiles::UpdateUsageInfo: not initialized"); + return false; + } + + video_widevine_client::sdk::File file; + if (!FileExists(usage_info_file_name)) { + LOGW("DeviceFiles::UpdateUsageInfo: Usage file does not exist"); + return false; + } + + + if (!RetrieveHashedFile(usage_info_file_name, &file)) { + LOGW("DeviceFiles::UpdateUsageInfo: Unable to parse file"); + return false; + } + + int index = 0; + for (; index < file.usage_info().sessions_size(); ++index) { + if (file.usage_info().sessions(index).token() == provider_session_token) { + UsageInfo* usage_info = file.mutable_usage_info(); + UsageInfo_ProviderSession* provider_session = + usage_info->mutable_sessions(index); + provider_session->set_license_request(usage_data.license_request); + provider_session->set_license(usage_data.license); + provider_session->set_key_set_id(usage_data.key_set_id); + provider_session->set_usage_entry(usage_data.usage_entry); + provider_session->set_usage_entry_number(usage_data.usage_entry_number); + + std::string serialized_file; + file.SerializeToString(&serialized_file); + return StoreFileWithHash(usage_info_file_name, serialized_file); + } + } + + return false; +} + +bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name, + std::vector* usage_data) { + if (!initialized_) { + LOGW("DeviceFiles::RetrieveUsageInfo: not initialized"); + return false; + } + + if (usage_data == NULL) { + LOGW("DeviceFiles::RetrieveUsageInfo: usage_data not provided"); + return false; + } + + if (!FileExists(usage_info_file_name) || + GetFileSize(usage_info_file_name) == 0) { + usage_data->resize(0); + return true; + } + + video_widevine_client::sdk::File file; + if (!RetrieveHashedFile(usage_info_file_name, &file)) { + return false; + } + + usage_data->resize(file.usage_info().sessions_size()); + for (int i = 0; i < file.usage_info().sessions_size(); ++i) { + (*usage_data)[i].provider_session_token = + file.usage_info().sessions(i).token(); + (*usage_data)[i].license_request = + file.usage_info().sessions(i).license_request(); + (*usage_data)[i].license = file.usage_info().sessions(i).license(); + (*usage_data)[i].key_set_id = file.usage_info().sessions(i).key_set_id(); + (*usage_data)[i].usage_entry = file.usage_info().sessions(i).usage_entry(); + (*usage_data)[i].usage_entry_number = + file.usage_info().sessions(i).usage_entry_number(); + } + + return true; +} + +bool DeviceFiles::RetrieveUsageInfo(const std::string& usage_info_file_name, + const std::string& provider_session_token, + CdmUsageData* usage_data) { + if (!initialized_) { + LOGW("DeviceFiles::RetrieveUsageInfo: not initialized"); + return false; + } + + if (usage_data == NULL) { + LOGW("DeviceFiles::RetrieveUsageInfo: usage_data not provided"); + return false; + } + + video_widevine_client::sdk::File file; + if (!RetrieveHashedFile(usage_info_file_name, &file)) { + return false; + } + + int index = 0; + for (; index < file.usage_info().sessions_size(); ++index) { + if (file.usage_info().sessions(index).token() == provider_session_token) { + usage_data->provider_session_token = + file.usage_info().sessions(index).token(); + usage_data->license_request = + file.usage_info().sessions(index).license_request(); + usage_data->license = file.usage_info().sessions(index).license(); + usage_data->key_set_id = file.usage_info().sessions(index).key_set_id(); + usage_data->usage_entry = file.usage_info().sessions(index).usage_entry(); + usage_data->usage_entry_number = + file.usage_info().sessions(index).usage_entry_number(); + return true; + } + } + + return false; +} + +bool DeviceFiles::ListUsageInfoFiles( + std::vector* usage_info_file_names) { + if (!initialized_) { + LOGW("DeviceFiles::ListUsageInfoFiles: not initialized"); + return false; + } + + if (usage_info_file_names == NULL) { + LOGW("DeviceFiles::ListUsageInfoFiles: usage_info_file_names not provided"); + return false; + } + + // Get list of filenames + std::vector filenames; + if (!ListFiles(&filenames)) { + return false; + } + + // Scan list of all filenames and return only usage info filenames + usage_info_file_names->clear(); + for (size_t i = 0; i < filenames.size(); i++) { + std::string* name = &filenames[i]; + std::size_t pos_prefix = name->find(kUsageInfoFileNamePrefix); + std::size_t pos_suffix = name->find(kUsageInfoFileNameExt); + if (pos_prefix == std::string::npos || + pos_suffix == std::string::npos) { + // Skip this file - extension does not match + continue; + } + + usage_info_file_names->push_back(*name); + } + return true; +} + bool DeviceFiles::StoreHlsAttributes( const std::string& key_set_id, const CdmHlsMethod method, const std::vector& media_segment_iv) { @@ -767,6 +983,117 @@ bool DeviceFiles::DeleteHlsAttributes(const std::string& key_set_id) { return RemoveFile(key_set_id + kHlsAttributesFileNameExt); } +bool DeviceFiles::StoreUsageTableInfo( + const CdmUsageTableHeader& usage_table_header, + const std::vector& usage_entry_info) { + if (!initialized_) { + LOGW("DeviceFiles::StoreUsageTableInfo: not initialized"); + return false; + } + + // Fill in file information + video_widevine_client::sdk::File file; + + file.set_type(video_widevine_client::sdk::File::USAGE_TABLE_INFO); + file.set_version(video_widevine_client::sdk::File::VERSION_1); + + UsageTableInfo* usage_table_info = file.mutable_usage_table_info(); + usage_table_info->set_usage_table_header(usage_table_header); + for (size_t i = 0; i < usage_entry_info.size(); ++i) { + UsageTableInfo_UsageEntryInfo* info = + usage_table_info->add_usage_entry_info(); + info->set_key_set_id(usage_entry_info[i].key_set_id); + switch (usage_entry_info[i].storage_type) { + case kStorageLicense: + info->set_storage( + UsageTableInfo_UsageEntryInfo_UsageEntryStorage_LICENSE); + break; + case kStorageUsageInfo: + info->set_storage( + UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO); + info->set_usage_info_file_name( + usage_entry_info[i].usage_info_file_name); + break; + case kStorageTypeUnknown: + default: + info->set_storage( + UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN); + break; + } + } + + std::string serialized_file; + file.SerializeToString(&serialized_file); + + return StoreFileWithHash(GetUsageTableFileName(), serialized_file); +} + +bool DeviceFiles::RetrieveUsageTableInfo( + CdmUsageTableHeader* usage_table_header, + std::vector* usage_entry_info) { + if (!initialized_) { + LOGW("DeviceFiles::RetrieveUsageTableInfo: not initialized"); + return false; + } + + if (usage_table_header == NULL) { + LOGW( + "DeviceFiles::RetrieveUsageTableInfo: usage_table_header not provided"); + return false; + } + + if (usage_entry_info == NULL) { + LOGW("DeviceFiles::RetrieveUsageTableInfo: usage_entry_info not provided"); + return false; + } + + video_widevine_client::sdk::File file; + if (!RetrieveHashedFile(GetUsageTableFileName(), &file)) { + return false; + } + + if (file.type() != video_widevine_client::sdk::File::USAGE_TABLE_INFO) { + LOGW("DeviceFiles::RetrieveUsageTableInfo: Incorrect file type"); + return false; + } + + if (file.version() != video_widevine_client::sdk::File::VERSION_1) { + LOGW("DeviceFiles::RetrieveUsageTableInfo: Incorrect file version"); + return false; + } + + if (!file.has_usage_table_info()) { + LOGW("DeviceFiles::RetrieveUsageTableInfo: Usage table info not present"); + return false; + } + + const UsageTableInfo& usage_table_info = file.usage_table_info(); + + *usage_table_header = usage_table_info.usage_table_header(); + usage_entry_info->resize(usage_table_info.usage_entry_info_size()); + for (int i = 0; i < usage_table_info.usage_entry_info_size(); ++i) { + const UsageTableInfo_UsageEntryInfo& info = + usage_table_info.usage_entry_info(i); + (*usage_entry_info)[i].key_set_id = info.key_set_id(); + switch (info.storage()) { + case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_LICENSE: + (*usage_entry_info)[i].storage_type = kStorageLicense; + break; + case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_USAGE_INFO: + (*usage_entry_info)[i].storage_type = kStorageUsageInfo; + (*usage_entry_info)[i].usage_info_file_name = + info.usage_info_file_name(); + break; + case UsageTableInfo_UsageEntryInfo_UsageEntryStorage_UNKNOWN: + default: + (*usage_entry_info)[i].storage_type = kStorageTypeUnknown; + break; + } + } + + return true; +} + bool DeviceFiles::StoreFileWithHash(const std::string& name, const std::string& serialized_file) { // calculate SHA hash @@ -912,7 +1239,7 @@ bool DeviceFiles::FileExists(const std::string& name) { bool DeviceFiles::ListFiles(std::vector* names) { std::string path; if (!Properties::GetDeviceFilesBasePath(security_level_, &path)) { - LOGW("DeviceFiles::RemoveFile: Unable to get base path"); + LOGW("DeviceFiles::ListFiles: Unable to get base path"); return false; } return file_system_->List(path, names); @@ -944,6 +1271,10 @@ std::string DeviceFiles::GetCertificateFileName() { return kCertificateFileName; } +std::string DeviceFiles::GetUsageTableFileName() { + return kUsageTableFileName; +} + std::string DeviceFiles::GetHlsAttributesFileNameExtension() { return kHlsAttributesFileNameExt; } diff --git a/core/src/device_files.proto b/core/src/device_files.proto index eef367d6..02b86bdf 100644 --- a/core/src/device_files.proto +++ b/core/src/device_files.proto @@ -43,6 +43,8 @@ message License { // contains the playback_start_time we should use as an override. This is // ignored if there is no grace period. optional int64 grace_period_end_time = 11 [default = 0]; + optional bytes usage_entry = 12; + optional int64 usage_entry_number = 13; } message UsageInfo { @@ -51,13 +53,15 @@ message UsageInfo { optional bytes license_request = 2; optional bytes license = 3; optional bytes key_set_id = 4; + optional bytes usage_entry = 5; + optional int64 usage_entry_number = 6; } repeated ProviderSession sessions = 1; } message HlsAttributes { - enum Method { + enum Method { AES_128 = 1; SAMPLE_AES = 2; } @@ -65,12 +69,30 @@ message HlsAttributes { optional bytes media_segment_iv = 2; } +message UsageTableInfo { + message UsageEntryInfo { + enum UsageEntryStorage { + LICENSE = 1; + USAGE_INFO = 2; + UNKNOWN = 3; + } + + optional UsageEntryStorage storage = 1; + optional bytes key_set_id = 2; + optional bytes usage_info_file_name = 3; // hash of the app_id + } + + optional bytes usage_table_header = 1; + repeated UsageEntryInfo usage_entry_info = 2; +} + message File { enum FileType { DEVICE_CERTIFICATE = 1; LICENSE = 2; USAGE_INFO = 3; HLS_ATTRIBUTES = 4; + USAGE_TABLE_INFO = 5; } enum FileVersion { @@ -83,6 +105,7 @@ message File { optional License license = 4; optional UsageInfo usage_info = 5; optional HlsAttributes hls_attributes = 6; + optional UsageTableInfo usage_table_info = 7; } message HashedFile { diff --git a/core/src/initialization_data.cpp b/core/src/initialization_data.cpp index 28f9f021..a4748a59 100644 --- a/core/src/initialization_data.cpp +++ b/core/src/initialization_data.cpp @@ -7,7 +7,6 @@ #include "buffer_reader.h" #include "jsmn.h" -#include "license_protocol.pb.h" #include "log.h" #include "properties.h" #include "string_conversions.h" @@ -15,10 +14,6 @@ namespace { const char kKeyFormatVersionsSeparator = '/'; -const char kColon = ':'; -const char kDoubleQuote = '\"'; -const char kLeftBracket = '['; -const char kRightBracket = ']'; const std::string kBase64String = "base64,"; const uint32_t kFourCcCbc1 = 0x63626331; const uint32_t kFourCcCbcs = 0x63626373; @@ -594,4 +589,19 @@ std::vector InitializationData::ExtractKeyFormatVersions( return versions; } +// Extract the key id of the group master key used to generate sublicense data. +// Returns an empty string if not defined. +const std::string InitializationData::ExtractGroupMasterKeyId() const { + if (!is_cenc_) { + return ""; + } + + WidevinePsshData cenc_header; + if (!cenc_header.ParseFromString(data_)) { + return ""; + } + + return cenc_header.group_master_key_id(); +} + } // namespace wvcdm diff --git a/core/src/license.cpp b/core/src/license.cpp index 1283d1e7..3b3d758f 100644 --- a/core/src/license.cpp +++ b/core/src/license.cpp @@ -7,6 +7,7 @@ #include #include "clock.h" +#include "cdm_session.h" #include "crypto_key.h" #include "crypto_session.h" #include "device_files.h" @@ -35,7 +36,6 @@ const uint32_t kFourCcCbcs = 0x63626373; const uint32_t kFourCcLittleEndianCbc1 = 0x31636263; const uint32_t kFourCcLittleEndianCbcs = 0x73636263; const uint32_t kFourCcCenc = 0x63656e63; -const uint32_t kFourCcCens = 0x63656e73; } // namespace @@ -123,7 +123,7 @@ static std::vector ExtractContentKeys(const License& license) { case kFourCcLittleEndianCbcs: key.set_cipher_mode(kCipherModeCbc); break; - default: + default: key.set_cipher_mode(kCipherModeCtr); break; } @@ -149,11 +149,11 @@ static std::vector ExtractContentKeys(const License& license) { std::vector sub_session_keys = ExtractSubSessionKeys(license); // Match the track label from the key arrays and add sub_license_key_id to // the content key array. - LOGI("Received %d subsession keys", sub_session_keys.size()); + LOGV("Received %d subsession keys", sub_session_keys.size()); if (!sub_session_keys.empty()) { - for (int i = 0; i < key_array.size(); ++i) { + for (size_t i = 0; i < key_array.size(); ++i) { if (key_array[i].track_label().empty()) continue; - for (int x = 0; x < sub_session_keys.size(); ++x) { + for (size_t x = 0; x < sub_session_keys.size(); ++x) { if (sub_session_keys[x].track_label() == key_array[i].track_label()) { key_array[i].set_sub_session_key_id(sub_session_keys[x].key_id()); key_array[i].set_sub_session_key(sub_session_keys[x].key_data()); @@ -172,6 +172,7 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id) initialized_(false), renew_with_client_id_(false), is_offline_(false), + use_privacy_mode_(false), clock_(new Clock()) {} CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock) @@ -180,16 +181,18 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock) session_id_(session_id), initialized_(false), renew_with_client_id_(false), - is_offline_(false) { + is_offline_(false), + use_privacy_mode_(false) { clock_.reset(clock); } CdmLicense::~CdmLicense() {} bool CdmLicense::Init( - ServiceCertificate* service_certificate, const std::string& client_token, - CdmClientTokenType client_token_type, const std::string& device_id, - CryptoSession* session, PolicyEngine* policy_engine) { + const std::string& client_token, CdmClientTokenType client_token_type, + const std::string& device_id, bool use_privacy_mode, + const std::string& signed_service_certificate, CryptoSession* session, + PolicyEngine* policy_engine) { if (clock_.get() == NULL) { LOGE("CdmLicense::Init: clock parameter not provided"); return false; @@ -211,12 +214,24 @@ bool CdmLicense::Init( return false; } - service_certificate_ = service_certificate; + if (use_privacy_mode) { + if (!signed_service_certificate.empty()) { + if (service_certificate_.Init(signed_service_certificate) != NO_ERROR) + return false; + } + if (!service_certificate_.has_certificate() && + !Properties::allow_service_certificate_requests()) { + LOGE("CdmLicense::Init: Required service certificate not provided"); + return false; + } + } + client_token_ = client_token; client_token_type_ = client_token_type; device_id_ = device_id; crypto_session_ = session; policy_engine_ = policy_engine; + use_privacy_mode_ = use_privacy_mode; initialized_ = true; return true; } @@ -229,6 +244,12 @@ CdmResponseType CdmLicense::PrepareKeyRequest( LOGE("CdmLicense::PrepareKeyRequest: not initialized"); return LICENSE_PARSER_NOT_INITIALIZED_4; } + if (init_data.IsEmpty() && stored_init_data_.get()) { + InitializationData restored_init_data = *stored_init_data_; + stored_init_data_.reset(); + return PrepareKeyRequest(restored_init_data, license_type, app_parameters, + signed_request, server_url); + } if (!init_data.is_supported()) { LOGE("CdmLicense::PrepareKeyRequest: unsupported init data type (%s)", init_data.type().c_str()); @@ -247,12 +268,25 @@ CdmResponseType CdmLicense::PrepareKeyRequest( return INVALID_PARAMETERS_LIC_7; } - // If privacy mode, must have service certificate - if (Properties::UsePrivacyMode(session_id_) && - !service_certificate_->has_certificate()) { - LOGE("CdmLicense::PrepareKeyRequest: failure with privacy mode - " + // If privacy mode and no service certificate, depending on platform + // configuration, request service certificate or declare error + if (use_privacy_mode_ && !service_certificate_.has_certificate()) { + + if (!Properties::allow_service_certificate_requests()) { + LOGE("CdmLicense::PrepareKeyRequest: failure with privacy mode - " "no service certificate."); - return PRIVACY_MODE_ERROR_1; + return PRIVACY_MODE_ERROR_1; + } + + stored_init_data_.reset(new InitializationData(init_data)); + + if (!ServiceCertificate::GetRequest(signed_request)) { + LOGE("CdmLicense::PrepareKeyRequest: failed to prepare a service " + "certificated request"); + return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR; + } + + return KEY_MESSAGE; } std::string request_id; @@ -284,30 +318,30 @@ CdmResponseType CdmLicense::PrepareKeyRequest( // initialization data. std::vector embedded_key_data = init_data.ExtractEmbeddedKeys(); - for (int i = 0; i < embedded_key_data.size(); ++i) { + for (size_t i = 0; i < embedded_key_data.size(); ++i) { bool exists = false; if (!crypto_session_->GenerateSubSessionNonce( embedded_key_data[i].sub_session_key_id(), &exists, &nonce)) { - return LICENSE_REQUEST_NONCE_GENERATION_ERROR; - } - if (exists) { - SignedMessage signed_sub_license; - License_KeyContainer keyc; - - // Parse the sub license for this track to extract the label. - if (!signed_sub_license.ParseFromString(embedded_key_data[i].key_msg()) || - !keyc.ParseFromString(signed_sub_license.msg()) || - keyc.track_label().empty()) { - return LICENSE_REQUEST_INVALID_SUBLICENSE; + if (exists) { + return LICENSE_REQUEST_NONCE_GENERATION_ERROR; } - - LicenseRequest::SubSessionData* sub_session_data = - license_request.add_sub_session_data(); - sub_session_data->set_sub_session_key_id( - embedded_key_data[i].sub_session_key_id()); - sub_session_data->set_nonce(nonce); - sub_session_data->set_track_label(keyc.track_label()); } + SignedMessage signed_sub_license; + License_KeyContainer keyc; + + // Parse the sub license for this track to extract the label. + if (!signed_sub_license.ParseFromString(embedded_key_data[i].key_msg()) || + !keyc.ParseFromString(signed_sub_license.msg()) || + keyc.track_label().empty()) { + return LICENSE_REQUEST_INVALID_SUBLICENSE; + } + + LicenseRequest::SubSessionData* sub_session_data = + license_request.add_sub_session_data(); + sub_session_data->set_sub_session_key_id( + embedded_key_data[i].sub_session_key_id()); + sub_session_data->set_nonce(nonce); + sub_session_data->set_track_label(keyc.track_label()); } license_request.set_protocol_version(video_widevine::VERSION_2_1); @@ -316,8 +350,7 @@ CdmResponseType CdmLicense::PrepareKeyRequest( std::string serialized_license_req; license_request.SerializeToString(&serialized_license_req); - if (Properties::use_certificates_as_identification()) - key_request_ = serialized_license_req; + key_request_ = serialized_license_req; // Derive signing and encryption keys and construct signature. std::string license_request_signature; @@ -347,7 +380,8 @@ CdmResponseType CdmLicense::PrepareKeyRequest( CdmResponseType CdmLicense::PrepareKeyUpdateRequest( bool is_renewal, const CdmAppParameterMap& app_parameters, - CdmKeyMessage* signed_request, std::string* server_url) { + CdmSession* cdm_session, CdmKeyMessage* signed_request, + std::string* server_url) { if (!initialized_) { LOGE("CdmLicense::PrepareKeyUpdateRequest: not initialized"); return LICENSE_PARSER_NOT_INITIALIZED_1; @@ -367,8 +401,7 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest( } if (renew_with_client_id_) { - if (Properties::UsePrivacyMode(session_id_) && - !service_certificate_->has_certificate()) { + if (use_privacy_mode_ && !service_certificate_.has_certificate()) { LOGE("CdmLicense::PrepareKeyUpdateRequest: failure with privacy mode - " "no service certificate."); return PRIVACY_MODE_ERROR_2; @@ -403,6 +436,13 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest( if (NO_ERROR != status) return status; } + // TODO(rfrias): Refactor to avoid needing to call CdmSession + if (cdm_session && + cdm_session->get_usage_support_type() == kUsageEntrySupport) { + CdmResponseType status = cdm_session->UpdateUsageEntryInformation(); + if (NO_ERROR != status) return status; + } + std::string usage_report; CdmResponseType status = crypto_session_->GenerateUsageReport( provider_session_token_, &usage_report, &usage_duration_status, @@ -483,16 +523,26 @@ CdmResponseType CdmLicense::HandleKeyResponse( return INVALID_LICENSE_RESPONSE; } - switch (signed_response.type()) { - case SignedMessage::LICENSE: - break; - case SignedMessage::ERROR_RESPONSE: - return HandleKeyErrorResponse(signed_response); - default: - LOGE( - "CdmLicense::HandleKeyResponse: unrecognized signed message type: %d", - signed_response.type()); - return INVALID_LICENSE_TYPE; + if (use_privacy_mode_ && + Properties::allow_service_certificate_requests() && + signed_response.type() == SignedMessage::SERVICE_CERTIFICATE) { + std::string signed_certificate; + CdmResponseType status = + ServiceCertificate::ParseResponse(license_response, + &signed_certificate); + if (status != NO_ERROR) return status; + + status = service_certificate_.Init(signed_certificate); + return (status == NO_ERROR) ? NEED_KEY : status; + } + + if (signed_response.type() == SignedMessage::ERROR_RESPONSE) + return HandleKeyErrorResponse(signed_response); + + if (signed_response.type() != SignedMessage::LICENSE) { + LOGE("CdmLicense::HandleKeyResponse: unrecognized signed message type: %d", + signed_response.type()); + return INVALID_LICENSE_TYPE; } if (!signed_response.has_signature()) { @@ -506,15 +556,13 @@ CdmResponseType CdmLicense::HandleKeyResponse( return LICENSE_RESPONSE_PARSE_ERROR_1; } - if (Properties::use_certificates_as_identification()) { - if (!signed_response.has_session_key()) { - LOGE("CdmLicense::HandleKeyResponse: no session keys present"); - return SESSION_KEYS_NOT_FOUND; - } - if (!crypto_session_->GenerateDerivedKeys(key_request_, - signed_response.session_key())) - return GENERATE_DERIVED_KEYS_ERROR; + if (!signed_response.has_session_key()) { + LOGE("CdmLicense::HandleKeyResponse: no session keys present"); + return SESSION_KEYS_NOT_FOUND; } + if (!crypto_session_->GenerateDerivedKeys(key_request_, + signed_response.session_key())) + return GENERATE_DERIVED_KEYS_ERROR; // Extract mac key std::string mac_key_iv; @@ -544,17 +592,17 @@ CdmResponseType CdmLicense::HandleKeyResponse( return NO_CONTENT_KEY; } + if (license.has_srm_update()) crypto_session_->LoadSrm(license.srm_update()); + if (license.id().type() == video_widevine::OFFLINE && license.policy().can_persist()) is_offline_ = true; - LOGV("Get Provider_session_token:"); - if (license.id().has_provider_session_token()) { + if (license.id().has_provider_session_token()) provider_session_token_ = license.id().provider_session_token(); - LOGV("Provider_session_token=%s", provider_session_token_.c_str()); - } else { - LOGV("NO Provider_session_token"); - } + + LOGV("provider_session_token: %s", provider_session_token_.empty() ? + "N/A" : provider_session_token_.c_str()); if (license.policy().has_renewal_server_url()) { server_url_ = license.policy().renewal_server_url(); @@ -564,10 +612,9 @@ CdmResponseType CdmLicense::HandleKeyResponse( renew_with_client_id_ = license.policy().always_include_client_id(); } - CdmResponseType resp = NO_ERROR; - resp = crypto_session_->LoadKeys( + CdmResponseType resp = crypto_session_->LoadKeys( signed_response.msg(), signed_response.signature(), mac_key_iv, mac_key, - key_array, provider_session_token_); + key_array, provider_session_token_, license.srm_requirement()); if (KEY_ADDED == resp) { loaded_keys_.clear(); @@ -667,7 +714,7 @@ CdmResponseType CdmLicense::HandleSubLicense( std::set loaded_keys; // Build a license with the rotated keys. License license; - for (int i = 0; i < subkeys.size(); ++i) { + for (size_t i = 0; i < subkeys.size(); ++i) { SignedMessage sm; if (!sm.ParseFromString(subkeys[i].key_msg())) { return LICENSE_REQUEST_INVALID_SUBLICENSE; @@ -676,18 +723,30 @@ CdmResponseType CdmLicense::HandleSubLicense( if (!keyc.ParseFromString(sm.msg())) { return LICENSE_REQUEST_INVALID_SUBLICENSE; } + size_t length; std::vector keys; keys.resize(1); keys[0].set_key_id(keyc.id()); - keys[0].set_key_data(keyc.key()); + + // Strip PKCS#5 padding from sublicense content keys. + // TODO(jfore): Refactor this to use ExtractContentKeys. + if (keyc.key().size() > KEY_SIZE) { + length = keyc.key().size() - KEY_SIZE; + } else { + length = 0; + } + keys[0].set_key_data(keyc.key().substr(0, length)); keys[0].set_key_data_iv(keyc.iv()); keys[0].set_key_control(keyc.key_control().key_control_block()); keys[0].set_key_control_iv(keyc.key_control().iv()); keys[0].set_track_label(keyc.track_label()); + //TODO: passing empty cipher_mode and srm_req params - OK? CdmResponseType result = crypto_session_->LoadKeys( sm.msg(), sm.signature(), std::string(), std::string(), keys, - std::string()); + std::string(), std::string()); if (result != KEY_ADDED) { + LOGE("CdmLicense::HandleSubLicense: LoadKeys() call failed, result=%d", + result); return result; } loaded_keys.insert(keyc.id()); @@ -703,7 +762,8 @@ bool CdmLicense::RestoreOfflineLicense( const CdmKeyMessage& license_request, const CdmKeyResponse& license_response, const CdmKeyResponse& license_renewal_response, int64_t playback_start_time, - int64_t last_playback_time, int64_t grace_period_end_time) { + int64_t last_playback_time, int64_t grace_period_end_time, + CdmSession* cdm_session) { if (license_request.empty() || license_response.empty()) { LOGE( "CdmLicense::RestoreOfflineLicense: key_request or response empty: " @@ -726,13 +786,7 @@ bool CdmLicense::RestoreOfflineLicense( return false; } - if (Properties::use_certificates_as_identification()) { - key_request_ = signed_request.msg(); - } else { - if (!crypto_session_->GenerateDerivedKeys(signed_request.msg())) { - return false; - } - } + key_request_ = signed_request.msg(); CdmResponseType sts = HandleKeyResponse(license_response); @@ -745,9 +799,16 @@ bool CdmLicense::RestoreOfflineLicense( } if (!provider_session_token_.empty()) { + if (cdm_session && + cdm_session->get_usage_support_type() == kUsageEntrySupport) { + CdmResponseType status = cdm_session->UpdateUsageEntryInformation(); + if (NO_ERROR != status) return false; + } + std::string usage_report; CryptoSession::UsageDurationStatus usage_duration_status = CryptoSession::kUsageDurationsInvalid; + int64_t seconds_since_started, seconds_since_last_played; sts = crypto_session_->GenerateUsageReport( provider_session_token_, &usage_report, &usage_duration_status, @@ -803,13 +864,7 @@ bool CdmLicense::RestoreLicenseForRelease( return false; } - if (Properties::use_certificates_as_identification()) { - key_request_ = signed_request.msg(); - } else { - if (!crypto_session_->GenerateDerivedKeys(signed_request.msg())) { - return false; - } - } + key_request_ = signed_request.msg(); SignedMessage signed_response; if (!signed_response.ParseFromString(license_response)) { @@ -848,19 +903,15 @@ bool CdmLicense::RestoreLicenseForRelease( if (license.policy().has_always_include_client_id()) renew_with_client_id_ = license.policy().always_include_client_id(); - if (Properties::use_certificates_as_identification()) { - if (!signed_response.has_session_key()) { - LOGE("CdmLicense::RestoreLicenseForRelease: no session keys present"); - return false; - } + if (!signed_response.has_session_key()) { + LOGE("CdmLicense::RestoreLicenseForRelease: no session keys present"); + return false; + } - if (license.id().has_provider_session_token()) { - if (!crypto_session_->GenerateDerivedKeys(key_request_, - signed_response.session_key())) - return false; - } else { - return KEY_ADDED == HandleKeyResponse(license_response); - } + if (license.id().has_provider_session_token()) { + if (!crypto_session_->GenerateDerivedKeys(key_request_, + signed_response.session_key())) + return false; } if (license.policy().has_renewal_server_url()) @@ -876,6 +927,44 @@ bool CdmLicense::IsKeyLoaded(const KeyId& key_id) { return loaded_keys_.find(key_id) != loaded_keys_.end(); } +bool CdmLicense::ExtractProviderSessionToken( + const CdmKeyResponse& license_response, + std::string* provider_session_token) { + if (license_response.empty()) { + LOGE("CdmLicense::ExtractProviderSessionToken: empty license response"); + return false; + } + + SignedMessage signed_response; + if (!signed_response.ParseFromString(license_response)) { + LOGE( + "CdmLicense::ExtractProviderSessionToken: unable to parse signed " + "license response"); + return false; + } + + if (signed_response.type() != SignedMessage::LICENSE) { + LOGE("CdmLicense::ExtractProviderSessionToken: unrecognized signed message " + "type: %d", signed_response.type()); + return false; + } + + License license; + if (!license.ParseFromString(signed_response.msg())) { + LOGE("CdmLicense::ExtractProviderSessionToken: unable to parse license " + "response"); + return false; + } + + if (license.id().has_provider_session_token() && + !license.id().provider_session_token().empty()) { + *provider_session_token = license.id().provider_session_token(); + return true; + } + + return false; +} + CdmResponseType CdmLicense::HandleKeyErrorResponse( const SignedMessage& signed_message) { LicenseError license_error; @@ -969,7 +1058,7 @@ CdmResponseType CdmLicense::PrepareClientId( client_info = client_id->add_client_info(); client_info->set_name(kDeviceIdKey); client_info->set_value(b2a_hex(device_id_)); - } else if (crypto_session_->GetDeviceUniqueId(&value)) { + } else if (crypto_session_->GetInternalDeviceUniqueId(&value)) { client_info = client_id->add_client_info(); client_info->set_name(kDeviceIdKey); client_info->set_value(value); @@ -1041,16 +1130,36 @@ CdmResponseType CdmLicense::PrepareClientId( } } + CryptoSession::SupportedCertificateTypes supported_certs; + if (crypto_session_->GetSupportedCertificateTypes(&supported_certs)) { + if (supported_certs.rsa_2048_bit) { + client_capabilities->add_supported_certificate_key_type( + video_widevine:: + ClientIdentification_ClientCapabilities_CertificateKeyType_RSA_2048); + } + if (supported_certs.rsa_3072_bit) { + client_capabilities->add_supported_certificate_key_type( + video_widevine:: + ClientIdentification_ClientCapabilities_CertificateKeyType_RSA_3072); + } + } + + client_capabilities->set_can_update_srm( + crypto_session_->IsSrmUpdateSupported()); + uint16_t srm_version; + if (crypto_session_->GetSrmVersion(&srm_version)) + client_capabilities->set_srm_version(srm_version); + if (Properties::UsePrivacyMode(session_id_)) { - if (service_certificate_->certificate().empty()) { + if (!service_certificate_.has_certificate()) { LOGE("CdmLicense::PrepareClientId: Service Certificate not staged"); return PRIVACY_MODE_ERROR_3; } EncryptedClientIdentification* encrypted_client_id = license_request->mutable_encrypted_client_id(); CdmResponseType status; - status = service_certificate_->EncryptClientId(crypto_session_, client_id, - encrypted_client_id); + status = service_certificate_.EncryptClientId(crypto_session_, client_id, + encrypted_client_id); if (NO_ERROR == status) { license_request->clear_client_id(); } else { diff --git a/core/src/license_protocol.proto b/core/src/license_protocol.proto index 9d432c72..a0893720 100644 --- a/core/src/license_protocol.proto +++ b/core/src/license_protocol.proto @@ -218,9 +218,13 @@ message License { // 4cc code specifying the CENC protection scheme as defined in the CENC 3.0 // specification. Propagated from Widevine PSSH box. Optional. optional uint32 protection_scheme = 7; - // Minimum HDCP SRM version needed for using this key on content sent to - // HDCP enabled outputs. - optional uint32 min_hdcp_srm_version = 8; + // 8 byte verification field "HDCPDATA" followed by unsigned 32 bit minimum + // HDCP SRM version. Additional details can be found in + // Widevine Modular DRM Security Integration Guide for CENC. + optional bytes srm_requirement = 8; + // If present this contains a signed SRM file that should be installed + // on the client device. + optional bytes srm_update = 9; } enum ProtocolVersion { @@ -527,6 +531,11 @@ message ClientIdentification { HDCP_NO_DIGITAL_OUTPUT = 0xff; } + enum CertificateKeyType { + RSA_2048 = 0; + RSA_3072 = 1; + } + optional bool client_token = 1 [default = false]; optional bool session_token = 2 [default = false]; optional bool video_resolution_constraints = 3 [default = false]; @@ -536,6 +545,12 @@ message ClientIdentification { // storing the generation number in secure memory. For Details, see: // Widevine Modular DRM Security Integration Guide for CENC optional bool anti_rollback_usage_table = 6 [default = false]; + // The client shall report |srm_version| if available. + optional uint32 srm_version = 7; + // A device may have SRM data, and report a version, but may not be capable + // of updating SRM data. + optional bool can_update_srm = 8 [default = false]; + repeated CertificateKeyType supported_certificate_key_type = 9; } // Type of factory-provisioned device root of trust. Optional. @@ -753,6 +768,10 @@ message WidevinePsshData { // Required when using content keys that are embedded in content. repeated SubLicense sub_licenses = 11; + + // Key ID used to identify the group master key License Server is supposed + // to use to generate group license. + optional string group_master_key_id = 12; } // Signed device certificate definition. diff --git a/core/src/oemcrypto_adapter_static.cpp b/core/src/oemcrypto_adapter_static.cpp index c561a6f1..3a8f890f 100644 --- a/core/src/oemcrypto_adapter_static.cpp +++ b/core/src/oemcrypto_adapter_static.cpp @@ -80,4 +80,49 @@ OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(SecurityLevel) { return ::OEMCrypto_GetProvisioningMethod(); } +uint32_t OEMCrypto_SupportedCertificates(SecurityLevel level) { + return ::OEMCrypto_SupportedCertificates(); +} + +OEMCryptoResult OEMCrypto_CreateUsageTableHeader(SecurityLevel level, + uint8_t* header_buffer, + size_t* header_buffer_length) { + return ::OEMCrypto_CreateUsageTableHeader(header_buffer, + header_buffer_length); +} + +OEMCryptoResult OEMCrypto_LoadUsageTableHeader(SecurityLevel level, + const uint8_t* buffer, + size_t buffer_length) { + return ::OEMCrypto_LoadUsageTableHeader(buffer, buffer_length); +} + +OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(SecurityLevel level, + uint32_t new_table_size, + uint8_t* header_buffer, + size_t* header_buffer_length) { + return ::OEMCrypto_ShrinkUsageTableHeader(new_table_size, header_buffer, + header_buffer_length); +} + +OEMCryptoResult OEMCrypto_CreateOldUsageEntry(SecurityLevel level, + uint64_t time_since_license_received, + uint64_t time_since_first_decrypt, + uint64_t time_since_last_decrypt, + OEMCrypto_Usage_Entry_Status status, + uint8_t *server_mac_key, + uint8_t *client_mac_key, + const uint8_t* pst, + size_t pst_length) { + return ::OEMCrypto_CreateOldUsageEntry(time_since_license_received, + time_since_first_decrypt, + time_since_last_decrypt, + status, + server_mac_key, + client_mac_key, + pst, + pst_length); +} + + } // namespace wvcdm diff --git a/core/src/oemcrypto_adapter_static_v11.cpp b/core/src/oemcrypto_adapter_static_v11.cpp index 9cad8a1e..9f7822e7 100644 --- a/core/src/oemcrypto_adapter_static_v11.cpp +++ b/core/src/oemcrypto_adapter_static_v11.cpp @@ -8,6 +8,7 @@ // to allow an older oemcrypto implementation to be linked with CDM. #include "OEMCryptoCENC.h" +#include "oemcrypto_adapter.h" extern "C" OEMCryptoResult OEMCrypto_DecryptCTR_V10( OEMCrypto_SESSION session, const uint8_t* data_addr, size_t data_length, @@ -43,7 +44,7 @@ extern "C" OEMCryptoResult OEMCrypto_DecryptCENC( subsample_flags); } -extern "C" OEMCryptoResult OEMCrypto_LoadKeys( +extern "C" OEMCryptoResult OEMCrypto_LoadKeys_V11_or_V12( OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, const uint8_t* signature, size_t signature_length, const uint8_t* enc_mac_keys_iv, const uint8_t* enc_mac_keys, diff --git a/core/src/oemcrypto_adapter_static_v13.cpp b/core/src/oemcrypto_adapter_static_v13.cpp new file mode 100644 index 00000000..a186470b --- /dev/null +++ b/core/src/oemcrypto_adapter_static_v13.cpp @@ -0,0 +1,102 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Wrapper of OEMCrypto APIs for platforms that support Level 1 only. +// This should be used when liboemcrypto.so is linked with the CDM code at +// compile time. +// +// Defines APIs introduced in newer version (v13) that are not available in v12. +// This allows an older oemcrypto implementation to be linked with CDM. + +#include "OEMCryptoCENC.h" +#include "oemcrypto_adapter.h" + +extern "C" { + +// Assume only 2048 bit certificates are supported +uint32_t OEMCrypto_SupportedCertificates() { + return OEMCrypto_Supports_RSA_2048bit; +} + +// SRM support is not available before v13. +bool OEMCrypto_IsSRMUpdateSupported() { + return false; +} + +OEMCryptoResult OEMCrypto_GetCurrentSRMVersion(uint16_t*) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t*, size_t) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_RemoveSRM() { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +// Large Usage Table support is not available before v13. +OEMCryptoResult OEMCrypto_CreateUsageTableHeader(uint8_t*, size_t*) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_LoadUsageTableHeader(const uint8_t*, size_t) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_CreateNewUsageEntry(OEMCrypto_SESSION, uint32_t*) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION, uint32_t, + const uint8_t*, size_t) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_UpdateUsageEntry(OEMCrypto_SESSION, uint8_t*, + size_t*, uint8_t*, size_t*) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +// Call V12 OEMCrypto_DeactivateUsageEntry - omit session param. +OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION, + const uint8_t* pst, + size_t pst_length) { + return OEMCrypto_DeactivateUsageEntry_V12(pst, pst_length); +} + +// Call V12 OEMCrypto_LoadKeys - omit srm_requirement param. +OEMCryptoResult OEMCrypto_LoadKeys( + OEMCrypto_SESSION session, const uint8_t* message, size_t message_length, + const uint8_t* signature, size_t signature_length, + const uint8_t* enc_mac_keys_iv, const uint8_t* enc_mac_keys, + size_t num_keys, const OEMCrypto_KeyObject* key_array, const uint8_t* pst, + size_t pst_length, const uint8_t* /* srm_requirement */) { + + return OEMCrypto_LoadKeys_V11_or_V12( + session, message, message_length, signature, signature_length, + enc_mac_keys_iv, enc_mac_keys, num_keys, key_array, pst, pst_length); +} + +OEMCryptoResult OEMCrypto_CreateOldUsageEntry( + uint64_t, uint64_t, uint64_t, OEMCrypto_Usage_Entry_Status, + uint8_t*, uint8_t*, const uint8_t*, size_t) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +// NOTE: OEMCrypto_DeleteOldUsageTable() uses OEMCrypto_DeleteUsageTable() +// entry point. + +OEMCryptoResult OEMCrypto_CopyOldUsageEntry( + OEMCrypto_SESSION, const uint8_t*pst, size_t) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_MoveEntry(OEMCrypto_SESSION, uint32_t) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(uint32_t, uint8_t*, size_t*) { + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} + +} // extern "C" diff --git a/core/src/oemcrypto_adapter_static_v9.cpp b/core/src/oemcrypto_adapter_static_v9.cpp index 4907348c..a77591e8 100644 --- a/core/src/oemcrypto_adapter_static_v9.cpp +++ b/core/src/oemcrypto_adapter_static_v9.cpp @@ -52,8 +52,8 @@ extern "C" OEMCryptoResult OEMCrypto_UpdateUsageTable() { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } -extern "C" OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t* pst, - size_t pst_length) { +extern "C" OEMCryptoResult OEMCrypto_DeactivateUsageEntry_V12(const uint8_t* pst, + size_t pst_length) { return OEMCrypto_ERROR_NOT_IMPLEMENTED; } @@ -72,6 +72,6 @@ extern "C" OEMCryptoResult OEMCrypto_DeleteUsageEntry( return OEMCrypto_ERROR_NOT_IMPLEMENTED; } -extern "C" OEMCryptoResult OEMCrypto_DeleteUsageTable(){ +extern "C" OEMCryptoResult OEMCrypto_DeleteOldUsageTable(){ return OEMCrypto_ERROR_NOT_IMPLEMENTED; } diff --git a/core/src/policy_engine.cpp b/core/src/policy_engine.cpp index fb0ff28d..84673380 100644 --- a/core/src/policy_engine.cpp +++ b/core/src/policy_engine.cpp @@ -14,6 +14,13 @@ using video_widevine::License; +namespace { + +const int kCdmPolicyTimerDurationSeconds = 1; +const int kClockSkewDelta = 5; // seconds + +} // namespace + namespace wvcdm { PolicyEngine::PolicyEngine(CdmSessionId session_id, @@ -28,6 +35,7 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id, last_expiry_time_set_(false), was_expired_on_load_(false), next_renewal_time_(0), + last_recorded_current_time_(0), session_id_(session_id), event_listener_(event_listener), license_keys_(new LicenseKeys), @@ -81,7 +89,8 @@ void PolicyEngine::CheckDeviceHdcpStatus() { } void PolicyEngine::OnTimerEvent() { - int64_t current_time = clock_->GetCurrentTime(); + last_recorded_current_time_ += kCdmPolicyTimerDurationSeconds; + int64_t current_time = GetCurrentTime(); // If we have passed the grace period, the expiration will update. if (grace_period_end_time_ == 0 && HasPlaybackStarted(current_time)) { @@ -202,7 +211,7 @@ void PolicyEngine::UpdateLicense(const License& license) { license_start_time_ = license.license_start_time(); next_renewal_time_ = license_start_time_ + policy_.renewal_delay_seconds(); - int64_t current_time = clock_->GetCurrentTime(); + int64_t current_time = GetCurrentTime(); if (!policy_.can_play() || HasLicenseOrPlaybackDurationExpired(current_time)) { license_state_ = kLicenseStateExpired; @@ -227,7 +236,7 @@ void PolicyEngine::BeginDecryption() { case kLicenseStateCanPlay: case kLicenseStateNeedRenewal: case kLicenseStateWaitingLicenseUpdate: - playback_start_time_ = clock_->GetCurrentTime(); + playback_start_time_ = GetCurrentTime(); last_playback_time_ = playback_start_time_; if (policy_.play_start_grace_period_seconds() == 0) grace_period_end_time_ = playback_start_time_; @@ -247,7 +256,7 @@ void PolicyEngine::BeginDecryption() { } void PolicyEngine::DecryptionEvent() { - last_playback_time_ = clock_->GetCurrentTime(); + last_playback_time_ = GetCurrentTime(); } void PolicyEngine::NotifyResolution(uint32_t width, uint32_t height) { @@ -261,7 +270,7 @@ void PolicyEngine::NotifySessionExpiration() { CdmResponseType PolicyEngine::Query(CdmQueryMap* query_response) { std::stringstream ss; - int64_t current_time = clock_->GetCurrentTime(); + int64_t current_time = GetCurrentTime(); if (license_state_ == kLicenseStateInitial) { query_response->clear(); @@ -302,7 +311,7 @@ CdmResponseType PolicyEngine::QueryKeyAllowedUsage( bool PolicyEngine::GetSecondsSinceStarted(int64_t* seconds_since_started) { if (playback_start_time_ == 0) return false; - *seconds_since_started = clock_->GetCurrentTime() - playback_start_time_; + *seconds_since_started = GetCurrentTime() - playback_start_time_; return (*seconds_since_started >= 0) ? true : false; } @@ -310,13 +319,15 @@ bool PolicyEngine::GetSecondsSinceLastPlayed( int64_t* seconds_since_last_played) { if (last_playback_time_ == 0) return false; - *seconds_since_last_played = clock_->GetCurrentTime() - last_playback_time_; + *seconds_since_last_played = GetCurrentTime() - last_playback_time_; return (*seconds_since_last_played >= 0) ? true : false; } int64_t PolicyEngine::GetLicenseOrPlaybackDurationRemaining() { - const int64_t current_time = clock_->GetCurrentTime(); - const int64_t expiry_time = GetExpiryTime(current_time, /* is_load */ false); + const int64_t current_time = GetCurrentTime(); + const int64_t expiry_time = + GetExpiryTime(current_time, + /* ignore_soft_enforce_playback_duration */ false); if (expiry_time == NEVER_EXPIRES) return LLONG_MAX; if (expiry_time < current_time) return 0; return expiry_time - current_time; @@ -337,8 +348,10 @@ void PolicyEngine::RestorePlaybackTimes(int64_t playback_start_time, playback_start_time_ = grace_period_end_time; } - const int64_t current_time = clock_->GetCurrentTime(); - const int64_t expiry_time = GetExpiryTime(current_time, /* is_load */ true); + const int64_t current_time = GetCurrentTime(); + const int64_t expiry_time = + GetExpiryTime(current_time, + /* ignore_soft_enforce_playback_duration */ true); was_expired_on_load_ = expiry_time != NEVER_EXPIRES && expiry_time < current_time; @@ -351,7 +364,9 @@ void PolicyEngine::UpdateRenewalRequest(int64_t current_time) { } bool PolicyEngine::HasLicenseOrPlaybackDurationExpired(int64_t current_time) { - const int64_t expiry_time = GetExpiryTime(current_time, /* is_load */ false); + const int64_t expiry_time = + GetExpiryTime(current_time, + /* ignore_soft_enforce_playback_duration */ false); return expiry_time != NEVER_EXPIRES && expiry_time <= current_time; } @@ -374,13 +389,15 @@ int64_t PolicyEngine::GetRentalExpiryTime() { return std::min(hard_limit, expiry_time); } -int64_t PolicyEngine::GetExpiryTime(int64_t current_time, bool is_load) { +int64_t PolicyEngine::GetExpiryTime( + int64_t current_time, + bool ignore_soft_enforce_playback_duration) { if (!HasPlaybackStarted(current_time)) return GetRentalExpiryTime(); const int64_t hard_limit = GetHardLicenseExpiryTime(); if (policy_.playback_duration_seconds() == 0) return hard_limit; - if (!is_load && !was_expired_on_load_ && + if (!ignore_soft_enforce_playback_duration && !was_expired_on_load_ && policy_.soft_enforce_playback_duration()) { return hard_limit; } @@ -401,8 +418,10 @@ int64_t PolicyEngine::GetLicenseOrRentalDurationRemaining( const int64_t license_expiry_time = GetRentalExpiryTime(); if (license_expiry_time == NEVER_EXPIRES) return LLONG_MAX; if (license_expiry_time < current_time) return 0; - return std::min(license_expiry_time - current_time, - policy_.license_duration_seconds()); + const int64_t policy_license_duration = policy_.license_duration_seconds(); + if (policy_license_duration == NEVER_EXPIRES) + return license_expiry_time - current_time; + return std::min(license_expiry_time - current_time, policy_license_duration); } int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) { @@ -414,8 +433,9 @@ int64_t PolicyEngine::GetPlaybackDurationRemaining(int64_t current_time) { const int64_t playback_expiry_time = playback_duration + playback_start_time_; if (playback_expiry_time < current_time) return 0; + const int64_t policy_playback_duration = policy_.playback_duration_seconds(); return std::min(playback_expiry_time - current_time, - policy_.playback_duration_seconds()); + policy_playback_duration); } bool PolicyEngine::HasRenewalDelayExpired(int64_t current_time) { @@ -457,7 +477,9 @@ void PolicyEngine::NotifyKeysChange(CdmKeyStatus new_status) { } void PolicyEngine::NotifyExpirationUpdate(int64_t current_time) { - const int64_t expiry_time = GetExpiryTime(current_time, /* is_load */ false); + const int64_t expiry_time = + GetExpiryTime(current_time, + /* ignore_soft_enforce_playback_duration */ false); if (!last_expiry_time_set_ || expiry_time != last_expiry_time_) { last_expiry_time_ = expiry_time; if (event_listener_) @@ -466,6 +488,15 @@ void PolicyEngine::NotifyExpirationUpdate(int64_t current_time) { last_expiry_time_set_ = true; } +int64_t PolicyEngine::GetCurrentTime() { + int64_t current_time = clock_->GetCurrentTime(); + if (current_time + kClockSkewDelta < last_recorded_current_time_) + current_time = last_recorded_current_time_; + else + last_recorded_current_time_ = current_time; + return current_time; +} + void PolicyEngine::set_clock(Clock* clock) { clock_.reset(clock); } } // namespace wvcdm diff --git a/core/src/privacy_crypto_apple.cpp b/core/src/privacy_crypto_apple.cpp index 5e89483e..9e982fac 100644 --- a/core/src/privacy_crypto_apple.cpp +++ b/core/src/privacy_crypto_apple.cpp @@ -56,8 +56,6 @@ SecKeyRef ImportPublicKey(const std::string& key) { kSecAttrKeyTypeRSA); CFDictionarySetValue(deleteAttributes.get(), kSecAttrApplicationTag, peerData.get()); - CFDictionarySetValue(deleteAttributes.get(), kSecAttrAccessible, - kSecAttrAccessibleAfterFirstUnlock); SecItemDelete(deleteAttributes.get()); // Create attributes to add to the keystore. @@ -72,10 +70,10 @@ SecKeyRef ImportPublicKey(const std::string& key) { keyData.get()); CFDictionarySetValue(addAttributes.get(), kSecAttrKeyClass, kSecAttrKeyClassPublic); - CFDictionarySetValue(addAttributes.get(), kSecAttrAccessible, - kSecAttrAccessibleAfterFirstUnlock); CFDictionarySetValue(addAttributes.get(), kSecReturnPersistentRef, kCFBooleanTrue); + CFDictionarySetValue(addAttributes.get(), kSecAttrAccessible, + kSecAttrAccessibleAfterFirstUnlock); // Add the key to the keystore. CFTypeRef temp = NULL; @@ -96,9 +94,9 @@ SecKeyRef ImportPublicKey(const std::string& key) { kSecAttrKeyTypeRSA); CFDictionarySetValue(queryAttributes.get(), kSecAttrKeyClass, kSecAttrKeyClassPublic); + CFDictionarySetValue(queryAttributes.get(), kSecReturnRef, kCFBooleanTrue); CFDictionarySetValue(queryAttributes.get(), kSecAttrAccessible, kSecAttrAccessibleAfterFirstUnlock); - CFDictionarySetValue(queryAttributes.get(), kSecReturnRef, kCFBooleanTrue); // Query the keychain to get the public key ref. CFTypeRef keyRef = NULL; diff --git a/core/src/privacy_crypto_openssl.cpp b/core/src/privacy_crypto_openssl.cpp index c489cb25..09206f4a 100644 --- a/core/src/privacy_crypto_openssl.cpp +++ b/core/src/privacy_crypto_openssl.cpp @@ -29,14 +29,15 @@ RSA* GetKey(const std::string& serialized_key) { return NULL; } RSA* key = d2i_RSAPublicKey_bio(bio, NULL); - BIO_free(bio); if (key == NULL) { LOGE("GetKey: RSA key deserialization failure: %s", ERR_error_string(ERR_get_error(), NULL)); + BIO_free(bio); return NULL; } + BIO_free(bio); return key; } @@ -87,34 +88,60 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out, return false; } - EVP_CIPHER_CTX ctx; - if (EVP_EncryptInit(&ctx, EVP_aes_128_cbc(), +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX ctx_struct; + EVP_CIPHER_CTX* evp_cipher_ctx = &ctx_struct; +#else + EVP_CIPHER_CTX* evp_cipher_ctx = EVP_CIPHER_CTX_new(); +#endif + if (EVP_EncryptInit(evp_cipher_ctx, EVP_aes_128_cbc(), reinterpret_cast(&key_[0]), reinterpret_cast(&(*iv)[0])) == 0) { LOGE("AesCbcKey::Encrypt: AES CBC setup failure: %s", ERR_error_string(ERR_get_error(), NULL)); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif return false; } out->resize(in.size() + AES_BLOCK_SIZE); int out_length = out->size(); if (EVP_EncryptUpdate( - &ctx, reinterpret_cast(&(*out)[0]), &out_length, + evp_cipher_ctx, reinterpret_cast(&(*out)[0]), &out_length, reinterpret_cast(const_cast(in.data())), in.size()) == 0) { LOGE("AesCbcKey::Encrypt: encryption failure: %s", ERR_error_string(ERR_get_error(), NULL)); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif return false; } int padding = 0; - if (EVP_EncryptFinal_ex(&ctx, reinterpret_cast(&(*out)[out_length]), + if (EVP_EncryptFinal_ex(evp_cipher_ctx, + reinterpret_cast(&(*out)[out_length]), &padding) == 0) { LOGE("AesCbcKey::Encrypt: PKCS7 padding failure: %s", ERR_error_string(ERR_get_error(), NULL)); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif return false; } +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_CIPHER_CTX_cleanup(evp_cipher_ctx); +#else + EVP_CIPHER_CTX_free(evp_cipher_ctx); +#endif out->resize(out_length + padding); return true; } @@ -177,6 +204,7 @@ bool RsaPublicKey::Encrypt(const std::string& clear_message, return false; } + FreeKey(key); return true; } @@ -189,11 +217,16 @@ static int LogOpenSSLError(const char* msg, size_t /* len */, void* /* ctx */) { static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message, const std::string &signature) { - EVP_MD_CTX ctx; - EVP_MD_CTX_init(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_MD_CTX ctx_struct; + EVP_MD_CTX* evp_md_ctx = &ctx_struct; + EVP_MD_CTX_init(evp_md_ctx); +#else + EVP_MD_CTX* evp_md_ctx = EVP_MD_CTX_new(); +#endif EVP_PKEY_CTX *pctx = NULL; - if (EVP_DigestVerifyInit(&ctx, &pctx, EVP_sha1(), NULL /* no ENGINE */, + if (EVP_DigestVerifyInit(evp_md_ctx, &pctx, EVP_sha1(), NULL /* no ENGINE */, pkey) != 1) { LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature"); goto err; @@ -215,13 +248,13 @@ static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message, goto err; } - if (EVP_DigestVerifyUpdate(&ctx, message.data(), message.size()) != 1) { + if (EVP_DigestVerifyUpdate(evp_md_ctx, message.data(), message.size()) != 1) { LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature"); goto err; } if (EVP_DigestVerifyFinal( - &ctx, const_cast( + evp_md_ctx, const_cast( reinterpret_cast(signature.data())), signature.size()) != 1) { LOGE( @@ -230,12 +263,20 @@ static bool VerifyPSSSignature(EVP_PKEY *pkey, const std::string &message, goto err; } - EVP_MD_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_MD_CTX_cleanup(evp_md_ctx); +#else + EVP_MD_CTX_free(evp_md_ctx); +#endif return true; err: ERR_print_errors_cb(LogOpenSSLError, NULL); - EVP_MD_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_MD_CTX_cleanup(evp_md_ctx); +#else + EVP_MD_CTX_free(evp_md_ctx); +#endif return false; } @@ -255,10 +296,15 @@ bool RsaPublicKey::VerifySignature(const std::string& message, return false; } EVP_PKEY *pkey = EVP_PKEY_new(); - if (pkey == NULL || - EVP_PKEY_set1_RSA(pkey, rsa_key) != 1) { + if (pkey == NULL) { + LOGE("RsaPublicKey::VerifySignature: EVP_PKEY allocation failed"); FreeKey(rsa_key); + return false; + } + if (EVP_PKEY_set1_RSA(pkey, rsa_key) != 1) { LOGE("RsaPublicKey::VerifySignature: failed to wrap key in an EVP_PKEY"); + FreeKey(rsa_key); + EVP_PKEY_free(pkey); return false; } FreeKey(rsa_key); diff --git a/core/src/properties.cpp b/core/src/properties.cpp index beaa15b3..a14d0f1b 100644 --- a/core/src/properties.cpp +++ b/core/src/properties.cpp @@ -12,8 +12,8 @@ namespace wvcdm { bool Properties::oem_crypto_use_secure_buffers_; bool Properties::oem_crypto_use_fifo_; bool Properties::oem_crypto_use_userspace_buffers_; -bool Properties::use_certificates_as_identification_; bool Properties::provisioning_messages_are_binary_; +bool Properties::allow_service_certificate_requests_; bool Properties::security_level_path_backward_compatibility_support_; scoped_ptr Properties::session_property_set_; diff --git a/core/src/service_certificate.cpp b/core/src/service_certificate.cpp index 3f331076..52983a8e 100644 --- a/core/src/service_certificate.cpp +++ b/core/src/service_certificate.cpp @@ -4,6 +4,7 @@ #include "crypto_key.h" #include "crypto_session.h" +#include "license_protocol.pb.h" #include "log.h" #include "privacy_crypto.h" #include "properties.h" @@ -123,6 +124,7 @@ namespace wvcdm { using video_widevine::ClientIdentification; using video_widevine::DrmDeviceCertificate; using video_widevine::EncryptedClientIdentification; +using video_widevine::LicenseError; using video_widevine::SignedDrmDeviceCertificate; using video_widevine::SignedMessage; @@ -189,7 +191,7 @@ CdmResponseType ServiceCertificate::Init(const std::string& certificate) { CdmResponseType ServiceCertificate::VerifySignedMessage( const std::string& message, const std::string& signature) { - if (!public_key_) { + if (public_key_.get() == NULL) { LOGE("Service certificate not set."); return DEVICE_CERTIFICATE_ERROR_4; } @@ -200,14 +202,22 @@ CdmResponseType ServiceCertificate::VerifySignedMessage( return NO_ERROR; } -CdmResponseType ServiceCertificate::EncryptClientId( - CryptoSession* crypto_session, const ClientIdentification* clear_client_id, - EncryptedClientIdentification* encrypted_client_id) { - if (!public_key_) { +CdmResponseType ServiceCertificate::EncryptRsaOaep(const std::string& plaintext, + std::string* ciphertext) { + if (public_key_.get() == NULL) { LOGE("Service certificate not set."); return DEVICE_CERTIFICATE_ERROR_4; } + if (!public_key_->Encrypt(plaintext, ciphertext)) + return CLIENT_ID_RSA_ENCRYPT_ERROR; + + return NO_ERROR; +} + +CdmResponseType ServiceCertificate::EncryptClientId( + CryptoSession* crypto_session, const ClientIdentification* clear_client_id, + EncryptedClientIdentification* encrypted_client_id) { encrypted_client_id->set_provider_id(provider_id_); encrypted_client_id->set_service_certificate_serial_number(serial_number_); @@ -227,8 +237,9 @@ CdmResponseType ServiceCertificate::EncryptClientId( if (!aes.Init(key)) return CLIENT_ID_AES_INIT_ERROR; if (!aes.Encrypt(id, &enc_id, &iv)) return CLIENT_ID_AES_ENCRYPT_ERROR; - if (!public_key_->Encrypt(key, &enc_key)) - return CLIENT_ID_RSA_ENCRYPT_ERROR; + CdmResponseType encrypt_result = EncryptRsaOaep(key, &enc_key); + if (encrypt_result != NO_ERROR) + return encrypt_result; encrypted_client_id->set_encrypted_client_id_iv(iv); encrypted_client_id->set_encrypted_privacy_key(enc_key); @@ -236,4 +247,55 @@ CdmResponseType ServiceCertificate::EncryptClientId( return NO_ERROR; } +bool ServiceCertificate::GetRequest(CdmKeyMessage* request) { + if (!request) { + LOGE("ServiceCertificate::PrepareRequest: no request parameter provided"); + return false; + } + SignedMessage message; + message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST); + message.SerializeToString(request); + return true; +} + +CdmResponseType ServiceCertificate::ParseResponse( + const std::string& response, std::string* certificate) { + if (response.empty()) { + LOGE("ServiceCertificate::ParseResponse: empty response"); + return EMPTY_RESPONSE_ERROR_1; + } + if (!certificate) { + LOGE("ServiceCertificate::ParseResponse: null return parameter"); + return INVALID_PARAMETERS_ENG_24; + } + + SignedMessage signed_response; + if (!signed_response.ParseFromString(response)) { + LOGE("ServiceCertificate::ParseResponse: cannot parse response"); + return PARSE_RESPONSE_ERROR_1; + } + + if (signed_response.type() == SignedMessage::ERROR_RESPONSE) { + LicenseError license_error; + if (!license_error.ParseFromString(signed_response.msg())) { + LOGE("ServiceCertificate::ParseResponse: cannot parse license error"); + return PARSE_RESPONSE_ERROR_2; + } + LOGE("ServiceCertificate::ParseResponse: server returned error = %d", + license_error.error_code()); + return PARSE_RESPONSE_ERROR_3; + } + + if (signed_response.type() != SignedMessage::SERVICE_CERTIFICATE) { + LOGE("ServiceCertificate::ParseResponse: response (%d) is wrong type", + signed_response.type()); + return PARSE_RESPONSE_ERROR_4; + } + + certificate->assign(signed_response.msg()); + return NO_ERROR; +} + + + } // namespace wvcdm diff --git a/core/src/usage_table_header.cpp b/core/src/usage_table_header.cpp new file mode 100644 index 00000000..c718929d --- /dev/null +++ b/core/src/usage_table_header.cpp @@ -0,0 +1,631 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +#include "usage_table_header.h" + +#include "crypto_session.h" +#include "license.h" +#include "log.h" +#include "wv_cdm_constants.h" + +namespace { +std::string kEmptyString; +uint64_t kOldUsageEntryTimeSinceLicenseReceived = 0; +uint64_t kOldUsageEntryTimeSinceFirstDecrypt = 0; +uint64_t kOldUsageEntryTimeSinceLastDecrypt = 0; +std::string kOldUsageEntryServerMacKey(wvcdm::MAC_KEY_SIZE, 0); +std::string kOldUsageEntryClientMacKey(wvcdm::MAC_KEY_SIZE, 0); +std::string kOldUsageEntryPoviderSessionToken = + "nahZ6achSheiqua3TohQuei0ahwohv"; +} + +namespace wvcdm { + +UsageTableHeader::UsageTableHeader() + : security_level_(kSecurityLevelUninitialized), + requested_security_level_(kLevelDefault), + is_inited_(false) { + file_handle_.reset(new DeviceFiles(file_system_.get())); +} + +bool UsageTableHeader::Init(CdmSecurityLevel security_level, + CryptoSession* crypto_session) { + LOGV("UsageTableHeader::Init: security level: %d", security_level); + if (crypto_session == NULL) { + LOGE("UsageTableHeader::Init: no crypto session provided"); + return false; + } + + switch (security_level) { + case kSecurityLevelL1: + case kSecurityLevelL3: + break; + default: + LOGE("UsageTableHeader::Init: invalid security level provided: %d", + security_level); + return false; + } + + security_level_ = security_level; + requested_security_level_ = + security_level_ == kSecurityLevelL3 ? kLevel3 : kLevelDefault; + + if (!file_handle_->Init(security_level)) { + LOGE("UsageTableHeader::Init: device files initialization failed"); + return false; + } + + CdmResponseType status = USAGE_INFO_NOT_FOUND; + if (file_handle_->RetrieveUsageTableInfo(&usage_table_header_, + &usage_entry_info_)) { + status = crypto_session->LoadUsageTableHeader(usage_table_header_); + if (status != NO_ERROR) { + LOGE( + "UsageTableHeader::Init: load usage table failed, security level: %d", + security_level); + file_handle_->DeleteAllLicenses(); + usage_entry_info_.clear(); + usage_table_header_.clear(); + status = crypto_session->CreateUsageTableHeader(&usage_table_header_); + if (status != NO_ERROR) return false; + file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); + } + } else { + status = crypto_session->CreateUsageTableHeader(&usage_table_header_); + if (status != NO_ERROR) return false; + file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); + + metrics::CryptoMetrics alternate_metrics; + metrics::CryptoMetrics* metrics = + crypto_session->GetCryptoMetrics() != NULL ? + crypto_session->GetCryptoMetrics() : &alternate_metrics; + + UpgradeFromUsageTable(file_handle_.get(), metrics); + file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); + } + + is_inited_ = true; + return true; +} + +CdmResponseType UsageTableHeader::AddEntry( + CryptoSession* crypto_session, bool persistent_license, + const CdmKeySetId& key_set_id, const std::string& usage_info_file_name, + uint32_t* usage_entry_number) { + LOGV("UsageTableHeader::AddEntry: Lock"); + AutoLock auto_lock(usage_table_header_lock_); + CdmResponseType status = crypto_session->CreateUsageEntry(usage_entry_number); + + if (status != NO_ERROR) return status; + + if (*usage_entry_number < usage_entry_info_.size()) { + LOGE("UsageTableHeader::AddEntry: new entry %d smaller than table size: %d", + *usage_entry_number, usage_entry_info_.size()); + return USAGE_INVALID_NEW_ENTRY; + } + + if (*usage_entry_number > usage_entry_info_.size()) { + LOGW("UsageTableHeader::AddEntry: new entry %d larger than table size: %d", + *usage_entry_number, usage_entry_info_.size()); + size_t number_of_entries = usage_entry_info_.size(); + usage_entry_info_.resize(*usage_entry_number + 1); + for (size_t i = number_of_entries; i < usage_entry_info_.size() - 1; ++i) { + usage_entry_info_[i].storage_type = kStorageTypeUnknown; + usage_entry_info_[i].key_set_id.clear(); + usage_entry_info_[i].usage_info_file_name.clear(); + } + } else /* *usage_entry_number == usage_entry_info_.size() */ { + usage_entry_info_.resize(*usage_entry_number + 1); + } + + usage_entry_info_[*usage_entry_number].storage_type = + persistent_license ? kStorageLicense : kStorageUsageInfo; + usage_entry_info_[*usage_entry_number].key_set_id = key_set_id; + if (!persistent_license) + usage_entry_info_[*usage_entry_number].usage_info_file_name = + usage_info_file_name; + + file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); + return NO_ERROR; +} + +CdmResponseType UsageTableHeader::LoadEntry(CryptoSession* crypto_session, + const CdmUsageEntry& usage_entry, + uint32_t usage_entry_number) { + LOGV("UsageTableHeader::LoadEntry: Lock"); + AutoLock auto_lock(usage_table_header_lock_); + + if (usage_entry_number >= usage_entry_info_.size()) { + LOGE( + "UsageTableHeader::LoadEntry: usage entry number %d larger than table " + "size: %d", + usage_entry_number, usage_entry_info_.size()); + return USAGE_INVALID_LOAD_ENTRY; + } + return crypto_session->LoadUsageEntry(usage_entry_number, usage_entry); +} + +CdmResponseType UsageTableHeader::UpdateEntry(CryptoSession* crypto_session, + CdmUsageEntry* usage_entry) { + LOGV("UsageTableHeader::UpdateEntryL: Lock"); + AutoLock auto_lock(usage_table_header_lock_); + CdmResponseType status = + crypto_session->UpdateUsageEntry(&usage_table_header_, usage_entry); + + if (status != NO_ERROR) return status; + + file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); + return NO_ERROR; +} + +CdmResponseType UsageTableHeader::DeleteEntry(uint32_t usage_entry_number, + DeviceFiles* handle, + metrics::CryptoMetrics* metrics) { + LOGV("UsageTableHeader::DeleteEntry: Lock"); + AutoLock auto_lock(usage_table_header_lock_); + if (usage_entry_number >= usage_entry_info_.size()) { + LOGE("UsageTableHeader::DeleteEntry: usage entry number %d larger than " + "usage entry size %d", usage_entry_number, usage_entry_info_.size()); + return USAGE_INVALID_PARAMETERS_1; + } + + // Find the last valid entry number, in order to swap + size_t swap_entry_number = usage_entry_info_.size() - 1; + CdmUsageEntry swap_usage_entry; + bool swap_usage_entry_valid = false; + + while (!swap_usage_entry_valid && swap_entry_number > usage_entry_number) { + switch (usage_entry_info_[swap_entry_number].storage_type) { + case kStorageLicense: + case kStorageUsageInfo: { + CdmResponseType status = + GetEntry(swap_entry_number, handle, &swap_usage_entry); + if (status == NO_ERROR) swap_usage_entry_valid = true; + break; + } + case kStorageTypeUnknown: + default: + break; + } + if (!swap_usage_entry_valid) --swap_entry_number; + } + + uint32_t number_of_entries_to_be_deleted = + usage_entry_info_.size() - usage_entry_number; + + if (swap_usage_entry_valid) { + CdmResponseType status = MoveEntry(swap_entry_number, swap_usage_entry, + usage_entry_number, handle, metrics); + // If unable to move entry, unset storage type of entry to be deleted and + // resize |usage_entry_info_| so that swap usage entry is the last entry. + if (status != NO_ERROR) { + usage_entry_info_[usage_entry_number].storage_type = kStorageTypeUnknown; + usage_entry_info_[usage_entry_number].key_set_id.clear(); + if (usage_entry_info_.size() - 1 == swap_entry_number) { + file_handle_->StoreUsageTableInfo(usage_table_header_, + usage_entry_info_); + } else { + Shrink(metrics, usage_entry_info_.size() - swap_entry_number - 1); + } + return NO_ERROR; + } + number_of_entries_to_be_deleted = + usage_entry_info_.size() - swap_entry_number; + } + return Shrink(metrics, number_of_entries_to_be_deleted); +} + +CdmResponseType UsageTableHeader::MoveEntry( + uint32_t from_usage_entry_number, const CdmUsageEntry& from_usage_entry, + uint32_t to_usage_entry_number, DeviceFiles* handle, + metrics::CryptoMetrics* metrics) { + LOGV("UsageTableHeader::MoveEntry"); + + // crypto_session points to an object whose scope is this method or a test + // object whose scope is the lifetime of this class + scoped_ptr scoped_crypto_session; + CryptoSession* crypto_session = test_crypto_session_.get(); + if (crypto_session == NULL) { + scoped_crypto_session.reset((new CryptoSession(metrics))); + crypto_session = scoped_crypto_session.get(); + } + + crypto_session->Open(requested_security_level_); + + CdmResponseType status = + crypto_session->LoadUsageEntry(from_usage_entry_number, from_usage_entry); + + if (status != NO_ERROR) { + LOGE("UsageTableHeader::MoveEntry: Failed to load usage entry: %d", + from_usage_entry_number); + return status; + } + + status = crypto_session->MoveUsageEntry(to_usage_entry_number); + + if (status != NO_ERROR) { + LOGE("UsageTableHeader::MoveEntry: Failed to move usage entry: %d->%d", + from_usage_entry_number, to_usage_entry_number); + return status; + } + + usage_entry_info_[to_usage_entry_number] = + usage_entry_info_[from_usage_entry_number]; + + CdmUsageEntry usage_entry; + status = crypto_session->UpdateUsageEntry(&usage_table_header_, &usage_entry); + + if (status != NO_ERROR) { + LOGE("UsageTableHeader::MoveEntry: Failed to update usage entry: %d", + to_usage_entry_number); + return status; + } + + file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); + + StoreEntry(to_usage_entry_number, handle, usage_entry); + + return NO_ERROR; +} + +CdmResponseType UsageTableHeader::GetEntry(uint32_t usage_entry_number, + DeviceFiles* handle, + CdmUsageEntry* usage_entry) { + uint32_t entry_number; + switch (usage_entry_info_[usage_entry_number].storage_type) { + case kStorageLicense: { + DeviceFiles::LicenseState license_state; + std::string init_data, key_request, key_response, key_renewal_request; + std::string key_renewal_response, release_server_url; + int64_t playback_start_time, last_playback_time, grace_period_end_time; + CdmAppParameterMap app_parameters; + if (!handle->RetrieveLicense( + usage_entry_info_[usage_entry_number].key_set_id, &license_state, + &init_data, &key_request, &key_response, &key_renewal_request, + &key_renewal_response, &release_server_url, &playback_start_time, + &last_playback_time, &grace_period_end_time, &app_parameters, + usage_entry, &entry_number)) { + LOGE("UsageTableHeader::GetEntry: Failed to retrieve license"); + return USAGE_GET_ENTRY_RETRIEVE_LICENSE_FAILED; + } + break; + } + case kStorageUsageInfo: { + std::string provider_session_token; + CdmKeyMessage license_request; + CdmKeyResponse license_response; + + if (!handle->RetrieveUsageInfoByKeySetId( + usage_entry_info_[usage_entry_number].usage_info_file_name, + usage_entry_info_[usage_entry_number].key_set_id, + &provider_session_token, &license_request, &license_response, + usage_entry, &entry_number)) { + LOGE( + "UsageTableHeader::GetEntry: Failed to retrieve usage information"); + return USAGE_GET_ENTRY_RETRIEVE_USAGE_INFO_FAILED; + } + break; + } + case kStorageTypeUnknown: + default: + LOGE( + "UsageTableHeader::GetEntry: Attempting to retrieve usage " + "information from unknown storage type: %d", + usage_entry_info_[usage_entry_number].storage_type); + return USAGE_GET_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE; + } + + if (usage_entry_number != entry_number) { + LOGE("UsageTableHeader::GetEntry: entry number mismatch: (%d, %d)", + usage_entry_number, entry_number); + return USAGE_ENTRY_NUMBER_MISMATCH; + } + + return NO_ERROR; +} + +CdmResponseType UsageTableHeader::StoreEntry(uint32_t usage_entry_number, + DeviceFiles* handle, + const CdmUsageEntry& usage_entry) { + uint32_t entry_number; + switch (usage_entry_info_[usage_entry_number].storage_type) { + case kStorageLicense: { + DeviceFiles::LicenseState license_state; + std::string init_data, key_request, key_response, key_renewal_request; + std::string key_renewal_response, release_server_url; + int64_t playback_start_time, last_playback_time, grace_period_end_time; + CdmAppParameterMap app_parameters; + CdmUsageEntry entry; + if (!handle->RetrieveLicense( + usage_entry_info_[usage_entry_number].key_set_id, &license_state, + &init_data, &key_request, &key_response, &key_renewal_request, + &key_renewal_response, &release_server_url, &playback_start_time, + &last_playback_time, &grace_period_end_time, &app_parameters, + &entry, &entry_number)) { + LOGE("UsageTableHeader::StoreEntry: Failed to retrieve license"); + return USAGE_STORE_ENTRY_RETRIEVE_LICENSE_FAILED; + } + if (!handle->StoreLicense( + usage_entry_info_[usage_entry_number].key_set_id, license_state, + init_data, key_request, key_response, key_renewal_request, + key_renewal_response, release_server_url, playback_start_time, + last_playback_time, grace_period_end_time, app_parameters, + usage_entry, usage_entry_number)) { + LOGE("UsageTableHeader::StoreEntry: Failed to store license"); + return USAGE_STORE_LICENSE_FAILED; + } + break; + } + case kStorageUsageInfo: { + CdmUsageEntry entry; + std::string provider_session_token, init_data, key_request, key_response, + key_renewal_request; + if (!handle->RetrieveUsageInfoByKeySetId( + usage_entry_info_[usage_entry_number].usage_info_file_name, + usage_entry_info_[usage_entry_number].key_set_id, + &provider_session_token, &key_request, &key_response, &entry, + &entry_number)) { + LOGE( + "UsageTableHeader::StoreEntry: Failed to retrieve usage " + "information"); + return USAGE_STORE_ENTRY_RETRIEVE_USAGE_INFO_FAILED; + } + handle->DeleteUsageInfo( + usage_entry_info_[usage_entry_number].usage_info_file_name, + provider_session_token); + if (!handle->StoreUsageInfo( + provider_session_token, key_request, key_response, + usage_entry_info_[usage_entry_number].usage_info_file_name, + usage_entry_info_[usage_entry_number].key_set_id, usage_entry, + usage_entry_number)) { + LOGE("UsageTableHeader::StoreEntry: Failed to store usage information"); + return USAGE_STORE_USAGE_INFO_FAILED; + } + break; + } + case kStorageTypeUnknown: + default: + LOGE( + "UsageTableHeader::GetUsageEntry: Attempting to retrieve usage " + "information from unknown storage type: %d", + usage_entry_info_[usage_entry_number].storage_type); + return USAGE_STORE_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE; + } + return NO_ERROR; +} + +CdmResponseType UsageTableHeader::Shrink( + metrics::CryptoMetrics* metrics, + uint32_t number_of_usage_entries_to_delete) { + if (usage_entry_info_.empty()) { + LOGE("UsageTableHeader::Shrink: usage entry info table unexpectedly empty"); + return NO_USAGE_ENTRIES; + } + + if (usage_entry_info_.size() < number_of_usage_entries_to_delete) { + LOGW( + "UsageTableHeader::Shrink: cannot delete %d entries when usage entry " + "table size is %d", number_of_usage_entries_to_delete, + usage_entry_info_.size()); + return NO_ERROR; + } + + if (number_of_usage_entries_to_delete == 0) return NO_ERROR; + + usage_entry_info_.resize(usage_entry_info_.size() - + number_of_usage_entries_to_delete); + + // crypto_session points to an object whose scope is this method or a test + // object whose scope is the lifetime of this class + scoped_ptr scoped_crypto_session; + CryptoSession* crypto_session = test_crypto_session_.get(); + if (crypto_session == NULL) { + scoped_crypto_session.reset((new CryptoSession(metrics))); + crypto_session = scoped_crypto_session.get(); + } + + CdmResponseType status = crypto_session->Open(requested_security_level_); + if (status != NO_ERROR) return status; + + status = crypto_session->ShrinkUsageTableHeader(usage_entry_info_.size(), + &usage_table_header_); + if (status != NO_ERROR) return status; + + file_handle_->StoreUsageTableInfo(usage_table_header_, usage_entry_info_); + return NO_ERROR; +} + +CdmResponseType UsageTableHeader::UpgradeFromUsageTable( + DeviceFiles* handle, metrics::CryptoMetrics* metrics) { + UpgradeLicensesFromUsageTable(handle, metrics); + UpgradeUsageInfoFromUsageTable(handle, metrics); + return NO_ERROR; +} + +bool UsageTableHeader::UpgradeLicensesFromUsageTable( + DeviceFiles* handle, metrics::CryptoMetrics* metrics) { + // Fetch the key set IDs for each offline license. For each license + // * retrieve the provider session token, + // * create a new usage entry + // * copy over the entry from the usage table + // * update the usage header table and entry numbers + // * save the usage table header and store the usage entry number and + // usage entry along with the license to persistent memory + std::vector key_set_ids; + if (!handle->ListLicenses(&key_set_ids)) { + LOGW( + "UpgradeUsageTableHeader::UpgradeLicensesFromUsageTable: unable to " + "retrieve list of licenses"); + return false; + } + + for (size_t i = 0; i < key_set_ids.size(); ++i) { + DeviceFiles::LicenseState license_state; + std::string init_data, key_request, key_response, key_renewal_request; + std::string key_renewal_response, release_server_url; + int64_t playback_start_time, last_playback_time, grace_period_end_time; + CdmAppParameterMap app_parameters; + CdmUsageEntry usage_entry; + uint32_t usage_entry_number; + if (!handle->RetrieveLicense( + key_set_ids[i], &license_state, &init_data, &key_request, + &key_response, &key_renewal_request, &key_renewal_response, + &release_server_url, &playback_start_time, &last_playback_time, + &grace_period_end_time, &app_parameters, &usage_entry, + &usage_entry_number)) { + LOGW( + "UsageTableHeader::UpgradeLicensesFromUsageTable: Failed to " + "retrieve license"); + continue; + } + + std::string provider_session_token; + if (!CdmLicense::ExtractProviderSessionToken(key_response, + &provider_session_token)) { + LOGW( + "UsageTableHeader::UpgradeLicensesFromUsageTable: Failed to " + "retrieve provider session token"); + continue; + } + + if (provider_session_token.empty()) continue; + + CryptoSession crypto_session(metrics); + CdmResponseType status = crypto_session.Open(requested_security_level_); + + if (status != NO_ERROR) continue; + + // TODO(fredgc): remove when b/65730828 is addressed + if (!CreateDummyOldUsageEntry(&crypto_session)) continue; + + status = AddEntry(&crypto_session, true /* persistent license */, + key_set_ids[i], kEmptyString, &usage_entry_number); + + if (status != NO_ERROR) continue; + + status = crypto_session.CopyOldUsageEntry(provider_session_token); + + if (status != NO_ERROR) { + crypto_session.Close(); + Shrink(metrics, 1); + continue; + } + + status = UpdateEntry(&crypto_session, &usage_entry); + + if (status != NO_ERROR) { + crypto_session.Close(); + Shrink(metrics, 1); + continue; + } + + if (!handle->StoreLicense( + key_set_ids[i], license_state, init_data, key_request, key_response, + key_renewal_request, key_renewal_response, release_server_url, + playback_start_time, last_playback_time, grace_period_end_time, + app_parameters, usage_entry, usage_entry_number)) { + LOGE( + "UsageTableHeader::UpgradeLicensesFromUsageTable: Failed to store " + "license"); + continue; + } + } + + return NO_ERROR; +} + +bool UsageTableHeader::UpgradeUsageInfoFromUsageTable( + DeviceFiles* handle, metrics::CryptoMetrics* metrics) { + // Fetch all usage files. For each file retrieve all the usage info records + // within the file. For each piece of usage information + // * create a new usage entry + // * copy over the entry from the usage table and + // * update the usage header table and entry numbers + // * save the usage table header + // * once done processing all the usage records from a file, save the usage + // information to persistent memory along with usage entry number and usage + // entry. + std::vector usage_info_file_names; + if (!handle->ListUsageInfoFiles(&usage_info_file_names)) { + LOGW( + "UpgradeUsageTableHeader::UpgradeUsageInfoFromUsageTable: Unable to " + "retrieve list of usage info file names"); + return false; + } + + for (size_t i = 0; i < usage_info_file_names.size(); ++i) { + std::vector usage_data; + if (!handle->RetrieveUsageInfo(usage_info_file_names[i], &usage_data)) { + LOGW( + "UsageTableHeader::UpgradeUsageInfoFromUsageTable: Failed to " + "retrieve usage records from %s", + usage_info_file_names[i].c_str()); + continue; + } + + for (size_t j = 0; j < usage_data.size(); ++j) { + if (usage_data[j].provider_session_token.empty()) { + LOGW( + "UsageTableHeader::UpgradeUsageInfoFromUsageTable: Provider " + "session id empty"); + continue; + } + + CryptoSession crypto_session(metrics); + CdmResponseType status = crypto_session.Open(requested_security_level_); + + if (status != NO_ERROR) continue; + + // TODO(fredgc): remove when b/65730828 is addressed + if (!CreateDummyOldUsageEntry(&crypto_session)) continue; + + // TODO(rfrias): We need to fill in the app id, but it is hashed + // and we have no way to extract. Use the hased filename instead? + status = AddEntry(&crypto_session, false /* usage info */, + usage_data[j].key_set_id, usage_info_file_names[i], + &(usage_data[j].usage_entry_number)); + + if (status != NO_ERROR) continue; + + status = crypto_session.CopyOldUsageEntry( + usage_data[j].provider_session_token); + + if (status != NO_ERROR) { + crypto_session.Close(); + Shrink(metrics, 1); + continue; + } + + status = UpdateEntry(&crypto_session, &(usage_data[j].usage_entry)); + + if (status != NO_ERROR) { + crypto_session.Close(); + Shrink(metrics, 1); + continue; + } + } + + if (!handle->StoreUsageInfo(usage_info_file_names[i], usage_data)) { + LOGE( + "UsageTableHeader::StoreUsageInfo: Failed to store usage records to " + "%s", + usage_info_file_names[i].c_str()); + continue; + } + } + + return NO_ERROR; +} + +// TODO(fredgc): remove when b/65730828 is addressed +bool UsageTableHeader::CreateDummyOldUsageEntry(CryptoSession* crypto_session) { + return crypto_session->CreateOldUsageEntry( + kOldUsageEntryTimeSinceLicenseReceived, + kOldUsageEntryTimeSinceFirstDecrypt, + kOldUsageEntryTimeSinceLastDecrypt, + CryptoSession::kUsageDurationsInvalid, + kOldUsageEntryServerMacKey, + kOldUsageEntryClientMacKey, + kOldUsageEntryPoviderSessionToken); +} + +} // namespace wvcdm diff --git a/core/test/cdm_engine_test.cpp b/core/test/cdm_engine_test.cpp index f2e4b0d1..4b31c503 100644 --- a/core/test/cdm_engine_test.cpp +++ b/core/test/cdm_engine_test.cpp @@ -12,11 +12,11 @@ #include "cdm_engine.h" #include "config_test_env.h" #include "initialization_data.h" +#include "file_store.h" #include "license_request.h" #include "log.h" #include "OEMCryptoCENC.h" #include "properties.h" -#include "properties_ce.h" #include "scoped_ptr.h" #include "string_conversions.h" #include "test_printers.h" @@ -49,7 +49,7 @@ const std::string kWebmMimeType = "video/webm"; static void CommonSetup(ServerConfigurationId which, bool bin_prov = false) { - widevine::PropertiesCE::SetProvisioningMessagesAreBinary(bin_prov); + Properties::set_provisioning_messages_are_binary(bin_prov); Properties::Init(); // NOTE: Select configuration @@ -119,13 +119,29 @@ class WvCdmEnginePreProvTest : public testing::Test { virtual ~WvCdmEnginePreProvTest() {} virtual void SetUp() { + session_opened_ = false; + } + + virtual void OpenSession() { CdmResponseType status = cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_); + if (status == NEED_PROVISIONING) { + Provision(); + status = cdm_engine_.OpenSession(g_key_system, NULL, NULL, &session_id_); + } ASSERT_EQ(status, NO_ERROR); + ASSERT_NE("", session_id_) << "Could not open CDM session."; + ASSERT_TRUE(cdm_engine_.IsOpenSession(session_id_)); session_opened_ = true; } virtual void TearDown() { + if (cdm_engine_.IsProvisioned(kSecurityLevelL1)) { + cdm_engine_.Unprovision(kSecurityLevelL1); + } + if (cdm_engine_.IsProvisioned(kSecurityLevelL3)) { + cdm_engine_.Unprovision(kSecurityLevelL3); + } if (session_opened_) { cdm_engine_.CloseSession(session_id_); session_opened_ = false; @@ -169,9 +185,19 @@ class WvCdmEnginePreProvTest : public testing::Test { std::string cert, wrapped_key; ASSERT_EQ(NO_ERROR, cdm_engine_.SetServiceCertificate( g_provisioning_service_certificate)); - ASSERT_EQ(NO_ERROR, cdm_engine_.GetProvisioningRequest( - cert_type, cert_authority, &prov_request, - &provisioning_server_url)); + CdmResponseType result = NO_ERROR; + for(int i = 0; i < 2; ++i) { // Retry once if there is a nonce problem. + result = cdm_engine_.GetProvisioningRequest( + cert_type, cert_authority, &prov_request, + &provisioning_server_url); + if (result == CERT_PROVISIONING_NONCE_GENERATION_ERROR) { + LOGW("Woops. Nonce problem. Try again?"); + sleep(1); + } else { + break; + } + } + ASSERT_EQ(NO_ERROR, result); LOGV("WvCdmEnginePreProvTest::Provision: req=%s", prov_request.c_str()); @@ -211,7 +237,7 @@ class WvCdmEnginePreProvTestStaging : public WvCdmEnginePreProvTest { static void SetUpTestCase() { // NOTE: Select server configuration - CommonSetup(kContentProtectionStagingLicense); + CommonSetup(kContentProtectionStagingServer); } }; @@ -223,7 +249,7 @@ class WvCdmEnginePreProvTestProd : public WvCdmEnginePreProvTest { static void SetUpTestCase() { // NOTE: Select server configuration - CommonSetup(kContentProtectionProdLicense); + CommonSetup(kContentProtectionProductionServer); } }; @@ -235,32 +261,20 @@ class WvCdmEnginePreProvTestUat : public WvCdmEnginePreProvTest { static void SetUpTestCase() { // NOTE: Select server configuration - CommonSetup(kContentProtectionUatLicense); + CommonSetup(kContentProtectionUatServer); } }; -class WvCdmEnginePreProvTestStagingProv30 : public WvCdmEnginePreProvTest { +class WvCdmEnginePreProvTestUatBinary : public WvCdmEnginePreProvTest { public: - WvCdmEnginePreProvTestStagingProv30() {} + WvCdmEnginePreProvTestUatBinary() {} - virtual ~WvCdmEnginePreProvTestStagingProv30() {} - - static void SetUpTestCase() { - // NOTE: Select server configuration - CommonSetup(kContentProtectionStagingPlusProv30); - } -}; - -class WvCdmEnginePreProvTestStagingProv30Binary : public WvCdmEnginePreProvTest { - public: - WvCdmEnginePreProvTestStagingProv30Binary() {} - - virtual ~WvCdmEnginePreProvTestStagingProv30Binary() {} + virtual ~WvCdmEnginePreProvTestUatBinary() {} static void SetUpTestCase() { // NOTE: Select server configuration // Override default setting of provisioning_messages_are_binary property - CommonSetup(kContentProtectionUatPlusProv30, true); + CommonSetup(kContentProtectionUatServer, true); } protected: @@ -326,25 +340,13 @@ class WvCdmEnginePreProvTestStagingProv30Binary : public WvCdmEnginePreProvTest }; -class WvCdmEnginePreProvTestUatProv30 : public WvCdmEnginePreProvTest { - public: - WvCdmEnginePreProvTestUatProv30() {} - - virtual ~WvCdmEnginePreProvTestUatProv30() {} - - static void SetUpTestCase() { - // NOTE: Select server configuration - CommonSetup(kContentProtectionStagingPlusProv30); - } -}; - class WvCdmEngineTest : public WvCdmEnginePreProvTest { public: WvCdmEngineTest() {} static void SetUpTestCase() { // NOTE: Select server configuration - CommonSetup(kContentProtectionStagingLicense); + CommonSetup(kContentProtectionUatServer); } virtual void SetUp() { @@ -370,10 +372,20 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest { CdmKeyRequest key_request; - EXPECT_EQ(KEY_MESSAGE, cdm_engine_.GenerateKeyRequest( - session_id_, key_set_id, init_data, - kLicenseTypeStreaming, app_parameters, - &key_request)); + CdmResponseType result = NO_ERROR; + for(int i=0; i < 2; i++) { // Retry once if there is a nonce problem. + result = cdm_engine_.GenerateKeyRequest( + session_id_, key_set_id, init_data, + kLicenseTypeStreaming, app_parameters, + &key_request); + if (result == LICENSE_REQUEST_NONCE_GENERATION_ERROR) { + LOGW("Woops. Nonce problem. Try again?"); + sleep(1); + } else { + break; + } + } + EXPECT_EQ(KEY_MESSAGE, result); key_msg_ = key_request.message; EXPECT_EQ(kKeyRequestTypeInitial, key_request.type); @@ -381,8 +393,17 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest { void GenerateRenewalRequest() { CdmKeyRequest request; - EXPECT_EQ(KEY_MESSAGE, - cdm_engine_.GenerateRenewalRequest(session_id_, &request)); + CdmResponseType result = NO_ERROR; + for (int i = 0; i < 2; i++) { // Retry once if there is a nonce problem. + result = cdm_engine_.GenerateRenewalRequest(session_id_, &request); + if (result == LICENSE_RENEWAL_NONCE_GENERATION_ERROR) { + LOGW("Woops. Nonce problem. Try again?"); + sleep(1); + } else { + break; + } + } + EXPECT_EQ(KEY_MESSAGE, result); key_msg_ = request.message; server_url_ = request.url; @@ -463,84 +484,8 @@ TEST_F(WvCdmEnginePreProvTestStaging, ServiceCertificateGoodTest) { ASSERT_TRUE(cdm_engine_.HasServiceCertificate()); }; -// Test that service certificate can be retrieved from the license server. -TEST_F(WvCdmEnginePreProvTestStaging, ServiceCertificateRequestResponse) { - CdmKeyMessage request; - std::string certificate; - - // Initial condition - no service certificate. - ASSERT_FALSE(cdm_engine_.HasServiceCertificate()); - - // Generate request. - // The request will be a serialized protobuf message. - ASSERT_TRUE(cdm_engine_.GetServiceCertificateRequest(&request)); - - std::string response; - ASSERT_TRUE(LicenseServerRequestResponse(request, &response)); - - // Extract the service certificate - ASSERT_EQ(cdm_engine_.ParseServiceCertificateResponse(response, &certificate), - NO_ERROR); - - ASSERT_TRUE(cdm_engine_.HasServiceCertificate()); - LOGV("ret'd service certificate:\n%s\n", b2a_hex(certificate).c_str()); -}; - -// Test that service certificate can be retrieved from the license server. -TEST_F(WvCdmEnginePreProvTestUat, ServiceCertificateRequestResponse) { - CdmKeyMessage request; - std::string certificate; - - // Initial condition - no service certificate. - ASSERT_FALSE(cdm_engine_.HasServiceCertificate()); - - // Generate request. - // The request will be a serialized protobuf message. - ASSERT_TRUE(cdm_engine_.GetServiceCertificateRequest(&request)); - - std::string response; - ASSERT_TRUE(LicenseServerRequestResponse(request, &response)); - - // Extract the service certificate - ASSERT_EQ(cdm_engine_.ParseServiceCertificateResponse(response, &certificate), - NO_ERROR); - - ASSERT_TRUE(cdm_engine_.HasServiceCertificate()); - LOGV("ret'd service certificate:\n%s\n", b2a_hex(certificate).c_str()); -}; - -// Test that service certificate can be retrieved from the license server. -TEST_F(WvCdmEnginePreProvTestProd, ServiceCertificateRequestResponse) { - CdmKeyMessage request; - std::string certificate; - - // Initial condition - no service certificate. - ASSERT_FALSE(cdm_engine_.HasServiceCertificate()); - - // Generate request. - // The request will be a serialized protobuf message. - ASSERT_TRUE(cdm_engine_.GetServiceCertificateRequest(&request)); - - std::string response; - ASSERT_TRUE(LicenseServerRequestResponse(request, &response)); - - // Extract the service certificate - ASSERT_EQ(cdm_engine_.ParseServiceCertificateResponse(response, &certificate), - NO_ERROR); - - ASSERT_TRUE(cdm_engine_.HasServiceCertificate()); - LOGV("ret'd service certificate:\n%s\n", b2a_hex(certificate).c_str()); -}; - -// Test that empty service certificate fails. -TEST_F(WvCdmEnginePreProvTestStaging, ServiceCertificateEmptyFailTest) { - std::string empty_cert; - ASSERT_EQ(cdm_engine_.SetServiceCertificate(g_license_service_certificate), - NO_ERROR); - ASSERT_TRUE(cdm_engine_.HasServiceCertificate()); -}; - -TEST_F(WvCdmEnginePreProvTestStaging, ProvisioningTest) { +// Test that provisioning works, even if device is already provisioned. +TEST_F(WvCdmEnginePreProvTestStaging, DISABLED_ProvisioningTest) { uint32_t nonce = 0; uint8_t buffer[1]; size_t size = 0; @@ -568,16 +513,12 @@ TEST_F(WvCdmEnginePreProvTestStaging, ProvisioningTest) { Provision(); } -TEST_F(WvCdmEnginePreProvTestStagingProv30, ProvisioningTest) { - Provision(); -} - -TEST_F(WvCdmEnginePreProvTestStagingProv30Binary, ProvisioningTest) { +TEST_F(WvCdmEnginePreProvTestUatBinary, DISABLED_ProvisioningTest) { Provision(); } // Test that provisioning works, even if device is already provisioned. -TEST_F(WvCdmEngineTest, ProvisioningTest) { +TEST_F(WvCdmEngineTest, DISABLED_ProvisioningTest) { Provision(); } diff --git a/core/test/cdm_session_unittest.cpp b/core/test/cdm_session_unittest.cpp index 0262ac34..c7538e2b 100644 --- a/core/test/cdm_session_unittest.cpp +++ b/core/test/cdm_session_unittest.cpp @@ -1,4 +1,3 @@ - // Copyright 2014 Google Inc. All Rights Reserved. #include @@ -103,6 +102,8 @@ class MockDeviceFiles : public DeviceFiles { class MockCryptoSession : public CryptoSession { public: + MockCryptoSession(metrics::CryptoMetrics* crypto_metrics) + : CryptoSession(crypto_metrics) { } MOCK_METHOD1(GetClientToken, bool(std::string*)); MOCK_METHOD1(GetProvisioningToken, bool(std::string*)); MOCK_METHOD0(GetPreProvisionTokenType, CdmClientTokenType()); @@ -125,8 +126,8 @@ class MockCdmLicense : public CdmLicense { MockCdmLicense(const CdmSessionId& session_id) : CdmLicense(session_id) {} - MOCK_METHOD6(Init, bool(ServiceCertificate*, const std::string&, - CdmClientTokenType, const std::string&, + MOCK_METHOD7(Init, bool(const std::string&, CdmClientTokenType, + const std::string&, bool, const std::string&, CryptoSession*, PolicyEngine*)); }; @@ -144,12 +145,11 @@ using ::testing::StrEq; class CdmSessionTest : public ::testing::Test { protected: virtual void SetUp() { - service_cert_ = new ServiceCertificate; - cdm_session_.reset(new CdmSession(NULL)); + cdm_session_.reset(new CdmSession(NULL, &metrics_)); // Inject testing mocks. license_parser_ = new MockCdmLicense(cdm_session_->session_id()); cdm_session_->set_license_parser(license_parser_); - crypto_session_ = new MockCryptoSession(); + crypto_session_ = new MockCryptoSession(&crypto_metrics_); cdm_session_->set_crypto_session(crypto_session_); policy_engine_ = new MockPolicyEngine(); cdm_session_->set_policy_engine(policy_engine_); @@ -157,12 +157,19 @@ class CdmSessionTest : public ::testing::Test { cdm_session_->set_file_handle(file_handle_); } + virtual void TearDown() { + // Force the cdm_session_ to be deleted. This enforces a requirement that + // the CDM session metrics exist at least as long as the CDM session. + cdm_session_.reset(); + } + + metrics::SessionMetrics metrics_; scoped_ptr cdm_session_; MockCdmLicense* license_parser_; + metrics::CryptoMetrics crypto_metrics_; MockCryptoSession* crypto_session_; MockPolicyEngine* policy_engine_; MockDeviceFiles* file_handle_; - ServiceCertificate* service_cert_; }; TEST_F(CdmSessionTest, InitWithBuiltInCertificate) { @@ -185,12 +192,11 @@ TEST_F(CdmSessionTest, InitWithBuiltInCertificate) { .WillOnce(Return(true)); EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true)); EXPECT_CALL(*license_parser_, - Init(NULL, Eq(kToken), Eq(kClientTokenDrmCert), - Eq(kEmptyString), Eq(crypto_session_), Eq(policy_engine_))) + Init(Eq(kToken), Eq(kClientTokenDrmCert), Eq(kEmptyString), + false, Eq(kEmptyString), Eq(crypto_session_), + Eq(policy_engine_))) .WillOnce(Return(true)); - Properties::set_use_certificates_as_identification(true); - ASSERT_EQ(NO_ERROR, cdm_session_->Init(NULL)); } @@ -214,37 +220,11 @@ TEST_F(CdmSessionTest, InitWithCertificate) { .InSequence(crypto_session_seq) .WillOnce(Return(true)); EXPECT_CALL(*license_parser_, - Init(NULL, Eq(kToken), Eq(kClientTokenDrmCert), - Eq(kEmptyString), Eq(crypto_session_), Eq(policy_engine_))) + Init(Eq(kToken), Eq(kClientTokenDrmCert), Eq(kEmptyString), + false, Eq(kEmptyString), Eq(crypto_session_), + Eq(policy_engine_))) .WillOnce(Return(true)); - Properties::set_use_certificates_as_identification(true); - - ASSERT_EQ(NO_ERROR, cdm_session_->Init(NULL)); -} - -TEST_F(CdmSessionTest, InitWithKeybox) { - Sequence crypto_session_seq; - CdmSecurityLevel level = kSecurityLevelL1; - EXPECT_CALL(*crypto_session_, Open(Eq(kLevelDefault))) - .InSequence(crypto_session_seq) - .WillOnce(Return(NO_ERROR)); - EXPECT_CALL(*crypto_session_, GetSecurityLevel()) - .InSequence(crypto_session_seq) - .WillOnce(Return(level)); - EXPECT_CALL(*crypto_session_, GetClientToken(NotNull())) - .InSequence(crypto_session_seq) - .WillOnce(DoAll(SetArgPointee<0>(kToken), Return(true))); - EXPECT_CALL(*crypto_session_, GetPreProvisionTokenType()) - .WillOnce(Return(kClientTokenKeybox)); - EXPECT_CALL(*file_handle_, Init(Eq(level))).WillOnce(Return(true)); - EXPECT_CALL(*license_parser_, - Init(NULL, Eq(kToken), Eq(kClientTokenKeybox), - Eq(kEmptyString), Eq(crypto_session_), Eq(policy_engine_))) - .WillOnce(Return(true)); - - Properties::set_use_certificates_as_identification(false); - ASSERT_EQ(NO_ERROR, cdm_session_->Init(NULL)); } @@ -268,12 +248,10 @@ TEST_F(CdmSessionTest, ReInitFail) { .InSequence(crypto_session_seq) .WillOnce(Return(true)); EXPECT_CALL(*license_parser_, - Init(NULL, Eq(kToken), Eq(kClientTokenDrmCert), + Init(Eq(kToken), Eq(kClientTokenDrmCert), Eq(kEmptyString), false, Eq(kEmptyString), Eq(crypto_session_), Eq(policy_engine_))) .WillOnce(Return(true)); - Properties::set_use_certificates_as_identification(true); - ASSERT_EQ(NO_ERROR, cdm_session_->Init(NULL)); ASSERT_NE(NO_ERROR, cdm_session_->Init(NULL)); } @@ -282,8 +260,6 @@ TEST_F(CdmSessionTest, InitFailCryptoError) { EXPECT_CALL(*crypto_session_, Open(Eq(kLevelDefault))) .WillOnce(Return(UNKNOWN_ERROR)); - Properties::set_use_certificates_as_identification(true); - ASSERT_EQ(UNKNOWN_ERROR, cdm_session_->Init(NULL)); } @@ -303,8 +279,6 @@ TEST_F(CdmSessionTest, InitNeedsProvisioning) { NotNull(), _)) .WillOnce(Return(false)); - Properties::set_use_certificates_as_identification(true); - ASSERT_EQ(NEED_PROVISIONING, cdm_session_->Init(NULL)); } diff --git a/core/test/config_test_env.cpp b/core/test/config_test_env.cpp index 3675a7f6..92999aa9 100644 --- a/core/test/config_test_env.cpp +++ b/core/test/config_test_env.cpp @@ -15,11 +15,109 @@ namespace { const std::string kWidevineKeySystem = "com.widevine.alpha"; -// For staging servers -// NOTE: This matches the service cert returned by the staging -// server. This is the one that the staging provisioning server uses. +// Content Protection license server (Production) data +// Testing should not be directed at this server. +const std::string kCpProductionLicenseServer = + "https://widevine-proxy.appspot.com/proxy"; +// NOTE: Provider ID = staging.google.com +const std::string kCpProductionServiceCertificate = + "0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522" + "8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417" + "7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781" + "5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D" + "0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6" + "AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0" + "40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A" + "0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6" + "283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0" + "D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301" + "00013A1273746167696E672E676F6F676C652E636F6D128003983E303526" + "75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA" + "EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA" + "97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91" + "5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0" + "32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0" + "EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28" + "8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932" + "1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784" + "C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742" + "BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940" + "383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8" + "38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A" + "250A4EB9C84AB3E6539F6B6FDF56899EA29914"; + +// Content Protection license server (UAT) data +// Testing should be directed to this server. The only exception +// is when testing against a new server feature that is not yet +// deployed to this server (which would be directed to staging). +const std::string kCpUatLicenseServer = + "https://proxy.uat.widevine.com/proxy"; +// TODO(rfrias): replace when b62880305 is addressed. For now use production URL +const std::string kCpUatProvisioningServerUrl = + "https://www.googleapis.com/" + "certificateprovisioning/v1/devicecertificates/create" + "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; +// NOTE: Provider ID = staging.google.com +const std::string kCpUatServiceCertificate = + "0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522" + "8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417" + "7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781" + "5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D" + "0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6" + "AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0" + "40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A" + "0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6" + "283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0" + "D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301" + "00013A1273746167696E672E676F6F676C652E636F6D128003983E303526" + "75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA" + "EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA" + "97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91" + "5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0" + "32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0" + "EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28" + "8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932" + "1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784" + "C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742" + "BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940" + "383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8" + "38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A" + "250A4EB9C84AB3E6539F6B6FDF56899EA29914"; +const std::string kCpClientAuth = ""; +const std::string kCpKeyId = + "00000042" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000022" // pssh data size + // pssh data: + "08011a0d7769646576696e655f746573" // "streaming_clip1" + "74220f73747265616d696e675f636c69" + "7031"; +const std::string kCpOfflineKeyId = + "00000040" // blob size + "70737368" // "pssh" + "00000000" // flags + "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id + "00000020" // pssh data size + // pssh data: + "08011a0d7769646576696e655f746573" // "offline_clip2" + "74220d6f66666c696e655f636c697032"; + +// Content Protection license server (staging) data. +// The staging server should be used only when testing against +// a new server (e.g., the client has a change that requires a +// corresponding change to the server, but the server change has +// not yet propagated to UAT). Normal testing should always be +// directed to UAT. +const std::string kCpStagingLicenseServer = + "https://proxy.staging.widevine.com/proxy"; +const std::string kCpStagingProvisioningServerUrl = + "https://staging-www.sandbox.googleapis.com/" + "certificateprovisioning/v1/devicecertificates/create" + "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; // NOTE: Provider ID = license.widevine.com -const std::string kStagingServiceCertificate = +const std::string kCpStagingServiceCertificate = "0ac102080312101705b917cc1204868b06333a2f772a8c1882b482920522" "8e023082010a028201010099ed5b3b327dab5e24efc3b62a95b598520ad5" "bccb37503e0645b814d876b8df40510441ad8ce3adb11bb88c4e725a5e4a" @@ -44,95 +142,25 @@ const std::string kStagingServiceCertificate = "f9b4342cc8df543cb1a1182f7c5fff33f10490faca5b25360b76015e9c5a" "06ab8ee02f00d2e8d5986104aacc4dd475fd96ee9ce4e326f21b83c70585" "77b38732cddabc6a6bed13fb0d49d38a45eb87a5f4"; - -// NOTE: Provider ID = staging.google.com -const std::string kProdServiceCertificate = - "0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522" - "8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417" - "7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781" - "5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D" - "0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6" - "AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0" - "40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A" - "0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6" - "283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0" - "D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301" - "00013A1273746167696E672E676F6F676C652E636F6D128003983E303526" - "75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA" - "EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA" - "97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91" - "5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0" - "32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0" - "EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28" - "8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932" - "1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784" - "C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742" - "BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940" - "383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8" - "38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A" - "250A4EB9C84AB3E6539F6B6FDF56899EA29914"; - -// For UAT License servers -// NOTE: This matches the service cert returned by the UAT server. -// NOTE: Provider ID = staging.google.com -const std::string kUatServiceCertificate = - "0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522" - "8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417" - "7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781" - "5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D" - "0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6" - "AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0" - "40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A" - "0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6" - "283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0" - "D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301" - "00013A1273746167696E672E676F6F676C652E636F6D128003983E303526" - "75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA" - "EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA" - "97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91" - "5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0" - "32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0" - "EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28" - "8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932" - "1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784" - "C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742" - "BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940" - "383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8" - "38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A" - "250A4EB9C84AB3E6539F6B6FDF56899EA29914"; - -// Content Protection license server (Production) data -const std::string kCpProdLicenseServer = - "https://widevine-proxy.appspot.com/proxy"; - -// Content Protection license server (UAT) data -const std::string kCpUatLicenseServer = - "https://proxy.uat.widevine.com/proxy"; - -const std::string kCpClientAuth = ""; -const std::string kCpKeyId = - "00000042" // blob size +const CdmInitData kCpStagingSrmOuputProtectionRequired = + "0000003d" // blob size "70737368" // "pssh" "00000000" // flags "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id - "00000022" // pssh data size + "0000001d" // pssh data size // pssh data: "08011a0d7769646576696e655f746573" - "74220f73747265616d696e675f636c69" - "7031"; -const std::string kCpOfflineKeyId = - "00000040" // blob size + "74220a74656172735f73726d32"; // "tears_srm2" +const CdmInitData kCpStagingSrmOuputProtectionRequested = + "0000003d" // blob size "70737368" // "pssh" "00000000" // flags "edef8ba979d64acea3c827dcd51d21ed" // Widevine system id - "00000020" // pssh data size + "0000001d" // pssh data size // pssh data: "08011a0d7769646576696e655f746573" - "74220d6f66666c696e655f636c697032"; - -// Content Protection license server (staging) data -const std::string kCpStagingLicenseServer = - "https://proxy.staging.widevine.com/proxy"; + "74220a74656172735f73726d32"; // "tears_srm1" +const CdmInitData kEmptyData; // Google Play license server data const std::string kGpLicenseServer = @@ -172,44 +200,25 @@ const std::string kWrongKeyId = "0901121094889920e8d6520098577df8" "f2dd5546"; -// URL of provisioning server (overrides value from GetProvisioningRequest()) -const std::string kProductionProvisioningServerUrl = +// Production provisioning server data +const std::string kCpProductionProvisioningServerUrl = "https://www.googleapis.com/" "certificateprovisioning/v1/devicecertificates/create" "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; -const std::string kStagingProvisioningServerUrl = - "https://staging-www.sandbox.googleapis.com/" - "certificateprovisioning/v1/devicecertificates/create" - "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; - const ConfigTestEnv::LicenseServerConfiguration license_servers[] = { - {kGooglePlayServer, kGpLicenseServer, "", kGpClientAuth, kGpKeyId, - kGpOfflineKeyId, kStagingProvisioningServerUrl, ""}, - - {kContentProtectionProdLicense, kCpProdLicenseServer, - kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, - kProductionProvisioningServerUrl, kProdServiceCertificate}, - - {kContentProtectionUatLicense, kCpUatLicenseServer, - kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, - kProductionProvisioningServerUrl, kProdServiceCertificate}, - - {kContentProtectionStagingLicense, kCpStagingLicenseServer, - kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, - kStagingProvisioningServerUrl, kStagingServiceCertificate}, - - {kContentProtectionProdPlusProv30, kCpProdLicenseServer, - kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, - kStagingProvisioningServerUrl, kStagingServiceCertificate}, - - {kContentProtectionUatPlusProv30, kCpUatLicenseServer, - kProdServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, - kStagingProvisioningServerUrl, kStagingServiceCertificate}, - - {kContentProtectionStagingPlusProv30, kCpStagingLicenseServer, - kStagingServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, - kStagingProvisioningServerUrl, kStagingServiceCertificate}, + {kGooglePlayServer, kGpLicenseServer, "", kGpClientAuth, kGpKeyId, + kGpOfflineKeyId, kCpProductionProvisioningServerUrl, ""}, + {kContentProtectionUatServer, kCpUatLicenseServer, kCpUatServiceCertificate, + kCpClientAuth, kCpKeyId, kCpOfflineKeyId, kCpUatProvisioningServerUrl, + kCpUatServiceCertificate}, + {kContentProtectionStagingServer, kCpStagingLicenseServer, + kCpStagingServiceCertificate, kCpClientAuth, kCpKeyId, kCpOfflineKeyId, + kCpStagingProvisioningServerUrl, kCpStagingServiceCertificate}, + {kContentProtectionProductionServer, kCpProductionLicenseServer, + kCpProductionServiceCertificate, kCpClientAuth, kCpKeyId, + kCpOfflineKeyId, kCpProductionProvisioningServerUrl, + kCpProductionServiceCertificate}, }; } // namespace @@ -254,4 +263,35 @@ void ConfigTestEnv::Init(ServerConfigurationId server_id) { wrong_key_id_ = kWrongKeyId; } +const CdmInitData ConfigTestEnv::GetInitData(ContentId content_id) { + switch (content_id) { + case kContentIdStreaming: + return wvcdm::a2bs_hex(kCpKeyId); + case kContentIdOffline: + return wvcdm::a2bs_hex(kCpOfflineKeyId); + case kContentIdStagingSrmOuputProtectionRequested: + return wvcdm::a2bs_hex(kCpStagingSrmOuputProtectionRequested); + case kContentIdStagingSrmOuputProtectionRequired: + return wvcdm::a2bs_hex(kCpStagingSrmOuputProtectionRequired); + default: + return kEmptyData; + } +} + +const std::string& ConfigTestEnv::GetLicenseServerUrl( + ServerConfigurationId server_configuration_id) { + switch (server_configuration_id) { + case kGooglePlayServer: + return kGpLicenseServer; + case kContentProtectionUatServer: + return kCpUatLicenseServer; + case kContentProtectionStagingServer: + return kCpStagingLicenseServer; + case kContentProtectionProductionServer: + return kCpProductionLicenseServer; + default: + return kEmptyData; + } +} + } // namespace wvcdm diff --git a/core/test/config_test_env.h b/core/test/config_test_env.h index 1a3f26dc..eebf6309 100644 --- a/core/test/config_test_env.h +++ b/core/test/config_test_env.h @@ -24,18 +24,25 @@ // Useful configurations namespace wvcdm { typedef enum { - kGooglePlayServer, // not tested recently - kContentProtectionProdLicense, - kContentProtectionUatLicense, - kContentProtectionStagingLicense, - kContentProtectionProdPlusProv30, - kContentProtectionUatPlusProv30, - kContentProtectionStagingPlusProv30, + kGooglePlayServer, + kContentProtectionUatServer, + kContentProtectionStagingServer, + kContentProtectionProductionServer, } ServerConfigurationId; +// Identifies content used in tests. Specify Prod/Uat/Staging if content +// has been registered across license services. +enum ContentId { + kContentIdStreaming, + kContentIdOffline, + kContentIdStagingSrmOuputProtectionRequested, + kContentIdStagingSrmOuputProtectionRequired, +}; + // Configures default test environment. class ConfigTestEnv { public: + typedef struct { ServerConfigurationId id; std::string license_server_url; @@ -68,6 +75,10 @@ class ConfigTestEnv { } const KeyId& wrong_key_id() const { return wrong_key_id_; } + static const CdmInitData GetInitData(ContentId content_id); + static const std::string& GetLicenseServerUrl( + ServerConfigurationId server_configuration_id); + void set_key_id(KeyId& key_id) { key_id_.assign(key_id); } void set_key_system(CdmKeySystem& key_system) { key_system_.assign(key_system); @@ -75,6 +86,9 @@ class ConfigTestEnv { void set_license_server(std::string& license_server) { license_server_.assign(license_server); } + void set_provisioning_server(std::string& provisioning_server) { + provisioning_server_.assign(provisioning_server); + } private: void Init(ServerConfigurationId server_id); diff --git a/core/test/device_files_unittest.cpp b/core/test/device_files_unittest.cpp index a22c1504..1fe4632b 100644 --- a/core/test/device_files_unittest.cpp +++ b/core/test/device_files_unittest.cpp @@ -17,11 +17,9 @@ namespace { const uint32_t kCertificateLen = 700; const uint32_t kWrappedKeyLen = 500; -const uint32_t kProtobufEstimatedOverhead = 75; -const uint32_t kLicenseRequestLen = 300; -const uint32_t kLicenseLen = 500; -const uint32_t kProviderSessionTokenLen = 128; -const uint32_t kKeySetIdLen = 20; +const uint32_t kProtobufEstimatedOverhead = 200; + +const std::string kEmptyString; // Structurally valid test certificate. // The data elements in this module are used to test the storage and @@ -120,6 +118,8 @@ struct LicenseInfo { int64_t last_playback_time; int64_t grace_period_end_time; std::string app_parameters; + std::string usage_entry; + uint32_t usage_entry_number; std::string file_data; }; @@ -226,9 +226,9 @@ LicenseInfo license_test_data[] = { "0112001A16200342120A106B63746C0000000000ECDCBE0000000020DBDF" "A68F051A20182F029E35047A3841FA176C74E5B387350E8D58DEA6878FF0" "BEA6CABACA1C2C"), - "https://test.google.com/license/GetCencLicense", 0x0, 0x0, 0x0, "", + "https://test.google.com/license/GetCencLicense", 0x0, 0x0, 0x0, "", "", 0, a2bs_hex( - "0AAA150802100122A3150801121408011210303132333435363738394142434445461" + "0AAE150802100122A7150801121408011210303132333435363738394142434445461" "A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0" "7A7D507618A5D3A68F05228E023082010A0282010100A947904B8DBD55FB685FDB302" "5574517CCCC74EE4FEAF6629D5179A52FF85CE7409528EFFA0E5DFC3DE9A34BA5F08B" @@ -307,8 +307,9 @@ LicenseInfo license_test_data[] = { "106B63746C0000000000ECDCBE0000000020DBDFA68F051A20182F029E35047A3841F" "A176C74E5B387350E8D58DEA6878FF0BEA6CABACA1C2C3A2E68747470733A2F2F7465" "73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7" - "365400048005800122066F4FFF1CB2C0F978149A9402F3E4FF8D49B19635E646A0678" - "71AA08E7A8FDC3")}, + "365400048005800620068001220785CE1756656A049E77F28C8449AB2DD115B6C43B2" + "FF232D23F98B72F1DCE96A" + )}, // license 1 {"ksidC8EAA2579A282EB0", DeviceFiles::kLicenseStateReleasing, @@ -409,7 +410,11 @@ LicenseInfo license_test_data[] = { "https://test.google.com/license/GetCencLicense", 0x12345678, 0x12348765, 0x0, "Name1 Value1", a2bs_hex( - "0AC3150802100122BC150802121408011210303132333435363738394142434445461" + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021" + "22232425262728292a2b2c2d2e2f"), + 5, + a2bs_hex( + "0AF7150802100122F0150802121408011210303132333435363738394142434445461" "A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0" "7A7D507618A5D3A68F05228E023082010A0282010100A947904B8DBD55FB685FDB302" "5574517CCCC74EE4FEAF6629D5179A52FF85CE7409528EFFA0E5DFC3DE9A34BA5F08B" @@ -488,8 +493,10 @@ LicenseInfo license_test_data[] = { "106B63746C00000000CA3A6A75000000002083E5A68F051A20BDA6A56F7CBFD094219" "8F87C23A34AA5CBD64AFEB134277774CCF8E789D815DD3A2E68747470733A2F2F7465" "73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7" - "36540F8ACD1910148E58ED29101520F0A054E616D6531120656616C75653158001220" - "9C0315FC1812C6A0E5936E36D04ECE2FA56AF4AB544ECDF3C9135D54B4A26167")}, + "36540F8ACD1910148E58ED29101520F0A054E616D6531120656616C75653158006230" + "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20212" + "2232425262728292A2B2C2D2E2F6805122010DB816A045F2AA5865B17FE2F20DA2114" + "17B2F8B2D7511C9DE89A87CB5208AB")}, // license 2 {"ksidE8C37662C88DC673", DeviceFiles::kLicenseStateReleasing, @@ -590,7 +597,11 @@ LicenseInfo license_test_data[] = { "https://test.google.com/license/GetCencLicense", 0x0123456789abcdef, 0x123456789abfedc, 0x0, "Name1 Value1 Name2 Param2", a2bs_hex( - "0AE9150802100122E2150802121408011210303132333435363738394142434445461" + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021" + "22232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"), + 12, + a2bs_hex( + "0AAD160802100122A6160802121408011210303132333435363738394142434445461" "A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0" "7A7D507618A5D3A68F05228E023082010A0282010100A947904B8DBD55FB685FDB302" "5574517CCCC74EE4FEAF6629D5179A52FF85CE7409528EFFA0E5DFC3DE9A34BA5F08B" @@ -671,8 +682,10 @@ LicenseInfo license_test_data[] = { "73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7" "36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD1910152150A054E616D6531120C5661" "6C756531204E616D653252160A0C4E616D653220506172616D321206506172616D325" - "8001220616E6AC4AF6EB4F7147E98CF7302425E2390B293BBC01F9F8E89B49F653EA3" - "45")}}; + "8006240000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E" + "1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F680" + "C12206AA0237760D1F06E5CB78F5AFC3D124BBF7C26921CB3CC2EA44766801E25D34" + "F")}}; // Sample license data and related data for storage and use for offline // playback. The license data and URLs in this test are not real. @@ -777,7 +790,11 @@ LicenseInfo license_update_test_data[] = { "https://test.google.com/license/GetCencLicense", 0x0123456789abcdef, 0x123456789abfedc, 0x0, "Name1 Value1 Name2 Value2 Name3 Value3", a2bs_hex( - "0ABA150802100122B3150801121408011210303132333435363738394142434445461" + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021" + "22232425262728292a2b2c2d2e2f"), + 15, + a2bs_hex( + "0AEE150802100122E7150801121408011210303132333435363738394142434445461" "A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0" "7A7D5076189EDFB68F05228E023082010A0282010100CC1715C81AD3F6F279C686F82" "6E6D7C8961EB13318367D06B4061BBC57E3C616A226A10F042CAD54D44C6484C725CD" @@ -856,14 +873,16 @@ LicenseInfo license_update_test_data[] = { "106B63746C0000000071FEF30B0000000020F4DFB68F051A2000351030900858FCFD6" "977B67803ADFD1280AA661E6B0BD30B08B2C4673551293A2E68747470733A2F2F7465" "73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7" - "36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD191015800122051F15CDA5B9414919D" - "B67769A781CC4F43138D314DAFFCBFBD620E53167E4AF2")}, + "36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD1910158006230000102030405060708" + "090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2" + "B2C2D2E2F680F122009B8588A8E9926339289BA373DB8479A71F7AA1164083D90613F" + "766D60B07CBC")}, // license being released. all fields are identical except for license // state and hashed file data {"", DeviceFiles::kLicenseStateReleasing, "", "", "", "", "", "", 0, 0, 0, - "", + "", "", 15, a2bs_hex( - "0ABA150802100122B3150802121408011210303132333435363738394142434445461" + "0AEE150802100122E7150802121408011210303132333435363738394142434445461" "A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0" "7A7D5076189EDFB68F05228E023082010A0282010100CC1715C81AD3F6F279C686F82" "6E6D7C8961EB13318367D06B4061BBC57E3C616A226A10F042CAD54D44C6484C725CD" @@ -942,8 +961,10 @@ LicenseInfo license_update_test_data[] = { "106B63746C0000000071FEF30B0000000020F4DFB68F051A2000351030900858FCFD6" "977B67803ADFD1280AA661E6B0BD30B08B2C4673551293A2E68747470733A2F2F7465" "73742E676F6F676C652E636F6D2F6C6963656E73652F47657443656E634C6963656E7" - "36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD191015800122093FDE0D42BC60D3932" - "02E0D5A49775E08093BF01560EF72C298321E921716E24")}}; + "36540EF9BAFCDF8ACD1910148DCFDAFCDF8ACD1910158006230000102030405060708" + "090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2" + "B2C2D2E2F680F12202F5B77A3168AC2A81832231A435D0587F6D1DF3B905A7058C5E8" + "565C81B96CA6")}}; // Application parameters were added to the License message. This data // is used to verify that a License saved without application parameters can @@ -1045,7 +1066,7 @@ LicenseInfo license_app_parameters_backwards_compatibility_test_data = { "0112001A16200342120A106B63746C0000000000ECDCBE0000000020DBDF" "A68F051A20182F029E35047A3841FA176C74E5B387350E8D58DEA6878FF0" "BEA6CABACA1C2C"), - "https://test.google.com/license/GetCencLicense", 0x0, 0x0, 0x0, "", + "https://test.google.com/license/GetCencLicense", 0x0, 0x0, 0x0, "", "", 0, a2bs_hex( "0AA8150802100122A1150801121408011210303132333435363738394142434445461" "A9D0E080112950C0AD70B080112EF090AB002080212103E560EC5335E346F591BC4D0" @@ -1129,286 +1150,702 @@ LicenseInfo license_app_parameters_backwards_compatibility_test_data = { "365400048001220CD0599C2B85D9F2D573AC7893CE77CB5A10B326828BA8C89047505" "A8C9B606AC")}; +struct DeviceFilesTestListUsageInfoData { + std::string file_name; + bool is_usage_info_file; +}; + +DeviceFilesTestListUsageInfoData kTestListUsageInfoData[] = { + {"ksid056fe6e9c.lic", false}, {"usage.bin", true}, + {"cert.bin", false}, {"usage345agdf==.bin", true}, + {"usageyd6e.lic", false}, {"usgtable.bin", false}, + {"usaget3ED.bin", true}, +}; + struct UsageInfo { - std::string provider_session_token; - std::string license_request; - std::string license; + std::string app_id; + DeviceFiles::CdmUsageData usage_data; std::string file_data; }; +std::string kEmptyUsageInfoFileData = a2bs_hex( + "0A06080310012A001220468A9A97B23D0E17147416276CB133175F0A18534155C6FFEF024A" + "D80371D7C4"); + UsageInfo kUsageInfoTestData[] = { - {"", "", "", // 0 usage info records + // test vector 0, app id: "", usage entry 0 + {"", + { + a2bs_hex("b8e7f26b6b8b59babf05b5a1f8927b412a85bc8551a928f00856329814ae" + "5a82"), + a2bs_hex("4463dc57079c27e34ae115c6f65b08f6311c4ea604a6512c42470b6f692a" + "76ea769d60d0b6bcf8d565ef31eb925f38e2095039c9f2f113ecee020f11" + "26eb30165372d538b551ebd7bae5cf0bbeebb3cdb6f180d42868051aab8f" + "f4947460dd96f0f8259fc6001059c998d2eb6902c064f9ae08e6cd3c7807" + "e50379507b41620d15dd76c0b1e7ed9417efd6825959b5077f464e6429a4" + "dee467a1ba2b05d38049912d5539f1ee9f5d8a569aa1c384384f847ed64f" + "6ffc101036da70e69c06e4916493e82e9fe3f65d85254c8c14f6ca0579bf" + "b3eaa86b2d7bb5ff572eccfd70f2ea4695f326beadf241ae4311e428c7c1" + "2a0d4d1915cd0537ff0f62cf13eb2fa2"), + a2bs_hex( + "bbe6b4b60da9d9bc34dcc8502fb81d8fd5fdbc8fa89605c86205f2b8c6530ff64" + "c8c31f579bd8eca603dfd5e397ac35e48931fd330351d01361bb31caaa7dbf816" + "a6144a12b6c22d1dceba20669ed635a40831066abd8071342119d7da11c43696b" + "2898d3df3b36beb8da013d9dc145343494b19d6da085f4a41e421d3def2ad8b72" + "dffffb6e79bbceaf8594045d16a62eed16904a3569860c531a32eaa5abb868b1d" + "d6a0b03d69c1a3f8336af80eb80badbbc7b80ca5943bd5b374302147052201fae" + "d30e9ffa99fc00b47f7eeb469512a413e873f91d959cccacccd3585b7f00ed8d4" + "685022101713c3adc439f27512a45926c1d2473477662c4bace72f380d105ddc9" + "f7be49ed71991b3d9e29a2038201373f98a845a57624a692f44ebd316d26c48fe" + "e82b655583317eca4aaa4dac841524a2dca111749629637ef29fa7e72645a8759" + "57fb3d98a5e6c8065b1349cfa23011cd6349d911c535475fed50be9eacb6a1ff3" + "ea7458823f0229f2fa90b7a7099e8ec349d3d0fe0277cba5157ca62c8fbaa893f" + "37b8c8cea2a0d2d2912caccf92d31aa17439479711b4a5b77445cc02c18bfe019" + "5ce8f1fe59e317214005cee5e25904c9fb0af7e2b6b4dccdf78e61e179a9f9399" + "6ec3c76f6da9b5a291bf08a73032b37edcbbbdd20cff94088a489167c56e5e75b" + "376874e9750212fe94d7656d9cd835db3b771ba5b58d2b255"), + "ksid854c8a60a5a547e122c24fca", + a2bs_hex("7d2b905e5eafd4b28aeeb7633283579e48add21a68eb26cc8c3b2e344579" + "003b12a38554336305525fa6ab70f024a18c73631bb1531eca3f0782c72d" + "ba017311b3f1e98c739632e305e4bc0b2561ae2b"), + 5, + }, a2bs_hex( - "0A06080210012A00122095053501C5FA405B7EF01DA94685C6B20CB36493" - "A9CF1653B720E2BEA3B77929")}, - {// 1 usage info record + "0AA407080310012A9D070A9A070A20B8E7F26B6B8B59BABF05B5A1F8927B412A85BC8" + "551A928F00856329814AE5A821280024463DC57079C27E34AE115C6F65B08F6311C4E" + "A604A6512C42470B6F692A76EA769D60D0B6BCF8D565EF31EB925F38E2095039C9F2F" + "113ECEE020F1126EB30165372D538B551EBD7BAE5CF0BBEEBB3CDB6F180D42868051A" + "AB8FF4947460DD96F0F8259FC6001059C998D2EB6902C064F9AE08E6CD3C7807E5037" + "9507B41620D15DD76C0B1E7ED9417EFD6825959B5077F464E6429A4DEE467A1BA2B05" + "D38049912D5539F1EE9F5D8A569AA1C384384F847ED64F6FFC101036DA70E69C06E49" + "16493E82E9FE3F65D85254C8C14F6CA0579BFB3EAA86B2D7BB5FF572ECCFD70F2EA46" + "95F326BEADF241AE4311E428C7C12A0D4D1915CD0537FF0F62CF13EB2FA21A8004BBE" + "6B4B60DA9D9BC34DCC8502FB81D8FD5FDBC8FA89605C86205F2B8C6530FF64C8C31F5" + "79BD8ECA603DFD5E397AC35E48931FD330351D01361BB31CAAA7DBF816A6144A12B6C" + "22D1DCEBA20669ED635A40831066ABD8071342119D7DA11C43696B2898D3DF3B36BEB" + "8DA013D9DC145343494B19D6DA085F4A41E421D3DEF2AD8B72DFFFFB6E79BBCEAF859" + "4045D16A62EED16904A3569860C531A32EAA5ABB868B1DD6A0B03D69C1A3F8336AF80" + "EB80BADBBC7B80CA5943BD5B374302147052201FAED30E9FFA99FC00B47F7EEB46951" + "2A413E873F91D959CCCACCCD3585B7F00ED8D4685022101713C3ADC439F27512A4592" + "6C1D2473477662C4BACE72F380D105DDC9F7BE49ED71991B3D9E29A2038201373F98A" + "845A57624A692F44EBD316D26C48FEE82B655583317ECA4AAA4DAC841524A2DCA1117" + "49629637EF29FA7E72645A875957FB3D98A5E6C8065B1349CFA23011CD6349D911C53" + "5475FED50BE9EACB6A1FF3EA7458823F0229F2FA90B7A7099E8EC349D3D0FE0277CBA" + "5157CA62C8FBAA893F37B8C8CEA2A0D2D2912CACCF92D31AA17439479711B4A5B7744" + "5CC02C18BFE0195CE8F1FE59E317214005CEE5E25904C9FB0AF7E2B6B4DCCDF78E61E" + "179A9F93996EC3C76F6DA9B5A291BF08A73032B37EDCBBBDD20CFF94088A489167C56" + "E5E75B376874E9750212FE94D7656D9CD835DB3B771BA5B58D2B255221C6B73696438" + "35346338613630613561353437653132326332346663612A507D2B905E5EAFD4B28AE" + "EB7633283579E48ADD21A68EB26CC8C3B2E344579003B12A38554336305525FA6AB70" + "F024A18C73631BB1531ECA3F0782C72DBA017311B3F1E98C739632E305E4BC0B2561A" + "E2B30051220BCA71B49A97A2CFD5A3C4619807FE9EFCB68F9C69C4D63254FF10B22F1" + "13FA82")}, + // test vector 1, app id: "", usage entry 1 + {"", + { + a2bs_hex("5d637be37a9722aa35c23d346470851aca7d2edcd1a27edf124ea6"), + a2bs_hex("bc96d6878e3086c33624821f1f3ece23f27e58222c2bb8d1615476a11792" + "63b58f6427e92911d961fc7a3afd947aed8c9aead1f08457925d2ce4e0f6" + "18b21942baa60b231eae864048f94f74ffa700e5777f812adb6f0cb6ba6f" + "0d145e3951191eb217140c32f2c7565053222131ff823bc36d80b24b561c" + "cbea9d397fe00942e7ff73b8152cdc083b63a9f9c4a77056a0d79f44f267" + "da0ed629d9c902f7e838957ea41aa442221c3aa9410db58302b468c6d7f2" + "113663809f0dcf187c108ced"), + a2bs_hex("f6fb3693413cc1d7d5e3459b856e4156c78f8d85d548939fd00474c8cded" + "c46835cc981758500fe61cc79383b4d9f87c3e33d19c2d25d7d15dd0f3d2" + "b1af4583b71e90c59886d297e78e929c2f3840c82c626914a4eb537b3a51" + "61d963472b6592c0fa1e415556bc009c2da22bcf743ac434e22f8a33b432" + "10dfd8aa09fe86105610f366e6fb7da18996cf7c7db425a9bb50e4a13190" + "a680b9f82d37d09658585abe3bf9f009a5c1ce38a7cefe17f71fb402768b" + "2d66b4ca523ed06729349695d7864d7cf7a1cc11d0da2b8a43db834d10b4" + "7d9579ec9e46986a133277b92c636cb2a6a823afe73317266c9c0601ddba" + "db76e1d254d6183b93a1ea91a7e6c567331b3ee3a5ab1484af91fd0f8dac" + "5fd980a67d8f33cc1d6cf20ee4c24582d03967ed48b6f28e7514e4d18f38" + "c8cb1e54fba59af7d6a79c6c5a7ab06baac964c7958d201910adca018022" + "fbeb8535b64f5ce83d3c"), + "ksid2f2e85ce8a677f60047d7024e07b5ae6", + a2bs_hex("b74880fbddc5bb9db82f09bc7de3ffd95a0a671b979d4c1f0564eaf63eb6" + "b5a8c3f16d9f964afbd011e2326f9c27afbe74536f3f0601a71d9c1c422f" + "335611bf3bf1a1c89e2dea27c17a9d9a58a74121e840b002e8a6fb590072" + "45be786c1f64"), + 9, + }, a2bs_hex( - "924B035FBDA56AE5EF0ED05A08DE7AECC8ABE1835E0C4A548F7803937F4C3B4520EB7" - "F3334FFCDFA00DE56408F09D5019FCE87072D0DC6789817468974B2EA51EE3944B8D7" - "E0A88E4F16EBB80F03BD845231A01E6146841CBAEF0134DCD9300DB2D92732992C0F2" - "310D8E386FB31C67B9477010DEF9D99C4272589572A26A17E"), + "0AE80C080310012AE10C0A9A070A20B8E7F26B6B8B59BABF05B5A1F8927B412A85BC8" + "551A928F00856329814AE5A821280024463DC57079C27E34AE115C6F65B08F6311C4E" + "A604A6512C42470B6F692A76EA769D60D0B6BCF8D565EF31EB925F38E2095039C9F2F" + "113ECEE020F1126EB30165372D538B551EBD7BAE5CF0BBEEBB3CDB6F180D42868051A" + "AB8FF4947460DD96F0F8259FC6001059C998D2EB6902C064F9AE08E6CD3C7807E5037" + "9507B41620D15DD76C0B1E7ED9417EFD6825959B5077F464E6429A4DEE467A1BA2B05" + "D38049912D5539F1EE9F5D8A569AA1C384384F847ED64F6FFC101036DA70E69C06E49" + "16493E82E9FE3F65D85254C8C14F6CA0579BFB3EAA86B2D7BB5FF572ECCFD70F2EA46" + "95F326BEADF241AE4311E428C7C12A0D4D1915CD0537FF0F62CF13EB2FA21A8004BBE" + "6B4B60DA9D9BC34DCC8502FB81D8FD5FDBC8FA89605C86205F2B8C6530FF64C8C31F5" + "79BD8ECA603DFD5E397AC35E48931FD330351D01361BB31CAAA7DBF816A6144A12B6C" + "22D1DCEBA20669ED635A40831066ABD8071342119D7DA11C43696B2898D3DF3B36BEB" + "8DA013D9DC145343494B19D6DA085F4A41E421D3DEF2AD8B72DFFFFB6E79BBCEAF859" + "4045D16A62EED16904A3569860C531A32EAA5ABB868B1DD6A0B03D69C1A3F8336AF80" + "EB80BADBBC7B80CA5943BD5B374302147052201FAED30E9FFA99FC00B47F7EEB46951" + "2A413E873F91D959CCCACCCD3585B7F00ED8D4685022101713C3ADC439F27512A4592" + "6C1D2473477662C4BACE72F380D105DDC9F7BE49ED71991B3D9E29A2038201373F98A" + "845A57624A692F44EBD316D26C48FEE82B655583317ECA4AAA4DAC841524A2DCA1117" + "49629637EF29FA7E72645A875957FB3D98A5E6C8065B1349CFA23011CD6349D911C53" + "5475FED50BE9EACB6A1FF3EA7458823F0229F2FA90B7A7099E8EC349D3D0FE0277CBA" + "5157CA62C8FBAA893F37B8C8CEA2A0D2D2912CACCF92D31AA17439479711B4A5B7744" + "5CC02C18BFE0195CE8F1FE59E317214005CEE5E25904C9FB0AF7E2B6B4DCCDF78E61E" + "179A9F93996EC3C76F6DA9B5A291BF08A73032B37EDCBBBDD20CFF94088A489167C56" + "E5E75B376874E9750212FE94D7656D9CD835DB3B771BA5B58D2B255221C6B73696438" + "35346338613630613561353437653132326332346663612A507D2B905E5EAFD4B28AE" + "EB7633283579E48ADD21A68EB26CC8C3B2E344579003B12A38554336305525FA6AB70" + "F024A18C73631BB1531ECA3F0782C72DBA017311B3F1E98C739632E305E4BC0B2561A" + "E2B30050AC1050A1B5D637BE37A9722AA35C23D346470851ACA7D2EDCD1A27EDF124E" + "A612C001BC96D6878E3086C33624821F1F3ECE23F27E58222C2BB8D1615476A117926" + "3B58F6427E92911D961FC7A3AFD947AED8C9AEAD1F08457925D2CE4E0F618B21942BA" + "A60B231EAE864048F94F74FFA700E5777F812ADB6F0CB6BA6F0D145E3951191EB2171" + "40C32F2C7565053222131FF823BC36D80B24B561CCBEA9D397FE00942E7FF73B8152C" + "DC083B63A9F9C4A77056A0D79F44F267DA0ED629D9C902F7E838957EA41AA442221C3" + "AA9410DB58302B468C6D7F2113663809F0DCF187C108CED1AD402F6FB3693413CC1D7" + "D5E3459B856E4156C78F8D85D548939FD00474C8CDEDC46835CC981758500FE61CC79" + "383B4D9F87C3E33D19C2D25D7D15DD0F3D2B1AF4583B71E90C59886D297E78E929C2F" + "3840C82C626914A4EB537B3A5161D963472B6592C0FA1E415556BC009C2DA22BCF743" + "AC434E22F8A33B43210DFD8AA09FE86105610F366E6FB7DA18996CF7C7DB425A9BB50" + "E4A13190A680B9F82D37D09658585ABE3BF9F009A5C1CE38A7CEFE17F71FB402768B2" + "D66B4CA523ED06729349695D7864D7CF7A1CC11D0DA2B8A43DB834D10B47D9579EC9E" + "46986A133277B92C636CB2A6A823AFE73317266C9C0601DDBADB76E1D254D6183B93A" + "1EA91A7E6C567331B3EE3A5AB1484AF91FD0F8DAC5FD980A67D8F33CC1D6CF20EE4C2" + "4582D03967ED48B6F28E7514E4D18F38C8CB1E54FBA59AF7D6A79C6C5A7AB06BAAC96" + "4C7958D201910ADCA018022FBEB8535B64F5CE83D3C22246B73696432663265383563" + "653861363737663630303437643730323465303762356165362A60B74880FBDDC5BB9" + "DB82F09BC7DE3FFD95A0A671B979D4C1F0564EAF63EB6B5A8C3F16D9F964AFBD011E2" + "326F9C27AFBE74536F3F0601A71D9C1C422F335611BF3BF1A1C89E2DEA27C17A9D9A5" + "8A74121E840B002E8A6FB59007245BE786C1F6430091220B9626315C7601BC2BD1E1C" + "88F752C956261CE7509669B2AEAA1E7F1304017941")}, + // test vector 2, app id: "app_1", usage entry 0 + {"app_1", + { + a2bs_hex("bb3370ccd3c3c49573d6b74386d1886d9888bd81fe3241bcd2bac9407d1a" + "834e"), + a2bs_hex("dc0e51cfa5863f6c0b32a4ad7fa40625dadcc2dcde9e7fa3983b8804d996" + "6803181682fc8ae831472e0b2fc26276242fbce624d286eedecce5555804" + "913b4f8f86c5ae86160b8434b109169a63da04c5265102d772c1180543ef" + "226d2140357aca6cf87da3f7e370dfc08ca92a1f7c7d314eab36292a9170" + "8f6c6ad84b37ee1c7dfafb99289206cb752d063f330efd85885f4b72ba1c" + "a5823eed865a461345e3d6417872bf3b0608b3d9e1004c11e7326d3ed406" + "192e13455d0ec4e1f558a147"), + a2bs_hex("f42a68ca3a14fb68f5992e4519f57970c3dae73f8da1d5b0b1da3eff7a95" + "4012a0dc634357f3f5477a820e182182f24ae8e835ab10c18386cc8a0727" + "d3f38b628639bfbd69a94d4053eab1c31e075e014cc578b226cfe24d6b42" + "db242972def8f23a4aae88451307c2abaf54c1803ae54e3f1149aa6e6d42" + "88cc7d474e876be07954e8b2deff4ade4bf30229fb6c92df4d66cd463f68" + "6b4754b940210eb59f1581d658ddf8de8389e0e2d123e2cae3c2be6eb194" + "8ccc896dd4cdf45f9090c96dfb925795cfb4ccda83e3eb4f745577b17fc1" + "66bf5f4103c9085134cad7863a41b04f32ef20201e54b55f1817ce589619" + "b096c254fd2c2fa4a06f4de35ccfd23e"), + "kside11109bf20cde544083ef4ee", + a2bs_hex("ea106c124476b753d39368a5966972a2729bb8bbea734a2b3e812b705eac" + "e016c8a03c9a406094d80059ef4ca26f1928fa2daa5de9a6f22372e5c7a9" + "41e610d1efb56ed7ce2228a70e2e150afb66edc2da066d463aa90ba0caff" + "078fbfec05c8"), + 0, + }, + a2bs_hex("0AF404080310012AED040AEA040A20BB3370CCD3C3C49573D6B74386D1886D98" + "88BD81FE3241BCD2BAC9407D1A834E12C001DC0E51CFA5863F6C0B32A4AD7FA4" + "0625DADCC2DCDE9E7FA3983B8804D9966803181682FC8AE831472E0B2FC26276" + "242FBCE624D286EEDECCE5555804913B4F8F86C5AE86160B8434B109169A63DA" + "04C5265102D772C1180543EF226D2140357ACA6CF87DA3F7E370DFC08CA92A1F" + "7C7D314EAB36292A91708F6C6AD84B37EE1C7DFAFB99289206CB752D063F330E" + "FD85885F4B72BA1CA5823EED865A461345E3D6417872BF3B0608B3D9E1004C11" + "E7326D3ED406192E13455D0EC4E1F558A1471A8002F42A68CA3A14FB68F5992E" + "4519F57970C3DAE73F8DA1D5B0B1DA3EFF7A954012A0DC634357F3F5477A820E" + "182182F24AE8E835AB10C18386CC8A0727D3F38B628639BFBD69A94D4053EAB1" + "C31E075E014CC578B226CFE24D6B42DB242972DEF8F23A4AAE88451307C2ABAF" + "54C1803AE54E3F1149AA6E6D4288CC7D474E876BE07954E8B2DEFF4ADE4BF302" + "29FB6C92DF4D66CD463F686B4754B940210EB59F1581D658DDF8DE8389E0E2D1" + "23E2CAE3C2BE6EB1948CCC896DD4CDF45F9090C96DFB925795CFB4CCDA83E3EB" + "4F745577B17FC166BF5F4103C9085134CAD7863A41B04F32EF20201E54B55F18" + "17CE589619B096C254FD2C2FA4A06F4DE35CCFD23E221C6B7369646531313130" + "396266323063646535343430383365663465652A60EA106C124476B753D39368" + "A5966972A2729BB8BBEA734A2B3E812B705EACE016C8A03C9A406094D80059EF" + "4CA26F1928FA2DAA5DE9A6F22372E5C7A941E610D1EFB56ED7CE2228A70E2E15" + "0AFB66EDC2DA066D463AA90BA0CAFF078FBFEC05C8300012203384AAAFD3A883" + "17E6ED20BB88B0B3C01388AB1DF721547AE6FCB586659BC437")}, + // test vector 3, app id: "app_2", usage entry 0 + {"app_2", + { + a2bs_hex( + "9212a6926f21c6727c1ee89d5607047a1636f206f70e21fda86e01b6a4b5"), + a2bs_hex("ef947abed64078edf5b21fe6d3fb65384595d63a6d03e4d1d397c5019dee" + "b6890d3ef8773002b91e255af0820fb594069df55d8abf96498e493f5c70" + "f6b85f50e12a1ed3c039ad0cd838fe44d3fa9e2bbddeb2919041203111ed" + "7778701b04d6b15f41d0bde799e20a38b27bf96fdbe844f10364baeb5935" + "96220993c608ac793de76c237ca350931a7e216538074dbd83ddf262d9f1" + "8acd91e1ea5372f7e773c5b64333"), + a2bs_hex("7709721b3aa48597e88c99e82eaf7dff07e87e0318d9d7cec29096ec5918" + "26aa7a359316d6de1d1329b408543e237de84c986987ead1bb6a0c38817e" + "93013e5c989d366f49590b834453ec64b7433bf0b3335b9e222bad4caf55" + "4d69575c58595283166fea42e89645fc7e2d3ac9e0c1399b096cf3fed1e5" + "deb1bc4e0ee894f0ae3f929dd7dba4530e5655edbbf6041df430482eb2e8" + "91b6a93af84d3c16dbad92733ffd34e8f4ce24506bead578d20cd3e291c2" + "fc2f811db875f49abc21a24277d2ba474fe6af6c14021cfead5513e0999e" + "094020ce08209bbc08f13fe2b96d7ba8213c8e9c85b6a623788d34da794e" + "17e4cd3bd65680b97fb30bad64ddc42b1bcfb0b83e5dda3501a5902ca609" + "f41837a0d5cd096e0659b67c"), + "ksid62d88ed7b292217b0238be", + a2bs_hex("5422463fd2e4dd47626e97dd6b4ee0b89523aaebe8d11e7e7be703ef01e4" + "9b17eaf020cede0a9e0e7b5d91e4db7abdce445936cb2deecdefefdb14b7" + "8f67b7ca5c733c9e88446fd814584584b86becbf6eb2b0e3d5603e8b"), + 25, + }, a2bs_hex( - "1E6FFBE66FC6153E7749906EC8F684E819467E16CAF317F315DB32B6D3FDD1A8E8A09" - "4174D92D063B88E4835EAB78BD09541EA7FE72F132EB7364E154BC1548FC40EC70927" - "75531508C95F9ED5D76F36BC0C198C3A33A1F9415B343905D6BE37645E6800F053B1D" - "A9A20286EFCBBC320424ADF7FB6E3D5D8E86C35E576A1A2A37D344A419C0F0034A1B5" - "F767D3C61D90DCA1119E5024C34EDE8FA7DD128696D8C435410F218E52A853AD214FD" - "05D0F8B3CB4832CFCD97FE159E6DEE64CE82CDAEC0321AE71B3BCBAE42DF9EA65E42E" - "151827086EADE71C138B972CC3992CF9ADA944C063816352ED8658D3FA07BE0F32239" - "E74A65932B069AAC4E8386DB59154AF9AEF71448128C66E510445294F44E511BD9B1A" - "F19D4D67E99363093BE888D4B2AB841CAFF252CAD13EDF8E"), + "0AE604080310012ADF040ADC040A1E9212A6926F21C6727C1EE89D5607047A1636F20" + "6F70E21FDA86E01B6A4B512A401EF947ABED64078EDF5B21FE6D3FB65384595D63A6D" + "03E4D1D397C5019DEEB6890D3EF8773002B91E255AF0820FB594069DF55D8ABF96498" + "E493F5C70F6B85F50E12A1ED3C039AD0CD838FE44D3FA9E2BBDDEB2919041203111ED" + "7778701B04D6B15F41D0BDE799E20A38B27BF96FDBE844F10364BAEB593596220993C" + "608AC793DE76C237CA350931A7E216538074DBD83DDF262D9F18ACD91E1EA5372F7E7" + "73C5B643331A9A027709721B3AA48597E88C99E82EAF7DFF07E87E0318D9D7CEC2909" + "6EC591826AA7A359316D6DE1D1329B408543E237DE84C986987EAD1BB6A0C38817E93" + "013E5C989D366F49590B834453EC64B7433BF0B3335B9E222BAD4CAF554D69575C585" + "95283166FEA42E89645FC7E2D3AC9E0C1399B096CF3FED1E5DEB1BC4E0EE894F0AE3F" + "929DD7DBA4530E5655EDBBF6041DF430482EB2E891B6A93AF84D3C16DBAD92733FFD3" + "4E8F4CE24506BEAD578D20CD3E291C2FC2F811DB875F49ABC21A24277D2BA474FE6AF" + "6C14021CFEAD5513E0999E094020CE08209BBC08F13FE2B96D7BA8213C8E9C85B6A62" + "3788D34DA794E17E4CD3BD65680B97FB30BAD64DDC42B1BCFB0B83E5DDA3501A5902C" + "A609F41837A0D5CD096E0659B67C221A6B73696436326438386564376232393232313" + "7623032333862652A585422463FD2E4DD47626E97DD6B4EE0B89523AAEBE8D11E7E7B" + "E703EF01E49B17EAF020CEDE0A9E0E7B5D91E4DB7ABDCE445936CB2DEECDEFEFDB14B" + "78F67B7CA5C733C9E88446FD814584584B86BECBF6EB2B0E3D5603E8B30191220E964" + "7EB0AC28F0CB11C85111D69B5FA74E80015F4A07FB5C144E6CFE0E8E3709")}, + // test vector 4, app id: "app_2", usage entry 1 + {"app_2", + { + a2bs_hex("831fad51e52a403524539eab6a1b201e46674ca3b9167b1c1b53f5e5e3"), + a2bs_hex("36d83acbc5e4ed027ed583e3b2169d98f4abedda15b781408e68efa14fef" + "a9f3f0309bcb5a9fff6580636ebe3548e5acb43b76cfeb29a9c86324e62a" + "eb40556005c6686e718f9bf61b0681d43b5b1e88084b3aea27a6b0e844e5" + "500b6fcfacf2ee44d6af7f64154ab3fd4fbd0b8056cf63971076a1eb3642" + "b78d5e76b84f4ed9f6220089863f8a4911691e79feffc9f804c4c36c7f85" + "e45b1d276c85875875267eb65da70fd2d5e9176d6914"), + a2bs_hex("317bd7063bfb9fae1b2e46f4cf15b7bc8c92517ff5c32cbb52ae4b67afc5" + "d569cb66a462def7a18a7d0acebf9f6e8a604356ade2c81450c5466a4728" + "90b03eefcf65388f060e24551c67b7d46ae5d4d841d5cc63d137fd543fae" + "2c771756590b90e480ca0126f1fc0090ace62499e47569fc52196c788f80" + "139755bdf12a7acb29fd6e23a46a4c036f04ff1ed6cd714094253bf1c587" + "62c93f0ddf8a73c4be927ffec2723a16d8ffe5128851f58537461275f6aa" + "1976e3b399b7243919207e040ec16c5328e8ab082278fce0e5d3df5c5f92" + "dba51fa6613587d4ece31f2c001b49bfaed434f9512e895c2e09c88ddbf1" + "84bfafe4d82e5d05a26ac06cde29faf6ab05b96685649c923779ce5ef7f3" + "16531ada8e74e45ab1dc1d75648aa2de052674728867e87639ff9b782a3" + "3"), + "kside1d30b33b55f2deb4716", + a2bs_hex("d44a9d70a7c582559f089b1c0fdfcbdaf5e26b672fca5d58e889b407a0ba" + "8599079cde11fadfab23aa1b97622839f3b7e1a96f8332bec5fbcbc9eb64" + "fd5ed05887b8fa3bfd6ecc7bc91e621342732062d2f4411b763e20328af6" + "f8ef5030e2f8027aef9e"), + 6, + }, a2bs_hex( - "40FC62339728520E6C0C09907C26F3FB78287231661952A8B699E47AE241B999C029F" - "D2067836DC4BC64F66998A3ECD197DAE36F808A2E5A4C5BF25DD580E52B1C39A8B037" - "72BF82D58929766F2DA04F0E616F92B3A0EB75661B8FF5DE1EB807C990F9E6BA991C8" - "BAD5EB63B37E8663A4E22AA9DB2015D3DF8BED1C8313F85F13B9483C7A39C592436C8" - "B13C23F78F55CE812795335059F7F527CA580306A0AEE5A6D957A91F498F64AA2EFC6" - "780716400E17C7EEA30B2E6523B902986995E003C2D919A7DC7C0122CE9410037A660" - "2B59A63B5C89473D4E02DE35C1F01B12ADB48A3D94D43693F08268FECCC78DAF6F4C3" - "5FA32C538CD73FBF3CEA274B01179C02473486311956E5A0C78E44C59B2F34FF24B06" - "53A6379A2F5F6F51467CAE26D55CC5BBDCFC9BCFA7B8C5CBF82EBE7BD340C3DAE6374" - "D0692052C529AA33D7A6799C8F1F59C78575E51F707013026CC4F83F6B3328EE6FB1A" - "C91929A4491338E93D10EE6193014A73BA241A9A833EA835217894EB4FD4BDB8904A5" - "999928325D0AC31B6D58609EDD9D85E88F74B5BD6FA7BDD83C51EEB91633ED267ACA2" - "E103904BBE4C031A6483858FBAD74DACD01711F7B882749FFFBA0DB6C7D7109D82989" - "C7D4DB5A0F1E7506AC24C89CECAF231EFF99F96AD76E57DABDD3C2DFBA7BAA869A771" - "F561B165987E552824B0C914E708E425C3"), + "0AE809080310012AE1090ADC040A1E9212A6926F21C6727C1EE89D5607047A1636F20" + "6F70E21FDA86E01B6A4B512A401EF947ABED64078EDF5B21FE6D3FB65384595D63A6D" + "03E4D1D397C5019DEEB6890D3EF8773002B91E255AF0820FB594069DF55D8ABF96498" + "E493F5C70F6B85F50E12A1ED3C039AD0CD838FE44D3FA9E2BBDDEB2919041203111ED" + "7778701B04D6B15F41D0BDE799E20A38B27BF96FDBE844F10364BAEB593596220993C" + "608AC793DE76C237CA350931A7E216538074DBD83DDF262D9F18ACD91E1EA5372F7E7" + "73C5B643331A9A027709721B3AA48597E88C99E82EAF7DFF07E87E0318D9D7CEC2909" + "6EC591826AA7A359316D6DE1D1329B408543E237DE84C986987EAD1BB6A0C38817E93" + "013E5C989D366F49590B834453EC64B7433BF0B3335B9E222BAD4CAF554D69575C585" + "95283166FEA42E89645FC7E2D3AC9E0C1399B096CF3FED1E5DEB1BC4E0EE894F0AE3F" + "929DD7DBA4530E5655EDBBF6041DF430482EB2E891B6A93AF84D3C16DBAD92733FFD3" + "4E8F4CE24506BEAD578D20CD3E291C2FC2F811DB875F49ABC21A24277D2BA474FE6AF" + "6C14021CFEAD5513E0999E094020CE08209BBC08F13FE2B96D7BA8213C8E9C85B6A62" + "3788D34DA794E17E4CD3BD65680B97FB30BAD64DDC42B1BCFB0B83E5DDA3501A5902C" + "A609F41837A0D5CD096E0659B67C221A6B73696436326438386564376232393232313" + "7623032333862652A585422463FD2E4DD47626E97DD6B4EE0B89523AAEBE8D11E7E7B" + "E703EF01E49B17EAF020CEDE0A9E0E7B5D91E4DB7ABDCE445936CB2DEECDEFEFDB14B" + "78F67B7CA5C733C9E88446FD814584584B86BECBF6EB2B0E3D5603E8B30190AFF040A" + "1D831FAD51E52A403524539EAB6A1B201E46674CA3B9167B1C1B53F5E5E312AC0136D" + "83ACBC5E4ED027ED583E3B2169D98F4ABEDDA15B781408E68EFA14FEFA9F3F0309BCB" + "5A9FFF6580636EBE3548E5ACB43B76CFEB29A9C86324E62AEB40556005C6686E718F9" + "BF61B0681D43B5B1E88084B3AEA27A6B0E844E5500B6FCFACF2EE44D6AF7F64154AB3" + "FD4FBD0B8056CF63971076A1EB3642B78D5E76B84F4ED9F6220089863F8A4911691E7" + "9FEFFC9F804C4C36C7F85E45B1D276C85875875267EB65DA70FD2D5E9176D69141AAC" + "02317BD7063BFB9FAE1B2E46F4CF15B7BC8C92517FF5C32CBB52AE4B67AFC5D569CB6" + "6A462DEF7A18A7D0ACEBF9F6E8A604356ADE2C81450C5466A472890B03EEFCF65388F" + "060E24551C67B7D46AE5D4D841D5CC63D137FD543FAE2C771756590B90E480CA0126F" + "1FC0090ACE62499E47569FC52196C788F80139755BDF12A7ACB29FD6E23A46A4C036F" + "04FF1ED6CD714094253BF1C58762C93F0DDF8A73C4BE927FFEC2723A16D8FFE512885" + "1F58537461275F6AA1976E3B399B7243919207E040EC16C5328E8AB082278FCE0E5D3" + "DF5C5F92DBA51FA6613587D4ECE31F2C001B49BFAED434F9512E895C2E09C88DDBF18" + "4BFAFE4D82E5D05A26AC06CDE29FAF6AB05B96685649C923779CE5EF7F316531ADA8E" + "74E45AB1DC1D75648AA2DE052674728867E87639FF9B782A3322186B7369646531643" + "3306233336235356632646562343731362A64D44A9D70A7C582559F089B1C0FDFCBDA" + "F5E26B672FCA5D58E889B407A0BA8599079CDE11FADFAB23AA1B97622839F3B7E1A96" + "F8332BEC5FBCBC9EB64FD5ED05887B8FA3BFD6ECC7BC91E621342732062D2F4411B76" + "3E20328AF6F8EF5030E2F8027AEF9E300612203F1EEC1DDC56EE480AC744C1D72379E" + "AFFD4675FF15A7D53BD56AC9736D62FC1")}, + // test vector 5, app id: "app_1", usage entry 1 + {"app_1", + { + a2bs_hex("eace80e30bfda213f1ce4dbcfd9d4d24b8e2ae00054d167d9d7ae9954706" + "2b9113"), + a2bs_hex("68a7665a21348fc0590328608dc520be40f5b749328568fe383ef69c1a58" + "7ab2446cf9c41d821373d0856a883b316519a42218f80e7bd5764d16bac9" + "a9b427a7278f5940e563fcf6dee0ff3aadbb702ebf2c54ec354ae7acc84e" + "e6a54bca1f30e38ef71c44a81d0009b1484feaf4f1a56f58c35bb2372c80" + "c6dfb389e6de60bdd3d46c03975715260f6fdbe42facb64c22eda9635c04" + "da79434e1c41fbd2fdbbab6598283226c0278e8e0a96d780d3436523efd" + "1"), + a2bs_hex("ff6fecf7157828812a2d6dcb15383a6d9af4519ef804c6053a10c436002d" + "e3a4efcc017755f4ad1101bdc813e2d211732418dee529cbb413c48aa588" + "4c76a5c6f556a715055560d4247f5bf310956949a3a171a4aa608a484468" + "84e7676d558ff64d392b84e617805693d90f1e9b7b540c383d384d7f7ce0" + "6c23618681bd838ceb1a514047f1c562c43159cc5e21588fbfce8a354111" + "160f1a1e2bd3d798a000579bdfdb977252809ee1502df8045972fe8aac84" + "0211c2f8d9e4d5be18509c327c647d654c4b6cc430b98f1ff37c96fab087" + "fb561b8cc18480f877c873594d3148ff74b0e3c6327c27ca876dae742239" + "8fc5e85269cba49ad099"), + "ksid8e80350cbef6463a0025e6cc", + a2bs_hex("7ccc7ce96055e16a52fa192ea2cf3c9df3e89b9133a52286f71e6c6d82d0" + "435f6b2155dfde590b347d8c86f62d7dfbaae640c237256f609e5da9cc6c" + "103465fe3441612bbdfdf4d1c24b2147feb8565cef4993e439c9d564a39a" + "4ac5bb1da69acb44da06e4522c9a93d310cdda5dac1e1e0b91abff41e4e2" + "edda4001"), + 7, + }, a2bs_hex( - "0AB307080210012AAC070AA9070A8001924B035FBDA56AE5EF0ED05A08DE7AECC8ABE" - "1835E0C4A548F7803937F4C3B4520EB7F3334FFCDFA00DE56408F09D5019FCE87072D" - "0DC6789817468974B2EA51EE3944B8D7E0A88E4F16EBB80F03BD845231A01E6146841" - "CBAEF0134DCD9300DB2D92732992C0F2310D8E386FB31C67B9477010DEF9D99C42725" - "89572A26A17E12AC021E6FFBE66FC6153E7749906EC8F684E819467E16CAF317F315D" - "B32B6D3FDD1A8E8A094174D92D063B88E4835EAB78BD09541EA7FE72F132EB7364E15" - "4BC1548FC40EC7092775531508C95F9ED5D76F36BC0C198C3A33A1F9415B343905D6B" - "E37645E6800F053B1DA9A20286EFCBBC320424ADF7FB6E3D5D8E86C35E576A1A2A37D" - "344A419C0F0034A1B5F767D3C61D90DCA1119E5024C34EDE8FA7DD128696D8C435410" - "F218E52A853AD214FD05D0F8B3CB4832CFCD97FE159E6DEE64CE82CDAEC0321AE71B3" - "BCBAE42DF9EA65E42E151827086EADE71C138B972CC3992CF9ADA944C063816352ED8" - "658D3FA07BE0F32239E74A65932B069AAC4E8386DB59154AF9AEF71448128C66E5104" - "45294F44E511BD9B1AF19D4D67E99363093BE888D4B2AB841CAFF252CAD13EDF8E1AF" - "40340FC62339728520E6C0C09907C26F3FB78287231661952A8B699E47AE241B999C0" - "29FD2067836DC4BC64F66998A3ECD197DAE36F808A2E5A4C5BF25DD580E52B1C39A8B" - "03772BF82D58929766F2DA04F0E616F92B3A0EB75661B8FF5DE1EB807C990F9E6BA99" - "1C8BAD5EB63B37E8663A4E22AA9DB2015D3DF8BED1C8313F85F13B9483C7A39C59243" - "6C8B13C23F78F55CE812795335059F7F527CA580306A0AEE5A6D957A91F498F64AA2E" - "FC6780716400E17C7EEA30B2E6523B902986995E003C2D919A7DC7C0122CE9410037A" - "6602B59A63B5C89473D4E02DE35C1F01B12ADB48A3D94D43693F08268FECCC78DAF6F" - "4C35FA32C538CD73FBF3CEA274B01179C02473486311956E5A0C78E44C59B2F34FF24" - "B0653A6379A2F5F6F51467CAE26D55CC5BBDCFC9BCFA7B8C5CBF82EBE7BD340C3DAE6" - "374D0692052C529AA33D7A6799C8F1F59C78575E51F707013026CC4F83F6B3328EE6F" - "B1AC91929A4491338E93D10EE6193014A73BA241A9A833EA835217894EB4FD4BDB890" - "4A5999928325D0AC31B6D58609EDD9D85E88F74B5BD6FA7BDD83C51EEB91633ED267A" - "CA2E103904BBE4C031A6483858FBAD74DACD01711F7B882749FFFBA0DB6C7D7109D82" - "989C7D4DB5A0F1E7506AC24C89CECAF231EFF99F96AD76E57DABDD3C2DFBA7BAA869A" - "771F561B165987E552824B0C914E708E425C3122051C8F84C5713500997DC5B325BAE" - "D208B224DFAEB2B034E58046A62F503FED6E")}, - {// 2 usage info records + "0AEC09080310012AE5090AEA040A20BB3370CCD3C3C49573D6B74386D1886D9888BD8" + "1FE3241BCD2BAC9407D1A834E12C001DC0E51CFA5863F6C0B32A4AD7FA40625DADCC2" + "DCDE9E7FA3983B8804D9966803181682FC8AE831472E0B2FC26276242FBCE624D286E" + "EDECCE5555804913B4F8F86C5AE86160B8434B109169A63DA04C5265102D772C11805" + "43EF226D2140357ACA6CF87DA3F7E370DFC08CA92A1F7C7D314EAB36292A91708F6C6" + "AD84B37EE1C7DFAFB99289206CB752D063F330EFD85885F4B72BA1CA5823EED865A46" + "1345E3D6417872BF3B0608B3D9E1004C11E7326D3ED406192E13455D0EC4E1F558A14" + "71A8002F42A68CA3A14FB68F5992E4519F57970C3DAE73F8DA1D5B0B1DA3EFF7A9540" + "12A0DC634357F3F5477A820E182182F24AE8E835AB10C18386CC8A0727D3F38B62863" + "9BFBD69A94D4053EAB1C31E075E014CC578B226CFE24D6B42DB242972DEF8F23A4AAE" + "88451307C2ABAF54C1803AE54E3F1149AA6E6D4288CC7D474E876BE07954E8B2DEFF4" + "ADE4BF30229FB6C92DF4D66CD463F686B4754B940210EB59F1581D658DDF8DE8389E0" + "E2D123E2CAE3C2BE6EB1948CCC896DD4CDF45F9090C96DFB925795CFB4CCDA83E3EB4" + "F745577B17FC166BF5F4103C9085134CAD7863A41B04F32EF20201E54B55F1817CE58" + "9619B096C254FD2C2FA4A06F4DE35CCFD23E221C6B736964653131313039626632306" + "3646535343430383365663465652A60EA106C124476B753D39368A5966972A2729BB8" + "BBEA734A2B3E812B705EACE016C8A03C9A406094D80059EF4CA26F1928FA2DAA5DE9A" + "6F22372E5C7A941E610D1EFB56ED7CE2228A70E2E150AFB66EDC2DA066D463AA90BA0" + "CAFF078FBFEC05C830000AF5040A21EACE80E30BFDA213F1CE4DBCFD9D4D24B8E2AE0" + "0054D167D9D7AE99547062B911312B40168A7665A21348FC0590328608DC520BE40F5" + "B749328568FE383EF69C1A587AB2446CF9C41D821373D0856A883B316519A42218F80" + "E7BD5764D16BAC9A9B427A7278F5940E563FCF6DEE0FF3AADBB702EBF2C54EC354AE7" + "ACC84EE6A54BCA1F30E38EF71C44A81D0009B1484FEAF4F1A56F58C35BB2372C80C6D" + "FB389E6DE60BDD3D46C03975715260F6FDBE42FACB64C22EDA9635C04DA79434E1C41" + "FBD2FDBBAB6598283226C0278E8E0A96D780D3436523EFD11AFA01FF6FECF71578288" + "12A2D6DCB15383A6D9AF4519EF804C6053A10C436002DE3A4EFCC017755F4AD1101BD" + "C813E2D211732418DEE529CBB413C48AA5884C76A5C6F556A715055560D4247F5BF31" + "0956949A3A171A4AA608A48446884E7676D558FF64D392B84E617805693D90F1E9B7B" + "540C383D384D7F7CE06C23618681BD838CEB1A514047F1C562C43159CC5E21588FBFC" + "E8A354111160F1A1E2BD3D798A000579BDFDB977252809EE1502DF8045972FE8AAC84" + "0211C2F8D9E4D5BE18509C327C647D654C4B6CC430B98F1FF37C96FAB087FB561B8CC" + "18480F877C873594D3148FF74B0E3C6327C27CA876DAE7422398FC5E85269CBA49AD0" + "99221C6B7369643865383033353063626566363436336130303235653663632A7C7CC" + "C7CE96055E16A52FA192EA2CF3C9DF3E89B9133A52286F71E6C6D82D0435F6B2155DF" + "DE590B347D8C86F62D7DFBAAE640C237256F609E5DA9CC6C103465FE3441612BBDFDF" + "4D1C24B2147FEB8565CEF4993E439C9D564A39A4AC5BB1DA69ACB44DA06E4522C9A93" + "D310CDDA5DAC1E1E0B91ABFF41E4E2EDDA40013007122062B0F22E176F77881114844" + "DABC6F0EB0F80381D8A06ECDF09913A5CF8E85B3F")}, + // test vector 6, app id: "", usage entry 2 + {"", + { + a2bs_hex("1fbf0a1d2432805a0f8292ff627a9a7c60b733a51b365892c832261d71"), + a2bs_hex("1ad116a26f423c7019fa8dca226c2d2bdeec91beb1fd38d6890e32745a4d" + "bb9409b65fe834c2522d92621b265a9d526b4fcadcacf4c4deb364661118" + "494fd1561621392bf4450e6833be290d49e59e665a031375ee56ad1f3392" + "436ba213abc5ac10a199e73123f84f7644282137da24cbde30c10a6eb847" + "ca72b8b311ad329d2c9cb0909c2ecbe3fbaf88e81bb5aeaa6480fff67e87" + "77fa00c783aa160f1e211cb9bf3835fa8f82923c05895c359cf306f5cb90" + "a73b8ce2ce9ed210485e1c57"), + a2bs_hex("0bf120627d01690d14321fa967d81997b959b35ed7745dee9e885dd402df" + "83c3b8f52999b16c2d1d47bf4724a9ced984175eb8a032d613294c148020" + "74154c34fd40fe6ea74fa830fdcb9cc7e0799a75aadaf41ddda3d0038896" + "da966b1a67ff4e6c7403debfdbe7d1d48f1a3304124f04c974bfa0eff4d0" + "b1733a84aa6f89ec74c89dad2168da4706f6dfcfd980502b573d0f7b3791" + "252cc918394e8b3a3e1ef37ce48b7fd6a2040db5915f55809f284ce4ec24" + "4149f53038b432964705e26c3bb6535461b7fff27ac8eff679dce8e5bfe1" + "d000b69a22d9efc1f310ba2f0115c96b00bc15888fbb3edd230834458491" + "eede2440550dd59c613dc8433efe979c71e9"), + "ksida5d27d7b0ccd433203e157", + a2bs_hex("1be7cd47cefdef69576348ef9a143be2311041a5f80259938fa886139679" + "4eabcc985a695be2ef4a8361d86979859c490922d92d3ed0484e1666270a" + "a96388bf6be3c4f4f0b7e2f59efc6b8e965d8fadd5ab86b2bb816d2573ec" + "36eb42b571297681be152d40639d"), + 0, + }, a2bs_hex( - "7290396E183156BDF830B7BF31BA762CB2675528C9004FD24A61DAFB587ABCF1D36F8" - "7795EE0B3DA0B425616A66C82349B2E3BB8841C1335536865F919ED2AE671487B608B" - "21A362D888E0AB4F7AB7175B82F108617C3503F175435788AECAF7FFBFE76995D93CD" - "79424A843A247A8D8A6054A5B5404C9C057AACAD91A203229"), + "0ADA11080310012AD3110A9A070A20B8E7F26B6B8B59BABF05B5A1F8927B412A85BC8" + "551A928F00856329814AE5A821280024463DC57079C27E34AE115C6F65B08F6311C4E" + "A604A6512C42470B6F692A76EA769D60D0B6BCF8D565EF31EB925F38E2095039C9F2F" + "113ECEE020F1126EB30165372D538B551EBD7BAE5CF0BBEEBB3CDB6F180D42868051A" + "AB8FF4947460DD96F0F8259FC6001059C998D2EB6902C064F9AE08E6CD3C7807E5037" + "9507B41620D15DD76C0B1E7ED9417EFD6825959B5077F464E6429A4DEE467A1BA2B05" + "D38049912D5539F1EE9F5D8A569AA1C384384F847ED64F6FFC101036DA70E69C06E49" + "16493E82E9FE3F65D85254C8C14F6CA0579BFB3EAA86B2D7BB5FF572ECCFD70F2EA46" + "95F326BEADF241AE4311E428C7C12A0D4D1915CD0537FF0F62CF13EB2FA21A8004BBE" + "6B4B60DA9D9BC34DCC8502FB81D8FD5FDBC8FA89605C86205F2B8C6530FF64C8C31F5" + "79BD8ECA603DFD5E397AC35E48931FD330351D01361BB31CAAA7DBF816A6144A12B6C" + "22D1DCEBA20669ED635A40831066ABD8071342119D7DA11C43696B2898D3DF3B36BEB" + "8DA013D9DC145343494B19D6DA085F4A41E421D3DEF2AD8B72DFFFFB6E79BBCEAF859" + "4045D16A62EED16904A3569860C531A32EAA5ABB868B1DD6A0B03D69C1A3F8336AF80" + "EB80BADBBC7B80CA5943BD5B374302147052201FAED30E9FFA99FC00B47F7EEB46951" + "2A413E873F91D959CCCACCCD3585B7F00ED8D4685022101713C3ADC439F27512A4592" + "6C1D2473477662C4BACE72F380D105DDC9F7BE49ED71991B3D9E29A2038201373F98A" + "845A57624A692F44EBD316D26C48FEE82B655583317ECA4AAA4DAC841524A2DCA1117" + "49629637EF29FA7E72645A875957FB3D98A5E6C8065B1349CFA23011CD6349D911C53" + "5475FED50BE9EACB6A1FF3EA7458823F0229F2FA90B7A7099E8EC349D3D0FE0277CBA" + "5157CA62C8FBAA893F37B8C8CEA2A0D2D2912CACCF92D31AA17439479711B4A5B7744" + "5CC02C18BFE0195CE8F1FE59E317214005CEE5E25904C9FB0AF7E2B6B4DCCDF78E61E" + "179A9F93996EC3C76F6DA9B5A291BF08A73032B37EDCBBBDD20CFF94088A489167C56" + "E5E75B376874E9750212FE94D7656D9CD835DB3B771BA5B58D2B255221C6B73696438" + "35346338613630613561353437653132326332346663612A507D2B905E5EAFD4B28AE" + "EB7633283579E48ADD21A68EB26CC8C3B2E344579003B12A38554336305525FA6AB70" + "F024A18C73631BB1531ECA3F0782C72DBA017311B3F1E98C739632E305E4BC0B2561A" + "E2B30050AC1050A1B5D637BE37A9722AA35C23D346470851ACA7D2EDCD1A27EDF124E" + "A612C001BC96D6878E3086C33624821F1F3ECE23F27E58222C2BB8D1615476A117926" + "3B58F6427E92911D961FC7A3AFD947AED8C9AEAD1F08457925D2CE4E0F618B21942BA" + "A60B231EAE864048F94F74FFA700E5777F812ADB6F0CB6BA6F0D145E3951191EB2171" + "40C32F2C7565053222131FF823BC36D80B24B561CCBEA9D397FE00942E7FF73B8152C" + "DC083B63A9F9C4A77056A0D79F44F267DA0ED629D9C902F7E838957EA41AA442221C3" + "AA9410DB58302B468C6D7F2113663809F0DCF187C108CED1AD402F6FB3693413CC1D7" + "D5E3459B856E4156C78F8D85D548939FD00474C8CDEDC46835CC981758500FE61CC79" + "383B4D9F87C3E33D19C2D25D7D15DD0F3D2B1AF4583B71E90C59886D297E78E929C2F" + "3840C82C626914A4EB537B3A5161D963472B6592C0FA1E415556BC009C2DA22BCF743" + "AC434E22F8A33B43210DFD8AA09FE86105610F366E6FB7DA18996CF7C7DB425A9BB50" + "E4A13190A680B9F82D37D09658585ABE3BF9F009A5C1CE38A7CEFE17F71FB402768B2" + "D66B4CA523ED06729349695D7864D7CF7A1CC11D0DA2B8A43DB834D10B47D9579EC9E" + "46986A133277B92C636CB2A6A823AFE73317266C9C0601DDBADB76E1D254D6183B93A" + "1EA91A7E6C567331B3EE3A5AB1484AF91FD0F8DAC5FD980A67D8F33CC1D6CF20EE4C2" + "4582D03967ED48B6F28E7514E4D18F38C8CB1E54FBA59AF7D6A79C6C5A7AB06BAAC96" + "4C7958D201910ADCA018022FBEB8535B64F5CE83D3C22246B73696432663265383563" + "653861363737663630303437643730323465303762356165362A60B74880FBDDC5BB9" + "DB82F09BC7DE3FFD95A0A671B979D4C1F0564EAF63EB6B5A8C3F16D9F964AFBD011E2" + "326F9C27AFBE74536F3F0601A71D9C1C422F335611BF3BF1A1C89E2DEA27C17A9D9A5" + "8A74121E840B002E8A6FB59007245BE786C1F6430090AEF040A1D1FBF0A1D2432805A" + "0F8292FF627A9A7C60B733A51B365892C832261D7112C0011AD116A26F423C7019FA8" + "DCA226C2D2BDEEC91BEB1FD38D6890E32745A4DBB9409B65FE834C2522D92621B265A" + "9D526B4FCADCACF4C4DEB364661118494FD1561621392BF4450E6833BE290D49E59E6" + "65A031375EE56AD1F3392436BA213ABC5AC10A199E73123F84F7644282137DA24CBDE" + "30C10A6EB847CA72B8B311AD329D2C9CB0909C2ECBE3FBAF88E81BB5AEAA6480FFF67" + "E8777FA00C783AA160F1E211CB9BF3835FA8F82923C05895C359CF306F5CB90A73B8C" + "E2CE9ED210485E1C571A82020BF120627D01690D14321FA967D81997B959B35ED7745" + "DEE9E885DD402DF83C3B8F52999B16C2D1D47BF4724A9CED984175EB8A032D613294C" + "14802074154C34FD40FE6EA74FA830FDCB9CC7E0799A75AADAF41DDDA3D0038896DA9" + "66B1A67FF4E6C7403DEBFDBE7D1D48F1A3304124F04C974BFA0EFF4D0B1733A84AA6F" + "89EC74C89DAD2168DA4706F6DFCFD980502B573D0F7B3791252CC918394E8B3A3E1EF" + "37CE48B7FD6A2040DB5915F55809F284CE4EC244149F53038B432964705E26C3BB653" + "5461B7FFF27AC8EFF679DCE8E5BFE1D000B69A22D9EFC1F310BA2F0115C96B00BC158" + "88FBB3EDD230834458491EEDE2440550DD59C613DC8433EFE979C71E9221A6B736964" + "613564323764376230636364343333323033653135372A681BE7CD47CEFDEF6957634" + "8EF9A143BE2311041A5F80259938FA8861396794EABCC985A695BE2EF4A8361D86979" + "859C490922D92D3ED0484E1666270AA96388BF6BE3C4F4F0B7E2F59EFC6B8E965D8FA" + "DD5AB86B2BB816D2573EC36EB42B571297681BE152D40639D3000122076CFC9DBA6CD" + "93FFC6BB74D61C1B644CC32121553C50817A9F6F00633575E659")}, + // test vector 7, app id: "app_2", usage entry 2 + {"app_2", + { + a2bs_hex("8f922e955b269458ed1345bde9a24516520a536817e8e8612154a1"), + a2bs_hex("d4acc596a52055cee710e1fec44796dbf3ae6b017ab156d9bff7bfdb8f1e" + "6352bfbe453034968f940c36ac18800e22bb2ff71268053702ef3fce3fb2" + "d607a078e0d1449fcc9d0675d41b1a65f78e3c02370d18112aae1e2577ff" + "9087825a45125db5dee8e27bd14ea8666b4e8e6aba6811c40b585aabb9c9" + "185209a48d11130ff690316916961f28286c71c3e985d7dc3352166e414b" + "89da2c17cc5b69fc9c00990697f5"), + a2bs_hex("169d3c432f9c2f8b99e11632bd7d6a63f3d57679c567bedcb2e596ace105" + "0453732040cb468e9c43f6009b430ca4a4046d017e67a4badd5b71c0c9fc" + "e2274817f0bcda311a4f8703e6dc32aedf30e6f9abd40e249fc8b0a5045c" + "c1e47e60a60b4893ef92602f5584e1162f4ff3ee6d906228f97b442ace1f" + "b175d113b671bdbe4ceffdd98f2bb094c0dfac03b79541a44d8affdc987f" + "4268706b5a554e998907eb7126e8c6bc07c837d8aeebea3249e37b4b7dd7" + "327300fe7e62c15981cf73a13e806d065bcadc2c747256907a5493592b07" + "a0c07f9cd805fcdc0d30f70e4c4b2959a0f52385c6bd3e6eeb4e3d81fdc1" + "a9dc3c76faf1bfed913d58567fa9b296d27dff5217c583e7c134a642601f" + "8237"), + "kside684918d6c39bfa652a40ad936", + a2bs_hex("703f69807c8f4d140168874b924a625132eb3b896a381d617b8fb83c7314" + "a6b634d840925f711ae330599f0e0863800902b05d201a8a87b88a4bc170" + "65a1a8a556c34bf86b53afcc9951be15bea9ab55"), + 27, + }, a2bs_hex( - "3478A2D76DEB90BE713B03A11037EA7C305D1AF65099E3F2B92C4D4443A8F481C1177" - "DEF0A3CB49BA5F1448A10AF1207AD2D361B4A1F961B4B1F215B76A9A5005B414EF45E" - "AFBCF2636ABFC01413B27DD11871103579F8C041A799E22888D9ADB798E92A5E29BC4" - "6DECBC90991C65FE151C49F18068C1B65D0E90A9ECDA9248B87C120D5FD8EC81D4D36" - "B529FB2DAD39E0D39578B13B158E2B07C752D86F1A9D8160C93930C1F4F9E1D0D8E2C" - "5AB308732EB27722A6BF8BE852624C2BE3E4FE85819B89BEBA6535FCFBE85FA63A57B" - "D0FBAF284C64FFD97A146B76B3F37B576FC091C03E2222FBD24C2211344B7E2417EFC" - "36C4A54DCCC460CF810E7EA8AC6386D6AB567C819FED88A22CE55EF9BBE62C2CBC7AE" - "EDE5E5A69FF3472418CE2F4514496C59D26E72F3BFE0131F"), + "0AB60E080310012AAF0E0ADC040A1E9212A6926F21C6727C1EE89D5607047A1636F20" + "6F70E21FDA86E01B6A4B512A401EF947ABED64078EDF5B21FE6D3FB65384595D63A6D" + "03E4D1D397C5019DEEB6890D3EF8773002B91E255AF0820FB594069DF55D8ABF96498" + "E493F5C70F6B85F50E12A1ED3C039AD0CD838FE44D3FA9E2BBDDEB2919041203111ED" + "7778701B04D6B15F41D0BDE799E20A38B27BF96FDBE844F10364BAEB593596220993C" + "608AC793DE76C237CA350931A7E216538074DBD83DDF262D9F18ACD91E1EA5372F7E7" + "73C5B643331A9A027709721B3AA48597E88C99E82EAF7DFF07E87E0318D9D7CEC2909" + "6EC591826AA7A359316D6DE1D1329B408543E237DE84C986987EAD1BB6A0C38817E93" + "013E5C989D366F49590B834453EC64B7433BF0B3335B9E222BAD4CAF554D69575C585" + "95283166FEA42E89645FC7E2D3AC9E0C1399B096CF3FED1E5DEB1BC4E0EE894F0AE3F" + "929DD7DBA4530E5655EDBBF6041DF430482EB2E891B6A93AF84D3C16DBAD92733FFD3" + "4E8F4CE24506BEAD578D20CD3E291C2FC2F811DB875F49ABC21A24277D2BA474FE6AF" + "6C14021CFEAD5513E0999E094020CE08209BBC08F13FE2B96D7BA8213C8E9C85B6A62" + "3788D34DA794E17E4CD3BD65680B97FB30BAD64DDC42B1BCFB0B83E5DDA3501A5902C" + "A609F41837A0D5CD096E0659B67C221A6B73696436326438386564376232393232313" + "7623032333862652A585422463FD2E4DD47626E97DD6B4EE0B89523AAEBE8D11E7E7B" + "E703EF01E49B17EAF020CEDE0A9E0E7B5D91E4DB7ABDCE445936CB2DEECDEFEFDB14B" + "78F67B7CA5C733C9E88446FD814584584B86BECBF6EB2B0E3D5603E8B30190AFF040A" + "1D831FAD51E52A403524539EAB6A1B201E46674CA3B9167B1C1B53F5E5E312AC0136D" + "83ACBC5E4ED027ED583E3B2169D98F4ABEDDA15B781408E68EFA14FEFA9F3F0309BCB" + "5A9FFF6580636EBE3548E5ACB43B76CFEB29A9C86324E62AEB40556005C6686E718F9" + "BF61B0681D43B5B1E88084B3AEA27A6B0E844E5500B6FCFACF2EE44D6AF7F64154AB3" + "FD4FBD0B8056CF63971076A1EB3642B78D5E76B84F4ED9F6220089863F8A4911691E7" + "9FEFFC9F804C4C36C7F85E45B1D276C85875875267EB65DA70FD2D5E9176D69141AAC" + "02317BD7063BFB9FAE1B2E46F4CF15B7BC8C92517FF5C32CBB52AE4B67AFC5D569CB6" + "6A462DEF7A18A7D0ACEBF9F6E8A604356ADE2C81450C5466A472890B03EEFCF65388F" + "060E24551C67B7D46AE5D4D841D5CC63D137FD543FAE2C771756590B90E480CA0126F" + "1FC0090ACE62499E47569FC52196C788F80139755BDF12A7ACB29FD6E23A46A4C036F" + "04FF1ED6CD714094253BF1C58762C93F0DDF8A73C4BE927FFEC2723A16D8FFE512885" + "1F58537461275F6AA1976E3B399B7243919207E040EC16C5328E8AB082278FCE0E5D3" + "DF5C5F92DBA51FA6613587D4ECE31F2C001B49BFAED434F9512E895C2E09C88DDBF18" + "4BFAFE4D82E5D05A26AC06CDE29FAF6AB05B96685649C923779CE5EF7F316531ADA8E" + "74E45AB1DC1D75648AA2DE052674728867E87639FF9B782A3322186B7369646531643" + "3306233336235356632646562343731362A64D44A9D70A7C582559F089B1C0FDFCBDA" + "F5E26B672FCA5D58E889B407A0BA8599079CDE11FADFAB23AA1B97622839F3B7E1A96" + "F8332BEC5FBCBC9EB64FD5ED05887B8FA3BFD6ECC7BC91E621342732062D2F4411B76" + "3E20328AF6F8EF5030E2F8027AEF9E30060ACB040A1B8F922E955B269458ED1345BDE" + "9A24516520A536817E8E8612154A112A401D4ACC596A52055CEE710E1FEC44796DBF3" + "AE6B017AB156D9BFF7BFDB8F1E6352BFBE453034968F940C36AC18800E22BB2FF7126" + "8053702EF3FCE3FB2D607A078E0D1449FCC9D0675D41B1A65F78E3C02370D18112AAE" + "1E2577FF9087825A45125DB5DEE8E27BD14EA8666B4E8E6ABA6811C40B585AABB9C91" + "85209A48D11130FF690316916961F28286C71C3E985D7DC3352166E414B89DA2C17CC" + "5B69FC9C00990697F51A9002169D3C432F9C2F8B99E11632BD7D6A63F3D57679C567B" + "EDCB2E596ACE1050453732040CB468E9C43F6009B430CA4A4046D017E67A4BADD5B71" + "C0C9FCE2274817F0BCDA311A4F8703E6DC32AEDF30E6F9ABD40E249FC8B0A5045CC1E" + "47E60A60B4893EF92602F5584E1162F4FF3EE6D906228F97B442ACE1FB175D113B671" + "BDBE4CEFFDD98F2BB094C0DFAC03B79541A44D8AFFDC987F4268706B5A554E998907E" + "B7126E8C6BC07C837D8AEEBEA3249E37B4B7DD7327300FE7E62C15981CF73A13E806D" + "065BCADC2C747256907A5493592B07A0C07F9CD805FCDC0D30F70E4C4B2959A0F5238" + "5C6BD3E6EEB4E3D81FDC1A9DC3C76FAF1BFED913D58567FA9B296D27DFF5217C583E7" + "C134A642601F8237221E6B73696465363834393138643663333962666136353261343" + "061643933362A50703F69807C8F4D140168874B924A625132EB3B896A381D617B8FB8" + "3C7314A6B634D840925F711AE330599F0E0863800902B05D201A8A87B88A4BC17065A" + "1A8A556C34BF86B53AFCC9951BE15BEA9AB55301B122002206F46D9D05740AD34B99F" + "10C21A2FA23B8E45DCB00713E32D5CECF239D0A8")}, + // test vector 8, app id: "app_1", usage entry 2 + {"app_1", + { + a2bs_hex("d0b9a07ad7ffeec13784bd60da011be3589f3e450227fd36b1a3f6786cdb" + "fe8f"), + a2bs_hex("a419c5687a592099dc67da8bc4f5ef238c80fe4ce3e2fcb025392efb1438" + "4b581b595a0e8fa95de637fb2184719eb36ad6539ee9df0f67697f91d018" + "6e04552e811196029cf4e256518ddf3215af8ec61442c17d6753b93f9d3a" + "9240bae39bacf5563659cf47d3a611ce20ed3ebbf86cddad60cc2847c459" + "5dcfd934d012ce205960052158461d7c5d480de2e597876e64e8f8de6928" + "29a3"), + a2bs_hex("f7c19357e50fc474437c1a635c5bae8f6f51afa20750766db19457dff7ae" + "f2cae78848a225cc6a088bbcffead5be6aab6fc8af091bf459c3bd9bcfa1" + "8de53ef76db1b4826cf0b8ff7b2d7c44bbadb3cd7aedd8f639d1f38c52a5" + "8611a9782aeace72be69a73d2e091a1120dc63f7ba6f1cb6cddd69e9a236" + "232ed8c14cee665756ba51f1d2e2530ab3662ce1b6efba91c5f10c53abc8" + "86d6f25b5dc40417e54270843f3b454c8c047fc366249e30379b0fbe0174" + "fcab8b8405ae7f20f6f2b81f11082ff0e270b75f1e1aa7ed5806f4e65b46" + "b872dbcb703d7bf20b9ecaa481425a5218d85a49595f3ed268d61f1be8e3" + "8e6126eb075fa6b7ae80431c8521c4bc2ce701e45d33bfca9a5b0b66b550" + "aab21eae41f84cadfd2517dee9a2c139ad475c387d25"), + "ksid321bb636f8a3f5cd5d54a236", + a2bs_hex("c3cb027611397b5d70cc0b08e0f5249cd19996da674e33722902173d45d7" + "09914a3d7e898d93170317bfcff34861c0d687048cc93542a75a2c99b232" + "3fafea1ee0c3e3d24edf2633"), + 7, + }, a2bs_hex( - "C45FDCB3296A0EBE24FF381E027E6E2EF1AC289C67D3B858330669A81E8131583D2F1" - "40FD64615BDED0ED8316ABFD9C7E887433E1CAA6EA8E0C4F87ADB2A7FC3CF6FF87A7F" - "02AFF03BF5DB640AD8DDB572C41532E673618DCD8C33EF2BFE4E25EE821DF7D742B09" - "90398543B16EFCDBB03C6327B79D3664CED442E894020F4410ECC178C92AAEDFE39DC" - "563AC226FE9E0EF22E1C896C4F2835CDFDCD50B6C4DBA2B27A3B65DE3963D0A5F6E44" - "2A3C32008AB9D1ACBE4F366990EB43F8EE213B71E98DA090282680ABDD649BECA8970" - "0764561379F1DD23490CE967632ECA349AF8E1CBFA1F3A4F39F453614C8FFB5A17975" - "6243CB1FDB515834229BC64917C47A2F2E1116FAAC13368015312C31FD41215106469" - "BEE77D0EF2FE10CF645B3E82902EAF53A676933D0EC433949C1833BE52E76602CC3E4" - "E784C002E20624BCE0F38F9CBC478439899DA7F15554D0ACADEC140C00C8FA8FC9886" - "2D9933938781B30CB9C76899B3A48DBF170DDA0A18ED37D77F048ABBC85CB19469638" - "C2A32AA3180CF3943BD6B8C5CB26F2EA70868F18B0707C882054141086997A1AE5B70" - "9D4D0AA2B358990F244BA76C8E40791D29A0C63C9EF620B97FDFFA9B671E5A65AFCC1" - "C94CAACE0443E9D91F14028935BEA3988831BEBBFD3EB7C3A5AC9605B3534712A0912" - "4345ACB09665E357E58946871BC140D365"), - a2bs_hex( - "0ADF0E080210012AD80E0AA9070A8001924B035FBDA56AE5EF0ED05A08DE7AECC8ABE" - "1835E0C4A548F7803937F4C3B4520EB7F3334FFCDFA00DE56408F09D5019FCE87072D" - "0DC6789817468974B2EA51EE3944B8D7E0A88E4F16EBB80F03BD845231A01E6146841" - "CBAEF0134DCD9300DB2D92732992C0F2310D8E386FB31C67B9477010DEF9D99C42725" - "89572A26A17E12AC021E6FFBE66FC6153E7749906EC8F684E819467E16CAF317F315D" - "B32B6D3FDD1A8E8A094174D92D063B88E4835EAB78BD09541EA7FE72F132EB7364E15" - "4BC1548FC40EC7092775531508C95F9ED5D76F36BC0C198C3A33A1F9415B343905D6B" - "E37645E6800F053B1DA9A20286EFCBBC320424ADF7FB6E3D5D8E86C35E576A1A2A37D" - "344A419C0F0034A1B5F767D3C61D90DCA1119E5024C34EDE8FA7DD128696D8C435410" - "F218E52A853AD214FD05D0F8B3CB4832CFCD97FE159E6DEE64CE82CDAEC0321AE71B3" - "BCBAE42DF9EA65E42E151827086EADE71C138B972CC3992CF9ADA944C063816352ED8" - "658D3FA07BE0F32239E74A65932B069AAC4E8386DB59154AF9AEF71448128C66E5104" - "45294F44E511BD9B1AF19D4D67E99363093BE888D4B2AB841CAFF252CAD13EDF8E1AF" - "40340FC62339728520E6C0C09907C26F3FB78287231661952A8B699E47AE241B999C0" - "29FD2067836DC4BC64F66998A3ECD197DAE36F808A2E5A4C5BF25DD580E52B1C39A8B" - "03772BF82D58929766F2DA04F0E616F92B3A0EB75661B8FF5DE1EB807C990F9E6BA99" - "1C8BAD5EB63B37E8663A4E22AA9DB2015D3DF8BED1C8313F85F13B9483C7A39C59243" - "6C8B13C23F78F55CE812795335059F7F527CA580306A0AEE5A6D957A91F498F64AA2E" - "FC6780716400E17C7EEA30B2E6523B902986995E003C2D919A7DC7C0122CE9410037A" - "6602B59A63B5C89473D4E02DE35C1F01B12ADB48A3D94D43693F08268FECCC78DAF6F" - "4C35FA32C538CD73FBF3CEA274B01179C02473486311956E5A0C78E44C59B2F34FF24" - "B0653A6379A2F5F6F51467CAE26D55CC5BBDCFC9BCFA7B8C5CBF82EBE7BD340C3DAE6" - "374D0692052C529AA33D7A6799C8F1F59C78575E51F707013026CC4F83F6B3328EE6F" - "B1AC91929A4491338E93D10EE6193014A73BA241A9A833EA835217894EB4FD4BDB890" - "4A5999928325D0AC31B6D58609EDD9D85E88F74B5BD6FA7BDD83C51EEB91633ED267A" - "CA2E103904BBE4C031A6483858FBAD74DACD01711F7B882749FFFBA0DB6C7D7109D82" - "989C7D4DB5A0F1E7506AC24C89CECAF231EFF99F96AD76E57DABDD3C2DFBA7BAA869A" - "771F561B165987E552824B0C914E708E425C30AA9070A80017290396E183156BDF830" - "B7BF31BA762CB2675528C9004FD24A61DAFB587ABCF1D36F87795EE0B3DA0B425616A" - "66C82349B2E3BB8841C1335536865F919ED2AE671487B608B21A362D888E0AB4F7AB7" - "175B82F108617C3503F175435788AECAF7FFBFE76995D93CD79424A843A247A8D8A60" - "54A5B5404C9C057AACAD91A20322912AC023478A2D76DEB90BE713B03A11037EA7C30" - "5D1AF65099E3F2B92C4D4443A8F481C1177DEF0A3CB49BA5F1448A10AF1207AD2D361" - "B4A1F961B4B1F215B76A9A5005B414EF45EAFBCF2636ABFC01413B27DD11871103579" - "F8C041A799E22888D9ADB798E92A5E29BC46DECBC90991C65FE151C49F18068C1B65D" - "0E90A9ECDA9248B87C120D5FD8EC81D4D36B529FB2DAD39E0D39578B13B158E2B07C7" - "52D86F1A9D8160C93930C1F4F9E1D0D8E2C5AB308732EB27722A6BF8BE852624C2BE3" - "E4FE85819B89BEBA6535FCFBE85FA63A57BD0FBAF284C64FFD97A146B76B3F37B576F" - "C091C03E2222FBD24C2211344B7E2417EFC36C4A54DCCC460CF810E7EA8AC6386D6AB" - "567C819FED88A22CE55EF9BBE62C2CBC7AEEDE5E5A69FF3472418CE2F4514496C59D2" - "6E72F3BFE0131F1AF403C45FDCB3296A0EBE24FF381E027E6E2EF1AC289C67D3B8583" - "30669A81E8131583D2F140FD64615BDED0ED8316ABFD9C7E887433E1CAA6EA8E0C4F8" - "7ADB2A7FC3CF6FF87A7F02AFF03BF5DB640AD8DDB572C41532E673618DCD8C33EF2BF" - "E4E25EE821DF7D742B0990398543B16EFCDBB03C6327B79D3664CED442E894020F441" - "0ECC178C92AAEDFE39DC563AC226FE9E0EF22E1C896C4F2835CDFDCD50B6C4DBA2B27" - "A3B65DE3963D0A5F6E442A3C32008AB9D1ACBE4F366990EB43F8EE213B71E98DA0902" - "82680ABDD649BECA89700764561379F1DD23490CE967632ECA349AF8E1CBFA1F3A4F3" - "9F453614C8FFB5A179756243CB1FDB515834229BC64917C47A2F2E1116FAAC1336801" - "5312C31FD41215106469BEE77D0EF2FE10CF645B3E82902EAF53A676933D0EC433949" - "C1833BE52E76602CC3E4E784C002E20624BCE0F38F9CBC478439899DA7F15554D0ACA" - "DEC140C00C8FA8FC98862D9933938781B30CB9C76899B3A48DBF170DDA0A18ED37D77" - "F048ABBC85CB19469638C2A32AA3180CF3943BD6B8C5CB26F2EA70868F18B0707C882" - "054141086997A1AE5B709D4D0AA2B358990F244BA76C8E40791D29A0C63C9EF620B97" - "FDFFA9B671E5A65AFCC1C94CAACE0443E9D91F14028935BEA3988831BEBBFD3EB7C3A" - "5AC9605B3534712A09124345ACB09665E357E58946871BC140D3651220464E4A1BB23" - "1A5B0287888B34CA0A8CF5396EB2B8313377DC5ED5C41A9B389A9")}, - {// 3 usage info records - a2bs_hex( - "983358221FB8DBF892047F00AA661F217EEC4E7A1626E8F98E025509E4D65A685E7D9" - "B169B98B16934F6E43E0E0E854A3FA9EB8E9A9D08E9D9B3A6C766AA44F7C655879BA2" - "DF5F38732FB7EDCA66D8C13A855B15E32CC9389B7DD119BA1F2417825FF1F52970F8E" - "985D34DD353D2AC8B24267353E5B8406C098427C4559A90CC"), - a2bs_hex( - "483EAC68243092009D06FAB41DB594ACB22E068C9524810758ECFF8BAB7E1B1ACA988" - "C3987023F01EFEC11529C7326279742E805E755A08EBBD9AA322F305805BE1166AB45" - "CB156FB0A9E6734371F4028707EE01CF2FB08465707E7E5613DD90D74B0D02536E26C" - "F1261CDDA8713943F3620ECC54095C76F8CD3CE31948C3CC0C9EB5582A4D087A54B39" - "1B4CDCBC98E35830B5932F6CF8D16427EF115CFF0A99499513702DD54C758E53248BB" - "5D195F2A2DD1DB18F97562F1F9034E223CEDB1E09ED1B0FE26089C20ED43B5D87B51F" - "6FC6C9F86255FBF70DF233F2665D604355BF9740A3B755521102E0B485C5CCCA607A9" - "A1BEB757BEDEF12327C637D17D6401E3756719F99BBE69B9CE4C8E47C2AC771F35A8E" - "E3FC4D58B2B2269CF85728E4DA7231BC8F0FD7C50E2A1EE9"), - a2bs_hex( - "5826D3A95F78879292612BCE06D845D64285CD45A7EAA6C87A9DBC3290B0B6AC95315" - "809F8CC7938768F9BD342C62CD4CE055866394489D955247CB0535001D50EFF4FEDF0" - "9501C58569B1EB9AA2305A113A5F4D4524AD34148A2DC48D2F522937F44A57FC76F57" - "EB1D4819C438EA42C7F8974FC7D2FE61CAAB3E1F27172FE6B8675DF4CCF1329A6EFB3" - "1F686FB0DC0F8B552D78970708D50C82ADBE333B585F6DE5A0D01D106F8232EB9ED45" - "42A2DC5AA031CC44652E8A42EDCA5AB08B0B5CA61A922E69A119E556F6014642522EA" - "1550F6D6E63EB25ACC03A4DD3F22F4686ED525F994FABA87629AF5939C16BA68C0F09" - "3EFE033CD319180BF69FCB72AC5123EBCB9DCF1AF00F0A68E31FF5B18FA8CFF3DFBB7" - "DA45413799105D67FA78217710D2F6C33394DD4088100013295FF43CF0598E6FE5C05" - "F03417CCD031F01CF63BECD444C750DF198345F155AB2B2AB94394A3C0C0AE05E386D" - "E6CC565AE82398BD0E377D6ABE103B9D5E84582C3772584B759891FC4B121A113370E" - "2DF5372DD81FB6358C64B0F6EB8F26193CA119E4D9D3D38036FA450EE2047CB2CE265" - "0FF37DF85BE23D58C17379FEC08DC0648236A107AE66178EEBF78F05F3B898424FA02" - "668B51F838AFA90D367B5CB425372D8CC3790BEA8AFB8795251FA09340D85A7F0B003" - "134C838F08BB1054D18404C3F69130700E"), - a2bs_hex( - "0A8B16080210012A84160AA9070A8001924B035FBDA56AE5EF0ED05A08DE7AECC8ABE" - "1835E0C4A548F7803937F4C3B4520EB7F3334FFCDFA00DE56408F09D5019FCE87072D" - "0DC6789817468974B2EA51EE3944B8D7E0A88E4F16EBB80F03BD845231A01E6146841" - "CBAEF0134DCD9300DB2D92732992C0F2310D8E386FB31C67B9477010DEF9D99C42725" - "89572A26A17E12AC021E6FFBE66FC6153E7749906EC8F684E819467E16CAF317F315D" - "B32B6D3FDD1A8E8A094174D92D063B88E4835EAB78BD09541EA7FE72F132EB7364E15" - "4BC1548FC40EC7092775531508C95F9ED5D76F36BC0C198C3A33A1F9415B343905D6B" - "E37645E6800F053B1DA9A20286EFCBBC320424ADF7FB6E3D5D8E86C35E576A1A2A37D" - "344A419C0F0034A1B5F767D3C61D90DCA1119E5024C34EDE8FA7DD128696D8C435410" - "F218E52A853AD214FD05D0F8B3CB4832CFCD97FE159E6DEE64CE82CDAEC0321AE71B3" - "BCBAE42DF9EA65E42E151827086EADE71C138B972CC3992CF9ADA944C063816352ED8" - "658D3FA07BE0F32239E74A65932B069AAC4E8386DB59154AF9AEF71448128C66E5104" - "45294F44E511BD9B1AF19D4D67E99363093BE888D4B2AB841CAFF252CAD13EDF8E1AF" - "40340FC62339728520E6C0C09907C26F3FB78287231661952A8B699E47AE241B999C0" - "29FD2067836DC4BC64F66998A3ECD197DAE36F808A2E5A4C5BF25DD580E52B1C39A8B" - "03772BF82D58929766F2DA04F0E616F92B3A0EB75661B8FF5DE1EB807C990F9E6BA99" - "1C8BAD5EB63B37E8663A4E22AA9DB2015D3DF8BED1C8313F85F13B9483C7A39C59243" - "6C8B13C23F78F55CE812795335059F7F527CA580306A0AEE5A6D957A91F498F64AA2E" - "FC6780716400E17C7EEA30B2E6523B902986995E003C2D919A7DC7C0122CE9410037A" - "6602B59A63B5C89473D4E02DE35C1F01B12ADB48A3D94D43693F08268FECCC78DAF6F" - "4C35FA32C538CD73FBF3CEA274B01179C02473486311956E5A0C78E44C59B2F34FF24" - "B0653A6379A2F5F6F51467CAE26D55CC5BBDCFC9BCFA7B8C5CBF82EBE7BD340C3DAE6" - "374D0692052C529AA33D7A6799C8F1F59C78575E51F707013026CC4F83F6B3328EE6F" - "B1AC91929A4491338E93D10EE6193014A73BA241A9A833EA835217894EB4FD4BDB890" - "4A5999928325D0AC31B6D58609EDD9D85E88F74B5BD6FA7BDD83C51EEB91633ED267A" - "CA2E103904BBE4C031A6483858FBAD74DACD01711F7B882749FFFBA0DB6C7D7109D82" - "989C7D4DB5A0F1E7506AC24C89CECAF231EFF99F96AD76E57DABDD3C2DFBA7BAA869A" - "771F561B165987E552824B0C914E708E425C30AA9070A80017290396E183156BDF830" - "B7BF31BA762CB2675528C9004FD24A61DAFB587ABCF1D36F87795EE0B3DA0B425616A" - "66C82349B2E3BB8841C1335536865F919ED2AE671487B608B21A362D888E0AB4F7AB7" - "175B82F108617C3503F175435788AECAF7FFBFE76995D93CD79424A843A247A8D8A60" - "54A5B5404C9C057AACAD91A20322912AC023478A2D76DEB90BE713B03A11037EA7C30" - "5D1AF65099E3F2B92C4D4443A8F481C1177DEF0A3CB49BA5F1448A10AF1207AD2D361" - "B4A1F961B4B1F215B76A9A5005B414EF45EAFBCF2636ABFC01413B27DD11871103579" - "F8C041A799E22888D9ADB798E92A5E29BC46DECBC90991C65FE151C49F18068C1B65D" - "0E90A9ECDA9248B87C120D5FD8EC81D4D36B529FB2DAD39E0D39578B13B158E2B07C7" - "52D86F1A9D8160C93930C1F4F9E1D0D8E2C5AB308732EB27722A6BF8BE852624C2BE3" - "E4FE85819B89BEBA6535FCFBE85FA63A57BD0FBAF284C64FFD97A146B76B3F37B576F" - "C091C03E2222FBD24C2211344B7E2417EFC36C4A54DCCC460CF810E7EA8AC6386D6AB" - "567C819FED88A22CE55EF9BBE62C2CBC7AEEDE5E5A69FF3472418CE2F4514496C59D2" - "6E72F3BFE0131F1AF403C45FDCB3296A0EBE24FF381E027E6E2EF1AC289C67D3B8583" - "30669A81E8131583D2F140FD64615BDED0ED8316ABFD9C7E887433E1CAA6EA8E0C4F8" - "7ADB2A7FC3CF6FF87A7F02AFF03BF5DB640AD8DDB572C41532E673618DCD8C33EF2BF" - "E4E25EE821DF7D742B0990398543B16EFCDBB03C6327B79D3664CED442E894020F441" - "0ECC178C92AAEDFE39DC563AC226FE9E0EF22E1C896C4F2835CDFDCD50B6C4DBA2B27" - "A3B65DE3963D0A5F6E442A3C32008AB9D1ACBE4F366990EB43F8EE213B71E98DA0902" - "82680ABDD649BECA89700764561379F1DD23490CE967632ECA349AF8E1CBFA1F3A4F3" - "9F453614C8FFB5A179756243CB1FDB515834229BC64917C47A2F2E1116FAAC1336801" - "5312C31FD41215106469BEE77D0EF2FE10CF645B3E82902EAF53A676933D0EC433949" - "C1833BE52E76602CC3E4E784C002E20624BCE0F38F9CBC478439899DA7F15554D0ACA" - "DEC140C00C8FA8FC98862D9933938781B30CB9C76899B3A48DBF170DDA0A18ED37D77" - "F048ABBC85CB19469638C2A32AA3180CF3943BD6B8C5CB26F2EA70868F18B0707C882" - "054141086997A1AE5B709D4D0AA2B358990F244BA76C8E40791D29A0C63C9EF620B97" - "FDFFA9B671E5A65AFCC1C94CAACE0443E9D91F14028935BEA3988831BEBBFD3EB7C3A" - "5AC9605B3534712A09124345ACB09665E357E58946871BC140D3650AA9070A8001983" - "358221FB8DBF892047F00AA661F217EEC4E7A1626E8F98E025509E4D65A685E7D9B16" - "9B98B16934F6E43E0E0E854A3FA9EB8E9A9D08E9D9B3A6C766AA44F7C655879BA2DF5" - "F38732FB7EDCA66D8C13A855B15E32CC9389B7DD119BA1F2417825FF1F52970F8E985" - "D34DD353D2AC8B24267353E5B8406C098427C4559A90CC12AC02483EAC68243092009" - "D06FAB41DB594ACB22E068C9524810758ECFF8BAB7E1B1ACA988C3987023F01EFEC11" - "529C7326279742E805E755A08EBBD9AA322F305805BE1166AB45CB156FB0A9E673437" - "1F4028707EE01CF2FB08465707E7E5613DD90D74B0D02536E26CF1261CDDA8713943F" - "3620ECC54095C76F8CD3CE31948C3CC0C9EB5582A4D087A54B391B4CDCBC98E35830B" - "5932F6CF8D16427EF115CFF0A99499513702DD54C758E53248BB5D195F2A2DD1DB18F" - "97562F1F9034E223CEDB1E09ED1B0FE26089C20ED43B5D87B51F6FC6C9F86255FBF70" - "DF233F2665D604355BF9740A3B755521102E0B485C5CCCA607A9A1BEB757BEDEF1232" - "7C637D17D6401E3756719F99BBE69B9CE4C8E47C2AC771F35A8EE3FC4D58B2B2269CF" - "85728E4DA7231BC8F0FD7C50E2A1EE91AF4035826D3A95F78879292612BCE06D845D6" - "4285CD45A7EAA6C87A9DBC3290B0B6AC95315809F8CC7938768F9BD342C62CD4CE055" - "866394489D955247CB0535001D50EFF4FEDF09501C58569B1EB9AA2305A113A5F4D45" - "24AD34148A2DC48D2F522937F44A57FC76F57EB1D4819C438EA42C7F8974FC7D2FE61" - "CAAB3E1F27172FE6B8675DF4CCF1329A6EFB31F686FB0DC0F8B552D78970708D50C82" - "ADBE333B585F6DE5A0D01D106F8232EB9ED4542A2DC5AA031CC44652E8A42EDCA5AB0" - "8B0B5CA61A922E69A119E556F6014642522EA1550F6D6E63EB25ACC03A4DD3F22F468" - "6ED525F994FABA87629AF5939C16BA68C0F093EFE033CD319180BF69FCB72AC5123EB" - "CB9DCF1AF00F0A68E31FF5B18FA8CFF3DFBB7DA45413799105D67FA78217710D2F6C3" - "3394DD4088100013295FF43CF0598E6FE5C05F03417CCD031F01CF63BECD444C750DF" - "198345F155AB2B2AB94394A3C0C0AE05E386DE6CC565AE82398BD0E377D6ABE103B9D" - "5E84582C3772584B759891FC4B121A113370E2DF5372DD81FB6358C64B0F6EB8F2619" - "3CA119E4D9D3D38036FA450EE2047CB2CE2650FF37DF85BE23D58C17379FEC08DC064" - "8236A107AE66178EEBF78F05F3B898424FA02668B51F838AFA90D367B5CB425372D8C" - "C3790BEA8AFB8795251FA09340D85A7F0B003134C838F08BB1054D18404C3F6913070" - "0E12202FF1FBA9926A24A1F79970EC427DDF87B4421488F7952499BC33CEB282D9E48" - "A")}}; + "0ABD0E080310012AB60E0AEA040A20BB3370CCD3C3C49573D6B74386D1886D9888BD8" + "1FE3241BCD2BAC9407D1A834E12C001DC0E51CFA5863F6C0B32A4AD7FA40625DADCC2" + "DCDE9E7FA3983B8804D9966803181682FC8AE831472E0B2FC26276242FBCE624D286E" + "EDECCE5555804913B4F8F86C5AE86160B8434B109169A63DA04C5265102D772C11805" + "43EF226D2140357ACA6CF87DA3F7E370DFC08CA92A1F7C7D314EAB36292A91708F6C6" + "AD84B37EE1C7DFAFB99289206CB752D063F330EFD85885F4B72BA1CA5823EED865A46" + "1345E3D6417872BF3B0608B3D9E1004C11E7326D3ED406192E13455D0EC4E1F558A14" + "71A8002F42A68CA3A14FB68F5992E4519F57970C3DAE73F8DA1D5B0B1DA3EFF7A9540" + "12A0DC634357F3F5477A820E182182F24AE8E835AB10C18386CC8A0727D3F38B62863" + "9BFBD69A94D4053EAB1C31E075E014CC578B226CFE24D6B42DB242972DEF8F23A4AAE" + "88451307C2ABAF54C1803AE54E3F1149AA6E6D4288CC7D474E876BE07954E8B2DEFF4" + "ADE4BF30229FB6C92DF4D66CD463F686B4754B940210EB59F1581D658DDF8DE8389E0" + "E2D123E2CAE3C2BE6EB1948CCC896DD4CDF45F9090C96DFB925795CFB4CCDA83E3EB4" + "F745577B17FC166BF5F4103C9085134CAD7863A41B04F32EF20201E54B55F1817CE58" + "9619B096C254FD2C2FA4A06F4DE35CCFD23E221C6B736964653131313039626632306" + "3646535343430383365663465652A60EA106C124476B753D39368A5966972A2729BB8" + "BBEA734A2B3E812B705EACE016C8A03C9A406094D80059EF4CA26F1928FA2DAA5DE9A" + "6F22372E5C7A941E610D1EFB56ED7CE2228A70E2E150AFB66EDC2DA066D463AA90BA0" + "CAFF078FBFEC05C830000AF5040A21EACE80E30BFDA213F1CE4DBCFD9D4D24B8E2AE0" + "0054D167D9D7AE99547062B911312B40168A7665A21348FC0590328608DC520BE40F5" + "B749328568FE383EF69C1A587AB2446CF9C41D821373D0856A883B316519A42218F80" + "E7BD5764D16BAC9A9B427A7278F5940E563FCF6DEE0FF3AADBB702EBF2C54EC354AE7" + "ACC84EE6A54BCA1F30E38EF71C44A81D0009B1484FEAF4F1A56F58C35BB2372C80C6D" + "FB389E6DE60BDD3D46C03975715260F6FDBE42FACB64C22EDA9635C04DA79434E1C41" + "FBD2FDBBAB6598283226C0278E8E0A96D780D3436523EFD11AFA01FF6FECF71578288" + "12A2D6DCB15383A6D9AF4519EF804C6053A10C436002DE3A4EFCC017755F4AD1101BD" + "C813E2D211732418DEE529CBB413C48AA5884C76A5C6F556A715055560D4247F5BF31" + "0956949A3A171A4AA608A48446884E7676D558FF64D392B84E617805693D90F1E9B7B" + "540C383D384D7F7CE06C23618681BD838CEB1A514047F1C562C43159CC5E21588FBFC" + "E8A354111160F1A1E2BD3D798A000579BDFDB977252809EE1502DF8045972FE8AAC84" + "0211C2F8D9E4D5BE18509C327C647D654C4B6CC430B98F1FF37C96FAB087FB561B8CC" + "18480F877C873594D3148FF74B0E3C6327C27CA876DAE7422398FC5E85269CBA49AD0" + "99221C6B7369643865383033353063626566363436336130303235653663632A7C7CC" + "C7CE96055E16A52FA192EA2CF3C9DF3E89B9133A52286F71E6C6D82D0435F6B2155DF" + "DE590B347D8C86F62D7DFBAAE640C237256F609E5DA9CC6C103465FE3441612BBDFDF" + "4D1C24B2147FEB8565CEF4993E439C9D564A39A4AC5BB1DA69ACB44DA06E4522C9A93" + "D310CDDA5DAC1E1E0B91ABFF41E4E2EDDA400130070ACE040A20D0B9A07AD7FFEEC13" + "784BD60DA011BE3589F3E450227FD36B1A3F6786CDBFE8F129801A419C5687A592099" + "DC67DA8BC4F5EF238C80FE4CE3E2FCB025392EFB14384B581B595A0E8FA95DE637FB2" + "184719EB36AD6539EE9DF0F67697F91D0186E04552E811196029CF4E256518DDF3215" + "AF8EC61442C17D6753B93F9D3A9240BAE39BACF5563659CF47D3A611CE20ED3EBBF86" + "CDDAD60CC2847C4595DCFD934D012CE205960052158461D7C5D480DE2E597876E64E8" + "F8DE692829A31AA402F7C19357E50FC474437C1A635C5BAE8F6F51AFA20750766DB19" + "457DFF7AEF2CAE78848A225CC6A088BBCFFEAD5BE6AAB6FC8AF091BF459C3BD9BCFA1" + "8DE53EF76DB1B4826CF0B8FF7B2D7C44BBADB3CD7AEDD8F639D1F38C52A58611A9782" + "AEACE72BE69A73D2E091A1120DC63F7BA6F1CB6CDDD69E9A236232ED8C14CEE665756" + "BA51F1D2E2530AB3662CE1B6EFBA91C5F10C53ABC886D6F25B5DC40417E54270843F3" + "B454C8C047FC366249E30379B0FBE0174FCAB8B8405AE7F20F6F2B81F11082FF0E270" + "B75F1E1AA7ED5806F4E65B46B872DBCB703D7BF20B9ECAA481425A5218D85A49595F3" + "ED268D61F1BE8E38E6126EB075FA6B7AE80431C8521C4BC2CE701E45D33BFCA9A5B0B" + "66B550AAB21EAE41F84CADFD2517DEE9A2C139AD475C387D25221C6B7369643332316" + "262363336663861336635636435643534613233362A48C3CB027611397B5D70CC0B08" + "E0F5249CD19996DA674E33722902173D45D709914A3D7E898D93170317BFCFF34861C" + "0D687048CC93542A75A2C99B2323FAFEA1EE0C3E3D24EDF263330071220B174821B32" + "5B0A6A900AD8C660C755D3B0273CA6E81D70E2C548CDEC07BE53FA")}, +}; + +DeviceFiles::CdmUsageData kUsageInfoUpdateTestData = { + a2bs_hex("b8e7f26b6b83d6b74386d1886d9888bd81fe3241bca928f09407329814ae" + "5a82"), + a2bs_hex("4463dc57079c27e34ae115c6f65b08f6311c4ea604a6512c42470b6f692a" + "6803181682fc8ae831472e0b2fc26276242fbce624d286eedecce5555804" + "913b4f8f86c5ae86160b8434b109169a63da04c5265102d772c1180543ef" + "226d2140357aca6cf87da3f7e370dfc08ca92a1f7c7d314eab36292a9170" + "26eb30165372d538b551ebd7bae5cf0bbeebb3cdb6f180d42868051aab8f" + "e50379507b41620d15dd76c0b1e7ed9417efd6825959b5077f464e6429a4" + "6ffc101036da70e69c06e4916493e82e9fe3f65d85254c8c14f6ca0579bf" + "2a0d4d1915cd0537ff0f62cf13eb2fa2"), + a2bs_hex("bbe6b4b60da9d9bc34dcc8502fb81d8fd5fdbc8fa89605c86205f2b8c6530ff64" + "c84012a0dc634357f3f5477a820e182182f24ae8e835ab10c18386cc8a0727816" + "a6144a12b6c22d1dceba20669ed635a40831066abd8071342119d7da11c43696b" + "289d3f38b628639bfbd69a94d4053eab1c31e075e014cc578b226cfe24d6b4272" + "dffffb6e79bbceaf8594045d16a62eed16904a3569860c531a32eaa5abb868b1d" + "d6adb242972def8f23a4aae88451307c2abaf54c1803ae54e3f1149aa6e6d42ae" + "d30e9ffa99fc00b47f7eeb469512a413e873f91d959cccacccd3585b7f00ed8d4" + "685022101713c3adc439f27512a45926c1d2473477662c4bace72f380d105ddc9" + "f88cc7d474e876be07954e8b2deff4ade4bf30229fb6c92df4d66cd463f6848fe" + "e82b655583317eca4aaa4dac841524a2dca111749629637ef29fa7e72645a8759" + "57fb6b4754b940210eb59f1581d658ddf8de8389e0e2d123e2cae3c2be6eb1943" + "ea7458823f0229f2fa90b7a7099e8ec349d3d0fe0277cba5157ca62c8fbaa893f" + "37b8c8cea2a0d2d2912caccf92d31aa17439479711b4a5b77445cc02c18bfe019" + "5ce8ccc896dd4cdf45f9090c96dfb925795cfb4ccda83e3eb4f745577b17fc199" + "6ec66bf5f4103c9085134cad7863a41b04f32ef20201e54b55f1817ce5896195b" + "376874e9750212fe94d7656d9cd835db3b771ba5b58d2b255"), + "ksid854c89bf20cde54122c24fca", + a2bs_hex("7d2b905e368a5966972a2729bb8bbea734a2b21a68eb26cc8c3b2e344579" + "003b12a3855016c8a03c9a406094d80059ef4ca26f1928fa2a3f0782c72d" + "ba0e2228a70e2e150afb66e305e4bc0b2561ae2b"), + 6, +}; struct HlsAttributesInfo { std::string key_set_id; @@ -1433,6 +1870,87 @@ HlsAttributesInfo kHlsAttributesTestData[] = { "41"), }}; +// Usage Table and Entry Test Data +// Note: Make sure the number of entries in kUsageEntriesTestData and +// kUsageTableInfoTestData are equal. +CdmUsageEntryInfo kUsageEntriesTestData[] = { + // usage entry 0 + { + kStorageLicense, "ksid0", "", + }, + // usage entry 1 + { + kStorageLicense, "ksid1", "", + }, + // usage entry 2 + { + kStorageUsageInfo, "", "app_id_2", + }, + // usage entry 3 + { + kStorageUsageInfo, "", "app_id_3", + }, + // usage entry 4 + { + kStorageLicense, "ksid4", "", + }, + // usage entry 5 + { + kStorageUsageInfo, "", "app_id_5", + }, + +}; + +struct UsageTableTestInfo { + CdmUsageTableHeader usage_table_header; + std::string file_data; +}; + +UsageTableTestInfo kUsageTableInfoTestData[] = { + // usage table 0 + + {a2bs_hex("5574517CCC"), + a2bs_hex("0A18080510013A120A055574517CCC1209080112056B73696430122018268E3F" + "384F28D04BEE00304089C000463C22E987532855390915FD02C36B5C")}, + // usage table 1 + {a2bs_hex("CA870203010001288001"), + a2bs_hex("0A2C080510013A260A0ACA870203010001288001120B080112056B736964301A" + "00120B080112056B736964311A00122049A8F3481444A5B64B6C4F05FBCC2EF8" + "CB67444A08654763F2F5B80F658D7B38")}, + // usage table 2 + {a2bs_hex("7A7D507618A5D3A68F05228E023082010A028201"), + a2bs_hex("0A46080510013A400A147A7D507618A5D3A68F05228E023082010A028201120B" + "080112056B736964301A00120B080112056B736964311A00120E080212001A08" + "6170705F69645F321220783E93A02223BDB94E743856C0F69C35B213ACCDDE91" + "93E48E9186AA83B80584")}, + // usage table 3 + {a2bs_hex("E83A4902772DAFD2740B7748E9C3B1752D6F12859CED07E82969B4EC"), + a2bs_hex("0A5E080510013A580A1CE83A4902772DAFD2740B7748E9C3B1752D6F12859CED" + "07E82969B4EC120B080112056B736964301A00120B080112056B736964311A00" + "120E080212001A086170705F69645F32120E080212001A086170705F69645F33" + "122084E67F1338727291BC3D92E28442DC8B0F44CB5AF7B98A799313B7EB7F55" + "ED18")}, + // usage table 4 + {a2bs_hex("CA870203010001288001300112800250D1F8B1ECF849B60FF93E37C4DEEF" + "52F1CCFC047EF42300131F9C4758F4"), + a2bs_hex("0A7C080510013A760A2DCA870203010001288001300112800250D1F8B1ECF849" + "B60FF93E37C4DEEF52F1CCFC047EF42300131F9C4758F4120B080112056B7369" + "64301A00120B080112056B736964311A00120E080212001A086170705F69645F" + "32120E080212001A086170705F69645F33120B080112056B736964341A001220" + "1CDFCFED5E58A1DF77E1B335305424E1F0260340F9CC15985684C43A4207652" + "1")}, + // usage table 5 + {a2bs_hex("EC83A4902772DAFD2740B7748E9C3B1752D6F12859CED07E8882969B433E" + "C29AC6FDBE79230B0FAED5D94CF6B829A420BBE3270323941776EE60DD6B"), + a2bs_hex("0A9C01080510013A95010A3CEC83A4902772DAFD2740B7748E9C3B1752D6F128" + "59CED07E8882969B433EC29AC6FDBE79230B0FAED5D94CF6B829A420BBE32703" + "23941776EE60DD6B120B080112056B736964301A00120B080112056B73696431" + "1A00120E080212001A086170705F69645F32120E080212001A086170705F6964" + "5F33120B080112056B736964341A00120E080212001A086170705F69645F3512" + "20305C7A27A918268119E1996FC182C153DF805034A387F90C3585749E764731" + "32")}, +}; + class MockFile : public File { public: MockFile() : File(NULL) {} @@ -1489,10 +2007,18 @@ class DeviceFilesTest : public ::testing::Test { } size_t GetLicenseDataSize(LicenseInfo& data) { + CdmAppParameterMap app_parameters = GetAppParameters(data.app_parameters); + size_t app_parameters_len = 0; + CdmAppParameterMap::const_iterator itr = app_parameters.begin(); + for (itr = app_parameters.begin(); itr != app_parameters.end(); ++itr) { + app_parameters_len += itr->first.length(); + app_parameters_len += itr->second.length(); + } return sizeof(DeviceFiles::LicenseState) + data.pssh_data.size() + data.key_request.size() + data.key_response.size() + data.key_renewal_request.size() + data.key_renewal_response.size() + - data.key_release_url.size() + 3 * sizeof(int64_t); + data.key_release_url.size() + 3 * sizeof(int64_t) + + app_parameters_len + data.usage_entry.size(); } CdmAppParameterMap GetAppParameters(std::string str) { @@ -1527,6 +2053,10 @@ class DeviceFilesSecurityLevelTest : public DeviceFilesTest, public ::testing::WithParamInterface {}; +class DeviceFilesUsageInfoListTest + : public DeviceFilesTest, + public ::testing::WithParamInterface {}; + class DeviceFilesUsageInfoTest : public DeviceFilesTest, public ::testing::WithParamInterface {}; @@ -1534,12 +2064,30 @@ class DeviceFilesHlsAttributesTest : public DeviceFilesTest, public ::testing::WithParamInterface {}; +class DeviceFilesUsageTableTest : public DeviceFilesTest, + public ::testing::WithParamInterface {}; + MATCHER(IsCreateFileFlagSet, "") { return FileSystem::kCreate & arg; } MATCHER_P(IsStrEq, str, "") { // Estimating the length of data. We can have gmock provide length // as well as pointer to data but that will introduce a dependency on tr1 return memcmp(arg, str.c_str(), str.size()) == 0; } +MATCHER_P(ContainsAllElementsInVector, str_vector, "") { + // Estimating the length of data. We can have gmock provide length + // as well as pointer to data but that will introduce a dependency on tr1 + size_t str_length = 0; + for (size_t i = 0; i < str_vector.size(); ++i) { + str_length += str_vector[i].size(); + } + std::string data(arg, str_length + kProtobufEstimatedOverhead); + bool all_entries_found = true; + for (size_t i = 0; i < str_vector.size(); ++i) { + if (data.find(str_vector[i]) == std::string::npos) + all_entries_found = false; + } + return all_entries_found; +} MATCHER_P2(Contains, str1, size, "") { // Estimating the length of data. We can have gmock provide length // as well as pointer to data but that will introduce a dependency on tr1 @@ -1563,30 +2111,19 @@ MATCHER_P4(Contains, str1, str2, str3, size, "") { data.find(str2) != std::string::npos && data.find(str3) != std::string::npos); } -MATCHER_P5(Contains, str1, str2, str3, str4, size, "") { +MATCHER_P6(Contains, str1, str2, str3, str4, str5, size, "") { // Estimating the length of data. We can have gmock provide length // as well as pointer to data but that will introduce a dependency on tr1 std::string data(arg, size + str1.size() + str2.size() + str3.size() + - str4.size() + kProtobufEstimatedOverhead); - return (data.find(str1) != std::string::npos && - data.find(str2) != std::string::npos && - data.find(str3) != std::string::npos && - data.find(str4) != std::string::npos); -} -MATCHER_P6(Contains, str1, str2, str3, str4, str5, str6, "") { - // Estimating the length of data. We can have gmock provide length - // as well as pointer to data but that will introduce a dependency on tr1 - std::string data(arg, str1.size() + str2.size() + str3.size() + str4.size() + - str5.size() + str6.size() + + str4.size() + str5.size() + kProtobufEstimatedOverhead); return (data.find(str1) != std::string::npos && data.find(str2) != std::string::npos && data.find(str3) != std::string::npos && data.find(str4) != std::string::npos && - data.find(str5) != std::string::npos && - data.find(str6) != std::string::npos); + data.find(str5) != std::string::npos); } -MATCHER_P7(Contains, str1, str2, str3, str4, str5, str6, map7, "") { +MATCHER_P8(Contains, str1, str2, str3, str4, str5, str6, map7, str8, "") { // Estimating the length of data. We can have gmock provide length // as well as pointer to data but that will introduce a dependency on tr1 size_t map7_len = 0; @@ -1596,7 +2133,7 @@ MATCHER_P7(Contains, str1, str2, str3, str4, str5, str6, map7, "") { map7_len += itr->second.length(); } std::string data(arg, str1.size() + str2.size() + str3.size() + str4.size() + - str5.size() + str6.size() + map7_len + + str5.size() + str6.size() + map7_len + str8.size() + kProtobufEstimatedOverhead); bool map7_entries_present = true; for (itr = map7.begin(); itr != map7.end(); ++itr) { @@ -1609,7 +2146,8 @@ MATCHER_P7(Contains, str1, str2, str3, str4, str5, str6, map7, "") { data.find(str3) != std::string::npos && data.find(str4) != std::string::npos && data.find(str5) != std::string::npos && - data.find(str6) != std::string::npos && map7_entries_present); + data.find(str6) != std::string::npos && map7_entries_present && + data.find(str8) != std::string::npos); } TEST_F(DeviceCertificateStoreTest, StoreCertificate) { @@ -1665,8 +2203,6 @@ TEST_F(DeviceCertificateTest, DISABLED_ReadCertificate) { &serial_number, &system_id)); EXPECT_EQ(kTestCertificate, b2a_hex(certificate)); EXPECT_EQ(kTestWrappedPrivateKey, b2a_hex(wrapped_private_key)); - EXPECT_EQ(0u, system_id); - EXPECT_EQ("", b2a_hex(serial_number)); } TEST_F(DeviceCertificateTest, HasCertificate) { @@ -1738,7 +2274,8 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) { license_test_data[license_num].key_renewal_request, license_test_data[license_num].key_renewal_response, license_test_data[license_num].key_release_url, - app_parameters), + app_parameters, + license_test_data[license_num].usage_entry), Gt(GetLicenseDataSize(license_test_data[license_num])))) .WillOnce(ReturnArg<1>()); EXPECT_CALL(file, Close()).Times(1); @@ -1757,7 +2294,9 @@ TEST_P(DeviceFilesStoreTest, StoreLicense) { license_test_data[license_num].key_release_url, license_test_data[license_num].playback_start_time, license_test_data[license_num].last_playback_time, - license_test_data[license_num].grace_period_end_time, app_parameters)); + license_test_data[license_num].grace_period_end_time, app_parameters, + license_test_data[license_num].usage_entry, + license_test_data[license_num].usage_entry_number)); } INSTANTIATE_TEST_CASE_P(StoreLicense, DeviceFilesStoreTest, ::testing::Bool()); @@ -1783,7 +2322,8 @@ TEST_F(DeviceFilesTest, StoreLicenses) { license_test_data[i].key_renewal_request, license_test_data[i].key_renewal_response, license_test_data[i].key_release_url, - app_parameters), + app_parameters, + license_test_data[i].usage_entry), Gt(GetLicenseDataSize(license_test_data[i])))) .WillOnce(ReturnArg<1>()); } @@ -1805,7 +2345,9 @@ TEST_F(DeviceFilesTest, StoreLicenses) { license_test_data[i].key_release_url, license_test_data[i].playback_start_time, license_test_data[i].last_playback_time, - license_test_data[i].grace_period_end_time, app_parameters)); + license_test_data[i].grace_period_end_time, app_parameters, + license_test_data[i].usage_entry, + license_test_data[i].usage_entry_number)); } } @@ -1846,6 +2388,8 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) { int64_t grace_period_end_time; std::string release_server_url; CdmAppParameterMap app_parameters; + std::string usage_entry; + uint32_t usage_entry_number; for (size_t i = 0; i < kNumberOfLicenses; i++) { DeviceFiles::LicenseState license_state; @@ -1853,7 +2397,8 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) { license_test_data[i].key_set_id, &license_state, &pssh_data, &key_request, &key_response, &key_renewal_request, &key_renewal_response, &release_server_url, &playback_start_time, - &last_playback_time, &grace_period_end_time, &app_parameters)); + &last_playback_time, &grace_period_end_time, &app_parameters, + &usage_entry, &usage_entry_number)); EXPECT_EQ(license_test_data[i].license_state, license_state); EXPECT_EQ(license_test_data[i].pssh_data, pssh_data); EXPECT_EQ(license_test_data[i].key_request, key_request); @@ -1864,6 +2409,8 @@ TEST_F(DeviceFilesTest, RetrieveLicenses) { EXPECT_EQ(license_test_data[i].last_playback_time, last_playback_time); EXPECT_EQ(license_test_data[i].grace_period_end_time, grace_period_end_time); + EXPECT_EQ(license_test_data[i].usage_entry, usage_entry); + EXPECT_EQ(license_test_data[i].usage_entry_number, usage_entry_number); std::map::iterator itr; for (itr = app_parameters.begin(); itr != app_parameters.end(); ++itr) { @@ -1911,12 +2458,15 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) { int64_t grace_period_end_time; std::string release_server_url; CdmAppParameterMap app_parameters; + std::string usage_entry; + uint32_t usage_entry_number; EXPECT_TRUE(device_files.RetrieveLicense( test_data->key_set_id, &license_state, &pssh_data, &key_request, &key_response, &key_renewal_request, &key_renewal_response, &release_server_url, &playback_start_time, &last_playback_time, - &grace_period_end_time, &app_parameters)); + &grace_period_end_time, &app_parameters, &usage_entry, + &usage_entry_number)); EXPECT_EQ(test_data->license_state, license_state); EXPECT_EQ(test_data->pssh_data, pssh_data); EXPECT_EQ(test_data->key_request, key_request); @@ -1927,6 +2477,8 @@ TEST_F(DeviceFilesTest, AppParametersBackwardCompatibility) { EXPECT_EQ(test_data->last_playback_time, last_playback_time); EXPECT_EQ(test_data->grace_period_end_time, grace_period_end_time); EXPECT_EQ(0u, app_parameters.size()); + EXPECT_EQ(test_data->usage_entry, usage_entry); + EXPECT_EQ(test_data->usage_entry_number, usage_entry_number); } TEST_F(DeviceFilesTest, UpdateLicenseState) { @@ -1962,7 +2514,9 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) { license_update_test_data[0].playback_start_time, license_update_test_data[0].last_playback_time, license_update_test_data[0].grace_period_end_time, - GetAppParameters(license_test_data[0].app_parameters))); + GetAppParameters(license_test_data[0].app_parameters), + license_update_test_data[0].usage_entry, + license_update_test_data[0].usage_entry_number)); EXPECT_TRUE(device_files.StoreLicense( license_update_test_data[0].key_set_id, @@ -1976,7 +2530,9 @@ TEST_F(DeviceFilesTest, UpdateLicenseState) { license_update_test_data[0].playback_start_time, license_update_test_data[0].last_playback_time, license_update_test_data[0].grace_period_end_time, - GetAppParameters(license_test_data[0].app_parameters))); + GetAppParameters(license_test_data[0].app_parameters), + license_update_test_data[0].usage_entry, + license_update_test_data[0].usage_entry_number)); } TEST_F(DeviceFilesTest, DeleteLicense) { @@ -2016,12 +2572,15 @@ TEST_F(DeviceFilesTest, DeleteLicense) { std::string release_server_url; int64_t playback_start_time, last_playback_time, grace_period_end_time; CdmAppParameterMap app_parameters; + std::string usage_entry; + uint32_t usage_entry_number; EXPECT_TRUE(device_files.RetrieveLicense( license_test_data[0].key_set_id, &license_state, &pssh_data, &key_request, &key_response, &key_renewal_request, &key_renewal_response, &release_server_url, &playback_start_time, &last_playback_time, - &grace_period_end_time, &app_parameters)); + &grace_period_end_time, &app_parameters, &usage_entry, + &usage_entry_number)); EXPECT_EQ(license_test_data[0].license_state, license_state); EXPECT_EQ(license_test_data[0].pssh_data, pssh_data); EXPECT_EQ(license_test_data[0].key_request, key_request); @@ -2038,6 +2597,8 @@ TEST_F(DeviceFilesTest, DeleteLicense) { EXPECT_NE(license_test_data[0].app_parameters.find(itr->second), std::string::npos); } + EXPECT_EQ(license_test_data[0].usage_entry, usage_entry); + EXPECT_EQ(license_test_data[0].usage_entry_number, usage_entry_number); EXPECT_TRUE(device_files.DeleteLicense(license_test_data[0].key_set_id)); EXPECT_FALSE(device_files.LicenseExists(license_test_data[0].key_set_id)); @@ -2059,205 +2620,304 @@ TEST_F(DeviceFilesTest, ReserveLicenseIdsDoesNotUseFileSystem) { } } -TEST_P(DeviceFilesUsageInfoTest, Read) { +TEST_F(DeviceFilesUsageInfoTest, ListNullParam) { + MockFileSystem file_system; + MockFile file; + + DeviceFiles device_files(&file_system); + EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); + EXPECT_FALSE(device_files.ListUsageInfoFiles(NULL)); +} + +TEST_P(DeviceFilesUsageInfoListTest, UsageInfoList) { MockFileSystem file_system; MockFile file; - std::string app_id; // TODO(fredgc): add tests with multiple app_ids. - std::string path = - device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id); int index = GetParam(); - std::string data; - if (index >= 0) { - data = kUsageInfoTestData[index].file_data; - } - if (index >= 0) { - EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(true)); - EXPECT_CALL(file_system, FileSize(StrEq(path))) - .WillRepeatedly(Return(data.size())); - EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file)); - EXPECT_CALL(file, Read(NotNull(), Eq(data.size()))) - .WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()), - Return(data.size()))); - EXPECT_CALL(file, Close()).Times(1); - } else { - EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(false)); - EXPECT_CALL(file_system, FileSize(_)).Times(0); - EXPECT_CALL(file_system, Open(_, _)).Times(0); + std::vector file_list; + std::vector expected_usage_file_list; + for (int i = 0; i <= index; ++i) { + file_list.push_back(kTestListUsageInfoData[i].file_name); + if (kTestListUsageInfoData[i].is_usage_info_file) + expected_usage_file_list.push_back(kTestListUsageInfoData[i].file_name); } - EXPECT_CALL(file, Write(_, _)).Times(0); + EXPECT_CALL(file_system, List(StrEq(device_base_path_), NotNull())) + .WillOnce(DoAll(SetArgPointee<1>(file_list), Return(true))); DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - std::vector > license_info; - ASSERT_TRUE(device_files.RetrieveUsageInfo(app_id, &license_info)); - if (index >= 0) { - EXPECT_EQ(static_cast(index), license_info.size()); - for (size_t i = 0; i < license_info.size(); ++i) { - bool found = false; - for (size_t j = 0; j <= static_cast(index); ++j) { - if ((license_info[i].first.compare( - kUsageInfoTestData[j].license_request) == 0) && - (license_info[i].second.compare(kUsageInfoTestData[j].license) == - 0)) { - found = true; - } - } - EXPECT_TRUE(found); - } - } else { - EXPECT_EQ(0u, license_info.size()); - } + std::vector usage_info_file_names; + EXPECT_TRUE(device_files.ListUsageInfoFiles(&usage_info_file_names)); + EXPECT_EQ(expected_usage_file_list.size(), usage_info_file_names.size()); + EXPECT_THAT(usage_info_file_names, + ::testing::UnorderedElementsAreArray(expected_usage_file_list)); } +INSTANTIATE_TEST_CASE_P(UsageInfo, DeviceFilesUsageInfoListTest, + ::testing::Range(0, 7)); + TEST_P(DeviceFilesUsageInfoTest, Store) { MockFileSystem file_system; - std::string app_id; // TODO(fredgc): multiple app ids. - std::string pst(GenerateRandomData(kProviderSessionTokenLen)); - std::string license_request(GenerateRandomData(kLicenseRequestLen)); - std::string license(GenerateRandomData(kLicenseLen)); - std::string key_set_id(GenerateRandomData(kKeySetIdLen)); - std::string path = - device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id); + MockFile file; int index = GetParam(); - std::string data; - if (index >= 0) { - data = kUsageInfoTestData[index].file_data; + + std::string app_id; + if (index >= 0) app_id = kUsageInfoTestData[index].app_id; + std::string file_name = DeviceFiles::GetUsageInfoFileName(app_id); + std::string path = device_base_path_ + file_name; + + size_t usage_data_fields_length = 0; + std::vector usage_data_fields; + std::vector usage_data_list; + + for (int i = 0; i <= index; ++i) { + if (kUsageInfoTestData[i].app_id == app_id) { + usage_data_list.push_back(kUsageInfoTestData[i].usage_data); + usage_data_fields.push_back( + kUsageInfoTestData[i].usage_data.provider_session_token); + usage_data_fields.push_back( + kUsageInfoTestData[i].usage_data.license_request); + usage_data_fields.push_back(kUsageInfoTestData[i].usage_data.license); + usage_data_fields.push_back(kUsageInfoTestData[i].usage_data.key_set_id); + usage_data_fields.push_back(kUsageInfoTestData[i].usage_data.usage_entry); + usage_data_fields_length += + kUsageInfoTestData[i].usage_data.provider_session_token.size() + + kUsageInfoTestData[i].usage_data.license_request.size() + + kUsageInfoTestData[i].usage_data.license.size() + + kUsageInfoTestData[i].usage_data.key_set_id.size() + + kUsageInfoTestData[i].usage_data.usage_entry.size(); + } } + EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file)); + EXPECT_CALL(file, Write(ContainsAllElementsInVector(usage_data_fields), + Gt(usage_data_fields_length))) + .WillOnce(ReturnArg<1>()); + EXPECT_CALL(file, Close()); + + DeviceFiles device_files(&file_system); + EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); + + EXPECT_TRUE(device_files.StoreUsageInfo(file_name, usage_data_list)); +} + +TEST_P(DeviceFilesUsageInfoTest, Retrieve) { + MockFileSystem file_system; MockFile file; - EXPECT_CALL(file_system, Exists(StrEq(path))) - .WillRepeatedly(Return(index >= 0)); + + int index = GetParam(); + + std::string app_id; + if (index >= 0) app_id = kUsageInfoTestData[index].app_id; + + std::string file_name = DeviceFiles::GetUsageInfoFileName(app_id); + std::string path = device_base_path_ + file_name; + std::string file_data = (index < 0) ? kEmptyUsageInfoFileData + : kUsageInfoTestData[index].file_data; if (index >= 0) { - EXPECT_CALL(file_system, FileSize(StrEq(path))) - .WillOnce(Return(data.size())); - EXPECT_CALL(file_system, Open(StrEq(path), _)) + EXPECT_CALL(file_system, Exists(StrEq(path))) .Times(2) - .WillRepeatedly(Return(&file)); - EXPECT_CALL(file, Read(NotNull(), Eq(data.size()))) - .WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()), - Return(data.size()))); - EXPECT_CALL(file, Close()).Times(2); - } else { - EXPECT_CALL(file_system, FileSize(_)).Times(0); - EXPECT_CALL(file_system, Open(_, _)).Times(1).WillOnce(Return(&file)); + .WillRepeatedly(Return(true)); + EXPECT_CALL(file_system, FileSize(StrEq(path))) + .Times(2) + .WillRepeatedly(Return(kUsageInfoTestData[index].file_data.size())); + EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file)); + EXPECT_CALL(file, + Read(NotNull(), Eq(kUsageInfoTestData[index].file_data.size()))) + .WillOnce(DoAll(SetArrayArgument<0>(file_data.begin(), file_data.end()), + Return(file_data.size()))); EXPECT_CALL(file, Close()); } + else { + EXPECT_CALL(file_system, Exists(StrEq(path))) + .WillOnce(Return(false)); + } - EXPECT_CALL(file, - Write(Contains(pst, license_request, license, key_set_id, - data.size()), - Gt(pst.size() + license_request.size() + license.size() + - key_set_id.size()))) - .WillOnce(ReturnArg<1>()); + std::vector usage_data_list; + DeviceFiles device_files(&file_system); + EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); + + EXPECT_TRUE(device_files.RetrieveUsageInfo(file_name, &usage_data_list)); + + for (size_t i = 0; i < usage_data_list.size(); ++i) { + bool found = false; + int j = 0; + while (!found && j <= index) { + if (app_id == kUsageInfoTestData[j].app_id && + usage_data_list[i].provider_session_token == + kUsageInfoTestData[j].usage_data.provider_session_token) { + EXPECT_EQ(kUsageInfoTestData[j].usage_data.license_request, + usage_data_list[i].license_request); + EXPECT_EQ(kUsageInfoTestData[j].usage_data.license, + usage_data_list[i].license); + EXPECT_EQ(kUsageInfoTestData[j].usage_data.key_set_id, + usage_data_list[i].key_set_id); + EXPECT_EQ(kUsageInfoTestData[j].usage_data.usage_entry, + usage_data_list[i].usage_entry); + EXPECT_EQ(kUsageInfoTestData[j].usage_data.usage_entry_number, + usage_data_list[i].usage_entry_number); + found = true; + } + ++j; + } + EXPECT_TRUE(found); + } +} + +TEST_P(DeviceFilesUsageInfoTest, RetrieveByProviderSessionToken) { + MockFileSystem file_system; + MockFile file; + + int index = GetParam(); + + std::string app_id; + if (index >= 0) app_id = kUsageInfoTestData[index].app_id; + + std::string file_name = DeviceFiles::GetUsageInfoFileName(app_id); + std::string path = device_base_path_ + file_name; + + size_t max_index_by_app_id = 0; + for (size_t i = 0; i < sizeof(kUsageInfoTestData) / sizeof(UsageInfo); ++i) { + if (app_id == kUsageInfoTestData[i].app_id) max_index_by_app_id = i; + } + std::string file_data = + (index < 0) ? kEmptyUsageInfoFileData + : kUsageInfoTestData[max_index_by_app_id].file_data; + std::string provider_session_token = kUsageInfoTestData[index < 0 ? 0 : index] + .usage_data.provider_session_token; + + EXPECT_CALL(file_system, Exists(StrEq(path))).WillOnce(Return(true)); + EXPECT_CALL(file_system, FileSize(StrEq(path))) + .WillOnce(Return(file_data.size())); + EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file)); + EXPECT_CALL(file, Read(NotNull(), Eq(file_data.size()))) + .WillOnce(DoAll(SetArrayArgument<0>(file_data.begin(), file_data.end()), + Return(file_data.size()))); + EXPECT_CALL(file, Close()); DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - ASSERT_TRUE( - device_files.StoreUsageInfo(pst, license_request, license, app_id, - key_set_id)); + DeviceFiles::CdmUsageData usage_data; + + if (index < 0) { + EXPECT_FALSE(device_files.RetrieveUsageInfo( + file_name, provider_session_token, &usage_data)); + } else { + EXPECT_TRUE(device_files.RetrieveUsageInfo( + file_name, provider_session_token, &usage_data)); + + EXPECT_EQ(kUsageInfoTestData[index].usage_data.provider_session_token, + usage_data.provider_session_token); + EXPECT_EQ(kUsageInfoTestData[index].usage_data.license_request, + usage_data.license_request); + EXPECT_EQ(kUsageInfoTestData[index].usage_data.license, usage_data.license); + EXPECT_EQ(kUsageInfoTestData[index].usage_data.key_set_id, + usage_data.key_set_id); + EXPECT_EQ(kUsageInfoTestData[index].usage_data.usage_entry, + usage_data.usage_entry); + EXPECT_EQ(kUsageInfoTestData[index].usage_data.usage_entry_number, + usage_data.usage_entry_number); + } } -TEST_P(DeviceFilesUsageInfoTest, Delete) { +TEST_P(DeviceFilesUsageInfoTest, UpdateUsageInfo) { MockFileSystem file_system; MockFile file; - std::string app_id; // TODO(fredgc): expand tests. - std::string path = - device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id); int index = GetParam(); - if (index < 0) return; - std::string data, pst, prev_data, prev_pst, prev_license; - if (index >= 0) { - data = kUsageInfoTestData[index].file_data; - if (index >= 1) { - pst = kUsageInfoTestData[index].provider_session_token; - prev_data = kUsageInfoTestData[index - 1].file_data; - prev_pst = kUsageInfoTestData[index - 1].provider_session_token; - prev_license = kUsageInfoTestData[index - 1].license; + std::string app_id; + if (index >= 0) app_id = kUsageInfoTestData[index].app_id; + + std::string file_name = DeviceFiles::GetUsageInfoFileName(app_id); + std::string path = device_base_path_ + file_name; + + size_t usage_data_fields_length = 0; + std::vector usage_data_fields; + + size_t max_index_by_app_id = 0; + for (size_t i = 0; i < sizeof(kUsageInfoTestData) / sizeof(UsageInfo); ++i) { + if (app_id == kUsageInfoTestData[i].app_id) { + max_index_by_app_id = i; + + if ((int)i != index) { + usage_data_fields.push_back( + kUsageInfoTestData[i].usage_data.provider_session_token); + usage_data_fields.push_back( + kUsageInfoTestData[i].usage_data.license_request); + usage_data_fields.push_back(kUsageInfoTestData[i].usage_data.license); + usage_data_fields.push_back( + kUsageInfoTestData[i].usage_data.key_set_id); + usage_data_fields.push_back( + kUsageInfoTestData[i].usage_data.usage_entry); + usage_data_fields_length += + kUsageInfoTestData[i].usage_data.provider_session_token.size() + + kUsageInfoTestData[i].usage_data.license_request.size() + + kUsageInfoTestData[i].usage_data.license.size() + + kUsageInfoTestData[i].usage_data.key_set_id.size() + + kUsageInfoTestData[i].usage_data.usage_entry.size(); + } } } - EXPECT_CALL(file_system, Exists(StrEq(path))).WillOnce(Return(index >= 0)); + if (index >= 0) { + usage_data_fields.push_back( + kUsageInfoTestData[index].usage_data.provider_session_token); + usage_data_fields.push_back(kUsageInfoUpdateTestData.license_request); + usage_data_fields.push_back(kUsageInfoUpdateTestData.license); + usage_data_fields.push_back(kUsageInfoUpdateTestData.key_set_id); + usage_data_fields.push_back(kUsageInfoUpdateTestData.usage_entry); + usage_data_fields_length += + kUsageInfoTestData[index].usage_data.provider_session_token.size() + + kUsageInfoUpdateTestData.license_request.size() + + kUsageInfoUpdateTestData.license.size() + + kUsageInfoUpdateTestData.key_set_id.size() + + kUsageInfoUpdateTestData.usage_entry.size(); + } - EXPECT_CALL(file_system, FileSize(StrEq(path))).WillOnce(Return(data.size())); - if (index >= 1) { + std::string file_data = + (index < 0) ? kEmptyUsageInfoFileData + : kUsageInfoTestData[max_index_by_app_id].file_data; + std::string provider_session_token = kUsageInfoTestData[index < 0 ? 0 : index] + .usage_data.provider_session_token; + + EXPECT_CALL(file_system, Exists(StrEq(path))) + .Times(2) + .WillRepeatedly(Return(true)); + EXPECT_CALL(file_system, FileSize(StrEq(path))) + .WillOnce(Return(file_data.size())); + EXPECT_CALL(file, Read(NotNull(), Eq(file_data.size()))) + .WillOnce(DoAll(SetArrayArgument<0>(file_data.begin(), file_data.end()), + Return(file_data.size()))); + + if (index < 0) { + EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file)); + EXPECT_CALL(file, Close()); + } else { EXPECT_CALL(file_system, Open(StrEq(path), _)) .Times(2) .WillRepeatedly(Return(&file)); - EXPECT_CALL(file, Write(Contains(prev_pst, prev_license, prev_data.size()), - Gt(prev_pst.size() + prev_license.size()))) + EXPECT_CALL(file, Write(ContainsAllElementsInVector(usage_data_fields), + Gt(usage_data_fields_length))) .WillOnce(ReturnArg<1>()); EXPECT_CALL(file, Close()).Times(2); - } else { - EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file)); - EXPECT_CALL(file, Write(_, _)).Times(0); - EXPECT_CALL(file, Close()).Times(1); - } - EXPECT_CALL(file, Read(NotNull(), Eq(data.size()))) - .WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()), - Return(data.size()))); - - DeviceFiles device_files(&file_system); - EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - - if (index >= 1) { - ASSERT_TRUE(device_files.DeleteUsageInfo(app_id, pst)); - } else { - ASSERT_FALSE(device_files.DeleteUsageInfo(app_id, pst)); - } -} - -TEST_P(DeviceFilesUsageInfoTest, DeleteAll) { - MockFileSystem file_system; - MockFile file; - std::string app_id; // TODO(fredgc): expand tests. - std::string path = - device_base_path_ + DeviceFiles::GetUsageInfoFileName(app_id); - - int index = GetParam(); - EXPECT_CALL(file, Write(_, _)).Times(0); - - std::string data; - if (index < 0) { - EXPECT_CALL(file_system, Exists(StrEq(path))).WillOnce(Return(false)); - } else { - data = kUsageInfoTestData[index].file_data; - EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(true)); - EXPECT_CALL(file_system, FileSize(StrEq(path))) - .WillOnce(Return(data.size())); - EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file)); - EXPECT_CALL(file, Read(NotNull(), Eq(data.size()))) - .WillOnce(DoAll(SetArrayArgument<0>(data.begin(), data.end()), - Return(data.size()))); - EXPECT_CALL(file, Close()).Times(1); - EXPECT_CALL(file_system, Remove(StrEq(path))).WillOnce(Return(true)); } DeviceFiles device_files(&file_system); EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); - std::vector psts; - ASSERT_TRUE(device_files.DeleteAllUsageInfoForApp(app_id, &psts)); - if (index < 0) { - EXPECT_EQ(0u, psts.size()); - } else { - // DeleteAllUsageInfoForApp returns a list of all psts that - // should be deleted by oemcrypto. - EXPECT_EQ(static_cast(index), psts.size()); - for (int i = 0; i < index; i++) { - EXPECT_EQ(kUsageInfoTestData[i + 1].provider_session_token, psts[i]); - } - } + bool expected_result = index >= 0; + EXPECT_EQ(expected_result, + device_files.UpdateUsageInfo(file_name, provider_session_token, + kUsageInfoUpdateTestData)); } INSTANTIATE_TEST_CASE_P(UsageInfo, DeviceFilesUsageInfoTest, - ::testing::Range(-1, 4)); + ::testing::Range(-1, 9)); TEST_P(DeviceFilesHlsAttributesTest, Read) { MockFileSystem file_system; @@ -2329,4 +2989,84 @@ TEST_P(DeviceFilesHlsAttributesTest, Delete) { INSTANTIATE_TEST_CASE_P(HlsAttributes, DeviceFilesHlsAttributesTest, ::testing::Range(&kHlsAttributesTestData[0], &kHlsAttributesTestData[2])); + +TEST_P(DeviceFilesUsageTableTest, Store) { + MockFileSystem file_system; + MockFile file; + int index = GetParam(); + + size_t entry_data_length = 0; + std::vector entry_data; + std::vector usage_entry_info; + usage_entry_info.resize(index + 1); + for (int i = 0; i <= index; ++i) { + usage_entry_info[i] = kUsageEntriesTestData[i]; + entry_data.push_back(kUsageEntriesTestData[i].key_set_id); + entry_data.push_back(kUsageEntriesTestData[i].usage_info_file_name); + entry_data_length += kUsageEntriesTestData[i].key_set_id.size() + + kUsageEntriesTestData[i].usage_info_file_name.size(); + } + entry_data.push_back(kUsageTableInfoTestData[index].usage_table_header); + entry_data_length += kUsageTableInfoTestData[index].usage_table_header.size(); + + std::string path = device_base_path_ + DeviceFiles::GetUsageTableFileName(); + + EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(true)); + EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file)); + EXPECT_CALL(file, Write(ContainsAllElementsInVector(entry_data), + Gt(entry_data_length))) + .WillOnce(ReturnArg<1>()); + EXPECT_CALL(file, Read(_, _)).Times(0); + EXPECT_CALL(file, Close()).Times(1); + + DeviceFiles device_files(&file_system); + EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); + ASSERT_TRUE(device_files.StoreUsageTableInfo( + kUsageTableInfoTestData[index].usage_table_header, usage_entry_info)); +} + +TEST_P(DeviceFilesUsageTableTest, Read) { + MockFileSystem file_system; + MockFile file; + size_t index = GetParam(); + + std::string path = device_base_path_ + DeviceFiles::GetUsageTableFileName(); + + const std::string& file_data = kUsageTableInfoTestData[index].file_data; + EXPECT_CALL(file_system, Exists(StrEq(path))).WillRepeatedly(Return(true)); + EXPECT_CALL(file_system, FileSize(StrEq(path))) + .WillRepeatedly(Return(file_data.size())); + EXPECT_CALL(file_system, Open(StrEq(path), _)).WillOnce(Return(&file)); + EXPECT_CALL(file, Read(NotNull(), Eq(file_data.size()))) + .WillOnce(DoAll(SetArrayArgument<0>(file_data.begin(), file_data.end()), + Return(file_data.size()))); + EXPECT_CALL(file, Close()).Times(1); + + EXPECT_CALL(file, Write(_, _)).Times(0); + + DeviceFiles device_files(&file_system); + EXPECT_TRUE(device_files.Init(kSecurityLevelL1)); + + std::vector usage_entry_info; + CdmUsageTableHeader usage_table_header; + ASSERT_TRUE(device_files.RetrieveUsageTableInfo(&usage_table_header, + &usage_entry_info)); + EXPECT_EQ(kUsageTableInfoTestData[index].usage_table_header, + usage_table_header); + EXPECT_EQ(index + 1u, usage_entry_info.size()); + + for (size_t i = 0; i <= index; ++i) { + EXPECT_EQ(kUsageEntriesTestData[i].storage_type, + usage_entry_info[i].storage_type); + EXPECT_EQ(kUsageEntriesTestData[i].key_set_id, + usage_entry_info[i].key_set_id); + EXPECT_EQ( + kUsageEntriesTestData[i].usage_info_file_name, + usage_entry_info[i].usage_info_file_name); + } +} + +INSTANTIATE_TEST_CASE_P(UsageInfo, DeviceFilesUsageTableTest, + ::testing::Range(0, 6)); + } // namespace wvcdm diff --git a/core/test/generic_crypto_unittest.cpp b/core/test/generic_crypto_unittest.cpp index 70bed6b0..254b3d40 100644 --- a/core/test/generic_crypto_unittest.cpp +++ b/core/test/generic_crypto_unittest.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include "cdm_engine.h" @@ -15,6 +16,7 @@ #include "log.h" #include "oec_session_util.h" #include "../../oemcrypto/mock/src/oemcrypto_key_mock.h" +#include "properties.h" #include "string_conversions.h" #include "url_request.h" #include "wv_cdm_constants.h" @@ -28,73 +30,32 @@ std::string g_provisioning_server; std::string g_license_service_certificate; std::string g_provisioning_service_certificate; -/* - * Locate the portion of the server's response message that is between - * the strings jason_start_substr and json_end_substr. Returns the string - * through *result. If the start substring match fails, assume the entire - * string represents a serialized protobuf mesaage and return true with - * the entire string. If the end_substring match fails, return false with - * an empty *result. - */ -bool ExtractSignedMessage(const std::string& response, - const std::string& json_start_substr, - const std::string& json_end_substr, - std::string* result) { - std::string b64_string; - size_t start = response.find(json_start_substr); - - if (start == response.npos) { - // Assume web safe protobuf - b64_string.assign(response); - } else { - // Assume JSON-wrapped protobuf - size_t end = response.find(json_end_substr, - start + json_start_substr.length()); - if (end == response.npos) { - LOGE("ExtractSignedMessage cannot locate end substring"); - result->clear(); - return false; - } - size_t b64_string_size = end - start - json_start_substr.length(); - b64_string.assign(response, start + json_start_substr.length(), - b64_string_size); - } - - if (b64_string.empty()) { - LOGE("Response message is empty"); - result->clear(); - return false; - } - - result->swap(b64_string); - return true; -} - } // namespace namespace wvcdm { class WvGenericOperationsTest : public testing::Test { public: + WvGenericOperationsTest() : crypto_session_(NULL) {} + virtual void SetUp() { ::testing::Test::SetUp(); - ConfigTestEnv config(kContentProtectionStagingPlusProv30); + ConfigTestEnv config(kContentProtectionStagingLicense); + Properties::set_provisioning_messages_are_binary(false); g_provisioning_service_certificate.assign( config.provisioning_service_certificate()); g_license_service_certificate.assign(config.license_service_certificate()); g_provisioning_server.assign(config.provisioning_server()); - cdm_engine_ = NULL; - // TODO(fredgc or gmorgan): This should be updated for provisioning 3.0 // Load test keybox. This keybox will be used by any CryptoSession // created by the CDM under test. ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox()); // Perform CdmEngine setup - cdm_engine_ = new CdmEngine(&file_system_); + cdm_engine_.reset(new CdmEngine(&file_system_)); Provision(); @@ -122,7 +83,7 @@ class WvGenericOperationsTest : public testing::Test { virtual void TearDown() { oec_util_session_.close(); - if (cdm_engine_ != NULL) { + if (cdm_engine_.get() != NULL) { cdm_engine_->CloseSession(session_id_); } // OEMCrypto_Terminate() will be performed during the test class's @@ -216,7 +177,7 @@ class WvGenericOperationsTest : public testing::Test { protected: virtual void Provision() { - LOGE("WvCdmEnginePreProvTest::Provision: url=%s", + LOGE("WvGenericOperationsTest::Provision: url=%s", g_provisioning_server.c_str()); CdmProvisioningRequest prov_request; std::string provisioning_server_url; @@ -229,7 +190,7 @@ class WvGenericOperationsTest : public testing::Test { cert_type, cert_authority, &prov_request, &provisioning_server_url)); - LOGV("WvCdmEnginePreProvTest::Provision: req=%s", prov_request.c_str()); + LOGV("WvGenericOperationsTest::Provision: req=%s", prov_request.c_str()); // Ignore URL provided by CdmEngine. Use ours, as configured // for test vs. production server. @@ -241,28 +202,14 @@ class WvGenericOperationsTest : public testing::Test { bool ok = url_request.GetResponse(&http_message); EXPECT_TRUE(ok); - LOGV("WvCdmEnginePreProvTest::Provision: http_message: \n%s\n", + LOGV("WvGenericOperationsTest::Provision: http_message: \n%s\n", http_message.c_str()); - // extract provisioning response from received message - // Extracts signed response from JSON string, decodes base64 signed response - const std::string kMessageStart = "\"signedResponse\": \""; - const std::string kMessageEnd = "\""; - std::string base64_response; - EXPECT_TRUE (ExtractSignedMessage(http_message, kMessageStart, kMessageEnd, - &base64_response)) << - "Failed to extract signed serialized response from JSON response"; - - LOGV("WvCdmEnginePreProvTest::Provision: extracted response " - "message: \n%s\n", base64_response.c_str()); - ASSERT_EQ(NO_ERROR, - cdm_engine_->HandleProvisioningResponse(base64_response, + cdm_engine_->HandleProvisioningResponse(http_message, &cert, &wrapped_key)); - ASSERT_EQ(NO_ERROR, - cdm_engine_->SetServiceCertificate( - g_license_service_certificate)); + cdm_engine_->SetServiceCertificate(g_license_service_certificate)); } // This CryptoSession object handles Initialization and Termination @@ -272,7 +219,7 @@ class WvGenericOperationsTest : public testing::Test { CryptoSession crypto_session_; FileSystem file_system_; - CdmEngine* cdm_engine_; + std::unique_ptr cdm_engine_; std::string key_msg_; std::string session_id_; std::string server_url_; @@ -309,7 +256,7 @@ TEST_F(WvGenericOperationsTest, GenericEncryptNoKey) { cdm_sts = cdm_engine_->GenericEncrypt(session_id_, in_buffer, key_id, iv, wvcdm::kEncryptionAlgorithmAesCbc128, &out_buffer); - EXPECT_EQ(KEY_ERROR_1, cdm_sts); + EXPECT_EQ(NO_CONTENT_KEY_2, cdm_sts); } TEST_F(WvGenericOperationsTest, GenericEncryptKeyNotAllowed) { diff --git a/core/test/http_socket.cpp b/core/test/http_socket.cpp index f1d20daf..f37db03a 100644 --- a/core/test/http_socket.cpp +++ b/core/test/http_socket.cpp @@ -2,6 +2,7 @@ #include "http_socket.h" +#include #include #include #include @@ -36,13 +37,14 @@ bool Tokenize(const std::string& source, const std::string& delim, } SSL_CTX* InitSslContext() { - const SSL_METHOD* method; - SSL_CTX* ctx; - OpenSSL_add_all_algorithms(); SSL_load_error_strings(); - method = TLSv1_2_client_method(); - ctx = SSL_CTX_new(method); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + const SSL_METHOD* method = TLSv1_2_client_method(); +#else + const SSL_METHOD* method = TLS_client_method(); +#endif + SSL_CTX* ctx = SSL_CTX_new(method); if (!ctx) LOGE("failed to create SSL context"); int ret = SSL_CTX_set_cipher_list( ctx, "ALL:!RC4-MD5:!RC4-SHA:!ECDHE-ECDSA-RC4-SHA:!ECDHE-RSA-RC4-SHA"); diff --git a/core/test/initialization_data_unittest.cpp b/core/test/initialization_data_unittest.cpp index c0c159b5..d718568b 100644 --- a/core/test/initialization_data_unittest.cpp +++ b/core/test/initialization_data_unittest.cpp @@ -420,6 +420,11 @@ class HlsParseTest : public ::testing::TestWithParam {}; class HlsTest : public ::testing::Test {}; } // namespace +TEST_F(InitializationDataTest, BadType) { + InitializationData init_data("bad", kSubLicensePsshBox); + EXPECT_TRUE(init_data.IsEmpty()); +} + TEST_P(InitializationDataTest, Parse) { InitializationData init_data(ISO_BMFF_VIDEO_MIME_TYPE, GetParam()); EXPECT_FALSE(init_data.IsEmpty()); @@ -431,21 +436,22 @@ INSTANTIATE_TEST_CASE_P( kWidevinePsshAfterV0Pssh, kWidevinePsshAfterNonZeroFlags, kWidevinePsshAfterV1Pssh, kWidevineV1Pssh, kOtherBoxFirst, kZeroSizedPsshBox, kSubLicensePsshBox)); + TEST_F(InitializationDataTest, ExtractSubLicense) { InitializationData init_data(ISO_BMFF_VIDEO_MIME_TYPE, kSubLicensePsshBox); - EXPECT_FALSE(init_data.IsEmpty()); + ASSERT_FALSE(init_data.IsEmpty()); std::vector keys = init_data.ExtractEmbeddedKeys(); ASSERT_EQ(keys.size(), 2UL); - ASSERT_EQ(keys[0].sub_session_key_id(), "sub_session_key_id_0"); - ASSERT_EQ(keys[1].sub_session_key_id(), "sub_session_key_id_1"); - ASSERT_EQ(keys[0].key_msg(), "sub_license_0"); - ASSERT_EQ(keys[1].key_msg(), "sub_license_1"); + EXPECT_EQ(keys[0].sub_session_key_id(), "sub_session_key_id_0"); + EXPECT_EQ(keys[1].sub_session_key_id(), "sub_session_key_id_1"); + EXPECT_EQ(keys[0].key_msg(), "sub_license_0"); + EXPECT_EQ(keys[1].key_msg(), "sub_license_1"); } TEST_F(InitializationDataTest, ExtractEmptySubLicense) { InitializationData init_data(ISO_BMFF_VIDEO_MIME_TYPE, kWidevinePssh); - EXPECT_FALSE(init_data.IsEmpty()); + ASSERT_FALSE(init_data.IsEmpty()); std::vector keys = init_data.ExtractEmbeddedKeys(); ASSERT_TRUE(keys.empty()); diff --git a/core/test/license_unittest.cpp b/core/test/license_unittest.cpp index 41fdd95e..87b648e6 100644 --- a/core/test/license_unittest.cpp +++ b/core/test/license_unittest.cpp @@ -3,6 +3,8 @@ #include #include +#include + #include "clock.h" #include "crypto_session.h" #include "initialization_data.h" @@ -34,6 +36,33 @@ const std::string kCryptoRequestId = a2bs_hex( "4341444542353737444337393044394330313030303030303030303030303030"); const uint32_t kNonce = 0x49e81305; const int64_t kLicenseStartTime = 1413517500; // ~ 01/01/2013 +const std::string kEmptyServiceCertificate; +const std::string kInvalidServiceCertificate = "0b"; +const std::string kDefaultServiceCertificate = a2bs_hex( + "0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522" + "8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417" + "7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781" + "5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D" + "0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6" + "AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0" + "40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A" + "0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6" + "283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0" + "D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301" + "00013A1273746167696E672E676F6F676C652E636F6D128003983E303526" + "75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA" + "EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA" + "97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91" + "5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0" + "32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0" + "EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28" + "8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932" + "1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784" + "C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742" + "BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940" + "383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8" + "38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A" + "250A4EB9C84AB3E6539F6B6FDF56899EA29914"); const std::string kToken = a2bs_hex( "0AAE02080212107E0A892DEEB021E7AF696B938BB1D5B1188B85AD9D05228E023082010A02" "82010100DBEDF2BFB0EC98213766E65049B9AB176FA4B1FBFBB2A0C96C87D9F2B895E0ED77" @@ -105,12 +134,21 @@ const std::string kSubSessionKeyID2 = const std::string kSubSessionKeyID3 = a2bs_hex("21cdec9b2105c6b643e71f68e5302c85"); +const CryptoSession::SupportedCertificateTypes kDefaultSupportedCertTypes = { + true, + true, + true + }; + class MockCryptoSession : public CryptoSession { public: + MockCryptoSession(metrics::CryptoMetrics* crypto_metrics) + : CryptoSession(crypto_metrics) { } MOCK_METHOD0(IsOpen, bool()); MOCK_METHOD1(GenerateRequestId, bool(std::string*)); MOCK_METHOD1(UsageInformationSupport, bool(bool*)); MOCK_METHOD2(GetHdcpCapabilities, bool(HdcpCapability*, HdcpCapability*)); + MOCK_METHOD1(GetSupportedCertificateTypes, bool(SupportedCertificateTypes*)); MOCK_METHOD1(GetApiVersion, bool(uint32_t*)); MOCK_METHOD1(GenerateNonce, bool(uint32_t*)); MOCK_METHOD3(PrepareRequest, bool(const std::string&, bool, std::string*)); @@ -152,6 +190,7 @@ using ::testing::Eq; using ::testing::NotNull; using ::testing::Return; using ::testing::SetArgPointee; +using ::testing::UnorderedElementsAre; class CdmLicenseTest : public ::testing::Test { protected: @@ -159,9 +198,13 @@ class CdmLicenseTest : public ::testing::Test { : pssh_(pssh) {} virtual void SetUp() { clock_ = new MockClock(); - crypto_session_ = new testing::StrictMock(); + crypto_session_ = new MockCryptoSession(&crypto_metrics_); init_data_ = new MockInitializationData(CENC_INIT_DATA_FORMAT, pssh_); policy_engine_ = new MockPolicyEngine(crypto_session_); + + ON_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull())) + .WillByDefault( + DoAll(SetArgPointee<0>(kDefaultSupportedCertTypes), Return(true))); } virtual void TearDown() { @@ -179,10 +222,10 @@ class CdmLicenseTest : public ::testing::Test { CdmLicense* cdm_license_; MockClock* clock_; + metrics::CryptoMetrics crypto_metrics_; MockCryptoSession* crypto_session_; MockInitializationData* init_data_; MockPolicyEngine* policy_engine_; - ServiceCertificate service_cert_; std::string pssh_; }; @@ -195,37 +238,60 @@ TEST_F(CdmLicenseTest, InitSuccess) { EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true)); CreateCdmLicense(); - EXPECT_TRUE(cdm_license_->Init( - &service_cert_, kToken, kClientTokenDrmCert, kEmptyString, - crypto_session_, policy_engine_)); + EXPECT_TRUE(cdm_license_->Init(kToken, kClientTokenDrmCert, kEmptyString, + false, kEmptyServiceCertificate, + crypto_session_, policy_engine_)); } TEST_F(CdmLicenseTest, InitFail_EmptyToken) { CreateCdmLicense(); - EXPECT_FALSE(cdm_license_->Init(&service_cert_, "", kClientTokenDrmCert, - "", crypto_session_, policy_engine_)); + EXPECT_FALSE(cdm_license_->Init("", kClientTokenDrmCert, "", false, + kEmptyServiceCertificate, crypto_session_, + policy_engine_)); } TEST_F(CdmLicenseTest, InitFail_CryptoSessionNull) { CreateCdmLicense(); - EXPECT_FALSE(cdm_license_->Init(&service_cert_, kToken, kClientTokenDrmCert, - "", NULL, policy_engine_)); + EXPECT_FALSE(cdm_license_->Init(kToken, kClientTokenDrmCert, "", false, + kEmptyServiceCertificate, NULL, + policy_engine_)); } TEST_F(CdmLicenseTest, InitFail_PolicyEngineNull) { EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true)); CreateCdmLicense(); - EXPECT_FALSE(cdm_license_->Init(&service_cert_, kToken, kClientTokenDrmCert, - "", crypto_session_, NULL)); + EXPECT_FALSE(cdm_license_->Init(kToken, kClientTokenDrmCert, "", false, + kEmptyServiceCertificate, crypto_session_, + NULL)); } -TEST_F(CdmLicenseTest, InitWithNullServiceCert) { +TEST_F(CdmLicenseTest, InitWithEmptyServiceCert) { EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true)); CreateCdmLicense(); - EXPECT_TRUE(cdm_license_->Init(NULL, kToken, kClientTokenDrmCert, - "", crypto_session_, policy_engine_)); + EXPECT_EQ(cdm_license_->Init(kToken, kClientTokenDrmCert, "", true, + kEmptyServiceCertificate, crypto_session_, + policy_engine_), + Properties::allow_service_certificate_requests()); +} + +TEST_F(CdmLicenseTest, InitWithInvalidServiceCert) { + EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true)); + + CreateCdmLicense(); + EXPECT_FALSE(cdm_license_->Init(kToken, kClientTokenDrmCert, "", true, + kInvalidServiceCertificate, crypto_session_, + policy_engine_)); +} + +TEST_F(CdmLicenseTest, InitWithServiceCert) { + EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true)); + + CreateCdmLicense(); + EXPECT_TRUE(cdm_license_->Init(kToken, kClientTokenDrmCert, "", true, + kDefaultServiceCertificate, crypto_session_, + policy_engine_)); } TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) { @@ -244,6 +310,7 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) { EXPECT_CALL(*crypto_session_, GetHdcpCapabilities(NotNull(), NotNull())) .WillOnce(DoAll(SetArgPointee<0>(current_hdcp_version), SetArgPointee<1>(max_hdcp_version), Return(true))); + EXPECT_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull())); EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull())) .WillOnce( DoAll(SetArgPointee<0>(crypto_session_api_version), Return(true))); @@ -255,15 +322,12 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) { DoAll(SetArgPointee<2>(kLicenseRequestSignature), Return(true))); CreateCdmLicense(); - // TODO(gmorgan) fix below - no default service certificate - //service_cert_.Init(kDefaultServiceCertificate); EXPECT_TRUE(cdm_license_->Init( - &service_cert_, kToken, kClientTokenDrmCert, kEmptyString, - crypto_session_, policy_engine_)); + kToken, kClientTokenDrmCert, kEmptyString, true, + kDefaultServiceCertificate, crypto_session_, policy_engine_)); CdmAppParameterMap app_parameters; CdmKeyMessage signed_request; - Properties::set_use_certificates_as_identification(true); std::string server_url; EXPECT_EQ(cdm_license_->PrepareKeyRequest( *init_data_, kLicenseTypeStreaming, app_parameters, @@ -315,6 +379,14 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) { EXPECT_EQ(crypto_session_api_version, client_capabilities.oem_crypto_api_version()); + EXPECT_THAT( + client_capabilities.supported_certificate_key_type(), + UnorderedElementsAre( + video_widevine:: + ClientIdentification_ClientCapabilities_CertificateKeyType_RSA_2048, + video_widevine:: + ClientIdentification_ClientCapabilities_CertificateKeyType_RSA_3072)); + // Verify Content Identification const LicenseRequest_ContentIdentification& content_id = license_request.content_id(); @@ -363,6 +435,7 @@ TEST_F(SubLicenseTest, VerifySubSessionData) { EXPECT_CALL(*crypto_session_, PrepareRequest(_, Eq(false), NotNull())) .WillOnce( DoAll(SetArgPointee<2>(kLicenseRequestSignature), Return(true))); + EXPECT_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull())); // SubLicense session data calls. // TODO(jfore): These calls are being invoked twice each. This should not @@ -381,14 +454,11 @@ TEST_F(SubLicenseTest, VerifySubSessionData) { DoAll(SetArgPointee<1>(true), SetArgPointee<2>(2), Return(true))); CreateCdmLicense(); - // TODO(gmorgan) fix below - no default service certificate - //service_cert_.Init(kDefaultServiceCertificate); EXPECT_TRUE(cdm_license_->Init( - &service_cert_, kToken, kClientTokenDrmCert, kEmptyString, - crypto_session_, policy_engine_)); + kToken, kClientTokenDrmCert, kEmptyString, true, + kDefaultServiceCertificate, crypto_session_, policy_engine_)); CdmAppParameterMap app_parameters; CdmKeyMessage signed_request; - Properties::set_use_certificates_as_identification(true); std::string server_url; EXPECT_EQ(cdm_license_->PrepareKeyRequest(*init_data_, kLicenseTypeStreaming, app_parameters, &signed_request, @@ -405,7 +475,7 @@ TEST_F(SubLicenseTest, VerifySubSessionData) { for (int i = 0; i < license_request.sub_session_data().size(); ++i) { const video_widevine::LicenseRequest_SubSessionData& sl = license_request.sub_session_data(i); - EXPECT_EQ(static_cast(i), sl.nonce()); + EXPECT_EQ(static_cast(i), sl.nonce()); switch (i) { case 0: EXPECT_EQ(kSubSessionKeyID1, sl.sub_session_key_id()); diff --git a/core/test/policy_engine_constraints_unittest.cpp b/core/test/policy_engine_constraints_unittest.cpp index f8e00d8a..d45d5e5a 100644 --- a/core/test/policy_engine_constraints_unittest.cpp +++ b/core/test/policy_engine_constraints_unittest.cpp @@ -5,6 +5,7 @@ #include "crypto_session.h" #include "license.h" +#include "metrics_collections.h" #include "policy_engine.h" #include "mock_clock.h" #include "scoped_ptr.h" @@ -61,6 +62,9 @@ const int64_t kHdcpInterval = 10; class HdcpOnlyMockCryptoSession : public CryptoSession { public: + HdcpOnlyMockCryptoSession(metrics::CryptoMetrics* crypto_metrics) + : CryptoSession(crypto_metrics) { } + MOCK_METHOD2(GetHdcpCapabilities, bool(HdcpCapability*, HdcpCapability*)); }; @@ -77,9 +81,13 @@ class MockCdmEventListener : public WvCdmEventListener { } // namespace class PolicyEngineConstraintsTest : public Test { + public: + PolicyEngineConstraintsTest() : + crypto_session_(&dummy_metrics_) { + } + protected: virtual void SetUp() { - mock_clock_ = new NiceMock(); current_time_ = 0; policy_engine_.reset(new PolicyEngine(kSessionId, &mock_event_listener_, @@ -205,6 +213,7 @@ class PolicyEngineConstraintsTest : public Test { scoped_ptr policy_engine_; MockClock* mock_clock_; int64_t current_time_; + metrics::CryptoMetrics dummy_metrics_; StrictMock crypto_session_; StrictMock mock_event_listener_; License license_; diff --git a/core/test/policy_engine_unittest.cpp b/core/test/policy_engine_unittest.cpp index 5b03e77a..49d80b02 100644 --- a/core/test/policy_engine_unittest.cpp +++ b/core/test/policy_engine_unittest.cpp @@ -56,7 +56,14 @@ int64_t ParseInt(const std::string& str) { class HdcpOnlyMockCryptoSession : public CryptoSession { public: + HdcpOnlyMockCryptoSession(metrics::CryptoMetrics* metrics) : + CryptoSession(metrics) {} + MOCK_METHOD2(GetHdcpCapabilities, bool(HdcpCapability*, HdcpCapability*)); + bool DoRealGetHdcpCapabilities(HdcpCapability* current, + HdcpCapability* max) { + return CryptoSession::GetHdcpCapabilities(current, max); + } }; class MockCdmEventListener : public WvCdmEventListener { @@ -82,6 +89,7 @@ using video_widevine::OFFLINE; using ::testing::_; using ::testing::AtLeast; using ::testing::InSequence; +using ::testing::Invoke; using ::testing::MockFunction; using ::testing::Pair; using ::testing::Return; @@ -89,6 +97,9 @@ using ::testing::StrictMock; using ::testing::UnorderedElementsAre; class PolicyEngineTest : public ::testing::Test { + public: + PolicyEngineTest() : crypto_session_(&dummy_metrics_) { + } protected: virtual void SetUp() { policy_engine_.reset( @@ -122,6 +133,12 @@ class PolicyEngineTest : public ::testing::Test { policy->set_renewal_delay_seconds(0); policy->set_renewal_retry_interval_seconds(kLicenseRenewalRetryInterval); policy->set_renew_with_usage(false); + + ON_CALL(crypto_session_, GetHdcpCapabilities(_, _)) + .WillByDefault( + Invoke( + &crypto_session_, + &HdcpOnlyMockCryptoSession::DoRealGetHdcpCapabilities)); } void InjectMockClock() { @@ -149,7 +166,8 @@ class PolicyEngineTest : public ::testing::Test { expected_has_new_usable_key)); } - StrictMock crypto_session_; + metrics::CryptoMetrics dummy_metrics_; + NiceMock crypto_session_; StrictMock mock_event_listener_; MockClock* mock_clock_; scoped_ptr policy_engine_; @@ -596,6 +614,128 @@ TEST_F(PolicyEngineTest, PlaybackOk_PlaybackAndRental0) { EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); } +TEST_F(PolicyEngineTest, PlaybackOk_PlaybackAndLicense0_WithoutPlayback) { + License_Policy* policy = license_.mutable_policy(); + policy->clear_license_duration_seconds(); + policy->clear_playback_duration_seconds(); + // Only |rental_duration_seconds| set. + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 10)) + .WillOnce(Return(kLicenseStartTime + kRentalDuration - 10)) + .WillOnce(Return(kLicenseStartTime + kRentalDuration + 10)); + + ExpectSessionKeysChange(kKeyStatusExpired, false); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + kRentalDuration)); + + policy_engine_->SetLicense(license_); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +TEST_F(PolicyEngineTest, PlaybackOk_PlaybackAndLicense0_WithPlayback) { + License_Policy* policy = license_.mutable_policy(); + policy->clear_license_duration_seconds(); + policy->clear_playback_duration_seconds(); + // Only |rental_duration_seconds| set. + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kPlaybackStartTime)) + .WillOnce(Return(kLicenseStartTime + 10)) + .WillOnce(Return(kLicenseStartTime + kRentalDuration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, 0)); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kLicenseStartTime + kRentalDuration)); + + policy_engine_->SetLicense(license_); + policy_engine_->BeginDecryption(); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +TEST_F(PolicyEngineTest, PlaybackOk_RentalAndLicense0_WithoutPlayback) { + License_Policy* policy = license_.mutable_policy(); + policy->clear_license_duration_seconds(); + policy->clear_rental_duration_seconds(); + // Only |playback_duration_seconds| set. + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 10)) + .WillOnce(Return(kLicenseStartTime + kPlaybackDuration + 10)); + + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, 0)); + + policy_engine_->SetLicense(license_); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + +TEST_F(PolicyEngineTest, PlaybackOk_RentalAndLicense0_WithPlayback) { + License_Policy* policy = license_.mutable_policy(); + policy->clear_license_duration_seconds(); + policy->clear_rental_duration_seconds(); + // Only |playback_duration_seconds| set. + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kPlaybackStartTime)) + .WillOnce(Return(kPlaybackStartTime + kPlaybackDuration - 10)) + .WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 10)); + + ExpectSessionKeysChange(kKeyStatusExpired, false); + ExpectSessionKeysChange(kKeyStatusUsable, true); + EXPECT_CALL(mock_event_listener_, OnExpirationUpdate(_, 0)); + EXPECT_CALL(mock_event_listener_, + OnExpirationUpdate(_, kPlaybackStartTime + kPlaybackDuration)); + + policy_engine_->SetLicense(license_); + policy_engine_->BeginDecryption(); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); +} + TEST_F(PolicyEngineTest, PlaybackOk_Durations0) { License_Policy* policy = license_.mutable_policy(); policy->set_rental_duration_seconds(kDurationUnlimited); @@ -2185,6 +2325,303 @@ TEST_F(PolicyEngineQueryTest, QuerySuccess_LicenseDuration0) { EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); } +TEST_F(PolicyEngineQueryTest, QuerySuccess_PlaybackAndRental0) { + License_Policy* policy = license_.mutable_policy(); + policy->set_rental_duration_seconds(kDurationUnlimited); + policy->set_playback_duration_seconds(kDurationUnlimited); + policy->set_license_duration_seconds(kLowDuration); + // Only |license_duration_seconds| set. + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kPlaybackStartTime)) + .WillOnce(Return(kLicenseStartTime + 10)) + .WillOnce(Return(kLicenseStartTime + kLowDuration - 10)) + .WillOnce(Return(kLicenseStartTime + kLowDuration + 10)) + .WillOnce(Return(kLicenseStartTime + kLowDuration + 10)); + + policy_engine_->SetLicense(license_); + policy_engine_->BeginDecryption(); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + EXPECT_EQ(10, ParseInt(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING])); + EXPECT_EQ(LLONG_MAX, + ParseInt(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING])); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); + + policy_engine_->OnTimerEvent(); + + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + EXPECT_EQ(0, ParseInt(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING])); + EXPECT_EQ(LLONG_MAX, + ParseInt(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING])); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineQueryTest, QuerySuccess_PlaybackAndLicense0_WithoutPlayback) { + License_Policy* policy = license_.mutable_policy(); + policy->set_rental_duration_seconds(kRentalDuration); + policy->set_playback_duration_seconds(kDurationUnlimited); + policy->set_license_duration_seconds(kDurationUnlimited); + // Only |rental_duration_seconds| set. + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 10)) + .WillOnce(Return(kLicenseStartTime + kRentalDuration - 10)) + .WillOnce(Return(kLicenseStartTime + kRentalDuration - 10)) + .WillOnce(Return(kLicenseStartTime + kRentalDuration + 10)) + .WillOnce(Return(kLicenseStartTime + kRentalDuration + 10)); + + policy_engine_->SetLicense(license_); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + EXPECT_EQ(10, ParseInt(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING])); + EXPECT_EQ(LLONG_MAX, + ParseInt(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING])); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); + + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + EXPECT_EQ(0, ParseInt(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING])); + EXPECT_EQ(LLONG_MAX, + ParseInt(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING])); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineQueryTest, QuerySuccess_PlaybackAndLicense0_WithPlayback) { + License_Policy* policy = license_.mutable_policy(); + policy->set_rental_duration_seconds(kRentalDuration); + policy->set_playback_duration_seconds(kDurationUnlimited); + policy->set_license_duration_seconds(kDurationUnlimited); + // Only |rental_duration_seconds| set. + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kPlaybackStartTime)) + .WillOnce(Return(kLicenseStartTime + kPlaybackDuration - 10)) + .WillOnce(Return(kLicenseStartTime + kPlaybackDuration - 10)) + .WillOnce(Return(kLicenseStartTime + kPlaybackDuration + 10)) + .WillOnce(Return(kLicenseStartTime + kPlaybackDuration + 10)) + .WillOnce(Return(kLicenseStartTime + kRentalDuration + 10)) + .WillOnce(Return(kLicenseStartTime + kRentalDuration + 10)); + + policy_engine_->SetLicense(license_); + policy_engine_->BeginDecryption(); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + EXPECT_EQ(kRentalDuration - kPlaybackDuration + 10, + ParseInt(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING])); + EXPECT_EQ(LLONG_MAX, + ParseInt(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING])); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + EXPECT_EQ(kRentalDuration - kPlaybackDuration - 10, + ParseInt(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING])); + EXPECT_EQ(LLONG_MAX, + ParseInt(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING])); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + EXPECT_EQ(0, ParseInt(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING])); + EXPECT_EQ(LLONG_MAX, + ParseInt(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING])); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineQueryTest, QuerySuccess_RentalAndLicense0_WithoutPlayback) { + License_Policy* policy = license_.mutable_policy(); + policy->set_rental_duration_seconds(kDurationUnlimited); + policy->set_playback_duration_seconds(kPlaybackDuration); + policy->set_license_duration_seconds(kDurationUnlimited); + // Only |playback_duration_seconds| set. + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kLicenseStartTime + 10)) + .WillOnce(Return(kLicenseStartTime + 10)) + .WillOnce(Return(kLicenseStartTime + kPlaybackDuration + 10)) + .WillOnce(Return(kLicenseStartTime + kPlaybackDuration + 10)); + + policy_engine_->SetLicense(license_); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + EXPECT_EQ(LLONG_MAX, + ParseInt(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING])); + EXPECT_EQ(kPlaybackDuration, + ParseInt(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING])); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + EXPECT_EQ(LLONG_MAX, + ParseInt(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING])); + EXPECT_EQ(kPlaybackDuration, + ParseInt(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING])); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + +TEST_F(PolicyEngineQueryTest, QuerySuccess_RentalAndLicense0_WithPlayback) { + License_Policy* policy = license_.mutable_policy(); + policy->set_rental_duration_seconds(kDurationUnlimited); + policy->set_playback_duration_seconds(kPlaybackDuration); + policy->set_license_duration_seconds(kDurationUnlimited); + // Only |playback_duration_seconds| set. + + EXPECT_CALL(*mock_clock_, GetCurrentTime()) + .WillOnce(Return(kLicenseStartTime + 1)) + .WillOnce(Return(kPlaybackStartTime)) + .WillOnce(Return(kPlaybackStartTime + 10)) + .WillOnce(Return(kPlaybackStartTime + 10)) + .WillOnce(Return(kPlaybackStartTime + kPlaybackDuration - 10)) + .WillOnce(Return(kPlaybackStartTime + kPlaybackDuration - 10)) + .WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 10)) + .WillOnce(Return(kPlaybackStartTime + kPlaybackDuration + 10)); + + policy_engine_->SetLicense(license_); + policy_engine_->BeginDecryption(); + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + CdmQueryMap query_info; + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + EXPECT_EQ(LLONG_MAX, + ParseInt(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING])); + EXPECT_EQ(kPlaybackDuration - 10, + ParseInt(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING])); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); + + policy_engine_->OnTimerEvent(); + + EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + EXPECT_EQ(LLONG_MAX, + ParseInt(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING])); + EXPECT_EQ(10, + ParseInt(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING])); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); + + policy_engine_->OnTimerEvent(); + + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); + EXPECT_FALSE(policy_engine_->CanDecryptContent(kSomeRandomKeyId)); + + EXPECT_EQ(NO_ERROR, policy_engine_->Query(&query_info)); + EXPECT_EQ(QUERY_VALUE_STREAMING, query_info[QUERY_KEY_LICENSE_TYPE]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_PLAY_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_FALSE, query_info[QUERY_KEY_PERSIST_ALLOWED]); + EXPECT_EQ(QUERY_VALUE_TRUE, query_info[QUERY_KEY_RENEW_ALLOWED]); + + EXPECT_EQ(0, + ParseInt(query_info[QUERY_KEY_LICENSE_DURATION_REMAINING])); + EXPECT_EQ(0, + ParseInt(query_info[QUERY_KEY_PLAYBACK_DURATION_REMAINING])); + EXPECT_EQ(kRenewalServerUrl, query_info[QUERY_KEY_RENEWAL_SERVER_URL]); +} + TEST_F(PolicyEngineQueryTest, QuerySuccess_Durations0) { License_Policy* policy = license_.mutable_policy(); policy->set_rental_duration_seconds(kDurationUnlimited); diff --git a/core/test/service_certificate_unittest.cpp b/core/test/service_certificate_unittest.cpp index 93ca9c67..2b1649a9 100644 --- a/core/test/service_certificate_unittest.cpp +++ b/core/test/service_certificate_unittest.cpp @@ -4,7 +4,6 @@ #include #include #include -#include "crypto_session.h" #include "properties.h" #include "string_conversions.h" #include "wv_cdm_constants.h" @@ -42,25 +41,9 @@ const std::string kTestSignedCertificate = a2bs_hex( } // unnamed namespace -class MockCryptoSession : public CryptoSession { - public: - MOCK_METHOD2(GetRandom, bool(size_t, uint8_t*)); -}; - class ServiceCertificateTest : public ::testing::Test { protected: - virtual void SetUp() { crypto_session_ = new MockCryptoSession(); } - - virtual void TearDown() { - if (crypto_session_) delete crypto_session_; - } - - void CreateServiceCertificate() { - service_certificate_ = new ServiceCertificate(); - } - - ServiceCertificate* service_certificate_; - MockCryptoSession* crypto_session_; + ServiceCertificate service_certificate_; }; class StubCdmClientPropertySet : public CdmClientPropertySet { @@ -77,11 +60,11 @@ class StubCdmClientPropertySet : public CdmClientPropertySet { virtual bool use_privacy_mode() const { return use_privacy_mode_; } virtual const std::string& service_certificate() const { - return service_certificate_; + return raw_service_certificate_; } virtual void set_service_certificate(const std::string& cert) { - service_certificate_ = cert; + raw_service_certificate_ = cert; } virtual bool is_session_sharing_enabled() const { @@ -98,7 +81,7 @@ class StubCdmClientPropertySet : public CdmClientPropertySet { private: std::string security_level_; - std::string service_certificate_; + std::string raw_service_certificate_; bool use_privacy_mode_; bool is_session_sharing_enabled_; uint32_t session_sharing_id_; @@ -106,11 +89,8 @@ class StubCdmClientPropertySet : public CdmClientPropertySet { }; TEST_F(ServiceCertificateTest, InitSuccess) { - MockCryptoSession crypto_session; - - CreateServiceCertificate(); - service_certificate_->Init(kTestSessionId1); - EXPECT_FALSE(service_certificate_->HasCertificate()); + service_certificate_.Init(kTestSessionId1); + EXPECT_FALSE(service_certificate_.has_certificate()); } TEST_F(ServiceCertificateTest, InitPrivacyModeRequired) { @@ -121,9 +101,8 @@ TEST_F(ServiceCertificateTest, InitPrivacyModeRequired) { Properties::Init(); Properties::AddSessionPropertySet(kTestSessionId1, &property_set); - CreateServiceCertificate(); - service_certificate_->Init(kTestSessionId1); - EXPECT_FALSE(service_certificate_->HasCertificate()); + service_certificate_.Init(kTestSessionId1); + EXPECT_FALSE(service_certificate_.has_certificate()); } TEST_F(ServiceCertificateTest, InitServiceCertificatePresent) { @@ -135,13 +114,12 @@ TEST_F(ServiceCertificateTest, InitServiceCertificatePresent) { Properties::Init(); Properties::AddSessionPropertySet(kTestSessionId1, &property_set); - CreateServiceCertificate(); - std::string service_certificate; + std::string raw_service_certificate; EXPECT_TRUE(Properties::GetServiceCertificate(kTestSessionId1, - &service_certificate)); + &raw_service_certificate)); EXPECT_EQ(NO_ERROR, - service_certificate_->Init(service_certificate)); - EXPECT_TRUE(service_certificate_->HasCertificate()); + service_certificate_.Init(raw_service_certificate)); + EXPECT_TRUE(service_certificate_.has_certificate()); } TEST_F(ServiceCertificateTest, SetServiceCertificate) { @@ -152,9 +130,8 @@ TEST_F(ServiceCertificateTest, SetServiceCertificate) { Properties::Init(); Properties::AddSessionPropertySet(kTestSessionId1, &property_set); - CreateServiceCertificate(); - EXPECT_EQ(NO_ERROR, service_certificate_->Init(kTestSignedCertificate)); - EXPECT_TRUE(service_certificate_->HasCertificate()); + EXPECT_EQ(NO_ERROR, service_certificate_.Init(kTestSignedCertificate)); + EXPECT_TRUE(service_certificate_.has_certificate()); } } diff --git a/core/test/shared_ptr_test.cpp b/core/test/shared_ptr_test.cpp new file mode 100644 index 00000000..e8414d32 --- /dev/null +++ b/core/test/shared_ptr_test.cpp @@ -0,0 +1,128 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +#include +#include +#include +#include + +#include "shared_ptr.h" + +#include "log.h" + +namespace wvcdm { + +class SharedPtrTest : public testing::Test { + public: + + static void NoteDeletion() { + deletions++; + } + + class Dummy { + public: + explicit Dummy(int v) : value_(v) { exists_ = true; } + ~Dummy() { + NoteDeletion(); + exists_ = false; + } + bool exists() { return exists_; } + int getValue() { return value_; } + void setValue(int v) { value_ = v; } + private: + bool exists_; + int value_; + }; + + static void ExpectedDeletions(int count) { + ASSERT_TRUE(deletions == count); + deletions = 0; + } + + virtual void SetUpTest() { + deletions = 0; + } + virtual void TearDownTest() { + deletions = 0; + } + private: + static int deletions; +}; + +int SharedPtrTest::deletions = 0; + +TEST_F(SharedPtrTest, NullSingletonCreate) { + shared_ptr sd1; + ASSERT_TRUE(sd1.unique()); + ASSERT_TRUE(sd1.get() == NULL); + ExpectedDeletions(0); +} + +TEST_F(SharedPtrTest, SingletonCreate) { + { + Dummy* d1 = new Dummy(42); + shared_ptr sd1(d1); + ASSERT_TRUE(sd1.unique()); + ASSERT_TRUE(sd1.get() == d1); + ASSERT_TRUE(sd1->getValue() == 42); + } + ExpectedDeletions(1); +} + +TEST_F(SharedPtrTest, ResetToNull) { + Dummy* d1 = new Dummy(42); + shared_ptr sd1(d1); + ASSERT_TRUE(sd1->getValue() == 42); + sd1.reset(); + ExpectedDeletions(1); + ASSERT_TRUE(sd1.get() == NULL); +} + +TEST_F(SharedPtrTest, SharedCreate) { + { + Dummy* d1 = new Dummy(42); + shared_ptr sd1(d1); + { + shared_ptr sd2(sd1); + ASSERT_FALSE(sd1.unique()); + ASSERT_TRUE(sd1.get() == d1); + ASSERT_TRUE(sd2.get() == d1); + ASSERT_TRUE(sd1.use_count() == 2); + } + ExpectedDeletions(0); + ASSERT_TRUE(sd1.use_count() == 1); + } + ExpectedDeletions(1); +} + +TEST_F(SharedPtrTest, SharedInstance) { + Dummy* d1 = new Dummy(42); + { + shared_ptr sd1(d1); + { + shared_ptr sd2(sd1); + ASSERT_FALSE(sd1.unique()); + ASSERT_TRUE(sd1.get() == d1); + ASSERT_TRUE(sd2.get() == d1); + sd2->setValue(55); + ASSERT_TRUE(sd1.use_count() == 2); + } + ExpectedDeletions(0); + ASSERT_TRUE(sd1.use_count() == 1); + ASSERT_TRUE(sd1->getValue() == 55); + } + ExpectedDeletions(1); +} + +TEST_F(SharedPtrTest, Reset) { + { + Dummy* d1 = new Dummy(42); + Dummy* d2 = new Dummy(96); + shared_ptr sd1(d1); + sd1.reset(d2); + ExpectedDeletions(1); + ASSERT_TRUE(sd1->getValue() == 96); + } + ExpectedDeletions(1); +} + +} // namespace wvcdm diff --git a/core/test/test_printers.cpp b/core/test/test_printers.cpp index 72ceb8a1..0a794482 100644 --- a/core/test/test_printers.cpp +++ b/core/test/test_printers.cpp @@ -42,8 +42,8 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { case CERT_PROVISIONING_REQUEST_ERROR_1: *os << "CERT_PROVISIONING_REQUEST_ERROR_1"; break; - case CERT_PROVISIONING_REQUEST_ERROR_2: - *os << "CERT_PROVISIONING_REQUEST_ERROR_2"; + case CERT_PROVISIONING_NONCE_GENERATION_ERROR: + *os << "CERT_PROVISIONING_NONCE_GENERATION_ERROR"; break; case CERT_PROVISIONING_REQUEST_ERROR_3: *os << "CERT_PROVISIONING_REQUEST_ERROR_3"; @@ -249,12 +249,18 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { case RELEASE_ALL_USAGE_INFO_ERROR_2: *os << "RELEASE_ALL_USAGE_INFO_ERROR_2"; break; - case RELEASE_ALL_USAGE_INFO_ERROR_3: - *os << "RELEASE_ALL_USAGE_INFO_ERROR_3"; - break; case RELEASE_ALL_USAGE_INFO_ERROR_4: *os << "RELEASE_ALL_USAGE_INFO_ERROR_4"; break; + case RELEASE_ALL_USAGE_INFO_ERROR_5: + *os << "RELEASE_ALL_USAGE_INFO_ERROR_5"; + break; + case RELEASE_ALL_USAGE_INFO_ERROR_6: + *os << "RELEASE_ALL_USAGE_INFO_ERROR_6"; + break; + case RELEASE_ALL_USAGE_INFO_ERROR_7: + *os << "RELEASE_ALL_USAGE_INFO_ERROR_7"; + break; case RELEASE_KEY_ERROR: *os << "RELEASE_KEY_ERROR"; break; case RELEASE_KEY_REQUEST_ERROR: *os << "RELEASE_KEY_REQUEST_ERROR"; @@ -426,13 +432,14 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { break; case KEY_NOT_FOUND_6: *os << "KEY_NOT_FOUND_6"; break; - case KEY_ERROR_1: *os << "KEY_ERROR_1"; + case INVALID_SESSION_1: *os << "INVALID_SESSION_1"; break; - case KEY_ERROR_2: *os << "KEY_ERROR_2"; + case NO_DEVICE_KEY_1: *os << "NO_DEVICE_KEY_1"; break; - case KEY_ERROR_3: *os << "KEY_ERROR_3"; + case NO_CONTENT_KEY_2: *os << "NO_CONTENT_KEY_2"; break; - case KEY_ERROR_4: *os << "KEY_ERROR_4"; + case INSUFFICIENT_CRYPTO_RESOURCES_2: + *os << "INSUFFICIENT_CRYPTO_RESOURCES_2"; break; case INVALID_PARAMETERS_ENG_13: *os << "INVALID_PARAMETERS_ENG_13"; break; @@ -457,6 +464,111 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { case LICENSING_CLIENT_TOKEN_ERROR_1: *os << "LICENSING_CLIENT_TOKEN_ERROR_1"; break; + case ANALOG_OUTPUT_ERROR: *os << "ANALOG_OUTPUT_ERROR"; + break; + case UNKNOWN_SELECT_KEY_ERROR_1: *os << "UNKNOWN_SELECT_KEY_ERROR_1"; + break; + case UNKNOWN_SELECT_KEY_ERROR_2: *os << "UNKNOWN_SELECT_KEY_ERROR_2"; + break; + case CREATE_USAGE_TABLE_ERROR: *os << "CREATE_USAGE_TABLE_ERROR"; + break; + case LOAD_USAGE_HEADER_GENERATION_SKEW: + *os << "LOAD_USAGE_HEADER_GENERATION_SKEW"; + break; + case LOAD_USAGE_HEADER_SIGNATURE_FAILURE: + *os << "LOAD_USAGE_HEADER_SIGNATURE_FAILURE"; + break; + case LOAD_USAGE_HEADER_BAD_MAGIC: *os << "LOAD_USAGE_HEADER_BAD_MAGIC"; + break; + case LOAD_USAGE_HEADER_UNKNOWN_ERROR: + *os << "LOAD_USAGE_HEADER_UNKNOWN_ERROR"; + break; + case INSUFFICIENT_CRYPTO_RESOURCES_3: + *os << "INSUFFICIENT_CRYPTO_RESOURCES_3"; + break; + case CREATE_USAGE_ENTRY_UNKNOWN_ERROR: + *os << "CREATE_USAGE_ENTRY_UNKNOWN_ERROR"; + break; + case LOAD_USAGE_ENTRY_GENERATION_SKEW: + *os << "LOAD_USAGE_ENTRY_GENERATION_SKEW"; + break; + case LOAD_USAGE_ENTRY_SIGNATURE_FAILURE: + *os << "LOAD_USAGE_ENTRY_SIGNATURE_FAILURE"; + break; + case LOAD_USAGE_ENTRY_UNKNOWN_ERROR: + *os << "LOAD_USAGE_ENTRY_UNKNOWN_ERROR"; + break; + case INVALID_PARAMETERS_ENG_20: *os << "INVALID_PARAMETERS_ENG_20"; + break; + case UPDATE_USAGE_ENTRY_UNKNOWN_ERROR: + *os << "UPDATE_USAGE_ENTRY_UNKNOWN_ERROR"; + break; + case INVALID_PARAMETERS_ENG_21: *os << "INVALID_PARAMETERS_ENG_21"; + break; + case INVALID_PARAMETERS_ENG_22: *os << "INVALID_PARAMETERS_ENG_22"; + break; + case SHRINK_USAGE_TABLER_HEADER_UNKNOWN_ERROR: + *os << "SHRINK_USAGE_TABLER_HEADER_UNKNOWN_ERROR"; + break; + case MOVE_USAGE_ENTRY_UNKNOWN_ERROR: + *os << "MOVE_USAGE_ENTRY_UNKNOWN_ERROR"; + break; + case COPY_OLD_USAGE_ENTRY_UNKNOWN_ERROR: + *os << "COPY_OLD_USAGE_ENTRY_UNKNOWN_ERROR"; + break; + case INVALID_PARAMETERS_ENG_23: *os << "INVALID_PARAMETERS_ENG_23"; + break; + case INVALID_PARAMETERS_ENG_24: *os << "INVALID_PARAMETERS_ENG_24"; + break; + case USAGE_INFORMATION_SUPPORT_FAILED: + *os << "USAGE_INFORMATION_SUPPORT_FAILED"; + break; + case USAGE_SUPPORT_GET_API_FAILED: + *os << "USAGE_SUPPORT_GET_API_FAILED"; + break; + case UNEXPECTED_EMPTY_USAGE_ENTRY: + *os << "UNEXPECTED_EMPTY_USAGE_ENTRY"; + break; + case INVALID_USAGE_ENTRY_NUMBER_MODIFICATION: + *os << "INVALID_USAGE_ENTRY_NUMBER_MODIFICATION"; + break; + case USAGE_INVALID_NEW_ENTRY: *os << "USAGE_INVALID_NEW_ENTRY"; + break; + case USAGE_INVALID_PARAMETERS_1: *os << "USAGE_INVALID_PARAMETERS_1"; + break; + case USAGE_GET_ENTRY_RETRIEVE_LICENSE_FAILED: + *os << "USAGE_GET_ENTRY_RETRIEVE_LICENSE_FAILED"; + break; + case USAGE_GET_ENTRY_RETRIEVE_USAGE_INFO_FAILED: + *os << "USAGE_GET_ENTRY_RETRIEVE_USAGE_INFO_FAILED"; + break; + case USAGE_GET_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE: + *os << "USAGE_GET_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE"; + break; + case USAGE_ENTRY_NUMBER_MISMATCH: *os << "USAGE_ENTRY_NUMBER_MISMATCH"; + break; + case USAGE_STORE_LICENSE_FAILED: *os << "USAGE_STORE_LICENSE_FAILED"; + break; + case USAGE_STORE_USAGE_INFO_FAILED: *os << "USAGE_STORE_USAGE_INFO_FAILED"; + break; + case USAGE_INVALID_LOAD_ENTRY: *os << "USAGE_INVALID_LOAD_ENTRY"; + break; + case RELEASE_USAGE_INFO_FAILED: *os << "RELEASE_USAGE_INFO_FAILED"; + break; + case INCORRECT_USAGE_SUPPORT_TYPE_1: + *os << "INCORRECT_USAGE_SUPPORT_TYPE_1"; + break; + case INCORRECT_USAGE_SUPPORT_TYPE_2: + *os << "INCORRECT_USAGE_SUPPORT_TYPE_2"; + break; + case KEY_PROHIBITED_FOR_SECURITY_LEVEL: + *os << "KEY_PROHIBITED_FOR_SECURITY_LEVEL"; + break; + case KEY_NOT_FOUND_IN_SESSION: + *os << "KEY_NOT_FOUND_IN_SESSION"; + break; + case NO_USAGE_ENTRIES: *os << "NO_USAGE_ENTRIES"; + break; case LIST_LICENSE_ERROR_1: *os << "LIST_LICENSE_ERROR_1"; break; case LIST_LICENSE_ERROR_2: *os << "LIST_LICENSE_ERROR_2"; @@ -487,27 +599,18 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) { break; case PARSE_RESPONSE_ERROR_4: *os << "PARSE_RESPONSE_ERROR_4"; break; + case USAGE_STORE_ENTRY_RETRIEVE_LICENSE_FAILED: + *os << "USAGE_STORE_ENTRY_RETRIEVE_LICENSE_FAILED"; + break; + case USAGE_STORE_ENTRY_RETRIEVE_USAGE_INFO_FAILED: + *os << "USAGE_STORE_ENTRY_RETRIEVE_USAGE_INFO_FAILED"; + break; + case USAGE_STORE_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE: + *os << "USAGE_STORE_ENTRY_RETRIEVE_INVALID_STORAGE_TYPE"; + break; case LICENSE_REQUEST_INVALID_SUBLICENSE: *os << "LICENSE_REQUEST_INVALID_SUBLICENSE"; break; - case INVALID_SESSION_1: - *os << "INVALID_SESSION_1"; - break; - case NO_DEVICE_KEY_1: - *os << "NO_DEVICE_KEY_1"; - break; - case NO_CONTENT_KEY_2: - *os << "NO_CONTENT_KEY_2"; - break; - case INSUFFICIENT_CRYPTO_RESOURCES_2: - *os << "INSUFFICIENT_CRYPTO_RESOURCES_2"; - break; - case UNKNOWN_SELECT_KEY_ERROR_1: - *os << "UNKNOWN_SELECT_KEY_ERROR_1"; - break; - case UNKNOWN_SELECT_KEY_ERROR_2: - *os << "UNKNOWN_SELECT_KEY_ERROR_2"; - break; default: *os << "Unknown CdmResponseType"; break; diff --git a/core/test/url_request.cpp b/core/test/url_request.cpp index df9f24b0..3db0fddf 100644 --- a/core/test/url_request.cpp +++ b/core/test/url_request.cpp @@ -171,7 +171,6 @@ bool UrlRequest::PostCertRequestInQueryString(const std::string& data) { path.append("signedRequest="); path.append(data); return PostRequestWithPath(path, ""); - return true; } } // namespace wvcdm diff --git a/core/test/usage_table_header_unittest.cpp b/core/test/usage_table_header_unittest.cpp new file mode 100644 index 00000000..56bae583 --- /dev/null +++ b/core/test/usage_table_header_unittest.cpp @@ -0,0 +1,1791 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +#include "usage_table_header.h" + +#include +#include +#include + +#include "crypto_session.h" +#include "device_files.h" +#include "file_store.h" +#include "wv_cdm_constants.h" +#include "wv_cdm_types.h" + +namespace wvcdm { + +namespace { + +const std::string kEmptyString; + +const CdmUsageTableHeader kEmptyUsageTableHeader; +const CdmUsageTableHeader kUsageTableHeader = "some usage table header"; +const CdmUsageTableHeader kAnotherUsageTableHeader = + "another usage table header"; +const CdmUsageEntry kUsageEntry = "usage entry"; +const CdmUsageEntry kAnotherUsageEntry = "another usage entry"; +const CdmUsageEntryInfo kUsageEntryInfoOfflineLicense1 = { + .storage_type = kStorageLicense, + .key_set_id = "offline_key_set_1", + .usage_info_file_name = ""}; +const CdmUsageEntryInfo kUsageEntryInfoOfflineLicense2 = { + .storage_type = kStorageLicense, + .key_set_id = "offline_key_set_2", + .usage_info_file_name = ""}; +const CdmUsageEntryInfo kUsageEntryInfoOfflineLicense3 = { + .storage_type = kStorageLicense, + .key_set_id = "offline_key_set_3", + .usage_info_file_name = ""}; +const CdmUsageEntryInfo kUsageEntryInfoSecureStop1 = { + .storage_type = kStorageUsageInfo, + .key_set_id = "secure_stop_key_set_1", + .usage_info_file_name = "usage_info_file_1"}; +const CdmUsageEntryInfo kUsageEntryInfoSecureStop2 = { + .storage_type = kStorageUsageInfo, + .key_set_id = "secure_stop_key_set_2", + .usage_info_file_name = "usage_info_file_2"}; +const CdmUsageEntryInfo kUsageEntryInfoSecureStop3 = { + .storage_type = kStorageUsageInfo, + .key_set_id = "secure_stop_key_set_3", + .usage_info_file_name = "usage_info_file_3"}; +const CdmUsageEntryInfo kUsageEntryInfoStorageTypeUnknown = { + .storage_type = kStorageTypeUnknown, + .key_set_id = "", + .usage_info_file_name = ""}; + +const std::vector kEmptyLicenseList; + +const std::string kLicenseArray[] = { + kUsageEntryInfoOfflineLicense1.key_set_id, + kUsageEntryInfoOfflineLicense2.key_set_id, + kUsageEntryInfoOfflineLicense3.key_set_id, +}; +const size_t kLicenseArraySize = sizeof(kLicenseArray)/ + sizeof(kLicenseArray[0]); +std::vector kLicenseList; + +const std::vector kEmptyUsageInfoFilesList; + +const std::string kUsageInfoFileArray[] = { + kUsageEntryInfoSecureStop1.usage_info_file_name, + kUsageEntryInfoSecureStop2.usage_info_file_name, + kUsageEntryInfoSecureStop3.usage_info_file_name, +}; +const size_t kUsageInfoFileArraySize = sizeof(kUsageInfoFileArray)/ + sizeof(kUsageInfoFileArray[0]); +std::vector kUsageInfoFileList; + +const DeviceFiles::CdmUsageData kCdmUsageData1 = { + .provider_session_token = "provider_session_token_1", + .license_request = "license_request_1", + .license = "license_1", + .key_set_id = "key_set_id_1", + .usage_entry = "usage_entry_1", + .usage_entry_number = 0, +}; +const DeviceFiles::CdmUsageData kCdmUsageData2 = { + .provider_session_token = "provider_session_token_2", + .license_request = "license_request_2", + .license = "license_2", + .key_set_id = "key_set_id_2", + .usage_entry = "usage_entry_2", + .usage_entry_number = 0, +}; +const DeviceFiles::CdmUsageData kCdmUsageData3 = { + .provider_session_token = "provider_session_token_3", + .license_request = "license_request_3", + .license = "license_3", + .key_set_id = "key_set_id_3", + .usage_entry = "usage_entry_3", + .usage_entry_number = 0, +}; +const std::vector kEmptyUsageInfoUsageDataList; + +const std::vector kEmptyUsageEntryInfoVector; +std::vector kUsageEntryInfoVector; + +const DeviceFiles::LicenseState kActiveLicenseState = + DeviceFiles::kLicenseStateActive; +const CdmInitData kPsshData = "pssh data"; +const CdmKeyMessage kKeyRequest = "key request"; +const CdmKeyResponse kKeyResponse = "key response"; +const CdmKeyMessage kKeyRenewalRequest = "key renewal request"; +const CdmKeyResponse kKeyRenewalResponse = "key renewal response"; +const std::string kReleaseServerUrl = "some url"; +const std::string kProviderSessionToken = "provider session token"; +const CdmAppParameterMap kEmptyAppParameters; +int64_t kPlaybackStartTime = 1030005; +int64_t kPlaybackDuration = 300; +int64_t kGracePeriodEndTime = 60; + +namespace { + +void InitVectorConstants() { + kUsageEntryInfoVector.clear(); + kUsageEntryInfoVector.push_back(kUsageEntryInfoOfflineLicense1); + kUsageEntryInfoVector.push_back(kUsageEntryInfoSecureStop1); + kUsageEntryInfoVector.push_back(kUsageEntryInfoStorageTypeUnknown); + + kUsageInfoFileList.clear(); + for (size_t i = 0; i < kUsageInfoFileArraySize; i++) { + kUsageInfoFileList.push_back(kUsageInfoFileArray[i]); + } + + kLicenseList.clear(); + for (size_t i = 0; i < kLicenseArraySize; i++) { + kLicenseList.push_back(kLicenseArray[i]); + } +} + +void ToVector(std::vector& vec, + const CdmUsageEntryInfo* arr, size_t total_size) { + size_t max = total_size / sizeof(CdmUsageEntryInfo); + vec.clear(); + for (size_t i = 0; i < max; i++) { + vec.push_back(arr[i]); + } +} + +}; // namespace + +class MockDeviceFiles : public DeviceFiles { + public: + MockDeviceFiles() : DeviceFiles(&file_system_) { Init(kSecurityLevelL1); } + + MOCK_METHOD2(RetrieveUsageTableInfo, + bool(CdmUsageTableHeader*, std::vector*)); + MOCK_METHOD2(StoreUsageTableInfo, + bool(const CdmUsageTableHeader&, + const std::vector&)); + MOCK_METHOD2(DeleteUsageInfo, bool(const std::string&, const std::string&)); + MOCK_METHOD7(RetrieveUsageInfoByKeySetId, + bool(const std::string&, const std::string&, std::string*, + CdmKeyMessage*, CdmKeyResponse*, CdmUsageEntry*, + uint32_t*)); + MOCK_METHOD0(DeleteAllLicenses, bool()); + MOCK_METHOD7(StoreUsageInfo, + bool(const std::string&, const CdmKeyMessage&, + const CdmKeyResponse&, const std::string&, + const std::string&, const CdmUsageEntry&, uint32_t)); + MOCK_METHOD2(RetrieveUsageInfo, + bool(const std::string&, std::vector*)); + MOCK_METHOD1(ListLicenses, + bool(std::vector* key_set_ids)); + MOCK_METHOD1(ListUsageInfoFiles, + bool(std::vector* usage_info_files)); + + private: + FileSystem file_system_; +}; + +class MockCryptoSession : public CryptoSession { + public: + MockCryptoSession(metrics::CryptoMetrics* metrics) + : CryptoSession(metrics) {} + MOCK_METHOD1(Open, CdmResponseType(SecurityLevel)); + MOCK_METHOD1(LoadUsageTableHeader, + CdmResponseType(const CdmUsageTableHeader&)); + MOCK_METHOD1(CreateUsageTableHeader, CdmResponseType(CdmUsageTableHeader*)); + MOCK_METHOD1(CreateUsageEntry, CdmResponseType(uint32_t*)); + MOCK_METHOD2(LoadUsageEntry, CdmResponseType(uint32_t, const CdmUsageEntry&)); + MOCK_METHOD2(UpdateUsageEntry, + CdmResponseType(CdmUsageTableHeader*, CdmUsageEntry*)); + MOCK_METHOD1(MoveUsageEntry, CdmResponseType(uint32_t)); + MOCK_METHOD2(ShrinkUsageTableHeader, + CdmResponseType(uint32_t, CdmUsageTableHeader*)); +}; + +} // namespace + +// gmock methods +using ::testing::_; +using ::testing::NotNull; +using ::testing::Return; +using ::testing::SetArgPointee; +using ::testing::StrEq; +using ::testing::UnorderedElementsAre; +using ::testing::UnorderedElementsAreArray; + +class UsageTableHeaderTest : public ::testing::Test { + public: + static void SetUpTestCase() { + InitVectorConstants(); + } + + protected: + virtual void SetUp() { + // UsageTableHeader will take ownership of the pointer + device_files_ = new MockDeviceFiles(); + crypto_session_ = new MockCryptoSession(&crypto_metrics_); + usage_table_header_ = new UsageTableHeader(); + + // usage_table_header_ object takes ownership of these objects + usage_table_header_->SetDeviceFiles(device_files_); + usage_table_header_->SetCryptoSession(crypto_session_); + } + + virtual void TearDown() { + if (usage_table_header_ != NULL) delete usage_table_header_; + } + + void Init(CdmSecurityLevel security_level, + const CdmUsageTableHeader& usage_table_header, + const std::vector& usage_entry_info_vector) { + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(usage_table_header), + SetArgPointee<1>(usage_entry_info_vector), + Return(true))); + EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(usage_table_header)) + .WillOnce(Return(NO_ERROR)); + + EXPECT_TRUE(usage_table_header_->Init(security_level, crypto_session_)); + } + + MockDeviceFiles* device_files_; + metrics::CryptoMetrics crypto_metrics_; + MockCryptoSession* crypto_session_; + UsageTableHeader* usage_table_header_; +}; + +TEST_F(UsageTableHeaderTest, InitError) { + EXPECT_FALSE( + usage_table_header_->Init(kSecurityLevelUninitialized, crypto_session_)); + EXPECT_FALSE(usage_table_header_->Init(kSecurityLevelL2, crypto_session_)); + EXPECT_FALSE( + usage_table_header_->Init(kSecurityLevelUnknown, crypto_session_)); + EXPECT_FALSE(usage_table_header_->Init(kSecurityLevelL1, NULL)); + EXPECT_FALSE(usage_table_header_->Init(kSecurityLevelL2, NULL)); +} + +class UsageTableHeaderInitializationTest + : public UsageTableHeaderTest, + public ::testing::WithParamInterface { + public: + static void SetUpTestCase() { + InitVectorConstants(); + } + +}; + +TEST_P(UsageTableHeaderInitializationTest, CreateUsageTableHeader) { + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), + SetArgPointee<1>(kEmptyUsageEntryInfoVector), + Return(false))); + EXPECT_CALL(*device_files_, ListLicenses(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kEmptyLicenseList), + Return(false))); + EXPECT_CALL(*device_files_, ListUsageInfoFiles(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kEmptyUsageInfoFilesList), + Return(false))); + EXPECT_CALL(*crypto_session_, CreateUsageTableHeader(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), Return(NO_ERROR))); + EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader, + kEmptyUsageEntryInfoVector)) + .Times(2) + .WillRepeatedly(Return(true)); + + EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); +} + +TEST_P(UsageTableHeaderInitializationTest, Upgrade_UnableToRetrieveLicenses) { + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), + SetArgPointee<1>(kEmptyUsageEntryInfoVector), + Return(false))); + EXPECT_CALL(*device_files_, ListLicenses(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kLicenseList), + Return(true))); + EXPECT_CALL(*device_files_, ListUsageInfoFiles(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kEmptyUsageInfoFilesList), + Return(false))); + EXPECT_CALL(*crypto_session_, CreateUsageTableHeader(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), Return(NO_ERROR))); + // TODO: Why not being called? + //EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true)); + EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader, + kEmptyUsageEntryInfoVector)) + .Times(2) + .WillRepeatedly(Return(true)); + + for (size_t i = 0; i < kLicenseList.size(); ++i) + device_files_->DeleteLicense(kLicenseList[i]); + EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); +} + +TEST_P(UsageTableHeaderInitializationTest, Upgrade_UnableToRetrieveUsageInfo) { + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), + SetArgPointee<1>(kEmptyUsageEntryInfoVector), + Return(false))); + EXPECT_CALL(*device_files_, ListLicenses(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kEmptyLicenseList), + Return(false))); + EXPECT_CALL(*device_files_, ListUsageInfoFiles(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kUsageInfoFileList), + Return(true))); + EXPECT_CALL(*crypto_session_, CreateUsageTableHeader(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), Return(NO_ERROR))); + EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader, + kEmptyUsageEntryInfoVector)) + .Times(2) + .WillRepeatedly(Return(true)); + for (size_t i = 0; i < kUsageInfoFileList.size(); ++i) { + EXPECT_CALL(*device_files_, + RetrieveUsageInfo(kUsageInfoFileList[i], NotNull())) + .WillOnce(DoAll(SetArgPointee<1>(kEmptyUsageInfoUsageDataList), + Return(false))); + } + + EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); +} + +TEST_P(UsageTableHeaderInitializationTest, UsageTableHeaderExists) { + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), + SetArgPointee<1>(kUsageEntryInfoVector), Return(true))); + EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(kUsageTableHeader)) + .WillOnce(Return(NO_ERROR)); + + EXPECT_TRUE(usage_table_header_->Init(GetParam(), crypto_session_)); +} + +INSTANTIATE_TEST_CASE_P(Cdm, UsageTableHeaderInitializationTest, + ::testing::Values(kSecurityLevelL1, kSecurityLevelL3)); + +TEST_F(UsageTableHeaderTest, AddEntry_CreateUsageEntryFailed) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number; + uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size(); + + EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(expect_usage_entry_number), + Return(CREATE_USAGE_ENTRY_UNKNOWN_ERROR))); + + EXPECT_NE(NO_ERROR, + usage_table_header_->AddEntry( + crypto_session_, + kUsageEntryInfoOfflineLicense1.storage_type == kStorageLicense, + kUsageEntryInfoOfflineLicense1.key_set_id, + kUsageEntryInfoOfflineLicense1.usage_info_file_name, + &usage_entry_number)); +} + +TEST_F(UsageTableHeaderTest, AddEntry_UsageEntryTooSmall) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number; + uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size() - 1; + + EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); + + EXPECT_NE(NO_ERROR, + usage_table_header_->AddEntry( + crypto_session_, + kUsageEntryInfoOfflineLicense1.storage_type == kStorageLicense, + kUsageEntryInfoOfflineLicense1.key_set_id, + kUsageEntryInfoOfflineLicense1.usage_info_file_name, + &usage_entry_number)); +} + +TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveOfflineUsageEntry) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number; + uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size(); + std::vector expect_usage_entry_info_vector = + kUsageEntryInfoVector; + + expect_usage_entry_info_vector.resize(expect_usage_entry_number + 1); + expect_usage_entry_info_vector[expect_usage_entry_number] = + kUsageEntryInfoOfflineLicense2; + + EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kUsageTableHeader, + UnorderedElementsAreArray(expect_usage_entry_info_vector))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->AddEntry( + crypto_session_, + kUsageEntryInfoOfflineLicense2.storage_type == kStorageLicense, + kUsageEntryInfoOfflineLicense2.key_set_id, + kUsageEntryInfoOfflineLicense2.usage_info_file_name, + &usage_entry_number)); + EXPECT_EQ(expect_usage_entry_number, usage_entry_number); +} + +TEST_F(UsageTableHeaderTest, AddEntry_NextConsecutiveSecureStopUsageEntry) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number; + uint32_t expect_usage_entry_number = kUsageEntryInfoVector.size(); + std::vector expect_usage_entry_info_vector = + kUsageEntryInfoVector; + + expect_usage_entry_info_vector.resize(expect_usage_entry_number + 1); + expect_usage_entry_info_vector[expect_usage_entry_number] = + kUsageEntryInfoSecureStop2; + + EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kUsageTableHeader, + UnorderedElementsAreArray(expect_usage_entry_info_vector))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->AddEntry( + crypto_session_, + kUsageEntryInfoSecureStop2.storage_type == kStorageLicense, + kUsageEntryInfoSecureStop2.key_set_id, + kUsageEntryInfoSecureStop2.usage_info_file_name, + &usage_entry_number)); + EXPECT_EQ(expect_usage_entry_number, usage_entry_number); +} + +TEST_F(UsageTableHeaderTest, AddEntry_SkipUsageEntries) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number; + uint32_t next_usage_entry_number = kUsageEntryInfoVector.size(); + size_t skip_usage_entries = 3; + uint32_t expect_usage_entry_number = + next_usage_entry_number + skip_usage_entries; + + EXPECT_CALL(*crypto_session_, CreateUsageEntry(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(expect_usage_entry_number), Return(NO_ERROR))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop2))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->AddEntry( + crypto_session_, + kUsageEntryInfoSecureStop2.storage_type == kStorageLicense, + kUsageEntryInfoSecureStop2.key_set_id, + kUsageEntryInfoSecureStop2.usage_info_file_name, + &usage_entry_number)); + EXPECT_EQ(expect_usage_entry_number, usage_entry_number); +} + +TEST_F(UsageTableHeaderTest, LoadEntry_InvalidEntryNumber) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number = kUsageEntryInfoVector.size() + 3; + + EXPECT_NE(NO_ERROR, usage_table_header_->LoadEntry( + crypto_session_, kUsageEntry, usage_entry_number)); +} + +TEST_F(UsageTableHeaderTest, LoadEntry_CryptoSessionError) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number = 1; + + EXPECT_CALL(*crypto_session_, LoadUsageEntry(usage_entry_number, kUsageEntry)) + .WillOnce(Return(LOAD_USAGE_ENTRY_GENERATION_SKEW)); + + EXPECT_NE(NO_ERROR, usage_table_header_->LoadEntry( + crypto_session_, kUsageEntry, usage_entry_number)); +} + +TEST_F(UsageTableHeaderTest, LoadEntry) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number = 1; + + EXPECT_CALL(*crypto_session_, LoadUsageEntry(usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + + EXPECT_EQ(NO_ERROR, usage_table_header_->LoadEntry( + crypto_session_, kUsageEntry, usage_entry_number)); +} + +TEST_F(UsageTableHeaderTest, UpdateEntry_CryptoSessionError) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + CdmUsageEntry usage_entry; + + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), + SetArgPointee<1>(kUsageEntry), + Return(UPDATE_USAGE_ENTRY_UNKNOWN_ERROR))); + + EXPECT_NE(NO_ERROR, + usage_table_header_->UpdateEntry(crypto_session_, &usage_entry)); +} + +TEST_F(UsageTableHeaderTest, UpdateEntry) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + CdmUsageEntry usage_entry; + + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), + SetArgPointee<1>(kUsageEntry), Return(NO_ERROR))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo(kUsageTableHeader, + UnorderedElementsAreArray(kUsageEntryInfoVector))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->UpdateEntry(crypto_session_, &usage_entry)); +} + +TEST_F(UsageTableHeaderTest, DeleteEntry_InvalidUsageEntryNumber) { + Init(kSecurityLevelL1, kUsageTableHeader, kUsageEntryInfoVector); + uint32_t usage_entry_number = kUsageEntryInfoVector.size(); + metrics::CryptoMetrics metrics; + + EXPECT_NE(NO_ERROR, usage_table_header_->DeleteEntry( + usage_entry_number, device_files_, &metrics)); +} + +// Initial Test state: +// 1. Entry to be delete is the last entry and is an Offline license. +// When attempting to delete the entry a crypto session error +// will occur. +// +// Attempting to delete the entry in (1) will result in: +// a. The usage entry requested to be deleted will not be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Secure Stop 1 1 1 +// Storage Type unknown 2 2 +// Offline License 2 3 3 +// +// # of usage entries 4 4 +TEST_F(UsageTableHeaderTest, DeleteEntry_CryptoSessionError) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoOfflineLicense2}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 1; // kUsageEntryInfoOfflineLicense2 + metrics::CryptoMetrics metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_info_vector.size() - 1, NotNull())) + .WillOnce(Return(SHRINK_USAGE_TABLER_HEADER_UNKNOWN_ERROR)); + + EXPECT_NE(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Entry to be delete is the last entry and is an Offline license. +// +// Attempting to delete the entry in (1) will result in: +// a. The usage entry requested to be deleted will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Secure Stop 1 1 1 +// Storage Type unknown 2 2 +// Offline License 2 3 Deleted +// +// # of usage entries 4 3 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastOfflineEntry) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoOfflineLicense2}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 1; // kUsageEntryInfoOfflineLicense2 + metrics::CryptoMetrics metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_info_vector.size() - 1, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre(kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Entry to be delete is the last entry and is a secure stop. +// +// Attempting to delete the entry in (1) will result in: +// a. The usage entry requested to be deleted will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Secure Stop 1 1 1 +// Storage Type unknown 2 2 +// Secure Stop 2 3 Deleted +// +// # of usage entries 4 3 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastSecureStopEntry) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop2}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 1; // kUsageEntryInfoSecureStop2 + metrics::CryptoMetrics metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_info_vector.size() - 1, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre(kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Last few entries are offline licenses, but have license files +// missing from persistent storage. +// 2. Usage entry to be deleted preceeds those in (1). +// +// Attempting to delete the entry in (2) will result in: +// a. Offline entries in (1) will be deleted. +// b. The usage entry requested to be deleted will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Secure Stop 1 0 0 +// Storage Type unknown 1 1 +// Offline License 1 2 Deleted +// Offline License 2 3 Deleted +// Offline License 3 4 Deleted +// +// # of usage entries 5 2 +TEST_F(UsageTableHeaderTest, + DeleteEntry_LastOfflineEntriesHaveMissingLicenses) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense1 + metrics::CryptoMetrics metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_number_to_be_deleted, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre(kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Last few entries are secure stops, but have entries +// missing from usage info file in persistent storage. +// 2. Usage entry to be deleted preceeds those in (1). +// +// Attempting to delete the entry in (2) will result in: +// a. Secure stops in (1) will be deleted. +// b. The usage entry requested to be deleted will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Storage Type unknown 1 1 +// Secure stop 1 2 Deleted +// Secure stop 2 3 Deleted +// Secure stop 3 4 Deleted +// +// # of usage entries 5 2 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastSecureStopEntriesAreMissing) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop1, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoSecureStop1 + metrics::CryptoMetrics metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_number_to_be_deleted, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop2.usage_info_file_name, + kUsageEntryInfoSecureStop2.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillOnce(Return(false)); + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillOnce(Return(false)); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre(kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Last few entries are offline licenses, but have incorrect usage +// entry number stored in persistent file store. +// 2. Usage entry to be deleted preceeds those in (1). +// +// Attempting to delete the entry in (2) will result in: +// a. Offline entries in (1) will be deleted. +// b. The usage entry requested to be deleted will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Secure Stop 1 0 0 +// Storage Type unknown 1 1 +// Offline License 1 2 Deleted +// Offline License 2 3 Deleted +// Offline License 3 4 Deleted +// +// # of usage entries 5 2 +TEST_F(UsageTableHeaderTest, + DeleteEntry_LastOfflineEntriesHaveIncorrectUsageEntryNumber) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense1 + metrics::CryptoMetrics metrics; + + EXPECT_TRUE(device_files_->StoreLicense( + usage_entry_info_vector[usage_entry_info_vector.size() - 1].key_set_id, + kActiveLicenseState, kPsshData, kKeyRequest, kKeyResponse, + kKeyRenewalRequest, kKeyRenewalResponse, kReleaseServerUrl, + kPlaybackStartTime, kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, kEmptyAppParameters, kUsageEntry, + usage_entry_info_vector.size() - 2)); + + EXPECT_TRUE(device_files_->StoreLicense( + usage_entry_info_vector[usage_entry_info_vector.size() - 2].key_set_id, + kActiveLicenseState, kPsshData, kKeyRequest, kKeyResponse, + kKeyRenewalRequest, kKeyRenewalResponse, kReleaseServerUrl, + kPlaybackStartTime, kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, kEmptyAppParameters, kUsageEntry, + usage_entry_info_vector.size() - 3)); + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_number_to_be_deleted, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre(kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Last few entries are secure stops, but have incorrect usage +// entry number stored in persistent file store. +// 2. Usage entry to be deleted preceeds those in (1). +// +// Attempting to delete the entry in (2) will result in: +// a. Secure stops entries in (1) will be deleted. +// b. The usage entry requested to be deleted will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Storage Type unknown 1 1 +// Secure stop 1 2 Deleted +// Secure stop 2 3 Deleted +// Secure stop 3 4 Deleted +// +// # of usage entries 5 2 +TEST_F(UsageTableHeaderTest, + DeleteEntry_LastSecureStopEntriesHaveIncorrectUsageEntryNumber) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop1, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoSecureStop1 + uint32_t usage_entry_number_after_deleted_entry = + usage_entry_number_to_be_deleted + 1; + metrics::CryptoMetrics metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_number_to_be_deleted, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop2.usage_info_file_name, + kUsageEntryInfoSecureStop2.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillOnce(DoAll( + SetArgPointee<2>(kProviderSessionToken), + SetArgPointee<3>(kKeyRequest), SetArgPointee<4>(kKeyResponse), + SetArgPointee<5>(kUsageEntry), + SetArgPointee<6>(usage_entry_number_to_be_deleted), Return(true))); + + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<2>(kProviderSessionToken), + SetArgPointee<3>(kKeyRequest), + SetArgPointee<4>(kKeyResponse), + SetArgPointee<5>(kUsageEntry), + SetArgPointee<6>(usage_entry_number_after_deleted_entry), + Return(true))); + + EXPECT_CALL(*device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre(kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Last few entries are of storage type unknown. +// 2. Usage entry to be deleted preceeds those in (1). +// +// Attempting to delete the entry in (2) will result in: +// a. Entries of storage type unknown at the end will be deleted. +// b. The usage entry requested to be deleted will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Secure Stop 1 0 0 +// Storage Type unknown 1 1 +// Offline License 1 2 2 +// Offline License 2 3 3 +// Offline License 3 4 Deleted +// Storage Type unknown 5 Deleted +// Storage Type unknown 6 Deleted +// +// # of usage entries 7 4 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastEntriesAreStorageTypeUnknown) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoSecureStop1, kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoOfflineLicense2, kUsageEntryInfoOfflineLicense3, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoStorageTypeUnknown}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense3 + metrics::CryptoMetrics metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(usage_entry_number_to_be_deleted, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo(kAnotherUsageTableHeader, + UnorderedElementsAre(kUsageEntryInfoSecureStop1, + kUsageEntryInfoOfflineLicense1, + kUsageEntryInfoOfflineLicense2))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last. +// 2. Last entry is an offline license and calling +// OEMCrypto_MoveUsageEntry on it will fail. +// +// Attempting to delete the entry in (1) will result in: +// b. The last offline usage entry will not be deleted/moved if the +// OEMCrypto_MoveUsageEntry operation fails. +// c. The usage entry requested to be deleted will be marked as +// storage type unknown. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Secure Stop 1 0 0 +// Storage Type unknown 1 1 +// Offline License 1 2 Deleted/storage type unknown +// Offline License 2 3 3 +// Offline License 3 4 4 +// +// # of usage entries 5 5 +TEST_F(UsageTableHeaderTest, + DeleteEntry_LastEntryIsOffline_MoveOfflineEntryFailed) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense1 + uint32_t last_usage_entry_number = + usage_entry_info_vector.size() - 1; // kUsageEntryInfoOfflineLicense3 + metrics::CryptoMetrics metrics; + + EXPECT_TRUE(device_files_->StoreLicense( + usage_entry_info_vector[last_usage_entry_number].key_set_id, + kActiveLicenseState, kPsshData, kKeyRequest, kKeyResponse, + kKeyRenewalRequest, kKeyRenewalResponse, kReleaseServerUrl, + kPlaybackStartTime, kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, kEmptyAppParameters, kUsageEntry, + last_usage_entry_number)); + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(MOVE_USAGE_ENTRY_UNKNOWN_ERROR)); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last. +// 2. Last entry is an secure stop and calling +// OEMCrypto_MoveUsageEntry on it will fail. +// +// Attempting to delete the entry in (1) will result in: +// b. The last secure stop usage entry will not be deleted/moved if the +// OEMCrypto_MoveUsageEntry operation fails. +// c. The usage entry requested to be deleted will be marked as +// storage type unknown. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Storage Type unknown 1 1 +// Secure stop 1 2 Deleted/storage type unknown +// Secure stop 2 3 3 +// Secure stop 3 4 4 +// +// # of usage entries 5 5 +TEST_F(UsageTableHeaderTest, + DeleteEntry_LastEntryIsSecureStop_MoveSecureStopEntryFailed) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop1, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoSecureStop1 + uint32_t last_usage_entry_number = + usage_entry_info_vector.size() - 1; // kUsageEntryInfoSecureStop3 + metrics::CryptoMetrics metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)).WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(MOVE_USAGE_ENTRY_UNKNOWN_ERROR)); + + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<2>(kProviderSessionToken), + SetArgPointee<3>(kKeyRequest), + SetArgPointee<4>(kKeyResponse), + SetArgPointee<5>(kUsageEntry), + SetArgPointee<6>(last_usage_entry_number), Return(true))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last +// 2. Last few entries are of storage type unknown. +// 3. Entry that preceeds those in (2) is an offline license and calling +// OEMCrypto_MoveUsageEntry on it will fail. +// +// Attempting to delete the entry in (1) will result in: +// a. Entries of storage type unknown at the end will be deleted. +// b. The offline usage entry that preceeds the entries in (a) will +// not be deleted/moved if the OEMCrypto_MoveUsageEntry operation fails. +// c. The usage entry requested to be deleted will be marked as +// storage type unknown. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Secure Stop 1 0 0 +// Storage Type unknown 1 1 +// Offline License 1 2 Deleted/storage type unknown +// Offline License 2 3 3 +// Offline License 3 4 4 +// Storage Type unknown 5 Deleted +// Storage Type unknown 6 Deleted +// +// # of usage entries 7 5 +TEST_F(UsageTableHeaderTest, + DeleteEntry_LastEntriesAreOfflineAndUnknown_MoveOfflineEntryFailed) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 5; // kUsageEntryInfoOfflineLicense1 + uint32_t last_valid_usage_entry_number = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense3 + metrics::CryptoMetrics metrics; + + EXPECT_TRUE(device_files_->StoreLicense( + usage_entry_info_vector[last_valid_usage_entry_number].key_set_id, + kActiveLicenseState, kPsshData, kKeyRequest, kKeyResponse, + kKeyRenewalRequest, kKeyRenewalResponse, kReleaseServerUrl, + kPlaybackStartTime, kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, kEmptyAppParameters, kUsageEntry, + last_valid_usage_entry_number)); + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)) + .Times(2) + .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_valid_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(MOVE_USAGE_ENTRY_UNKNOWN_ERROR)); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(last_valid_usage_entry_number + 1, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last +// 2. Last few entries are of storage type unknown. +// 3. Entry that preceeds those in (2) is an offline license and calling +// OEMCrypto_MoveUsageEntry on it will fail. +// +// Attempting to delete the entry in (1) will result in: +// a. Entries of storage type unknown at the end will be deleted. +// b. The offline usage entry that preceeds the entries in (a) will +// not be deleted/moved if the OEMCrypto_MoveUsageEntry operation fails. +// c. The usage entry requested to be deleted will be marked as +// storage type unknown. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Storage Type unknown 1 1 +// Secure stop 1 2 Deleted/storage type unknown +// Secure stop 2 3 3 +// Secure stop 3 4 4 +// Storage Type unknown 5 Deleted +// Storage Type unknown 6 Deleted +// +// # of usage entries 7 5 +TEST_F(UsageTableHeaderTest, + DeleteEntry_LastEntriesAreSecureStopAndUnknown_MoveOfflineEntryFailed) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop1, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 5; // kUsageEntryInfoOfflineLicense1 + uint32_t last_valid_usage_entry_number = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense3 + metrics::CryptoMetrics metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)) + .Times(2) + .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_valid_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(MOVE_USAGE_ENTRY_UNKNOWN_ERROR)); + + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillOnce( + DoAll(SetArgPointee<2>(kProviderSessionToken), + SetArgPointee<3>(kKeyRequest), SetArgPointee<4>(kKeyResponse), + SetArgPointee<5>(kUsageEntry), + SetArgPointee<6>(last_valid_usage_entry_number), Return(true))); + EXPECT_CALL( + *crypto_session_, + ShrinkUsageTableHeader(last_valid_usage_entry_number + 1, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last. +// 2. Last entry is an offline license. +// +// Attempting to delete the entry in (1) will result in: +// a. The usage entry requested to be deleted will be replaced with the last +// entry. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Secure Stop 1 0 0 +// Storage Type unknown 1 1 +// Offline License 1 2 Deleted +// Offline License 2 3 3 +// Offline License 3 4 2 +// +// # of usage entries 5 4 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastEntryIsOffline) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense1 + uint32_t last_usage_entry_number = + usage_entry_info_vector.size() - 1; // kUsageEntryInfoOfflineLicense3 + metrics::CryptoMetrics metrics; + + EXPECT_TRUE(device_files_->StoreLicense( + usage_entry_info_vector[last_usage_entry_number].key_set_id, + kActiveLicenseState, kPsshData, kKeyRequest, kKeyResponse, + kKeyRenewalRequest, kKeyRenewalResponse, kReleaseServerUrl, + kPlaybackStartTime, kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, kEmptyAppParameters, kUsageEntry, + last_usage_entry_number)); + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)) + .Times(2) + .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), + SetArgPointee<1>(kAnotherUsageEntry), Return(NO_ERROR))); + EXPECT_CALL(*crypto_session_, + ShrinkUsageTableHeader(last_usage_entry_number, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense3, kUsageEntryInfoOfflineLicense2))) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense3, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); + + DeviceFiles::LicenseState license_state = DeviceFiles::kLicenseStateUnknown; + CdmInitData pssh_data; + CdmKeyMessage key_request; + CdmKeyResponse key_response; + CdmKeyMessage key_renewal_request; + CdmKeyResponse key_renewal_response; + std::string release_server_url; + int64_t playback_start_time; + int64_t last_playback_time; + int64_t grace_period_end_time; + CdmAppParameterMap app_parameters; + CdmUsageEntry usage_entry; + uint32_t usage_entry_number = ~0; + + EXPECT_TRUE(device_files_->RetrieveLicense( + kUsageEntryInfoOfflineLicense3.key_set_id, &license_state, &pssh_data, + &key_request, &key_response, &key_renewal_request, &key_renewal_response, + &release_server_url, &playback_start_time, &last_playback_time, + &grace_period_end_time, &app_parameters, &usage_entry, + &usage_entry_number)); + EXPECT_EQ(kActiveLicenseState, license_state); + EXPECT_EQ(kPsshData, pssh_data); + EXPECT_EQ(kKeyRequest, key_request); + EXPECT_EQ(kKeyResponse, key_response); + EXPECT_EQ(kKeyRenewalRequest, key_renewal_request); + EXPECT_EQ(kKeyRenewalResponse, key_renewal_response); + EXPECT_EQ(kReleaseServerUrl, release_server_url); + EXPECT_EQ(kPlaybackStartTime, playback_start_time); + EXPECT_EQ(kPlaybackStartTime + kPlaybackDuration, last_playback_time); + EXPECT_EQ(kGracePeriodEndTime, grace_period_end_time); + EXPECT_EQ(kEmptyAppParameters.size(), app_parameters.size()); + EXPECT_EQ(kAnotherUsageEntry, usage_entry); + EXPECT_EQ(usage_entry_number_to_be_deleted, usage_entry_number); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last. +// 2. Last entry is a secure stop. +// +// Attempting to delete the entry in (1) will result in: +// a. The usage entry requested to be deleted will be replaced with the last +// entry. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Storage Type unknown 1 1 +// Secure stop 1 2 Deleted +// Secure stop 2 3 3 +// Secure stop 3 4 2 +// +// # of usage entries 5 4 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastEntryIsSecureStop) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop1, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoSecureStop1 + uint32_t last_usage_entry_number = + usage_entry_info_vector.size() - 1; // kUsageEntryInfoSecureStop3 + metrics::CryptoMetrics metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)) + .Times(2) + .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), + SetArgPointee<1>(kAnotherUsageEntry), Return(NO_ERROR))); + EXPECT_CALL(*crypto_session_, + ShrinkUsageTableHeader(last_usage_entry_number, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<2>(kProviderSessionToken), + SetArgPointee<3>(kKeyRequest), SetArgPointee<4>(kKeyResponse), + SetArgPointee<5>(kUsageEntry), + SetArgPointee<6>(last_usage_entry_number), Return(true))); + + EXPECT_CALL(*device_files_, + DeleteUsageInfo(kUsageEntryInfoSecureStop3.usage_info_file_name, + kProviderSessionToken)) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageInfo(kProviderSessionToken, kKeyRequest, kKeyResponse, + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, kAnotherUsageEntry, + usage_entry_number_to_be_deleted)) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop3, kUsageEntryInfoSecureStop2))) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop3, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last. +// 2. Last few entries are of storage type unknown. +// 3. Entry that preceeds those in (2) is an offline license. +// +// Attempting to delete the entry in (1) will result in: +// a. The entry being deleted and replaced with the offline entry in (3). +// b. The entries with unknown storage type in (2) will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Secure Stop 1 0 0 +// Storage Type unknown 1 1 +// Offline License 1 2 Deleted +// Offline License 2 3 3 +// Offline License 3 4 2 +// Storage Type unknown 5 Deleted +// Storage Type unknown 6 Deleted +// +// # of usage entries 7 4 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastEntriesAreOfflineAndUnknknown) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 5; // kUsageEntryInfoOfflineLicense1 + uint32_t last_valid_usage_entry_number = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoOfflineLicense3 + metrics::CryptoMetrics metrics; + + EXPECT_TRUE(device_files_->StoreLicense( + usage_entry_info_vector[last_valid_usage_entry_number].key_set_id, + kActiveLicenseState, kPsshData, kKeyRequest, kKeyResponse, + kKeyRenewalRequest, kKeyRenewalResponse, kReleaseServerUrl, + kPlaybackStartTime, kPlaybackStartTime + kPlaybackDuration, + kGracePeriodEndTime, kEmptyAppParameters, kUsageEntry, + last_valid_usage_entry_number)); + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)) + .Times(2) + .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_valid_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), + SetArgPointee<1>(kAnotherUsageEntry), Return(NO_ERROR))); + EXPECT_CALL(*crypto_session_, + ShrinkUsageTableHeader(last_valid_usage_entry_number, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense3, kUsageEntryInfoOfflineLicense2))) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoSecureStop1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoOfflineLicense3, kUsageEntryInfoOfflineLicense2, + kUsageEntryInfoOfflineLicense3, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); + + DeviceFiles::LicenseState license_state = DeviceFiles::kLicenseStateUnknown; + CdmInitData pssh_data; + CdmKeyMessage key_request; + CdmKeyResponse key_response; + CdmKeyMessage key_renewal_request; + CdmKeyResponse key_renewal_response; + std::string release_server_url; + int64_t playback_start_time; + int64_t last_playback_time; + int64_t grace_period_end_time; + CdmAppParameterMap app_parameters; + CdmUsageEntry usage_entry; + uint32_t usage_entry_number = ~0; + + EXPECT_TRUE(device_files_->RetrieveLicense( + kUsageEntryInfoOfflineLicense3.key_set_id, &license_state, &pssh_data, + &key_request, &key_response, &key_renewal_request, &key_renewal_response, + &release_server_url, &playback_start_time, &last_playback_time, + &grace_period_end_time, &app_parameters, &usage_entry, + &usage_entry_number)); + EXPECT_EQ(kActiveLicenseState, license_state); + EXPECT_EQ(kPsshData, pssh_data); + EXPECT_EQ(kKeyRequest, key_request); + EXPECT_EQ(kKeyResponse, key_response); + EXPECT_EQ(kKeyRenewalRequest, key_renewal_request); + EXPECT_EQ(kKeyRenewalResponse, key_renewal_response); + EXPECT_EQ(kReleaseServerUrl, release_server_url); + EXPECT_EQ(kPlaybackStartTime, playback_start_time); + EXPECT_EQ(kPlaybackStartTime + kPlaybackDuration, last_playback_time); + EXPECT_EQ(kGracePeriodEndTime, grace_period_end_time); + EXPECT_EQ(kEmptyAppParameters.size(), app_parameters.size()); + EXPECT_EQ(kAnotherUsageEntry, usage_entry); + EXPECT_EQ(usage_entry_number_to_be_deleted, usage_entry_number); +} + +// Initial Test state: +// 1. Usage entry to be deleted is not last. +// 2. Last few entries are of storage type unknown. +// 3. Entry that preceeds those in (2) is a secure stop. +// +// Attempting to delete the entry in (1) will result in: +// a. The entry being deleted and replaced with the secure stop entry in (3). +// b. The entries with unknown storage type in (2) will be deleted. +// +// Storage type Usage entries +// at start at end +// ============= ======== ====== +// Offline License 1 0 0 +// Storage Type unknown 1 1 +// Secure stop 1 2 Deleted +// Secure stop 2 3 3 +// Secure stop 3 4 2 +// Storage Type unknown 5 Deleted +// Storage Type unknown 6 Deleted +// +// # of usage entries 7 4 +TEST_F(UsageTableHeaderTest, DeleteEntry_LastEntriesAreSecureStopAndUnknknown) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop1, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + Init(kSecurityLevelL1, kUsageTableHeader, usage_entry_info_vector); + uint32_t usage_entry_number_to_be_deleted = + usage_entry_info_vector.size() - 5; // kUsageEntryInfoSecureStop1 + uint32_t last_valid_usage_entry_number = + usage_entry_info_vector.size() - 3; // kUsageEntryInfoSecureStop3 + metrics::CryptoMetrics metrics; + + EXPECT_CALL(*crypto_session_, Open(kLevelDefault)) + .Times(2) + .WillRepeatedly(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + LoadUsageEntry(last_valid_usage_entry_number, kUsageEntry)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, + MoveUsageEntry(usage_entry_number_to_be_deleted)) + .WillOnce(Return(NO_ERROR)); + EXPECT_CALL(*crypto_session_, UpdateUsageEntry(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kAnotherUsageTableHeader), + SetArgPointee<1>(kAnotherUsageEntry), Return(NO_ERROR))); + EXPECT_CALL(*crypto_session_, + ShrinkUsageTableHeader(last_valid_usage_entry_number, NotNull())) + .WillOnce( + DoAll(SetArgPointee<1>(kAnotherUsageTableHeader), Return(NO_ERROR))); + + EXPECT_CALL(*device_files_, + RetrieveUsageInfoByKeySetId( + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, NotNull(), NotNull(), + NotNull(), NotNull(), NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<2>(kProviderSessionToken), + SetArgPointee<3>(kKeyRequest), SetArgPointee<4>(kKeyResponse), + SetArgPointee<5>(kUsageEntry), + SetArgPointee<6>(last_valid_usage_entry_number), Return(true))); + + EXPECT_CALL(*device_files_, + DeleteUsageInfo(kUsageEntryInfoSecureStop3.usage_info_file_name, + kProviderSessionToken)) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageInfo(kProviderSessionToken, kKeyRequest, kKeyResponse, + kUsageEntryInfoSecureStop3.usage_info_file_name, + kUsageEntryInfoSecureStop3.key_set_id, kAnotherUsageEntry, + usage_entry_number_to_be_deleted)) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop3, kUsageEntryInfoSecureStop2))) + .WillOnce(Return(true)); + + EXPECT_CALL( + *device_files_, + StoreUsageTableInfo( + kAnotherUsageTableHeader, + UnorderedElementsAre( + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoSecureStop3, kUsageEntryInfoSecureStop2, + kUsageEntryInfoSecureStop3, kUsageEntryInfoStorageTypeUnknown, + kUsageEntryInfoStorageTypeUnknown))) + .WillOnce(Return(true)); + + EXPECT_EQ(NO_ERROR, + usage_table_header_->DeleteEntry(usage_entry_number_to_be_deleted, + device_files_, &metrics)); +} + +// If the crypto session says the usage table header is stale, init should fail. +TEST_F(UsageTableHeaderTest, StaleHeader) { + std::vector usage_entry_info_vector; + const CdmUsageEntryInfo usage_entry_info_array[] = { + kUsageEntryInfoOfflineLicense1, kUsageEntryInfoSecureStop1, + kUsageEntryInfoStorageTypeUnknown, kUsageEntryInfoOfflineLicense2}; + ToVector(usage_entry_info_vector, usage_entry_info_array, + sizeof(usage_entry_info_array)); + + EXPECT_CALL(*device_files_, RetrieveUsageTableInfo(NotNull(), NotNull())) + .WillOnce(DoAll(SetArgPointee<0>(kUsageTableHeader), + SetArgPointee<1>(usage_entry_info_vector), + Return(true))); + EXPECT_CALL(*crypto_session_, LoadUsageTableHeader(kUsageTableHeader)) + .WillOnce(Return(LOAD_USAGE_HEADER_GENERATION_SKEW)); + EXPECT_CALL(*crypto_session_, CreateUsageTableHeader(NotNull())) + .WillOnce( + DoAll(SetArgPointee<0>(kEmptyUsageTableHeader), Return(NO_ERROR))); + EXPECT_CALL(*device_files_, DeleteAllLicenses()).WillOnce(Return(true)); + EXPECT_CALL(*device_files_, StoreUsageTableInfo(kEmptyUsageTableHeader, + kEmptyUsageEntryInfoVector)) + .WillOnce(Return(true)); + + EXPECT_TRUE(usage_table_header_->Init(kSecurityLevelL1, crypto_session_)); +} + +} // namespace wvcdm diff --git a/metrics/include/counter_metric.h b/metrics/include/counter_metric.h new file mode 100644 index 00000000..c66e9996 --- /dev/null +++ b/metrics/include/counter_metric.h @@ -0,0 +1,205 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// This file contains the declarations for the Metric class and related +// types. +#ifndef WVCDM_METRICS_COUNTER_METRIC_H_ +#define WVCDM_METRICS_COUNTER_METRIC_H_ + +#include +#include +#include +#include +#include + +#include "field_tuples.h" +#include "lock.h" +#include "metric_serialization.h" + +namespace wvcdm { +namespace metrics { + +class CounterMetricTest; + +// This base class provides the common defintion used by all templated +// instances of CounterMetric. +class BaseCounterMetric : public MetricSerializable { + public: + // Send metric values to the MetricSerializer. |serializer| must + // not be null and is owned by the caller. + virtual void Serialize(MetricSerializer* serializer); + + protected: + // Instantiates a BaseCounterMetric. + BaseCounterMetric(const std::string& metric_name) + : metric_name_(metric_name) {} + + virtual ~BaseCounterMetric() {} + + // Increment will look for an existing instance of the field names and + // add the new value to the existing value. If the instance does not exist, + // this method will create it. + void Increment(const std::string& field_names_values, int64_t value); + + private: + friend class CounterMetricTest; + const std::string metric_name_; + // value_map_ contains a mapping from the field name/value pairs to the + // counter(int64_t) instance. + std::map value_map_; + + /* + * This locks the internal state of the counter metric to ensure safety across + * multiple threads preventing the caller from worrying about locking. + */ + Lock internal_lock_; +}; + +// The CounterMetric class is used to track a counter such as the number of +// times a method is called. It can also track a delta that could be positive +// or negative. +// +// The CounterMetric class supports the ability to keep track of multiple +// variations of the count based on certain "field" values. E.g. keep track of +// the counts of success and failure counts for a method call. Or keep track of +// number of times the method was called with a particular parameter. +// Fields define what variations to track. Each Field is a separate dimension. +// The count for each combination of field values is tracked independently. +// +// Example usage: +// +// CounterMetric my_metric("multiple/fields/metric", +// "request_type", // Field name. +// "error_code"); // Field name. +// +// my_metric.Increment(1, 7, 23); // (counter value, request type, error code). +// +// The CounterMetric supports serialization. A call to Serialize will serialize +// all values to the provided MetricsSerializer instance. +// +// example: +// +// class MyMetricSerializer : public MetricSerializer { +// // Add implementation here. +// } +// +// MyMetricSerializer serializer; +// my_metric.Serialize(&serializer); +template +class CounterMetric : public BaseCounterMetric { + public: + // Create a CounterMetric instance with the name |metric_name|. + CounterMetric(const std::string& metric_name); + + // Overloaded constructors with variable field name arguments. The number + // of |field_name| arguments must match the number of used Field type + // arguments. + CounterMetric(const std::string& metric_name, + const char* field_name); + CounterMetric(const std::string& metric_name, + const char* field_name1, + const char* field_name2); + CounterMetric(const std::string& metric_name, + const char* field_name1, + const char* field_name2, + const char* field_name3); + CounterMetric(const std::string& metric_name, + const char* field_name1, + const char* field_name2, + const char* field_name3, + const char* field_name4); + + // Increment will update the counter value associated with the provided + // field values. + void Increment(F1 field1 = util::Unused(), F2 field2 = util::Unused(), + F3 field3 = util::Unused(), F4 field4 = util::Unused()); + + // Increment will add the value to the counter associated with the provided + // field values. + void Increment(int64_t value, + F1 field1 = util::Unused(), F2 field2 = util::Unused(), + F3 field3 = util::Unused(), F4 field4 = util::Unused()); + + private: + friend class CounterMetricTest; + std::vector field_names_; +}; + +// Overloaded template constructor implementations for CounterMetric. +template +CounterMetric::CounterMetric( + const std::string& metric_name) + : BaseCounterMetric(metric_name) { + ASSERT_METRIC_UNUSED_START_FROM(1); +} + +template +CounterMetric::CounterMetric( + const std::string& metric_name, + const char* field_name) + : BaseCounterMetric(metric_name) { + ASSERT_METRIC_UNUSED_START_FROM(2); + util::AppendFieldNames(&field_names_, + util::FirstUnusedType::value - 1, + field_name); +} +template +CounterMetric::CounterMetric( + const std::string& metric_name, + const char* field_name1, + const char* field_name2) + : BaseCounterMetric(metric_name) { + ASSERT_METRIC_UNUSED_START_FROM(3); + util::AppendFieldNames(&field_names_, + util::FirstUnusedType::value - 1, + field_name1, field_name2); +} +template +CounterMetric::CounterMetric( + const std::string& metric_name, + const char* field_name1, + const char* field_name2, + const char* field_name3) + : BaseCounterMetric(metric_name) { + ASSERT_METRIC_UNUSED_START_FROM(4); + util::AppendFieldNames(&field_names_, + util::FirstUnusedType::value - 1, + field_name1, field_name2, field_name3); +} +template +CounterMetric::CounterMetric( + const std::string& metric_name, + const char* field_name1, + const char* field_name2, + const char* field_name3, + const char* field_name4) + : BaseCounterMetric(metric_name) { + ASSERT_METRIC_UNUSED_START_FROM(5); + util::AppendFieldNames(&field_names_, + util::FirstUnusedType::value - 1, + field_name1, field_name2, + field_name3, field_name4); +} + +template +void CounterMetric::Increment( + F1 field1, F2 field2, F3 field3, F4 field4) { + std::string field_name_values = + util::MakeFieldNameString(field_names_, field1, field2, field3, field4); + BaseCounterMetric::Increment(field_name_values, 1); +} + +template +void CounterMetric::Increment( + int64_t value, F1 field1, F2 field2, F3 field3, F4 field4) { + std::string field_name_values = + util::MakeFieldNameString(field_names_, field1, field2, field3, field4); + BaseCounterMetric::Increment(field_name_values, value); +} + +} // namespace metrics +} // namespace wvcdm + +#endif // WVCDM_METRICS_COUNTER_METRIC_H_ diff --git a/metrics/include/distribution.h b/metrics/include/distribution.h new file mode 100644 index 00000000..9b42d395 --- /dev/null +++ b/metrics/include/distribution.h @@ -0,0 +1,53 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// This file contains the definition of a Distribution class which computes +// the distribution values of a series of samples. + +#ifndef WVCDM_METRICS_DISTRIBUTION_H_ +#define WVCDM_METRICS_DISTRIBUTION_H_ + +#include + +namespace wvcdm { +namespace metrics { + +// The Distribution class holds statistics about a series of values that the +// client provides via the Record method. A caller will call Record once for +// each of the values in a series. The Distribution instance will calculate the +// mean, count, min, max and variance of the distribution. +// +// Example usage: +// Distribution dist; +// dist.Record(1); +// dist.Record(2); +// dist.Mean(); // Returns 1.5. +// dist.Count(); // Returns 2. +class Distribution { + public: + Distribution(); + + // Uses the provided sample value to update the computed statistics. + void Record(double value); + + // Return the value for each of the stats computed about the series of + // values (min, max, count, etc.). + double Min() const { return min_; } + double Max() const { return max_; } + double Mean() const { return mean_; } + int64_t Count() const { return count_; } + double Variance() const { + return count_ == 0 ? 0.0 : sum_squared_deviation_ / count_; + } + + private: + int64_t count_; + double min_; + double max_; + double mean_; + double sum_squared_deviation_; +}; + +} // namespace metrics +} // namespace wvcdm + +#endif // WVCDM_METRICS_DISTRIBUTION_H_ diff --git a/metrics/include/event_metric.h b/metrics/include/event_metric.h new file mode 100644 index 00000000..dcfd5223 --- /dev/null +++ b/metrics/include/event_metric.h @@ -0,0 +1,232 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// This file contains the declarations for the EventMetric class and related +// types. +#ifndef WVCDM_METRICS_EVENT_METRIC_H_ +#define WVCDM_METRICS_EVENT_METRIC_H_ + +#include +#include +#include +#include + +#include "distribution.h" +#include "field_tuples.h" +#include "lock.h" +#include "metric_serialization.h" + +namespace wvcdm { +namespace metrics { + +class EventMetricTest; + +// This base class provides the common defintion used by all templated +// instances of EventMetric. +class BaseEventMetric : public MetricSerializable { + public: + // Send metric values to the MetricSerializer. |serializer| must + // not be null and is owned by the caller. + virtual void Serialize(MetricSerializer* serializer); + + protected: + // Instantiates a BaseEventMetric. + BaseEventMetric(const std::string& metric_name) + : metric_name_(metric_name) {} + + virtual ~BaseEventMetric(); + + // Record will look for an existing instance of the Distribution identified + // by the field_names_values string and update it. If the instance does + // not exist, this will create it. + void Record(const std::string& field_names_values, double value); + + private: + friend class EventMetricTest; + const std::string metric_name_; + // value_map_ contains a mapping from the field name/value pairs to the + // distribution instance which holds the metric information (min, max, sum, + // etc.). + std::map value_map_; + + /* + * This locks the internal state of the event metric to ensure safety across + * multiple threads preventing the caller from worrying about locking. + */ + Lock internal_lock_; +}; + +// This class converts the size_t value into the highest power of two +// below the value. E.g. for 7, the value is 4. For 11, the value is 8. +// This class is intended to simplify the use of EventMetric Fields that may +// have many possible values, but we want to bucket them into a small set of +// numbers (32 or 64). +class Pow2Bucket { + public: + explicit Pow2Bucket(size_t value) : value_(GetLowerBucket(value)) {} + + Pow2Bucket(const Pow2Bucket& value) : value_(value.value_) {} + + // Support for converting to string. + friend std::ostream& operator<<(std::ostream& os, const Pow2Bucket& log) + { return os << log.value_; } + + private: + inline size_t GetLowerBucket(size_t value) { + if (!value) { + return 0; + } + + size_t log = 0; + while (value) { + log++; + value >>= 1; + } + + return 1u << (log - 1); + } + + size_t value_; +}; + +// The EventMetric class is used to capture statistics about an event such as +// a method call. EventMetric keeps track of the count, mean, min, max and +// variance for the value being measured. For example, we may want to track the +// latency of a particular operation. Each time the operation is run, the time +// is calculated and provided to the EventMetric by using the +// EventMetric::Record method to capture and maintain statistics about the +// latency (max, min, count, mean, variance). +// +// The EventMetric class supports the ability to breakdown the statistics based +// on certain "field" values. For example, if a particular operation can run in +// one of two modes, it's useful to track the latency of the operation in each +// mode separately. You can use Fields to define how to breakdown the +// statistics. Each Field is a separate dimension. The statistics for each +// combination of field values are tracked independently. +// +// Example usage: +// +// EventMetric my_metric("multiple/fields/metric", +// "request_type", "error_code", // Field names); +// +// my_metric.Record(1, 7, 23); // (latency value, request type, error code). +// +// The EventMetric supports serialization. A call to Serialize will +// serialize all values to the provided MetricsSerializer instance. +// +// example: +// +// class MyMetricSerializer : public MetricSerializer { +// // Add implementation here. +// } +// +// MyMetricSerializer serializer; +// my_metric.Serialize(&serializer); +// +template +class EventMetric : public BaseEventMetric { + public: + // Create an EventMetric instance with the name |metric_name|. + EventMetric(const std::string& metric_name); + + // Overloaded constructors with variable field name arguments. The number + // of |field_name| arguments must match the number of used Field type + // arguments. + EventMetric(const std::string& metric_name, + const char* field_name); + EventMetric(const std::string& metric_name, + const char* field_name1, + const char* field_name2); + EventMetric(const std::string& metric_name, + const char* field_name1, + const char* field_name2, + const char* field_name3); + EventMetric(const std::string& metric_name, + const char* field_name1, + const char* field_name2, + const char* field_name3, + const char* field_name4); + + // Record will update the statistics of the EventMetric broken down by the + // given field values. + void Record(double value, + F1 field1 = util::Unused(), + F2 field2 = util::Unused(), + F3 field3 = util::Unused(), + F4 field4 = util::Unused()); + + private: + friend class EventMetricTest; + std::vector field_names_; +}; + +// Overloaded template constructor implementations for EventMetric. +template +EventMetric::EventMetric( + const std::string& metric_name) + : BaseEventMetric(metric_name) { + ASSERT_METRIC_UNUSED_START_FROM(1); +} + +template +EventMetric::EventMetric( + const std::string& metric_name, + const char* field_name) + : BaseEventMetric(metric_name) { + ASSERT_METRIC_UNUSED_START_FROM(2); + util::AppendFieldNames(&field_names_, + util::FirstUnusedType::value - 1, + field_name); +} +template +EventMetric::EventMetric( + const std::string& metric_name, + const char* field_name1, + const char* field_name2) + : BaseEventMetric(metric_name) { + ASSERT_METRIC_UNUSED_START_FROM(3); + util::AppendFieldNames(&field_names_, + util::FirstUnusedType::value - 1, + field_name1, field_name2); +} +template +EventMetric::EventMetric( + const std::string& metric_name, + const char* field_name1, + const char* field_name2, + const char* field_name3) + : BaseEventMetric(metric_name) { + ASSERT_METRIC_UNUSED_START_FROM(4); + util::AppendFieldNames(&field_names_, + util::FirstUnusedType::value - 1, + field_name1, field_name2, field_name3); +} +template +EventMetric::EventMetric( + const std::string& metric_name, + const char* field_name1, + const char* field_name2, + const char* field_name3, + const char* field_name4) + : BaseEventMetric(metric_name) { + ASSERT_METRIC_UNUSED_START_FROM(5); + util::AppendFieldNames(&field_names_, + util::FirstUnusedType::value - 1, + field_name1, field_name2, + field_name3, field_name4); +} + +template +void EventMetric::Record( + double value, F1 field1, F2 field2, F3 field3, F4 field4) { + std::string field_name_values = + util::MakeFieldNameString(field_names_, field1, field2, field3, field4); + BaseEventMetric::Record(field_name_values, value); +} + +} // namespace metrics +} // namespace wvcdm + +#endif // WVCDM_METRICS_EVENT_METRIC_H_ diff --git a/metrics/include/field_tuples.h b/metrics/include/field_tuples.h new file mode 100644 index 00000000..40d964c0 --- /dev/null +++ b/metrics/include/field_tuples.h @@ -0,0 +1,128 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// This file contains the helper classes and methods for using field tuples +// used by metrics classes to record variations of a single metric. +#ifndef WVCDM_METRICS_FIELD_TUPLES_H_ +#define WVCDM_METRICS_FIELD_TUPLES_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace wvcdm { +namespace metrics { +namespace util { + +// TODO(blueeyes): Change to use C++ 11 support for variadic template args. +// The C++ 03 pattern is no longer needed since we require C++11. b/68766426. + +// This is a placeholder type for unused type parameters. It aids in supporting +// templated classes with "variable" type arguments. +struct Unused { + // Required for compilation. Should never be used. + inline friend std::ostream& operator<< (std::ostream& out, const Unused&) + { return out; } +}; + +// This utility method formats the collection of field name/value pairs. +// The format of the string is: +// +// [{field:value[&field:value]*}] +// +// If there are no pairs, returns a blank string. +template +std::string MakeFieldNameString(const std::vector& field_names, + const F1 field1, const F2 field2, + const F3 field3, const F4 field4) { + std::stringstream field_name_and_values; + std::vector::const_iterator field_name_iterator = + field_names.begin(); + if (field_name_iterator == field_names.end()) { + return field_name_and_values.str(); + } + // There is at least one name/value pair. Prepend open brace. + field_name_and_values << "{"; + field_name_and_values << *field_name_iterator << ':' << field1; + if (++field_name_iterator == field_names.end()) { + field_name_and_values << "}"; + return field_name_and_values.str(); + } + field_name_and_values << '&' << *field_name_iterator << ':' << field2; + if (++field_name_iterator == field_names.end()) { + field_name_and_values << "}"; + return field_name_and_values.str(); + } + field_name_and_values << '&' << *field_name_iterator << ':' << field3; + if (++field_name_iterator == field_names.end()) { + field_name_and_values << "}"; + return field_name_and_values.str(); + } + field_name_and_values << '&' << *field_name_iterator << ':' << field4; + field_name_and_values << "}"; + return field_name_and_values.str(); +} + +// This specialization of the helper method is a shortcut for class +// instances with no fields. +template<> +inline std::string MakeFieldNameString( + const std::vector& /* field_names */, + const Unused /* unused1 */, const Unused /* unused2 */, + const Unused /* unused3 */, const Unused /* unused4 */) { + return ""; +} + +// This helper function appends the field names to a vector of strings. +inline void AppendFieldNames(std::vector* field_name_vector, + int field_count, ...) { + va_list field_names; + + va_start(field_names, field_count); + for (int x = 0; x < field_count; x++) { + field_name_vector->push_back(va_arg(field_names, const char*)); + } + va_end(field_names); +} + +// These helper methods and FirstUnusedType assure that there is no mismatch +// between the specified types for metrics type parameters and the constructors +// and methods used for the specializations. +template +struct CompileAssert {}; +#define COMPILE_ASSERT(expr, msg) \ + typedef util::CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] + +template struct is_unused { static const bool value = false; }; +template <> struct is_unused { static const bool value = true; }; + +template +class FirstUnusedType { + static const bool a = is_unused::value; + static const bool b = is_unused::value; + static const bool c = is_unused::value; + static const bool d = is_unused::value; + // Check that all types after the first Unused are also Unused. + COMPILE_ASSERT(a <= b, Invalid_Unused_At_Position_2); + COMPILE_ASSERT(b <= c, Invalid_Unused_At_Position_3); + COMPILE_ASSERT(c <= d, Invalid_Unused_At_Position_4); + + public: + static const int value = 5 - (a + b + c + d); +}; + +// Asserts that no Unused types exist before N; after N, are all Unused types. +#define ASSERT_METRIC_UNUSED_START_FROM(N) \ + COMPILE_ASSERT((\ + util::FirstUnusedType::value) == N, \ + Unused_Start_From_##N) + +} // namespace util +} // namespace metrics +} // namespace wvcdm + +#endif // WVCDM_METRICS_FIELD_TUPLES_H_ + diff --git a/metrics/include/metric_serialization.h b/metrics/include/metric_serialization.h new file mode 100644 index 00000000..b2aa3a2b --- /dev/null +++ b/metrics/include/metric_serialization.h @@ -0,0 +1,46 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// This file contains the declarations for the Metric class and related +// types. +#ifndef WVCDM_METRICS_METRIC_SERIALIZATION_H_ +#define WVCDM_METRICS_METRIC_SERIALIZATION_H_ + +namespace wvcdm { +namespace metrics { + +// The MetricSerializer is implemented by the code that instantiates +// MetricSerializable instances. The caller provides this MetricSerializer +// instance to MetricSerializable::Serialize. In turn, Serialize will make a +// call to Set* for each value to be Serialized. The MetricSerialiable +// implementation may set zero or more values on the MetricSerializer. +class MetricSerializer { + public: + virtual ~MetricSerializer() {}; + + // The metric_id is the metric name, plus the metric part name, and the + // metric field name/value string. E.g. + // name: "drm/cdm/decrypt/duration" + // part: "mean" + // field name/value string: "{error_code:0&buffer_size:1024}" + // becomes: "drm/cdm/decrypt/duration/mean/{error_code:0&buffer_size:1024}". + virtual void SetString(const std::string& metric_id, + const std::string& value) = 0; + virtual void SetInt32(const std::string& metric_id, int32_t value) = 0; + virtual void SetInt64(const std::string& metric_id, int64_t value) = 0; + virtual void SetDouble(const std::string& metric_id, double value) = 0; +}; + +// This abstract class merely provides the definition for serializing the value +// of the metric. +class MetricSerializable { + public: + virtual ~MetricSerializable() { } + // Serialize metric values to the MetricSerializer. |serializer| must + // not be null and is owned by the caller. + virtual void Serialize(MetricSerializer* serializer) = 0; +}; + +} // namespace metrics +} // namespace wvcdm + +#endif // WVCDM_METRICS_METRIC_SERIALIZATION_H_ diff --git a/metrics/include/metrics_collections.h b/metrics/include/metrics_collections.h new file mode 100644 index 00000000..f047c1fa --- /dev/null +++ b/metrics/include/metrics_collections.h @@ -0,0 +1,301 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// This file contains definitions for metrics being collected throughout the +// CDM. + +#ifndef WVCDM_METRICS_METRICS_GROUP_H_ +#define WVCDM_METRICS_METRICS_GROUP_H_ + +#include +#include +#include + +#include "counter_metric.h" +#include "event_metric.h" +#include "metrics.pb.h" +#include "OEMCryptoCENC.h" +#include "value_metric.h" +#include "wv_cdm_types.h" + +// This definition indicates that a given metric does not need timing +// stats. Example: +// +// M_RECORD(my_metrics, my_metric_name, NO_TIME); +#define NO_TIME 0 + +// Used to record metric timing and additional information about a specific +// event. Assumes that a microsecond timing has been provided. Example: +// +// long total_time = 0; +// long error_code = TimeSomeOperation(&total_time); +// M_RECORD(my_metrics, my_metric_name, total_time, error_code); +#define M_RECORD(GROUP, METRIC, TIME, ...) \ + if ( GROUP ) { \ + ( GROUP ) -> METRIC . Record( TIME, ##__VA_ARGS__ ); \ + } + +// This definition automatically times an operation and records the time and +// additional information such as error code to the provided metric. +// Example: +// +// OEMCryptoResult sts; +// M_TIME( +// sts = OEMCrypto_Initialize(), +// my_metrics_collection, +// oemcrypto_initialize_, +// sts); +#define M_TIME(CALL, GROUP, METRIC, ...) \ + if ( GROUP ) { \ + wvcdm::metrics::TimerMetric timer; \ + timer.Start(); \ + CALL; \ + ( GROUP ) -> METRIC . Record(timer.AsUs(), ##__VA_ARGS__ ); \ + } else { \ + CALL; \ + } + +namespace wvcdm { +namespace metrics { + +// This enum defines the conditions encountered during OEMCrypto Initialization +// in oemcrypto_adapter_dynamic. +typedef enum OEMCryptoInitializationMode { + OEMCrypto_INITIALIZED_USING_IN_APP = 0, + OEMCrypto_INITIALIZED_FORCING_L3 = 1, + OEMCrypto_INITIALIZED_USING_L3_NO_L1_LIBRARY_PATH = 2, + OEMCrypto_INITIALIZED_USING_L3_L1_OPEN_FAILED = 3, + OEMCrypto_INITIALIZED_USING_L3_L1_LOAD_FAILED = 4, + OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INITIALIZE_L1 = 5, + OEMCrypto_INITIALIZED_USING_L3_WRONG_L1_VERSION = 6, + OEMCrypto_INITIALIZED_USING_L1_WITH_KEYBOX = 7, + OEMCrypto_INITIALIZED_USING_L1_WITH_CERTIFICATE = 8, + OEMCrypto_INITIALIZED_USING_L1_CERTIFICATE_MIX = 9, + OEMCrypto_INITIALIZED_USING_L3_BAD_KEYBOX = 10, + OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_OPEN_FACTORY_KEYBOX = 11, + OEMCrypto_INITIALIZED_USING_L3_COULD_NOT_INSTALL_KEYBOX = 12, + OEMCrypto_INITIALIZED_USING_L1_INSTALLED_KEYBOX = 13, + OEMCrypto_INITIALIZED_USING_L3_INVALID_L1 = 14, + OEMCrypto_INITIALIZED_USING_L1_WITH_PROVISIONING_3_0 = 15, + OEMCrypto_INITIALIZED_L3_INITIALIZATION_FAILED = 16 +} OEMCryptoInitializationMode; + + +// This class contains metrics for Crypto Session and OEM Crypto. +class CryptoMetrics { + public: + CryptoMetrics(); + + void Serialize(drm_metrics::MetricsGroup* metrics); + + /* CRYPTO SESSION */ + // TODO(blueeyes): Convert this to crypto_session_default_security_level_. + ValueMetric crypto_session_security_level_; + CounterMetric crypto_session_delete_all_usage_reports_; + CounterMetric crypto_session_delete_multiple_usage_information_; + EventMetric crypto_session_generic_decrypt_; + EventMetric crypto_session_generic_encrypt_; + EventMetric crypto_session_generic_sign_; + EventMetric crypto_session_generic_verify_; + CounterMetric crypto_session_get_device_unique_id_; + CounterMetric crypto_session_get_token_; + ValueMetric crypto_session_life_span_; + EventMetric crypto_session_load_certificate_private_key_; + EventMetric crypto_session_open_; // This is the requested security level. + ValueMetric crypto_session_system_id_; + EventMetric crypto_session_update_usage_information_; + ValueMetric crypto_session_usage_information_support_; + /* OEMCRYPTO */ + ValueMetric oemcrypto_api_version_; + CounterMetric oemcrypto_close_session_; + EventMetric oemcrypto_copy_buffer_; + ValueMetric oemcrypto_current_hdcp_capability_; + CounterMetric oemcrypto_deactivate_usage_entry_; + EventMetric oemcrypto_decrypt_cenc_; + CounterMetric oemcrypto_delete_usage_entry_; + CounterMetric oemcrypto_delete_usage_table_; + EventMetric oemcrypto_derive_keys_from_session_key_; + CounterMetric oemcrypto_force_delete_usage_entry_; + EventMetric oemcrypto_generate_derived_keys_; + CounterMetric oemcrypto_generate_nonce_; + EventMetric oemcrypto_generate_rsa_signature_; + EventMetric oemcrypto_generate_signature_; + EventMetric oemcrypto_generic_decrypt_; + EventMetric oemcrypto_generic_encrypt_; + EventMetric oemcrypto_generic_sign_; + EventMetric oemcrypto_generic_verify_; + CounterMetric oemcrypto_get_device_id_; + EventMetric oemcrypto_get_key_data_; + CounterMetric oemcrypto_get_oem_public_certificate_; + CounterMetric oemcrypto_get_random_; + EventMetric oemcrypto_initialize_; + EventMetric oemcrypto_install_keybox_; + ValueMetric oemcrypto_is_anti_rollback_hw_present_; + ValueMetric oemcrypto_is_keybox_valid_; + EventMetric oemcrypto_load_device_rsa_key_; + EventMetric oemcrypto_load_keys_; + ValueMetric oemcrypto_max_hdcp_capability_; + ValueMetric oemcrypto_max_number_of_sessions_; + ValueMetric oemcrypto_number_of_open_sessions_; + ValueMetric oemcrypto_provisioning_method_; + EventMetric oemcrypto_refresh_keys_; + CounterMetric oemcrypto_report_usage_; + EventMetric oemcrypto_rewrap_device_rsa_key_; + EventMetric oemcrypto_rewrap_device_rsa_key_30_; + ValueMetric oemcrypto_security_patch_level_; + EventMetric oemcrypto_select_key_; + ValueMetric oemcrypto_supports_usage_table_; + CounterMetric oemcrypto_update_usage_table_; + EventMetric oemcrypto_wrap_keybox_; +}; + +// This class contains session-scoped metrics. All properties and +// statistics related to operations within a single session are +// recorded here. +class SessionMetrics { + public: + SessionMetrics(); + + // Sets the session id of the metrics group. This allows the session + // id to be captured and reported as part of the collection of metrics. + void SetSessionId(const CdmSessionId& session_id) { + session_id_ = session_id; } + + // Returns the session id or an empty session id if it has not been set. + const CdmSessionId& GetSessionId() const { return session_id_; } + + // Marks the metrics object as completed and ready for serialization. + void SetCompleted() { completed_ = true; } + + // Returns true if the object is completed. This is used to determine + // when the stats are ready to be published. + bool IsCompleted() const { return completed_; } + + // Returns a pointer to the crypto metrics belonging to the engine instance. + // This instance retains ownership of the object. + CryptoMetrics* GetCryptoMetrics() { return &crypto_metrics_; } + + // Metrics collected at the session level. + ValueMetric cdm_session_life_span_; // Milliseconds. + EventMetric cdm_session_renew_key_; + CounterMetric cdm_session_restore_offline_session_; + CounterMetric cdm_session_restore_usage_session_; + + // Serialize the session metrics to the provided |metric_group|. + // |metric_group| is owned by the caller and must not be null. + void Serialize(drm_metrics::MetricsGroup* metric_group); + + private: + void SerializeSessionMetrics(drm_metrics::MetricsGroup* metric_group); + CdmSessionId session_id_; + bool completed_; + CryptoMetrics crypto_metrics_; +}; + +// This class contains metrics for the OEMCrypto Dynamic Adapter. They are +// separated from other metrics because they need to be encapsulated in a +// singleton object. This is because the dynamic adapter uses the OEMCrypto +// function signatures and contract and cannot be extended to inject +// dependencies. +// +// Operations for this metrics class are serialized since these particular +// metrics may be accessed by a separate thread during intialize even as +// the metric may be serialized. +class OemCryptoDynamicAdapterMetrics { + public: + explicit OemCryptoDynamicAdapterMetrics(); + + // Set methods for OEMCrypto metrics. + void SetInitializationMode(OEMCryptoInitializationMode mode); + void SetL1ApiVersion(uint32_t version); + void SetL1MinApiVersion(uint32_t version); + + // Serialize the session metrics to the provided |metric_group|. + // |metric_group| is owned by the caller and must not be null. + void Serialize(drm_metrics::MetricsGroup* metric_group); + + // Clears the existing metric values. + void Clear(); + + private: + Lock adapter_lock_; + ValueMetric oemcrypto_initialization_mode_; + ValueMetric oemcrypto_l1_api_version_; + ValueMetric oemcrypto_l1_min_api_version_; +}; + +// This will fetch the singleton instance for dynamic adapter metrics. +// This method is safe only if we use C++ 11. In C++ 11, static function-local +// initialization is guaranteed to be threadsafe. We return the reference to +// avoid non-guaranteed destructor order problems. Effectively, the destructor +// is never run for the created instance. +OemCryptoDynamicAdapterMetrics& GetDynamicAdapterMetricsInstance(); + +// This class contains engine-scoped metrics. All properties and +// statistics related to operations within the engine, but outside +// the scope of a session are recorded here. +class EngineMetrics { + public: + EngineMetrics(); + ~EngineMetrics(); + + // Add a new SessionMetrics instance and return a pointer to the caller. + // The new SessionMetrics instance is owned by this EngineMetrics instance + // and will exist until RemoveSession is called or this object is deleted. + SessionMetrics* AddSession(); + + // Removes the metrics object for the given session id. This should only + // be called when the SessionMetrics instance is no longer in use. + void RemoveSession(CdmSessionId session_id); + + // Returns a pointer to the crypto metrics belonging to the engine instance. + // The CryptoMetrics instance is still owned by this object and will exist + // until this object is deleted. + CryptoMetrics* GetCryptoMetrics() { return &crypto_metrics_; } + + // Serialize engine and session metrics into a serialized MetricsGroup + // instance and output that instance to the provided |metric_group|. + // |metric_group| is owned by the caller and must NOT be null. + // |completed_only| indicates that this call should only publish + // SessionMetrics instances that are marked as completed. + // |clear_sessions| indicates that this call should clear sessions metrics + // for those sessions that were serialized. This allows atomic + // serialization and closing of session-level metrics. + void Serialize(drm_metrics::MetricsGroup* metric_group, bool completed_only, + bool clear_serialized_sessions); + + void SetAppPackageName(const std::string& app_package_name); + + // Metrics recorded at the engine level. + EventMetric cdm_engine_add_key_; + ValueMetric cdm_engine_cdm_version_; + CounterMetric cdm_engine_close_session_; + ValueMetric cdm_engine_creation_time_millis_; + EventMetric cdm_engine_decrypt_; + CounterMetric cdm_engine_find_session_for_key_; + EventMetric cdm_engine_generate_key_request_; + EventMetric cdm_engine_get_provisioning_request_; + EventMetric cdm_engine_get_usage_info_; + EventMetric cdm_engine_handle_provisioning_response_; + ValueMetric cdm_engine_life_span_; // Milliseconds + CounterMetric cdm_engine_open_key_set_session_; + CounterMetric cdm_engine_open_session_; + EventMetric cdm_engine_query_key_status_; + CounterMetric cdm_engine_release_all_usage_info_; + CounterMetric cdm_engine_release_usage_info_; + CounterMetric cdm_engine_remove_keys_; + EventMetric cdm_engine_restore_key_; + CounterMetric cdm_engine_unprovision_; + + private: + Lock session_metrics_lock_; + std::vector session_metrics_list_; + CryptoMetrics crypto_metrics_; + std::string app_package_name_; + + void SerializeEngineMetrics(drm_metrics::MetricsGroup* out); +}; + +} // namespace metrics +} // namespace wvcdm + +#endif // WVCDM_METRICS_METRICS_GROUP_H_ diff --git a/metrics/include/timer_metric.h b/metrics/include/timer_metric.h new file mode 100644 index 00000000..ce00c2fd --- /dev/null +++ b/metrics/include/timer_metric.h @@ -0,0 +1,24 @@ +#ifndef WVCDM_METRICS_TIMER_METRIC_H_ +#define WVCDM_METRICS_TIMER_METRIC_H_ + +#include + +namespace wvcdm { +namespace metrics { + +class TimerMetric { + + public: + void Start(); + double AsMs() const; + double AsUs() const; + + private: + double sec_; + double usec_; + +}; + +} // namespace metrics +} // namespace wvcdm +#endif diff --git a/metrics/include/value_metric.h b/metrics/include/value_metric.h new file mode 100644 index 00000000..5c0704ef --- /dev/null +++ b/metrics/include/value_metric.h @@ -0,0 +1,106 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// This file contains the declarations for the Metric class and related +// types. +#ifndef WVCDM_METRICS_VALUE_METRIC_H_ +#define WVCDM_METRICS_VALUE_METRIC_H_ + +#include +#include + +#include "metric_serialization.h" + +namespace wvcdm { +namespace metrics { + +// Private namespace for some helper implementation functions. +namespace impl { + +// These helper functions map the templated ValueMetric class +// Serialize call to the MetricSerializer explicit calls. +template +void Serialize(MetricSerializer* serializer, + const std::string& metric_name, const T& t); + +inline void SerializeError(MetricSerializer* serializer, + const std::string& metric_name, + const int& error_code) { + serializer->SetInt32(metric_name + "/error", error_code); +} + +} // namespace impl + +// The Metric class supports storing a single value which can be overwritten. +// the Metric class also supports the MetricSerializer interface through +// which the value can be serialized. If the value was never given a value +// or an error code, then the metric will not serialize anything. +// +// Example Usage: +// Metric cdm_version("drm/cdm/version") +// .Record("a.b.c.d"); +// +// MyMetricSerializerImpl serialzer; +// cdm_version.Serialize(&serializer); +// +// Example Error Usage: +// +// Metric cdm_version("drm/cdm/version") +// .SetError(error_code); +// +// Note that serialization is the same. But the ValueMetric will serialize +// the error code to /error instead of just . +template +class ValueMetric : public MetricSerializable { + public: + // Constructs a metric with the given metric name. + explicit ValueMetric(const std::string& metric_name) + : metric_name_(metric_name), error_code_(0), + has_error_(false), has_value_(false) {} + + // Serialize the metric name and value using the given serializer. + // Caller owns |serializer| which cannot be null. + virtual void Serialize(MetricSerializer* serializer) { + if (has_value_) { + impl::Serialize(serializer, metric_name_, value_); + } else if (has_error_) { + impl::SerializeError(serializer, metric_name_, error_code_); + } else { + // Do nothing if there is no value and no error. + } + } + + // Record the value of the metric. + void Record(const T& value) { + value_ = value; + has_value_ = true; + has_error_ = false; + } + + // Set the error code if an error was encountered. + void SetError(int error_code) { + error_code_ = error_code; + has_value_ = false; + has_error_ = true; + } + + // Get the current value of the metric. + const T& GetValue() { return value_; } + + // Clears the indicators that the metric or error was set. + void Clear() { + has_value_ = false; + has_error_ = false; + } + + private: + std::string metric_name_; + T value_; + int error_code_; + bool has_error_; + bool has_value_; +}; + +} // namespace metrics +} // namespace wvcdm + +#endif // WVCDM_METRICS_VALUE_METRIC_H_ diff --git a/metrics/src/counter_metric.cpp b/metrics/src/counter_metric.cpp new file mode 100644 index 00000000..5f561a74 --- /dev/null +++ b/metrics/src/counter_metric.cpp @@ -0,0 +1,33 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// This file contains implementations for the BaseCounterMetric, the base class +// for CounterMetric. + +#include "counter_metric.h" + +namespace wvcdm { +namespace metrics { + +void BaseCounterMetric::Increment(const std::string& field_names_values, + int64_t value) { + AutoLock lock(internal_lock_); + + if (value_map_.find(field_names_values) == value_map_.end()) { + value_map_[field_names_values] = value; + } else { + value_map_[field_names_values] = value_map_[field_names_values] + value; + } +} + +void BaseCounterMetric::Serialize(MetricSerializer* serializer) { + AutoLock lock(internal_lock_); + + for (std::map::iterator it + = value_map_.begin(); it != value_map_.end(); it++) { + serializer->SetInt64(metric_name_ + "/count" + it->first, it->second); + } +} + +} // namespace metrics +} // namespace wvcdm + diff --git a/metrics/src/distribution.cpp b/metrics/src/distribution.cpp new file mode 100644 index 00000000..378e75ff --- /dev/null +++ b/metrics/src/distribution.cpp @@ -0,0 +1,33 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// This file contains the definitions for the Distribution class members. + +#include "distribution.h" + +#include + +namespace wvcdm { +namespace metrics { + +Distribution::Distribution() : + count_(0LL), + min_(DBL_MAX), + max_(-DBL_MAX), + mean_(0.0), + sum_squared_deviation_(0.0) { +} + +void Distribution::Record(double value) { + // Using method of provisional means. + double deviation = value - mean_; + mean_ = mean_ + (deviation / ++count_); + sum_squared_deviation_ = + sum_squared_deviation_ + (deviation * (value - mean_)); + + min_ = min_ < value ? min_ : value; + max_ = max_ > value ? max_ : value; +} + +} // namespace metrics +} // namespace wvcdm + diff --git a/metrics/src/event_metric.cpp b/metrics/src/event_metric.cpp new file mode 100644 index 00000000..06e2ac51 --- /dev/null +++ b/metrics/src/event_metric.cpp @@ -0,0 +1,63 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// This file contains implementations for the BaseEventMetric. + +#include "event_metric.h" + +namespace wvcdm { +namespace metrics { + +BaseEventMetric::~BaseEventMetric() { + AutoLock lock(internal_lock_); + + for (std::map::iterator it + = value_map_.begin(); it != value_map_.end(); it++) { + delete it->second; + } +} + +void BaseEventMetric::Record(const std::string& field_names_values, + double value) { + AutoLock lock(internal_lock_); + + Distribution* distribution; + + if (value_map_.find(field_names_values) == value_map_.end()) { + distribution = new Distribution(); + value_map_[field_names_values] = distribution; + } else { + distribution = value_map_[field_names_values]; + } + + distribution->Record(value); +} + +void BaseEventMetric::Serialize(MetricSerializer* serializer) { + AutoLock lock(internal_lock_); + + for (std::map::iterator it + = value_map_.begin(); it != value_map_.end(); it++) { + serializer->SetInt64( + metric_name_ + "/count" + it->first, + it->second->Count()); + serializer->SetDouble( + metric_name_ + "/mean" + it->first, + it->second->Mean()); + // Only publish additional information if there was more than one sample. + if (it->second->Count() > 1) { + serializer->SetDouble( + metric_name_ + "/variance" + it->first, + it->second->Variance()); + serializer->SetDouble( + metric_name_ + "/min" + it->first, + it->second->Min()); + serializer->SetDouble( + metric_name_ + "/max" + it->first, + it->second->Max()); + } + } +} + +} // namespace metrics +} // namespace wvcdm + diff --git a/metrics/src/metrics.proto b/metrics/src/metrics.proto new file mode 100644 index 00000000..02c5af24 --- /dev/null +++ b/metrics/src/metrics.proto @@ -0,0 +1,39 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// This file contains a proto definition for serialization of metrics data. +// +syntax = "proto2"; + +package drm_metrics; + +// need this if we are using libprotobuf-cpp-2.3.0-lite +option optimize_for = LITE_RUNTIME; + +// The MetricsGroup is a collection of metric name/value pair instances +// that can be serialized and provided to a caller. +message MetricsGroup { + message Metric { + message MetricValue { + // Only one of the following values must be set. Note that the oneof + // keyword is not supported in the protobuf version checked into the CDM. + optional int64 int_value = 1; + optional double double_value = 2; + optional string string_value = 3; + } + + // The name of the metric. Must be valid UTF-8. Required. + optional string name = 1; + + // The value of the metric. Required. + optional MetricValue value = 2; + } + + // The list of name/value pairs of metrics. + repeated Metric metric = 1; + + // Allow multiple sub groups of metrics. + repeated MetricsGroup metric_sub_group = 2; + + // Name of the application package associated with the metrics. + optional string app_package_name = 3; +} diff --git a/metrics/src/metrics_collections.cpp b/metrics/src/metrics_collections.cpp new file mode 100644 index 00000000..f7a2a291 --- /dev/null +++ b/metrics/src/metrics_collections.cpp @@ -0,0 +1,541 @@ +// Copyright 2016 Google Inc. All Rights Reserved. + +#include "metrics_collections.h" + +#include + +#include "log.h" +#include "metrics.pb.h" + +using drm_metrics::MetricsGroup; +using wvcdm::metrics::MetricSerializer; + +namespace { +// Helper struct for comparing session ids. +struct CompareSessionIds { + const std::string& target_; + + CompareSessionIds(const wvcdm::CdmSessionId& target) : target_(target) {}; + + bool operator()(const wvcdm::metrics::SessionMetrics* metrics) const { + return metrics->GetSessionId() == target_; + } +}; + +// Local class used to serialize to the MetricsGroup proto message. +class ProtoMetricSerializer : public wvcdm::metrics::MetricSerializer { + public: + ProtoMetricSerializer(MetricsGroup* metric_group) + : metric_group_(metric_group) {} + + virtual void SetString(const std::string& metric_id, + const std::string& value) { + MetricsGroup::Metric* metric = metric_group_->add_metric(); + metric->set_name(metric_id); + metric->mutable_value()->set_string_value(value); + } + + virtual void SetInt32(const std::string& metric_id, int32_t value) { + MetricsGroup::Metric* metric = metric_group_->add_metric(); + metric->set_name(metric_id); + metric->mutable_value()->set_int_value(value); + } + + virtual void SetInt64(const std::string& metric_id, int64_t value) { + MetricsGroup::Metric* metric = metric_group_->add_metric(); + metric->set_name(metric_id); + metric->mutable_value()->set_int_value(value); + } + + virtual void SetDouble(const std::string& metric_id, double value) { + MetricsGroup::Metric* metric = metric_group_->add_metric(); + metric->set_name(metric_id); + metric->mutable_value()->set_double_value(value); + } + + private: + MetricsGroup* metric_group_; +}; + +} // anonymous namespace + +namespace wvcdm { +namespace metrics { + +CryptoMetrics::CryptoMetrics() : + crypto_session_security_level_( + "/drm/widevine/crypto_session/security_level"), + crypto_session_delete_all_usage_reports_( + "/drm/widevine/crypto_session/delete_all_usage_reports", + "error"), + crypto_session_delete_multiple_usage_information_( + "/drm/widevine/crypto_session/delete_multiple_usage_information", + "error"), + crypto_session_generic_decrypt_( + "/drm/widevine/crypto_session/generic_decrypt/time", + "error", + "length", + "encryption_algorithm"), + crypto_session_generic_encrypt_( + "/drm/widevine/crypto_session/generic_encrypt/time", + "error", + "length", + "encryption_algorithm"), + crypto_session_generic_sign_( + "/drm/widevine/crypto_session/generic_sign/time", + "error", + "length", + "signing_algorithm"), + crypto_session_generic_verify_( + "/drm/widevine/crypto_session/generic_verify/time", + "error", + "length", + "signing_algorithm"), + crypto_session_get_device_unique_id_( + "/drm/widevine/crypto_session/get_device_unique_id", + "success"), + crypto_session_get_token_( + "/drm/widevine/crypto_session/get_token", + "success"), + crypto_session_life_span_( + "/drm/widevine/crypto_session/life_span"), + crypto_session_load_certificate_private_key_( + "/drm/widevine/crypto_session/load_certificate_private_key/time", + "success"), + crypto_session_open_( + "/drm/widevine/crypto_session/open/time", + "error", + "requested_security_level"), + crypto_session_system_id_( + "/drm/widevine/crypto_session/system_id"), + crypto_session_update_usage_information_( + "/drm/widevine/crypto_session/update_usage_information/time", + "error"), + crypto_session_usage_information_support_( + "/drm/widevine/crypto_session/usage_information_support"), + oemcrypto_api_version_( + "/drm/widevine/oemcrypto/api_version"), + oemcrypto_close_session_( + "/drm/widevine/oemcrypto/close_session", + "oemcrypto_error"), + oemcrypto_copy_buffer_( + "/drm/widevine/oemcrypto/copy_buffer/time", + "oemcrypto_error", + "length"), + oemcrypto_current_hdcp_capability_( + "/drm/widevine/oemcrypto/current_hdcp_capability"), + oemcrypto_deactivate_usage_entry_( + "/drm/widevine/oemcrypto/deactivate_usage_entry", + "oemcrypto_error"), + oemcrypto_decrypt_cenc_( + "/drm/widevine/oemcrypto/decrypt_cenc/time", + "oemcrypto_error", + "length"), + oemcrypto_delete_usage_entry_( + "/drm/widevine/oemcrypto/delete_usage_entry", + "oemcrypto_error"), + oemcrypto_delete_usage_table_( + "/drm/widevine/oemcrypto/delete_usage_table", + "oemcrypto_error"), + oemcrypto_derive_keys_from_session_key_( + "/drm/widevine/oemcrypto/derive_keys_from_session_key/time", + "oemcrypto_error"), + oemcrypto_force_delete_usage_entry_( + "/drm/widevine/oemcrypto/force_delete_usage_entry", + "oemcrypto_error"), + oemcrypto_generate_derived_keys_( + "/drm/widevine/oemcrypto/generate_derived_keys/time", + "oemcrypto_error"), + oemcrypto_generate_nonce_( + "/drm/widevine/oemcrypto/generate_nonce", + "oemcrypto_error"), + oemcrypto_generate_rsa_signature_( + "/drm/widevine/oemcrypto/generate_rsa_signature/time", + "oemcrypto_error", + "length"), + oemcrypto_generate_signature_( + "/drm/widevine/oemcrypto/generate_signature/time", + "oemcrypto_error", + "length"), + oemcrypto_generic_decrypt_( + "/drm/widevine/oemcrypto/generic_decrypt/time", + "oemcrypto_error", + "length"), + oemcrypto_generic_encrypt_( + "/drm/widevine/oemcrypto/generic_encrypt/time", + "oemcrypto_error", + "length"), + oemcrypto_generic_sign_( + "/drm/widevine/oemcrypto/generic_sign/time", + "oemcrypto_error", + "length"), + oemcrypto_generic_verify_( + "/drm/widevine/oemcrypto/generic_verify/time", + "oemcrypto_error", + "length"), + oemcrypto_get_device_id_( + "/drm/widevine/oemcrypto/get_device_id", + "oemcrypto_error"), + oemcrypto_get_key_data_( + "/drm/widevine/oemcrypto/get_key_data/time", + "oemcrypto_error", + "length"), + oemcrypto_get_oem_public_certificate_( + "/drm/widevine/oemcrypto/get_oem_public_certificate", + "oemcrypto_error"), + oemcrypto_get_random_( + "/drm/widevine/oemcrypto/get_random", + "oemcrypto_error"), + oemcrypto_initialize_( + "/drm/widevine/oemcrypto/initialize/time", + "oemcrypto_error"), + oemcrypto_install_keybox_( + "/drm/widevine/oemcrypto/install_keybox/time", + "oemcrypto_error"), + oemcrypto_is_anti_rollback_hw_present_( + "/drm/widevine/oemcrypto/is_anti_rollback_hw_present"), + oemcrypto_is_keybox_valid_( + "/drm/widevine/oemcrypto/is_keybox_valid"), + oemcrypto_load_device_rsa_key_( + "/drm/widevine/oemcrypto/load_device_rsa_key/time", + "oemcrypto_error"), + oemcrypto_load_keys_( + "/drm/widevine/oemcrypto/load_keys/time", + "oemcrypto_error"), + oemcrypto_max_hdcp_capability_( + "/drm/widevine/oemcrypto/max_hdcp_capability"), + oemcrypto_max_number_of_sessions_( + "/drm/widevine/oemcrypto/max_number_of_sessions"), + oemcrypto_number_of_open_sessions_( + "/drm/widevine/oemcrypto/number_of_open_sessions"), + oemcrypto_provisioning_method_( + "/drm/widevine/oemcrypto/provisioning_method"), + oemcrypto_refresh_keys_( + "/drm/widevine/oemcrypto/refresh_keys/time", + "oemcrypto_error"), + oemcrypto_report_usage_( + "/drm/widevine/oemcrypto/report_usage", + "oemcrypto_error"), + oemcrypto_rewrap_device_rsa_key_( + "/drm/widevine/oemcrypto/rewrap_device_rsa_key/time", + "oemcrypto_error"), + oemcrypto_rewrap_device_rsa_key_30_( + "/drm/widevine/oemcrypto/rewrap_device_rsa_key_30/time", + "oemcrypto_error"), + oemcrypto_security_patch_level_( + "/drm/widevine/oemcrypto/security_patch_level"), + oemcrypto_select_key_( + "/drm/widevine/oemcrypto/select_key/time", + "oemcrypto_error"), + oemcrypto_supports_usage_table_( + "/drm/widevine/oemcrypto/supports_usage_table"), + oemcrypto_update_usage_table_( + "/drm/widevine/oemcrypto/update_usage_table", + "oemcrypto_error"), + oemcrypto_wrap_keybox_( + "/drm/widevine/oemcrypto/wrap_keybox/time", + "oemcrypto_error") { +} + +void CryptoMetrics::Serialize(MetricsGroup* metrics) { + ProtoMetricSerializer serializer(metrics); + /* CRYPTO SESSION */ + crypto_session_security_level_.Serialize(&serializer); + crypto_session_delete_all_usage_reports_.Serialize(&serializer); + crypto_session_delete_multiple_usage_information_.Serialize(&serializer); + crypto_session_generic_decrypt_.Serialize(&serializer); + crypto_session_generic_encrypt_.Serialize(&serializer); + crypto_session_generic_sign_.Serialize(&serializer); + crypto_session_generic_verify_.Serialize(&serializer); + crypto_session_get_device_unique_id_.Serialize(&serializer); + crypto_session_get_token_.Serialize(&serializer); + crypto_session_life_span_.Serialize(&serializer); + crypto_session_load_certificate_private_key_.Serialize(&serializer); + crypto_session_open_.Serialize(&serializer); + crypto_session_system_id_.Serialize(&serializer); + crypto_session_update_usage_information_.Serialize(&serializer); + crypto_session_usage_information_support_.Serialize(&serializer); + + /* OEMCRYPTO */ + oemcrypto_api_version_.Serialize(&serializer); + oemcrypto_close_session_.Serialize(&serializer); + oemcrypto_copy_buffer_.Serialize(&serializer); + oemcrypto_current_hdcp_capability_.Serialize(&serializer); + oemcrypto_deactivate_usage_entry_.Serialize(&serializer); + oemcrypto_decrypt_cenc_.Serialize(&serializer); + oemcrypto_delete_usage_entry_.Serialize(&serializer); + oemcrypto_delete_usage_table_.Serialize(&serializer); + oemcrypto_derive_keys_from_session_key_.Serialize(&serializer); + oemcrypto_force_delete_usage_entry_.Serialize(&serializer); + oemcrypto_generate_derived_keys_.Serialize(&serializer); + oemcrypto_generate_nonce_.Serialize(&serializer); + oemcrypto_generate_rsa_signature_.Serialize(&serializer); + oemcrypto_generate_signature_.Serialize(&serializer); + oemcrypto_generic_decrypt_.Serialize(&serializer); + oemcrypto_generic_encrypt_.Serialize(&serializer); + oemcrypto_generic_sign_.Serialize(&serializer); + oemcrypto_generic_verify_.Serialize(&serializer); + oemcrypto_get_device_id_.Serialize(&serializer); + oemcrypto_get_key_data_.Serialize(&serializer); + oemcrypto_get_oem_public_certificate_.Serialize(&serializer); + oemcrypto_get_random_.Serialize(&serializer); + oemcrypto_initialize_.Serialize(&serializer); + oemcrypto_install_keybox_.Serialize(&serializer); + oemcrypto_is_anti_rollback_hw_present_.Serialize(&serializer); + oemcrypto_is_keybox_valid_.Serialize(&serializer); + oemcrypto_load_device_rsa_key_.Serialize(&serializer); + oemcrypto_load_keys_.Serialize(&serializer); + oemcrypto_max_hdcp_capability_.Serialize(&serializer); + oemcrypto_max_number_of_sessions_.Serialize(&serializer); + oemcrypto_number_of_open_sessions_.Serialize(&serializer); + oemcrypto_provisioning_method_.Serialize(&serializer); + oemcrypto_refresh_keys_.Serialize(&serializer); + oemcrypto_report_usage_.Serialize(&serializer); + oemcrypto_rewrap_device_rsa_key_.Serialize(&serializer); + oemcrypto_rewrap_device_rsa_key_30_.Serialize(&serializer); + oemcrypto_security_patch_level_.Serialize(&serializer); + oemcrypto_select_key_.Serialize(&serializer); + oemcrypto_supports_usage_table_.Serialize(&serializer); + oemcrypto_update_usage_table_.Serialize(&serializer); + oemcrypto_wrap_keybox_.Serialize(&serializer); +} + +SessionMetrics::SessionMetrics() : + cdm_session_life_span_( + "/drm/widevine/cdm_session/life_span"), + cdm_session_renew_key_( + "/drm/widevine/cdm_session/renew_key/time", + "error"), + cdm_session_restore_offline_session_( + "/drm/widevine/cdm_session/restore_offline_session", + "error"), + cdm_session_restore_usage_session_( + "/drm/widevine/cdm_session/restore_usage_session", + "error"), + completed_(false) { +} + +void SessionMetrics::Serialize(MetricsGroup* metric_group) { + SerializeSessionMetrics(metric_group); + crypto_metrics_.Serialize(metric_group); +} + +void SessionMetrics::SerializeSessionMetrics(MetricsGroup* metric_group) { + ProtoMetricSerializer serializer(metric_group); + // Add the session id as a single-valued metric. + serializer.SetString("/drm/widevine/cdm_session/session_id", session_id_); + cdm_session_life_span_.Serialize(&serializer); + cdm_session_renew_key_.Serialize(&serializer); + cdm_session_restore_offline_session_.Serialize(&serializer); + cdm_session_restore_usage_session_.Serialize(&serializer); +} + +OemCryptoDynamicAdapterMetrics::OemCryptoDynamicAdapterMetrics() : + oemcrypto_initialization_mode_( + "/drm/widevine/oemcrypto/initialization_mode"), + oemcrypto_l1_api_version_( + "/drm/widevine/oemcrypto/l1_api_version"), + oemcrypto_l1_min_api_version_( + "/drm/widevine/oemcrypto/l1_min_api_version") { +} + +void OemCryptoDynamicAdapterMetrics::SetInitializationMode( + OEMCryptoInitializationMode mode) { + AutoLock lock(adapter_lock_); + oemcrypto_initialization_mode_.Record(mode); +} + +void OemCryptoDynamicAdapterMetrics::SetL1ApiVersion(uint32_t version) { + AutoLock lock(adapter_lock_); + oemcrypto_l1_api_version_.Record(version); +} + +void OemCryptoDynamicAdapterMetrics::SetL1MinApiVersion(uint32_t version) { + AutoLock lock(adapter_lock_); + oemcrypto_l1_min_api_version_.Record(version); +} + +void OemCryptoDynamicAdapterMetrics::Serialize( + drm_metrics::MetricsGroup* metric_group) { + AutoLock lock(adapter_lock_); + ProtoMetricSerializer serializer(metric_group); + + oemcrypto_initialization_mode_.Serialize(&serializer); + oemcrypto_l1_api_version_.Serialize(&serializer); + oemcrypto_l1_min_api_version_.Serialize(&serializer); +} + +void OemCryptoDynamicAdapterMetrics::Clear() { + AutoLock lock(adapter_lock_); + + oemcrypto_initialization_mode_.Clear(); + oemcrypto_l1_api_version_.Clear(); + oemcrypto_l1_min_api_version_.Clear(); +} + +// This method returns a reference. This means that the destructor is never +// executed for the returned object. +OemCryptoDynamicAdapterMetrics& GetDynamicAdapterMetricsInstance() { + // This is safe in C++ 11 since the initialization is guaranteed to run + // only once regardless of multi-threaded access. + static OemCryptoDynamicAdapterMetrics* adapter_metrics = + new OemCryptoDynamicAdapterMetrics(); + return *adapter_metrics; +} + +EngineMetrics::EngineMetrics() : + cdm_engine_add_key_( + "/drm/widevine/cdm_engine/add_key/time", + "error"), + cdm_engine_cdm_version_( + "/drm/widevine/cdm_engine/version"), + cdm_engine_close_session_( + "/drm/widevine/cdm_engine/close_session", + "error"), + cdm_engine_creation_time_millis_( + "/drm/widevine/cdm_engine/creation_time_millis"), + cdm_engine_decrypt_( + "/drm/widevine/cdm_engine/decrypt/time", + "error", + "length"), + cdm_engine_find_session_for_key_( + "/drm/widevine/cdm_engine/find_session_for_key", + "success"), + cdm_engine_generate_key_request_( + "/drm/widevine/cdm_engine/generate_key_request/time", + "error"), + cdm_engine_get_provisioning_request_( + "/drm/widevine/cdm_engine/get_provisioning_request/time", + "error"), + cdm_engine_get_usage_info_( + "/drm/widevine/cdm_engine/get_usage_info/time", + "error"), + cdm_engine_handle_provisioning_response_( + "/drm/widevine/cdm_engine/handle_provisioning_response/time", + "error"), + cdm_engine_life_span_( + "/drm/widevine/cdm_engine/life_span"), + cdm_engine_open_key_set_session_( + "/drm/widevine/cdm_engine/open_key_set_session", + "error"), + cdm_engine_open_session_( + "/drm/widevine/cdm_engine/open_session", + "error"), + cdm_engine_query_key_status_( + "/drm/widevine/cdm_engine/query_key_status/time", + "error"), + cdm_engine_release_all_usage_info_( + "/drm/widevine/cdm_engine/release_all_usage_info", + "error"), + cdm_engine_release_usage_info_( + "/drm/widevine/cdm_engine/release_usage_info", + "error"), + cdm_engine_remove_keys_( + "/drm/widevine/cdm_engine/remove_keys", + "error"), + cdm_engine_restore_key_( + "/drm/widevine/cdm_engine/restore_key/time", + "error"), + cdm_engine_unprovision_( + "/drm/widevine/cdm_engine/unprovision", + "error", + "security_level"), + app_package_name_("") { +} + +EngineMetrics::~EngineMetrics() { + AutoLock lock(session_metrics_lock_); + std::vector::iterator i; + if (!session_metrics_list_.empty()) { + LOGV("EngineMetrics::~EngineMetrics. Session count: %d", + session_metrics_list_.size()); + } + for (i = session_metrics_list_.begin(); i != session_metrics_list_.end(); + i++) { + delete *i; + } + session_metrics_list_.clear(); +} + +SessionMetrics* EngineMetrics::AddSession() { + AutoLock lock(session_metrics_lock_); + SessionMetrics* metrics = new SessionMetrics(); + session_metrics_list_.push_back(metrics); + return metrics; +} + +void EngineMetrics::RemoveSession(wvcdm::CdmSessionId session_id) { + AutoLock lock(session_metrics_lock_); + session_metrics_list_.erase( + std::remove_if(session_metrics_list_.begin(), + session_metrics_list_.end(), + CompareSessionIds(session_id)), + session_metrics_list_.end()); +} + +void EngineMetrics::Serialize(drm_metrics::MetricsGroup* metric_group, + bool completed_only, + bool clear_serialized_sessions) { + AutoLock lock(session_metrics_lock_); + + // Serialize the most recent metrics from the OemCyrpto dynamic adapter. + OemCryptoDynamicAdapterMetrics& adapter_metrics = + GetDynamicAdapterMetricsInstance(); + adapter_metrics.Serialize(metric_group); + if (!app_package_name_.empty()) { + metric_group->set_app_package_name(app_package_name_); + } + SerializeEngineMetrics(metric_group); + std::vector::iterator i; + for (i = session_metrics_list_.begin(); i != session_metrics_list_.end(); + /* no increment */) { + bool serialized = false; + if (!completed_only || (*i)->IsCompleted()) { + MetricsGroup* metric_sub_group = metric_group->add_metric_sub_group(); + if (!app_package_name_.empty()) { + metric_sub_group->set_app_package_name(app_package_name_); + } + (*i)->Serialize(metric_sub_group); + serialized = true; + } + + // Clear the serialized session metrics if requested. + if (serialized && clear_serialized_sessions) { + session_metrics_list_.erase(i); + } else { + i++; + } + } +} + +void EngineMetrics::SetAppPackageName(const std::string& app_package_name) { + app_package_name_ = app_package_name; +} + +void EngineMetrics::SerializeEngineMetrics(MetricsGroup* metric_group) { + ProtoMetricSerializer serializer(metric_group); + cdm_engine_add_key_.Serialize(&serializer); + cdm_engine_cdm_version_.Serialize(&serializer); + cdm_engine_close_session_.Serialize(&serializer); + cdm_engine_creation_time_millis_.Serialize(&serializer); + cdm_engine_decrypt_.Serialize(&serializer); + cdm_engine_find_session_for_key_.Serialize(&serializer); + cdm_engine_generate_key_request_.Serialize(&serializer); + cdm_engine_get_provisioning_request_.Serialize(&serializer); + cdm_engine_get_usage_info_.Serialize(&serializer); + cdm_engine_handle_provisioning_response_.Serialize(&serializer); + cdm_engine_life_span_.Serialize(&serializer); + cdm_engine_open_key_set_session_.Serialize(&serializer); + cdm_engine_open_session_.Serialize(&serializer); + cdm_engine_query_key_status_.Serialize(&serializer); + cdm_engine_release_all_usage_info_.Serialize(&serializer); + cdm_engine_release_usage_info_.Serialize(&serializer); + cdm_engine_remove_keys_.Serialize(&serializer); + cdm_engine_restore_key_.Serialize(&serializer); + cdm_engine_unprovision_.Serialize(&serializer); + + crypto_metrics_.Serialize(metric_group); +} + +} // metrics +} // wvcdm diff --git a/metrics/src/timer_metric.cpp b/metrics/src/timer_metric.cpp new file mode 100644 index 00000000..f1990949 --- /dev/null +++ b/metrics/src/timer_metric.cpp @@ -0,0 +1,33 @@ +#include "timer_metric.h" + +#include +#include + +namespace wvcdm { +namespace metrics { + +void TimerMetric::Start() { + struct timeval tv; + gettimeofday(&tv, NULL); + sec_ = tv.tv_sec; + usec_ = tv.tv_usec; +} + +double TimerMetric::AsMs() const { + struct timeval tv; + gettimeofday(&tv, NULL); + return usec_ > tv.tv_usec ? + (tv.tv_sec - sec_ - 1) * 1000.0 + (tv.tv_usec - usec_ + 1000000.0) / 1000.0 : + (tv.tv_sec - sec_) * 1000.0 + (tv.tv_usec - usec_) / 1000.0; +} + +double TimerMetric::AsUs() const { + struct timeval tv; + gettimeofday(&tv, NULL); + return usec_ > tv.tv_usec ? + (tv.tv_sec - sec_ - 1) * 1000000.0 + (tv.tv_usec - usec_ + 1000000.0) : + (tv.tv_sec - sec_) * 1000000.0 + (tv.tv_usec - usec_); +} + +} // namespace metrics +} // namespace wvcdm diff --git a/metrics/src/value_metric.cpp b/metrics/src/value_metric.cpp new file mode 100644 index 00000000..78e4f644 --- /dev/null +++ b/metrics/src/value_metric.cpp @@ -0,0 +1,115 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// This file contains the specializations for helper methods for the +// ValueMetric class. + +#include +#include + +#include "value_metric.h" + +#include "metrics_collections.h" +#include "OEMCryptoCENC.h" +#include "wv_cdm_types.h" + +namespace wvcdm { +namespace metrics { + +// Private namespace for some helper implementation functions. +namespace impl { + +template<> +void Serialize(MetricSerializer* serializer, + const std::string& metric_name, + const int32_t& value) { + serializer->SetInt32(metric_name, value); +} + +template<> +void Serialize(MetricSerializer* serializer, + const std::string& metric_name, + const int64_t& value) { + serializer->SetInt64(metric_name, value); +} + +// This specialization forces the uint32_t to an int32_t. +template<> +void Serialize(MetricSerializer* serializer, + const std::string& metric_name, + const uint32_t& value) { + serializer->SetInt32(metric_name, value); +} + +// This specialization forces the uint32_t to an int64_t. +template<> +void Serialize(MetricSerializer* serializer, + const std::string& metric_name, + const uint64_t& value) { + serializer->SetInt64(metric_name, value); +} + +// This specialization forces a bool to an int32_t. +template<> +void Serialize(MetricSerializer* serializer, + const std::string& metric_name, + const bool& value) { + serializer->SetInt32(metric_name, value); +} + +// This specialization forces an unsigned short to an int32_t. +template<> +void Serialize(MetricSerializer* serializer, + const std::string& metric_name, + const unsigned short& value) { + serializer->SetInt32(metric_name, value); +} + +template<> +void Serialize(MetricSerializer* serializer, + const std::string& metric_name, + const std::string& value) { + serializer->SetString(metric_name, value); +} + +template<> +void Serialize(MetricSerializer* serializer, + const std::string& metric_name, + const double& value) { + serializer->SetDouble(metric_name, value); +} + +// These specializations force CDM-specific types to int32_t +template<> +void Serialize(MetricSerializer* serializer, + const std::string& metric_name, + const CdmSecurityLevel& value) { + serializer->SetInt32(metric_name, value); +} + +template<> +void Serialize( + MetricSerializer* serializer, + const std::string& metric_name, + const OEMCrypto_HDCP_Capability& value) { + serializer->SetInt32(metric_name, value); +} + +template<> +void Serialize( + MetricSerializer* serializer, + const std::string& metric_name, + const OEMCrypto_ProvisioningMethod& value) { + serializer->SetInt32(metric_name, value); +} + +template<> +void Serialize( + MetricSerializer* serializer, + const std::string& metric_name, + const OEMCryptoInitializationMode& value) { + serializer->SetInt32(metric_name, value); +} + +} // namespace impl +} // namespace metrics +} // namespace wvcdm diff --git a/metrics/test/counter_metric_unittest.cpp b/metrics/test/counter_metric_unittest.cpp new file mode 100644 index 00000000..c3e26036 --- /dev/null +++ b/metrics/test/counter_metric_unittest.cpp @@ -0,0 +1,177 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Unit tests for CounterMetric + +#include "counter_metric.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "metric_serialization.h" +#include "scoped_ptr.h" + +using testing::IsNull; +using testing::NotNull; + +namespace wvcdm { +namespace metrics { + +class MockCounterMetricSerializer : public MetricSerializer { + public: + MOCK_METHOD2(SetString, void(const std::string& metric_id, + const std::string& value)); + MOCK_METHOD2(SetInt32, void(const std::string& metric_id, + int32_t value)); + MOCK_METHOD2(SetInt64, void(const std::string& metric_id, + int64_t value)); + MOCK_METHOD2(SetDouble, void(const std::string& metric_id, + double value)); +}; + +class CounterMetricTest : public ::testing::Test { + public: + void SetUp() { + mock_serializer_.reset(new MockCounterMetricSerializer()); + } + protected: + template + const std::map + GetValueMap( + const wvcdm::metrics::CounterMetric& + metric) { + return metric.value_map_; + } + + scoped_ptr mock_serializer_; +}; + +TEST_F(CounterMetricTest, NoFieldsSuccessNullCallback) { + wvcdm::metrics::CounterMetric<> metric("no/fields/metric"); + metric.Increment(); + metric.Increment(10); + + std::map value_map = GetValueMap(metric); + ASSERT_EQ(1u, GetValueMap(metric).size()); + EXPECT_EQ(11, value_map.begin()->second); + EXPECT_EQ("", value_map.begin()->first); +} + +TEST_F(CounterMetricTest, NoFieldsSuccessWithCallback) { + wvcdm::metrics::CounterMetric<> metric("no/fields/metric"); + EXPECT_CALL(*mock_serializer_, + SetInt64("no/fields/metric/count", 11)); + + metric.Increment(); + metric.Increment(10); + metric.Serialize(mock_serializer_.get()); + + std::map value_map = GetValueMap(metric); + ASSERT_EQ(1u, GetValueMap(metric).size()); + EXPECT_EQ(11, value_map.begin()->second); + EXPECT_EQ("", value_map.begin()->first); +} + +TEST_F(CounterMetricTest, NoFieldsSuccessSingleIncrementWithCallback) { + wvcdm::metrics::CounterMetric<> metric("no/fields/metric"); + EXPECT_CALL(*mock_serializer_, + SetInt64("no/fields/metric/count", 1)); + + metric.Increment(); + metric.Serialize(mock_serializer_.get()); + + std::map value_map = GetValueMap(metric); + ASSERT_EQ(1u, GetValueMap(metric).size()); + EXPECT_EQ(1, value_map.begin()->second); + EXPECT_EQ("", value_map.begin()->first); +} + +TEST_F(CounterMetricTest, OneFieldSuccessNoCallback) { + wvcdm::metrics::CounterMetric metric( + "single/fields/metric", + "error_code"); + metric.Increment(7); + metric.Increment(10, 7); + metric.Increment(13); + metric.Increment(20, 13); + std::map value_map = GetValueMap(metric); + ASSERT_EQ(2u, GetValueMap(metric).size()); + + // Verify both instances. + EXPECT_EQ(11, value_map["{error_code:7}"]); + EXPECT_EQ(21, value_map["{error_code:13}"]); +} + +TEST_F(CounterMetricTest, TwoFieldsSuccess) { + wvcdm::metrics::CounterMetric metric( + "two/fields/metric", + "error_code", + "size"); + metric.Increment(7, 23); + metric.Increment(2, 7, 29); + metric.Increment(3, 11, 23); + metric.Increment(4, 11, 29); + metric.Increment(5, 7, 23); + metric.Increment(-5, 7, 29); + + std::map value_map = GetValueMap(metric); + ASSERT_EQ(4u, GetValueMap(metric).size()); + + // Verify all instances. + EXPECT_EQ(6, value_map["{error_code:7&size:23}"]); + EXPECT_EQ(-3, value_map["{error_code:7&size:29}"]); + EXPECT_EQ(3, value_map["{error_code:11&size:23}"]); + EXPECT_EQ(4, value_map["{error_code:11&size:29}"]); + + // Verify that a non-existent distribution returns default 0 + EXPECT_EQ(0, value_map["error_code:1,size:1"]); +} + +TEST_F(CounterMetricTest, TwoFieldsSuccessWithCallback) { + wvcdm::metrics::CounterMetric metric("two/fields/metric", + "error_code", + "stringval"); + + EXPECT_CALL( + *mock_serializer_, + SetInt64("two/fields/metric/count{error_code:11&stringval:foo}", 5)); + metric.Increment(11, "foo"); + metric.Increment(4, 11, "foo"); + metric.Serialize(mock_serializer_.get()); +} + +TEST_F(CounterMetricTest, ThreeFieldsSuccess) { + wvcdm::metrics::CounterMetric metric( + "three/fields/metric", + "error_code", + "size", + "woke up happy"); + metric.Increment(7, 13, false); + + std::map value_map = GetValueMap(metric); + ASSERT_EQ(1u, GetValueMap(metric).size()); + EXPECT_EQ("{error_code:7&size:13&woke up happy:0}", + value_map.begin()->first); + EXPECT_EQ(1, value_map.begin()->second); +} + +TEST_F(CounterMetricTest, FourFieldsSuccess) { + wvcdm::metrics::CounterMetric metric( + "Four/fields/metric", + "error_code", + "size", + "woke up happy", + "horoscope"); + metric.Increment(10LL, 7, 13, true, "find your true love"); + + std::map value_map = GetValueMap(metric); + ASSERT_EQ(1u, GetValueMap(metric).size()); + EXPECT_EQ( + "{error_code:7&size:13&woke up happy:1&horoscope:find your true love}", + value_map.begin()->first); + EXPECT_EQ(10, value_map.begin()->second); +} + +} // namespace metrics +} // namespace wvcdm diff --git a/metrics/test/distribution_unittest.cpp b/metrics/test/distribution_unittest.cpp new file mode 100644 index 00000000..787978c0 --- /dev/null +++ b/metrics/test/distribution_unittest.cpp @@ -0,0 +1,47 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Unit tests for Distribution. + +#include + +#include "distribution.h" + +#include "gtest/gtest.h" + +namespace wvcdm { +namespace metrics { + +TEST(DistributionTest, NoValuesRecorded) { + Distribution distribution; + EXPECT_EQ(DBL_MAX, distribution.Min()); + EXPECT_EQ(-DBL_MAX, distribution.Max()); + EXPECT_EQ(0, distribution.Mean()); + EXPECT_EQ(0, distribution.Count()); + EXPECT_EQ(0, distribution.Variance()); +} + +TEST(DistributionTest, OneValueRecorded) { + Distribution distribution; + distribution.Record(5.0); + EXPECT_EQ(5, distribution.Min()); + EXPECT_EQ(5, distribution.Max()); + EXPECT_EQ(5, distribution.Mean()); + EXPECT_EQ(1, distribution.Count()); + EXPECT_EQ(0, distribution.Variance()); +} + +TEST(DistributionTest, MultipleValuesRecorded) { + Distribution distribution; + distribution.Record(5.0); + distribution.Record(10.0); + distribution.Record(15.0); + EXPECT_EQ(5, distribution.Min()); + EXPECT_EQ(15, distribution.Max()); + EXPECT_EQ(10, distribution.Mean()); + EXPECT_EQ(3, distribution.Count()); + EXPECT_NEAR(16.6667, distribution.Variance(), 0.0001); +} + +} // namespace metrics +} // namespace wvcdm + diff --git a/metrics/test/event_metric_unittest.cpp b/metrics/test/event_metric_unittest.cpp new file mode 100644 index 00000000..ac376308 --- /dev/null +++ b/metrics/test/event_metric_unittest.cpp @@ -0,0 +1,242 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Unit tests for EventMetric + +#include "event_metric.h" +#include "metric_serialization.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "scoped_ptr.h" + +using testing::IsNull; +using testing::NotNull; + +namespace wvcdm { +namespace metrics { + +class MockEventMetricSerializer : public MetricSerializer { + public: + MOCK_METHOD2(SetString, void(const std::string& metric_id, + const std::string& value)); + MOCK_METHOD2(SetInt32, void(const std::string& metric_id, + int32_t value)); + MOCK_METHOD2(SetInt64, void(const std::string& metric_id, + int64_t value)); + MOCK_METHOD2(SetDouble, void(const std::string& metric_id, + double value)); +}; + +class EventMetricTest : public ::testing::Test { + public: + void SetUp() { + mock_serializer_.reset(new MockEventMetricSerializer()); + } + protected: + template + const std::map + GetValueMap( + const wvcdm::metrics::EventMetric& + metric) { + return metric.value_map_; + } + + scoped_ptr mock_serializer_; +}; + +TEST_F(EventMetricTest, NoFieldsSuccessNullCallback) { + wvcdm::metrics::EventMetric<> metric("no/fields/metric"); + metric.Record(10LL); + metric.Record(10LL); + + std::map value_map = GetValueMap(metric); + ASSERT_EQ(1u, GetValueMap(metric).size()); + EXPECT_EQ(2, value_map.begin()->second->Count()); + EXPECT_EQ("", value_map.begin()->first); +} + +TEST_F(EventMetricTest, NoFieldsSuccessWithCallback) { + wvcdm::metrics::EventMetric<> metric("no/fields/metric"); + EXPECT_CALL(*mock_serializer_, + SetInt64("no/fields/metric/count", 2)); + EXPECT_CALL(*mock_serializer_, + SetDouble("no/fields/metric/mean", 10.0)); + EXPECT_CALL(*mock_serializer_, + SetDouble("no/fields/metric/variance", 0.0)); + EXPECT_CALL(*mock_serializer_, + SetDouble("no/fields/metric/min", 10.0)); + EXPECT_CALL(*mock_serializer_, + SetDouble("no/fields/metric/max", 10.0)); + + metric.Record(10); + metric.Record(10); + metric.Serialize(mock_serializer_.get()); + + std::map value_map = GetValueMap(metric); + ASSERT_EQ(1u, GetValueMap(metric).size()); + EXPECT_EQ(2, value_map.begin()->second->Count()); + EXPECT_EQ("", value_map.begin()->first); +} + +TEST_F(EventMetricTest, NoFieldsSuccessSingleRecordWithCallback) { + wvcdm::metrics::EventMetric<> metric("no/fields/metric"); + EXPECT_CALL(*mock_serializer_, + SetInt64("no/fields/metric/count", 1.0)); + EXPECT_CALL(*mock_serializer_, + SetDouble("no/fields/metric/mean", 10.0)); + + metric.Record(10); + metric.Serialize(mock_serializer_.get()); + + std::map value_map = GetValueMap(metric); + ASSERT_EQ(1u, GetValueMap(metric).size()); + EXPECT_EQ(1, value_map.begin()->second->Count()); + EXPECT_EQ("", value_map.begin()->first); +} + +TEST_F(EventMetricTest, OneFieldSuccessNoCallback) { + wvcdm::metrics::EventMetric metric( + "single/fields/metric", + "error_code"); + metric.Record(10LL, 7); + metric.Record(11LL, 13); + metric.Record(12LL, 13); + std::map value_map = GetValueMap(metric); + ASSERT_EQ(2u, GetValueMap(metric).size()); + + // Verify both instances. + // TODO(blueeyes): Export MakeFieldNameString so it can be used here. + Distribution* distribution_error_7 = value_map["{error_code:7}"]; + Distribution* distribution_error_13 = value_map["{error_code:13}"]; + ASSERT_THAT(distribution_error_7, NotNull()); + ASSERT_THAT(distribution_error_13, NotNull()); + + EXPECT_EQ(1, distribution_error_7->Count()); + EXPECT_EQ(2, distribution_error_13->Count()); +} + +TEST_F(EventMetricTest, TwoFieldsSuccess) { + wvcdm::metrics::EventMetric metric( + "two/fields/metric", + "error_code", + "size"); + metric.Record(1, 7, 23); + metric.Record(2, 7, 29); + metric.Record(3, 11, 23); + metric.Record(4, 11, 29); + metric.Record(5, 7, 23); + + std::map value_map = GetValueMap(metric); + ASSERT_EQ(4u, GetValueMap(metric).size()); + + // Verify all instances. + Distribution* distribution_7_23 = value_map["{error_code:7&size:23}"]; + Distribution* distribution_7_29 = value_map["{error_code:7&size:29}"]; + Distribution* distribution_11_23 = value_map["{error_code:11&size:23}"]; + Distribution* distribution_11_29 = value_map["{error_code:11&size:29}"]; + ASSERT_THAT(distribution_7_23, NotNull()); + ASSERT_THAT(distribution_7_29, NotNull()); + ASSERT_THAT(distribution_11_23, NotNull()); + ASSERT_THAT(distribution_11_29, NotNull()); + + EXPECT_EQ(2, distribution_7_23->Count()); + EXPECT_EQ(1, distribution_7_29->Count()); + EXPECT_EQ(1, distribution_11_23->Count()); + EXPECT_EQ(1, distribution_11_29->Count()); + + // Verify that a non-existent distribution returns nullptr. + Distribution* null_distribution = value_map["error_code:1,size:1"]; + ASSERT_THAT(null_distribution, IsNull()); +} + +TEST_F(EventMetricTest, TwoFieldsSuccessWithCallback) { + wvcdm::metrics::EventMetric metric("two/fields/metric", + "error_code", + "pow2_size"); + + // Callbacks from second record operation. + EXPECT_CALL( + *mock_serializer_, + SetInt64("two/fields/metric/count{error_code:11&pow2_size:16}", 2.0)); + EXPECT_CALL( + *mock_serializer_, + SetDouble("two/fields/metric/mean{error_code:11&pow2_size:16}", 3.5)); + EXPECT_CALL( + *mock_serializer_, + SetDouble("two/fields/metric/variance{error_code:11&pow2_size:16}", + 0.25)); + EXPECT_CALL( + *mock_serializer_, + SetDouble("two/fields/metric/min{error_code:11&pow2_size:16}", 3.0)); + EXPECT_CALL( + *mock_serializer_, + SetDouble("two/fields/metric/max{error_code:11&pow2_size:16}", 4.0)); + metric.Record(3, 11, Pow2Bucket(29)); + metric.Record(4, 11, Pow2Bucket(29)); + metric.Serialize(mock_serializer_.get()); +} + +TEST_F(EventMetricTest, ThreeFieldsSuccess) { + wvcdm::metrics::EventMetric metric( + "three/fields/metric", + "error_code", + "size", + "woke up happy"); + metric.Record(10LL, 7, 13, false); + + + std::map value_map = GetValueMap(metric); + ASSERT_EQ(1u, GetValueMap(metric).size()); + EXPECT_EQ("{error_code:7&size:13&woke up happy:0}", + value_map.begin()->first); + EXPECT_EQ(1, value_map.begin()->second->Count()); +} + +TEST_F(EventMetricTest, FourFieldsSuccess) { + wvcdm::metrics::EventMetric metric( + "Four/fields/metric", + "error_code", + "size", + "woke up happy", + "horoscope"); + metric.Record(10LL, 7, 13, true, "find your true love"); + + std::map value_map = GetValueMap(metric); + ASSERT_EQ(1u, GetValueMap(metric).size()); + EXPECT_EQ( + "{error_code:7&size:13&woke up happy:1&horoscope:find your true love}", + value_map.begin()->first); + EXPECT_EQ(1, value_map.begin()->second->Count()); +} + +TEST_F(EventMetricTest, Pow2BucketTest) { + std::stringstream value; + value << Pow2Bucket(1); + EXPECT_EQ("1", value.str()); + + value.str(""); + value << Pow2Bucket(0); + EXPECT_EQ("0", value.str()); + + value.str(""); + value << Pow2Bucket(2); + EXPECT_EQ("2", value.str()); + + value.str(""); + value << Pow2Bucket(0xFFFFFFFFu); + EXPECT_EQ("2147483648", value.str()); + + value.str(""); + value << Pow2Bucket(0x80000000u); + EXPECT_EQ("2147483648", value.str()); + + value.str(""); + value << Pow2Bucket(0x7FFFFFFF); + EXPECT_EQ("1073741824", value.str()); +} + +} // namespace metrics +} // namespace wvcdm diff --git a/metrics/test/metrics_collections_test.cpp b/metrics/test/metrics_collections_test.cpp new file mode 100644 index 00000000..56672149 --- /dev/null +++ b/metrics/test/metrics_collections_test.cpp @@ -0,0 +1,445 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Unit tests for the metrics collections, +// EngineMetrics, SessionMetrics and CrytpoMetrics. + +#include "metrics_collections.h" + +#include + +#include "gmock/gmock.h" +#include "google/protobuf/text_format.h" +#include "gtest/gtest.h" +#include "log.h" +#include "metrics.pb.h" +#include "wv_cdm_types.h" + +using drm_metrics::MetricsGroup; +using google::protobuf::TextFormat; + +namespace wvcdm { +namespace metrics { + +// TODO(blueeyes): Improve this implementation by supporting full message +// API In CDM. That allows us to use MessageDifferencer. +class EngineMetricsTest : public ::testing::Test { +}; + +TEST_F(EngineMetricsTest, AllEngineMetrics) { + EngineMetrics engine_metrics; + + // Set some values in all of the engine metrics. + engine_metrics.cdm_engine_add_key_.Record(1.0, KEY_ADDED); + engine_metrics.cdm_engine_close_session_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_decrypt_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_find_session_for_key_.Record(1.0, false); + engine_metrics.cdm_engine_generate_key_request_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_get_provisioning_request_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_get_usage_info_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_handle_provisioning_response_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_life_span_.Record(1.0); + engine_metrics.cdm_engine_open_key_set_session_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_open_session_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_query_key_status_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_release_all_usage_info_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_release_usage_info_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_remove_keys_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_restore_key_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_unprovision_.Record(1.0, NO_ERROR, kSecurityLevelL1); + + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, true, false); + + // For each EventMetric, 2 metrics get serialized since only one sample was + // taken. So, the total number of serialized metrics are 2*17. + ASSERT_EQ(2 * 17, actual_metrics.metric_size()); + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); + + // Spot check some metrics. + EXPECT_EQ("/drm/widevine/cdm_engine/add_key/time/count{error:2}", + actual_metrics.metric(0).name()); + EXPECT_EQ("/drm/widevine/cdm_engine/close_session/time/mean{error:0}", + actual_metrics.metric(3).name()); + EXPECT_EQ("/drm/widevine/cdm_engine/decrypt/time/mean{error:0}", + actual_metrics.metric(5).name()); + EXPECT_EQ(1.0, actual_metrics.metric(5).value().double_value()); +} + +TEST_F(EngineMetricsTest, EngineAndCryptoMetrics) { + EngineMetrics engine_metrics; + + // Set some values in some of the engine metrics and some crypto metrics. + engine_metrics.cdm_engine_add_key_.Record(1.0, KEY_ADDED); + engine_metrics.cdm_engine_close_session_.Record(1.0, NO_ERROR); + CryptoMetrics* crypto_metrics = engine_metrics.GetCryptoMetrics(); + + crypto_metrics->crypto_session_generic_decrypt_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kEncryptionAlgorithmAesCbc128); + crypto_metrics->crypto_session_get_device_unique_id_ + .Record(4.0, false); + + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, true, false); + + // For each EventMetric, 2 metrics get serialized since only one sample was + // taken. So, the total number of serialized metrics are 2*4 since we + // touched 4 metrics. + ASSERT_EQ(2 * 4, actual_metrics.metric_size()); + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); + + // Spot check some metrics. + EXPECT_EQ("/drm/widevine/cdm_engine/add_key/time/count{error:2}", + actual_metrics.metric(0).name()); + EXPECT_EQ( + "/drm/widevine/crypto_session/generic_decrypt/time/count" + "{error:0&length:1024&encryption_algorithm:1}", + actual_metrics.metric(4).name()); + EXPECT_EQ( + "/drm/widevine/crypto_session/get_device_unique_id/time/mean{success:0}", + actual_metrics.metric(7).name()); + EXPECT_EQ(4.0, actual_metrics.metric(7).value().double_value()); +} + +TEST_F(EngineMetricsTest, EmptyEngineMetrics) { + EngineMetrics engine_metrics; + + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, true, false); + + EXPECT_EQ(0, actual_metrics.metric_size()); + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); +} + +TEST_F(EngineMetricsTest, EngineMetricsWithCompletedSessions) { + EngineMetrics engine_metrics; + + // Set a values in an engine metric and in a crypto metric. + engine_metrics.cdm_engine_add_key_.Record(1.0, KEY_ADDED); + engine_metrics.GetCryptoMetrics() + ->crypto_session_load_certificate_private_key_.Record(2.0, true); + + // Create two sessions and record some metrics. + SessionMetrics* session_metrics_1 = engine_metrics.AddSession(); + session_metrics_1->SetSessionId("session_id_1"); + SessionMetrics* session_metrics_2 = engine_metrics.AddSession(); + session_metrics_2->SetSessionId("session_id_2"); + // Record a CryptoMetrics metric in the session. + session_metrics_2->GetCryptoMetrics()->crypto_session_generic_decrypt_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kEncryptionAlgorithmAesCbc128); + session_metrics_2->SetSessionId("session_id_2"); + // Mark only session 2 as completed. + session_metrics_2->SetCompleted(); + + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, true, false); + + // Validate metric counts. + // For each EventMetric, 2 metrics get serialized since only one sample was + // taken. So, the total number of serialized metrics are 2*2 since we + // touched 2 metrics. + ASSERT_EQ(2 * 2, actual_metrics.metric_size()); + ASSERT_EQ(1, actual_metrics.metric_sub_group_size()); + ASSERT_EQ(3, actual_metrics.metric_sub_group(0).metric_size()); + + // Spot check some metrics. + EXPECT_EQ("/drm/widevine/cdm_engine/add_key/time/count{error:2}", + actual_metrics.metric(0).name()); + EXPECT_EQ("/drm/widevine/crypto_session/load_certificate_private_key" + "/time/count{success:1}", + actual_metrics.metric(2).name()); + EXPECT_EQ("/drm/widevine/cdm_session/session_id", + actual_metrics.metric_sub_group(0).metric(0).name()); + EXPECT_EQ( + "session_id_2", + actual_metrics.metric_sub_group(0).metric(0).value().string_value()); + EXPECT_EQ( + "/drm/widevine/crypto_session/generic_decrypt/time/count" + "{error:0&length:1024&encryption_algorithm:1}", + actual_metrics.metric_sub_group(0).metric(1).name()); +} + +TEST_F(EngineMetricsTest, EngineMetricsSerializeAllSessions) { + EngineMetrics engine_metrics; + + // Create two sessions and record some metrics. + SessionMetrics* session_metrics_1 = engine_metrics.AddSession(); + session_metrics_1->SetSessionId("session_id_1"); + SessionMetrics* session_metrics_2 = engine_metrics.AddSession(); + session_metrics_2->SetSessionId("session_id_2"); + // Mark only session 2 as completed. + session_metrics_2->SetCompleted(); + + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, false, false); + + // Validate metric counts. + // No Engine-level metrics were recorded. + ASSERT_EQ(0, actual_metrics.metric_size()); + // Two sub groups, 1 per session. + ASSERT_EQ(2, actual_metrics.metric_sub_group_size()); + ASSERT_EQ(1, actual_metrics.metric_sub_group(0).metric_size()); + + // Spot check some metrics. + EXPECT_EQ("/drm/widevine/cdm_session/session_id", + actual_metrics.metric_sub_group(0).metric(0).name()); + EXPECT_EQ( + "session_id_1", + actual_metrics.metric_sub_group(0).metric(0).value().string_value()); + EXPECT_EQ("/drm/widevine/cdm_session/session_id", + actual_metrics.metric_sub_group(1).metric(0).name()); + EXPECT_EQ( + "session_id_2", + actual_metrics.metric_sub_group(1).metric(0).value().string_value()); +} + +TEST_F(EngineMetricsTest, EngineMetricsRemoveSessions) { + EngineMetrics engine_metrics; + + // Create two sessions and record some metrics. + SessionMetrics* session_metrics_1 = engine_metrics.AddSession(); + session_metrics_1->SetSessionId("session_id_1"); + SessionMetrics* session_metrics_2 = engine_metrics.AddSession(); + session_metrics_2->SetSessionId("session_id_2"); + // Mark only session 2 as completed. + session_metrics_2->SetCompleted(); + + // Serialize all metrics, don't remove any. + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, false, false); + + // Validate metric counts. + // Two sub groups, 1 per session. + ASSERT_EQ(2, actual_metrics.metric_sub_group_size()); + + // Serialize completed metrics, remove them. + actual_metrics.Clear(); + engine_metrics.Serialize(&actual_metrics, true, true); + // Validate metric counts. + // Only one, completed session should exist. + ASSERT_EQ(1, actual_metrics.metric_sub_group_size()); + ASSERT_EQ(1, actual_metrics.metric_sub_group(0).metric_size()); + EXPECT_EQ( + "session_id_2", + actual_metrics.metric_sub_group(0).metric(0).value().string_value()); + + // Serialize all metrics, remove them. + actual_metrics.Clear(); + engine_metrics.Serialize(&actual_metrics, false, true); + // Validate metric counts. + // Only one, non-complete session should exist. + ASSERT_EQ(1, actual_metrics.metric_sub_group_size()); + ASSERT_EQ(1, actual_metrics.metric_sub_group(0).metric_size()); + EXPECT_EQ( + "session_id_1", + actual_metrics.metric_sub_group(0).metric(0).value().string_value()); + + // Serialize all metrics, don't remove any. + actual_metrics.Clear(); + engine_metrics.Serialize(&actual_metrics, false, false); + // Validate metric counts. + // There should be no more metric subgroups left. + ASSERT_EQ(0, actual_metrics.metric_sub_group_size()); +} + +class SessionMetricsTest : public ::testing::Test { +}; + +TEST_F(SessionMetricsTest, AllSessionMetrics) { + SessionMetrics session_metrics; + session_metrics.SetSessionId("session_id 1"); + + session_metrics.cdm_session_life_span_.Record(1.0); + session_metrics.cdm_session_renew_key_.Record(1.0, NO_ERROR); + session_metrics.cdm_session_restore_offline_session_.Record(1.0, NO_ERROR); + session_metrics.cdm_session_restore_usage_session_.Record(1.0, NO_ERROR); + + // Record a CryptoMetrics metric in the session. + session_metrics.GetCryptoMetrics()->crypto_session_generic_decrypt_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kEncryptionAlgorithmAesCbc128); + + MetricsGroup actual_metrics; + session_metrics.Serialize(&actual_metrics); + + ASSERT_EQ(11, actual_metrics.metric_size()); + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); + + // Spot check some metrics. + EXPECT_EQ("/drm/widevine/cdm_session/session_id", + actual_metrics.metric(0).name()); + EXPECT_EQ("/drm/widevine/cdm_session/life_span/time/count", + actual_metrics.metric(1).name()); + EXPECT_EQ("/drm/widevine/cdm_session/renew_key/time/mean{error:0}", + actual_metrics.metric(4).name()); + EXPECT_EQ(1.0, actual_metrics.metric(4).value().double_value()); + EXPECT_EQ("/drm/widevine/crypto_session/generic_decrypt/time/count" + "{error:0&length:1024&encryption_algorithm:1}", + actual_metrics.metric(9).name()); +} + +TEST_F(SessionMetricsTest, EmptySessionMetrics) { + SessionMetrics session_metrics; + + MetricsGroup actual_metrics; + session_metrics.Serialize(&actual_metrics); + + // Session metric always has a session id. + ASSERT_EQ(1, actual_metrics.metric_size()); + EXPECT_EQ("/drm/widevine/cdm_session/session_id", + actual_metrics.metric(0).name()); + EXPECT_EQ("", actual_metrics.metric(0).value().string_value()); + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); +} + +class CryptoMetricsTest : public ::testing::Test { +}; + +TEST_F(CryptoMetricsTest, AllCryptoMetrics) { + CryptoMetrics crypto_metrics; + + // Crypto session metrics. + crypto_metrics.crypto_session_delete_all_usage_reports_ + .Record(1.0, NO_ERROR); + crypto_metrics.crypto_session_delete_multiple_usage_information_ + .Record(1.0, NO_ERROR); + crypto_metrics.crypto_session_generic_decrypt_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kEncryptionAlgorithmAesCbc128); + crypto_metrics.crypto_session_generic_encrypt_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kEncryptionAlgorithmAesCbc128); + crypto_metrics.crypto_session_generic_sign_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kSigningAlgorithmHmacSha256); + crypto_metrics.crypto_session_generic_verify_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kSigningAlgorithmHmacSha256); + crypto_metrics.crypto_session_get_device_unique_id_.Record(1.0, true); + crypto_metrics.crypto_session_get_security_level_ + .Record(1.0, kSecurityLevelL1); + crypto_metrics.crypto_session_get_system_id_.Record(1.0, true, 1234); + crypto_metrics.crypto_session_get_token_.Record(1.0, true); + crypto_metrics.crypto_session_life_span_.Record(1.0); + crypto_metrics.crypto_session_load_certificate_private_key_ + .Record(1.0, true); + crypto_metrics.crypto_session_open_.Record(1.0, NO_ERROR, kLevelDefault); + crypto_metrics.crypto_session_update_usage_information_ + .Record(1.0, NO_ERROR); + crypto_metrics.crypto_session_usage_information_support_.Record(1.0, true); + + // Oem crypto metrics. + crypto_metrics.oemcrypto_api_version_.Record(1.0, 123, kLevelDefault); + crypto_metrics.oemcrypto_close_session_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_copy_buffer_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, + kLevelDefault, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_deactivate_usage_entry_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_decrypt_cenc_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_delete_usage_entry_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_delete_usage_table_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_derive_keys_from_session_key_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_force_delete_usage_entry_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_generate_derived_keys_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_generate_nonce_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_generate_rsa_signature_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_generate_signature_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_generic_decrypt_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_generic_encrypt_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_generic_sign_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_generic_verify_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_get_device_id_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_get_hdcp_capability_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_get_key_data_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, + Pow2Bucket(1025), kLevelDefault); + crypto_metrics.oemcrypto_get_max_number_of_sessions_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_get_number_of_open_sessions_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_get_oem_public_certificate_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + + crypto_metrics.oemcrypto_get_provisioning_method_ + .Record(1.0, OEMCrypto_Keybox, kLevelDefault); + + crypto_metrics.oemcrypto_get_random_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_initialize_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_install_keybox_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_is_anti_rollback_hw_present_ + .Record(1.0, true, kLevelDefault); + + crypto_metrics.oemcrypto_is_keybox_valid_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_load_device_rsa_key_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_load_keys_.Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_load_test_keybox_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_load_test_rsa_key_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_open_session_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_refresh_keys_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_report_usage_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_rewrap_device_rsa_key_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_rewrap_device_rsa_key_30_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_security_level_ + .Record(1.0, kSecurityLevelL2, kLevelDefault); + crypto_metrics.oemcrypto_security_patch_level_ + .Record(1.0, 123, kLevelDefault); + crypto_metrics.oemcrypto_select_key_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_supports_usage_table_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_update_usage_table_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_wrap_keybox_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + + // Internal OEMCrypto Metrics + crypto_metrics.oemcrypto_initialization_mode_ + .Record(1.0, OEMCrypto_INITIALIZED_FORCING_L3); + crypto_metrics.oemcrypto_l1_api_version_.Record(1.0, 12, 123); + + MetricsGroup actual_metrics; + crypto_metrics.Serialize(&actual_metrics); + + // 61 EventMetric instances, 2 values each. + ASSERT_EQ(122, actual_metrics.metric_size()); + + // Spot check some metrics. + EXPECT_EQ( + "/drm/widevine/crypto_session/delete_all_usage_reports/time/count" + "{error:0}", + actual_metrics.metric(0).name()); + EXPECT_EQ(1, actual_metrics.metric(0).value().int_value()); + EXPECT_EQ( + "/drm/widevine/oemcrypto/l1_api_version/mean{version:12&min_version:123}", + actual_metrics.metric(121).name()); + EXPECT_EQ(1.0, actual_metrics.metric(121).value().double_value()); + + // No subgroups should exist. + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); +} + +} // namespace metrics +} // namespace wvcdm diff --git a/metrics/test/metrics_collections_unittest.cpp b/metrics/test/metrics_collections_unittest.cpp new file mode 100644 index 00000000..ae51fade --- /dev/null +++ b/metrics/test/metrics_collections_unittest.cpp @@ -0,0 +1,445 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Unit tests for the metrics collections, +// EngineMetrics, SessionMetrics and CryptoMetrics. + +#include "metrics_collections.h" + +#include + +#include "gmock/gmock.h" +#include "google/protobuf/text_format.h" +#include "gtest/gtest.h" +#include "log.h" +#include "metrics.pb.h" +#include "wv_cdm_types.h" + +using drm_metrics::MetricsGroup; +using google::protobuf::TextFormat; + +namespace wvcdm { +namespace metrics { + +// TODO(blueeyes): Improve this implementation by supporting full message +// API In CDM. That allows us to use MessageDifferencer. +class EngineMetricsTest : public ::testing::Test { +}; + +TEST_F(EngineMetricsTest, AllEngineMetrics) { + EngineMetrics engine_metrics; + + // Set some values in all of the engine metrics. + engine_metrics.cdm_engine_add_key_.Record(1.0, KEY_ADDED); + engine_metrics.cdm_engine_close_session_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_decrypt_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_find_session_for_key_.Record(1.0, false); + engine_metrics.cdm_engine_generate_key_request_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_get_provisioning_request_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_get_usage_info_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_handle_provisioning_response_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_life_span_.Record(1.0); + engine_metrics.cdm_engine_open_key_set_session_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_open_session_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_query_key_status_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_release_all_usage_info_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_release_usage_info_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_remove_keys_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_restore_key_.Record(1.0, NO_ERROR); + engine_metrics.cdm_engine_unprovision_.Record(1.0, NO_ERROR, kSecurityLevelL1); + + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, true, false); + + // For each EventMetric, 2 metrics get serialized since only one sample was + // taken. So, the total number of serialized metrics are 2*17. + ASSERT_EQ(2 * 17, actual_metrics.metric_size()); + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); + + // Spot check some metrics. + EXPECT_EQ("/drm/widevine/cdm_engine/add_key/time/count{error:2}", + actual_metrics.metric(0).name()); + EXPECT_EQ("/drm/widevine/cdm_engine/close_session/count{error:0}", + actual_metrics.metric(3).name()); + EXPECT_EQ("/drm/widevine/cdm_engine/decrypt/time/mean{error:0}", + actual_metrics.metric(5).name()); + EXPECT_EQ(1.0, actual_metrics.metric(5).value().double_value()); +} + +TEST_F(EngineMetricsTest, EngineAndCryptoMetrics) { + EngineMetrics engine_metrics; + + // Set some values in some of the engine metrics and some crypto metrics. + engine_metrics.cdm_engine_add_key_.Record(1.0, KEY_ADDED); + engine_metrics.cdm_engine_close_session_.Record(1.0, NO_ERROR); + CryptoMetrics* crypto_metrics = engine_metrics.GetCryptoMetrics(); + + crypto_metrics->crypto_session_generic_decrypt_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kEncryptionAlgorithmAesCbc128); + crypto_metrics->crypto_session_get_device_unique_id_ + .Record(4.0, false); + + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, true, false); + + // For each EventMetric, 2 metrics get serialized since only one sample was + // taken. So, the total number of serialized metrics are 2*4 since we + // touched 4 metrics. + ASSERT_EQ(2 * 4, actual_metrics.metric_size()); + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); + + // Spot check some metrics. + EXPECT_EQ("/drm/widevine/cdm_engine/add_key/time/count{error:2}", + actual_metrics.metric(0).name()); + EXPECT_EQ( + "/drm/widevine/crypto_session/generic_decrypt/time/count" + "{error:0&length:1024&encryption_algorithm:1}", + actual_metrics.metric(4).name()); + EXPECT_EQ( + "/drm/widevine/crypto_session/get_device_unique_id/count{success:0}", + actual_metrics.metric(7).name()); + EXPECT_EQ(4.0, actual_metrics.metric(7).value().double_value()); +} + +TEST_F(EngineMetricsTest, EmptyEngineMetrics) { + EngineMetrics engine_metrics; + + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, true, false); + + EXPECT_EQ(0, actual_metrics.metric_size()); + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); +} + +TEST_F(EngineMetricsTest, EngineMetricsWithCompletedSessions) { + EngineMetrics engine_metrics; + + // Set a values in an engine metric and in a crypto metric. + engine_metrics.cdm_engine_add_key_.Record(1.0, KEY_ADDED); + engine_metrics.GetCryptoMetrics() + ->crypto_session_load_certificate_private_key_.Record(2.0, true); + + // Create two sessions and record some metrics. + SessionMetrics* session_metrics_1 = engine_metrics.AddSession(); + session_metrics_1->SetSessionId("session_id_1"); + SessionMetrics* session_metrics_2 = engine_metrics.AddSession(); + session_metrics_2->SetSessionId("session_id_2"); + // Record a CryptoMetrics metric in the session. + session_metrics_2->GetCryptoMetrics()->crypto_session_generic_decrypt_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kEncryptionAlgorithmAesCbc128); + session_metrics_2->SetSessionId("session_id_2"); + // Mark only session 2 as completed. + session_metrics_2->SetCompleted(); + + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, true, false); + + // Validate metric counts. + // For each EventMetric, 2 metrics get serialized since only one sample was + // taken. So, the total number of serialized metrics are 2*2 since we + // touched 2 metrics. + ASSERT_EQ(2 * 2, actual_metrics.metric_size()); + ASSERT_EQ(1, actual_metrics.metric_sub_group_size()); + ASSERT_EQ(3, actual_metrics.metric_sub_group(0).metric_size()); + + // Spot check some metrics. + EXPECT_EQ("/drm/widevine/cdm_engine/add_key/time/count{error:2}", + actual_metrics.metric(0).name()); + EXPECT_EQ("/drm/widevine/crypto_session/load_certificate_private_key" + "/time/count{success:1}", + actual_metrics.metric(2).name()); + EXPECT_EQ("/drm/widevine/cdm_session/session_id", + actual_metrics.metric_sub_group(0).metric(0).name()); + EXPECT_EQ( + "session_id_2", + actual_metrics.metric_sub_group(0).metric(0).value().string_value()); + EXPECT_EQ( + "/drm/widevine/crypto_session/generic_decrypt/time/count" + "{error:0&length:1024&encryption_algorithm:1}", + actual_metrics.metric_sub_group(0).metric(1).name()); +} + +TEST_F(EngineMetricsTest, EngineMetricsSerializeAllSessions) { + EngineMetrics engine_metrics; + + // Create two sessions and record some metrics. + SessionMetrics* session_metrics_1 = engine_metrics.AddSession(); + session_metrics_1->SetSessionId("session_id_1"); + SessionMetrics* session_metrics_2 = engine_metrics.AddSession(); + session_metrics_2->SetSessionId("session_id_2"); + // Mark only session 2 as completed. + session_metrics_2->SetCompleted(); + + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, false, false); + + // Validate metric counts. + // No Engine-level metrics were recorded. + ASSERT_EQ(0, actual_metrics.metric_size()); + // Two sub groups, 1 per session. + ASSERT_EQ(2, actual_metrics.metric_sub_group_size()); + ASSERT_EQ(1, actual_metrics.metric_sub_group(0).metric_size()); + + // Spot check some metrics. + EXPECT_EQ("/drm/widevine/cdm_session/session_id", + actual_metrics.metric_sub_group(0).metric(0).name()); + EXPECT_EQ( + "session_id_1", + actual_metrics.metric_sub_group(0).metric(0).value().string_value()); + EXPECT_EQ("/drm/widevine/cdm_session/session_id", + actual_metrics.metric_sub_group(1).metric(0).name()); + EXPECT_EQ( + "session_id_2", + actual_metrics.metric_sub_group(1).metric(0).value().string_value()); +} + +TEST_F(EngineMetricsTest, EngineMetricsRemoveSessions) { + EngineMetrics engine_metrics; + + // Create two sessions and record some metrics. + SessionMetrics* session_metrics_1 = engine_metrics.AddSession(); + session_metrics_1->SetSessionId("session_id_1"); + SessionMetrics* session_metrics_2 = engine_metrics.AddSession(); + session_metrics_2->SetSessionId("session_id_2"); + // Mark only session 2 as completed. + session_metrics_2->SetCompleted(); + + // Serialize all metrics, don't remove any. + drm_metrics::MetricsGroup actual_metrics; + engine_metrics.Serialize(&actual_metrics, false, false); + + // Validate metric counts. + // Two sub groups, 1 per session. + ASSERT_EQ(2, actual_metrics.metric_sub_group_size()); + + // Serialize completed metrics, remove them. + actual_metrics.Clear(); + engine_metrics.Serialize(&actual_metrics, true, true); + // Validate metric counts. + // Only one, completed session should exist. + ASSERT_EQ(1, actual_metrics.metric_sub_group_size()); + ASSERT_EQ(1, actual_metrics.metric_sub_group(0).metric_size()); + EXPECT_EQ( + "session_id_2", + actual_metrics.metric_sub_group(0).metric(0).value().string_value()); + + // Serialize all metrics, remove them. + actual_metrics.Clear(); + engine_metrics.Serialize(&actual_metrics, false, true); + // Validate metric counts. + // Only one, non-complete session should exist. + ASSERT_EQ(1, actual_metrics.metric_sub_group_size()); + ASSERT_EQ(1, actual_metrics.metric_sub_group(0).metric_size()); + EXPECT_EQ( + "session_id_1", + actual_metrics.metric_sub_group(0).metric(0).value().string_value()); + + // Serialize all metrics, don't remove any. + actual_metrics.Clear(); + engine_metrics.Serialize(&actual_metrics, false, false); + // Validate metric counts. + // There should be no more metric subgroups left. + ASSERT_EQ(0, actual_metrics.metric_sub_group_size()); +} + +class SessionMetricsTest : public ::testing::Test { +}; + +TEST_F(SessionMetricsTest, AllSessionMetrics) { + SessionMetrics session_metrics; + session_metrics.SetSessionId("session_id 1"); + + session_metrics.cdm_session_life_span_.Record(1.0); + session_metrics.cdm_session_renew_key_.Record(1.0, NO_ERROR); + session_metrics.cdm_session_restore_offline_session_.Record(1.0, NO_ERROR); + session_metrics.cdm_session_restore_usage_session_.Record(1.0, NO_ERROR); + + // Record a CryptoMetrics metric in the session. + session_metrics.GetCryptoMetrics()->crypto_session_generic_decrypt_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kEncryptionAlgorithmAesCbc128); + + MetricsGroup actual_metrics; + session_metrics.Serialize(&actual_metrics); + + ASSERT_EQ(11, actual_metrics.metric_size()); + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); + + // Spot check some metrics. + EXPECT_EQ("/drm/widevine/cdm_session/session_id", + actual_metrics.metric(0).name()); + EXPECT_EQ("/drm/widevine/cdm_session/life_span", + actual_metrics.metric(1).name()); + EXPECT_EQ("/drm/widevine/cdm_session/renew_key/time/mean{error:0}", + actual_metrics.metric(4).name()); + EXPECT_EQ(1.0, actual_metrics.metric(4).value().double_value()); + EXPECT_EQ("/drm/widevine/crypto_session/generic_decrypt/time/count" + "{error:0&length:1024&encryption_algorithm:1}", + actual_metrics.metric(9).name()); +} + +TEST_F(SessionMetricsTest, EmptySessionMetrics) { + SessionMetrics session_metrics; + + MetricsGroup actual_metrics; + session_metrics.Serialize(&actual_metrics); + + // Session metric always has a session id. + ASSERT_EQ(1, actual_metrics.metric_size()); + EXPECT_EQ("/drm/widevine/cdm_session/session_id", + actual_metrics.metric(0).name()); + EXPECT_EQ("", actual_metrics.metric(0).value().string_value()); + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); +} + +class CryptoMetricsTest : public ::testing::Test { +}; + +TEST_F(CryptoMetricsTest, AllCryptoMetrics) { + CryptoMetrics crypto_metrics; + + // Crypto session metrics. + crypto_metrics.crypto_session_delete_all_usage_reports_ + .Record(1.0, NO_ERROR); + crypto_metrics.crypto_session_delete_multiple_usage_information_ + .Record(1.0, NO_ERROR); + crypto_metrics.crypto_session_generic_decrypt_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kEncryptionAlgorithmAesCbc128); + crypto_metrics.crypto_session_generic_encrypt_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kEncryptionAlgorithmAesCbc128); + crypto_metrics.crypto_session_generic_sign_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kSigningAlgorithmHmacSha256); + crypto_metrics.crypto_session_generic_verify_ + .Record(2.0, NO_ERROR, Pow2Bucket(1025), kSigningAlgorithmHmacSha256); + crypto_metrics.crypto_session_get_device_unique_id_.Record(1.0, true); + crypto_metrics.crypto_session_get_security_level_ + .Record(1.0, kSecurityLevelL1); + crypto_metrics.crypto_session_get_system_id_.Record(1.0, true, 1234); + crypto_metrics.crypto_session_get_token_.Record(1.0, true); + crypto_metrics.crypto_session_life_span_.Record(1.0); + crypto_metrics.crypto_session_load_certificate_private_key_ + .Record(1.0, true); + crypto_metrics.crypto_session_open_.Record(1.0, NO_ERROR, kLevelDefault); + crypto_metrics.crypto_session_update_usage_information_ + .Record(1.0, NO_ERROR); + crypto_metrics.crypto_session_usage_information_support_.Record(1.0, true); + + // Oem crypto metrics. + crypto_metrics.oemcrypto_api_version_.Record(1.0, 123, kLevelDefault); + crypto_metrics.oemcrypto_close_session_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_copy_buffer_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, + kLevelDefault, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_deactivate_usage_entry_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_decrypt_cenc_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_delete_usage_entry_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_delete_usage_table_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_derive_keys_from_session_key_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_force_delete_usage_entry_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_generate_derived_keys_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_generate_nonce_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_generate_rsa_signature_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_generate_signature_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_generic_decrypt_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_generic_encrypt_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_generic_sign_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_generic_verify_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_get_device_id_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_get_hdcp_capability_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_get_key_data_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, + Pow2Bucket(1025), kLevelDefault); + crypto_metrics.oemcrypto_get_max_number_of_sessions_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_get_number_of_open_sessions_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_get_oem_public_certificate_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + + crypto_metrics.oemcrypto_get_provisioning_method_ + .Record(1.0, OEMCrypto_Keybox, kLevelDefault); + + crypto_metrics.oemcrypto_get_random_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, Pow2Bucket(1025)); + crypto_metrics.oemcrypto_initialize_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_install_keybox_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_is_anti_rollback_hw_present_ + .Record(1.0, true, kLevelDefault); + + crypto_metrics.oemcrypto_is_keybox_valid_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_load_device_rsa_key_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_load_keys_.Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_load_test_keybox_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_load_test_rsa_key_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_open_session_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_refresh_keys_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_report_usage_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_rewrap_device_rsa_key_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_rewrap_device_rsa_key_30_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_security_level_ + .Record(1.0, kSecurityLevelL2, kLevelDefault); + crypto_metrics.oemcrypto_security_patch_level_ + .Record(1.0, 123, kLevelDefault); + crypto_metrics.oemcrypto_select_key_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_supports_usage_table_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED, kLevelDefault); + crypto_metrics.oemcrypto_update_usage_table_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + crypto_metrics.oemcrypto_wrap_keybox_ + .Record(1.0, OEMCrypto_ERROR_INIT_FAILED); + + // Internal OEMCrypto Metrics + crypto_metrics.oemcrypto_initialization_mode_ + .Record(1.0, OEMCrypto_INITIALIZED_FORCING_L3); + crypto_metrics.oemcrypto_l1_api_version_.Record(1.0, 12, 123); + + MetricsGroup actual_metrics; + crypto_metrics.Serialize(&actual_metrics); + + // 61 EventMetric instances, 2 values each. + ASSERT_EQ(122, actual_metrics.metric_size()); + + // Spot check some metrics. + EXPECT_EQ( + "/drm/widevine/crypto_session/delete_all_usage_reports/count" + "{error:0}", + actual_metrics.metric(0).name()); + EXPECT_EQ(1, actual_metrics.metric(0).value().int_value()); + EXPECT_EQ( + "/drm/widevine/oemcrypto/l1_api_version/mean{version:12&min_version:123}", + actual_metrics.metric(121).name()); + EXPECT_EQ(1.0, actual_metrics.metric(121).value().double_value()); + + // No subgroups should exist. + EXPECT_EQ(0, actual_metrics.metric_sub_group_size()); +} + +} // namespace metrics +} // namespace wvcdm diff --git a/metrics/test/value_metric_unittest.cpp b/metrics/test/value_metric_unittest.cpp new file mode 100644 index 00000000..77f5570a --- /dev/null +++ b/metrics/test/value_metric_unittest.cpp @@ -0,0 +1,77 @@ +// Copyright 2017 Google Inc. All Rights Reserved. +// +// Unit tests for ValueMetric. + +#include + +#include "value_metric.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "metric_serialization.h" +#include "scoped_ptr.h" + +namespace wvcdm { +namespace metrics { + +class MockMetricSerializer : public MetricSerializer { + public: + MOCK_METHOD2(SetString, void(const std::string& metric_id, + const std::string& value)); + MOCK_METHOD2(SetInt32, void(const std::string& metric_id, + int32_t value)); + MOCK_METHOD2(SetInt64, void(const std::string& metric_id, + int64_t value)); + MOCK_METHOD2(SetDouble, void(const std::string& metric_id, + double value)); +}; + +class ValueMetricTest : public ::testing::Test { + public: + void SetUp() { + mock_serializer_.reset(new MockMetricSerializer()); + } + + protected: + scoped_ptr mock_serializer_; +}; + +TEST_F(ValueMetricTest, StringValue) { + ValueMetric value_metric("string/metric"); + value_metric.Record("foo"); + + EXPECT_CALL(*mock_serializer_, + SetString("string/metric", "foo")); + value_metric.Serialize(mock_serializer_.get()); +} + +TEST_F(ValueMetricTest, DoubleValue) { + ValueMetric value_metric("double/metric"); + value_metric.Record(42.0); + + EXPECT_CALL(*mock_serializer_, + SetDouble("double/metric", 42.0)); + value_metric.Serialize(mock_serializer_.get()); +} + +TEST_F(ValueMetricTest, Int32Value) { + ValueMetric value_metric("int32/metric"); + value_metric.Record(42); + + EXPECT_CALL(*mock_serializer_, + SetInt32("int32/metric", 42)); + value_metric.Serialize(mock_serializer_.get()); +} + +TEST_F(ValueMetricTest, Int64Value) { + ValueMetric value_metric("int64/metric"); + value_metric.Record(42); + + EXPECT_CALL(*mock_serializer_, + SetInt64("int64/metric", 42)); + value_metric.Serialize(mock_serializer_.get()); +} + +} // namespace metrics +} // namespace wvcdm + diff --git a/oemcrypto/include/OEMCryptoCENC.h b/oemcrypto/include/OEMCryptoCENC.h index 0e663be9..525e93ff 100644 --- a/oemcrypto/include/OEMCryptoCENC.h +++ b/oemcrypto/include/OEMCryptoCENC.h @@ -6,9 +6,9 @@ * Reference APIs needed to support Widevine's crypto algorithms. * * See the document "WV Modular DRM Security Integration Guide for Common - * Encryption (CENC) -- version 12" for a description of this API. You + * Encryption (CENC) -- version 13" for a description of this API. You * can find this document in the widevine repository as - * docs/WidevineModularDRMSecurityIntegrationGuideforCENC.pdf + * docs/WidevineModularDRMSecurityIntegrationGuideforCENC_v13.pdf * Changes between different versions of this API are documented in the files * docs/Widevine_Modular_DRM_Version_*_Delta.pdf * @@ -33,10 +33,10 @@ typedef enum OEMCryptoResult { OEMCrypto_ERROR_TERMINATE_FAILED = 2, OEMCrypto_ERROR_OPEN_FAILURE = 3, OEMCrypto_ERROR_CLOSE_FAILURE = 4, - OEMCrypto_ERROR_ENTER_SECURE_PLAYBACK_FAILED = 5, - OEMCrypto_ERROR_EXIT_SECURE_PLAYBACK_FAILED = 6, + OEMCrypto_ERROR_ENTER_SECURE_PLAYBACK_FAILED = 5, // deprecated + OEMCrypto_ERROR_EXIT_SECURE_PLAYBACK_FAILED = 6, // deprecated OEMCrypto_ERROR_SHORT_BUFFER = 7, - OEMCrypto_ERROR_NO_DEVICE_KEY = 8, + OEMCrypto_ERROR_NO_DEVICE_KEY = 8, // no keybox device key. OEMCrypto_ERROR_NO_ASSET_KEY = 9, OEMCrypto_ERROR_KEYBOX_INVALID = 10, OEMCrypto_ERROR_NO_KEYDATA = 11, @@ -68,6 +68,17 @@ typedef enum OEMCryptoResult { OEMCrypto_ERROR_INSUFFICIENT_RESOURCES = 37, OEMCrypto_ERROR_INSUFFICIENT_HDCP = 38, OEMCrypto_ERROR_BUFFER_TOO_LARGE = 39, + OEMCrypto_WARNING_GENERATION_SKEW = 40, // Warning, not an error. + OEMCrypto_ERROR_GENERATION_SKEW = 41, + OEMCrypto_LOCAL_DISPLAY_ONLY = 42, // Info, not an error. + OEMCrypto_ERROR_ANALOG_OUTPUT = 43, + OEMCrypto_ERROR_WRONG_PST = 44, + OEMCrypto_ERROR_WRONG_KEYS = 45, + OEMCrypto_ERROR_MISSING_MASTER = 46, + OEMCrypto_ERROR_LICENSE_INACTIVE = 47, + OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE = 48, + OEMCrypto_ERROR_ENTRY_IN_USE = 49, + OEMCrypto_ERROR_USAGE_TABLE_UNRECOVERABLE = 50, // Reserved. Do not use. } OEMCryptoResult; /* @@ -235,12 +246,19 @@ typedef struct { typedef enum OEMCrypto_Usage_Entry_Status { kUnused = 0, kActive = 1, - kInactive = 2 + kInactive = 2, // Deprecated. Used kInactiveUsed or kInactiveUnused. + kInactiveUsed = 3, + kInactiveUnused = 4, } OEMCrypto_Usage_Entry_Status; /* * OEMCrypto_PST_Report is used to report an entry from the Usage Table. + * + * Platforms that have compilers that support packed structures, may use the + * following definition. Other platforms may use the header pst_report.h which + * defines a wrapper class. */ +#if 0 // If your compiler supports __attribute__((packed)). typedef struct { uint8_t signature[20]; // -- HMAC SHA1 of the rest of the report. uint8_t status; // current status of entry. (OEMCrypto_Usage_Entry_Status) @@ -252,6 +270,7 @@ typedef struct { int64_t seconds_since_last_decrypt; // now - time_of_last_decrypt uint8_t pst[]; } __attribute__((packed)) OEMCrypto_PST_Report; +#endif /* * OEMCrypto_Clock_Security_Level. @@ -274,12 +293,12 @@ typedef enum RSA_Padding_Scheme { * level, and in GetHDCPCapability for reporting. */ typedef enum OEMCrypto_HDCP_Capability { - HDCP_NONE = 0, // No HDCP supported, no secure data path. - HDCP_V1 = 1, // HDCP version 1.0 - HDCP_V2 = 2, // HDCP version 2.0 - HDCP_V2_1 = 3, // HDCP version 2.1 - HDCP_V2_2 = 4, // HDCP version 2.2 Type 1. - HDCP_NO_DIGITAL_OUTPUT = 0xff // No digital output. + HDCP_NONE = 0, // No HDCP supported, no secure data path. + HDCP_V1 = 1, // HDCP version 1.0 + HDCP_V2 = 2, // HDCP version 2.0 Type 1. + HDCP_V2_1 = 3, // HDCP version 2.1 Type 1. + HDCP_V2_2 = 4, // HDCP version 2.2 Type 1. + HDCP_NO_DIGITAL_OUTPUT = 0xff // No digital output. } OEMCrypto_HDCP_Capability; /* Return value for OEMCrypto_GetProvisioningMethod(). */ @@ -291,6 +310,19 @@ typedef enum OEMCrypto_ProvisioningMethod { OEMCrypto_OEMCertificate = 3 // Device has factory installed OEM certificate. } OEMCrypto_ProvisioningMethod; +/* + * Flags indicating RSA keys supported. + */ +#define OEMCrypto_Supports_RSA_2048bit 0x1 +#define OEMCrypto_Supports_RSA_3072bit 0x2 +#define OEMCrypto_Supports_RSA_CAST 0x10 + +/* + * Flags indicating full decrypt path hash supported. + */ +#define OEMCrypto_Hash_Not_Supported 0 +#define OEMCrypto_HMAC_Clear_Buffer 1 + /* * Obfuscation Renames. */ @@ -324,10 +356,10 @@ typedef enum OEMCrypto_ProvisioningMethod { #define OEMCrypto_GetHDCPCapability_V9 _oecc28 #define OEMCrypto_SupportsUsageTable _oecc29 #define OEMCrypto_UpdateUsageTable _oecc30 -#define OEMCrypto_DeactivateUsageEntry _oecc31 +#define OEMCrypto_DeactivateUsageEntry_V12 _oecc31 #define OEMCrypto_ReportUsage _oecc32 #define OEMCrypto_DeleteUsageEntry _oecc33 -#define OEMCrypto_DeleteUsageTable _oecc34 +#define OEMCrypto_DeleteOldUsageTable _oecc34 #define OEMCrypto_LoadKeys_V9_or_V10 _oecc35 #define OEMCrypto_GenerateRSASignature _oecc36 #define OEMCrypto_GetMaxNumberOfSessions _oecc37 @@ -340,11 +372,27 @@ typedef enum OEMCrypto_ProvisioningMethod { #define OEMCrypto_GetHDCPCapability _oecc44 #define OEMCrypto_LoadTestRSAKey _oecc45 #define OEMCrypto_Security_Patch_Level _oecc46 -#define OEMCrypto_LoadKeys _oecc47 +#define OEMCrypto_LoadKeys_V11_or_V12 _oecc47 #define OEMCrypto_DecryptCENC _oecc48 #define OEMCrypto_GetProvisioningMethod _oecc49 #define OEMCrypto_GetOEMPublicCertificate _oecc50 #define OEMCrypto_RewrapDeviceRSAKey30 _oecc51 +#define OEMCrypto_SupportedCertificates _oecc52 +#define OEMCrypto_IsSRMUpdateSupported _oecc53 +#define OEMCrypto_GetCurrentSRMVersion _oecc54 +#define OEMCrypto_LoadSRM _oecc55 +#define OEMCrypto_LoadKeys _oecc56 +#define OEMCrypto_RemoveSRM _oecc57 +#define OEMCrypto_CreateUsageTableHeader _oecc61 +#define OEMCrypto_LoadUsageTableHeader _oecc62 +#define OEMCrypto_CreateNewUsageEntry _oecc63 +#define OEMCrypto_LoadUsageEntry _oecc64 +#define OEMCrypto_UpdateUsageEntry _oecc65 +#define OEMCrypto_DeactivateUsageEntry _oecc66 +#define OEMCrypto_ShrinkUsageTableHeader _oecc67 +#define OEMCrypto_MoveEntry _oecc68 +#define OEMCrypto_CopyOldUsageEntry _oecc69 +#define OEMCrypto_CreateOldUsageEntry _oecc70 /* @@ -424,7 +472,10 @@ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session); * * Description: * Closes the crypto security engine session and frees any associated - * resources. + * resources. If this session is associated with a Usage Entry, all resident + * memory associated with it will be freed. It is the CDM layer’s + * responsibility to call OEMCrypto_UpdateUsageEntry before closing the + * session. * * Parameters: * session (in) - handle for the session to be closed. @@ -643,6 +694,9 @@ OEMCryptoResult OEMCrypto_GenerateSignature(OEMCrypto_SESSION session, * Refer to document "Widevine Modular DRM Security Integration Guide for * CENC" for details. * + * OEMCrypto may assume that the key_id_length is at most 16. However, + * OEMCrypto shall correctly handle key id lengths from 1 to 16 bytes. + * * OEMCrypto shall handle at least 20 keys per session. This allows a * single license to contain separate keys for 3 key rotations (previous * interval, current interval, next interval) times 4 content keys (audio, @@ -779,7 +833,7 @@ OEMCryptoResult OEMCrypto_LoadKeys( const uint8_t* signature, size_t signature_length, const uint8_t* enc_mac_keys_iv, const uint8_t* enc_mac_keys, size_t num_keys, const OEMCrypto_KeyObject* key_array, const uint8_t* pst, - size_t pst_length); + size_t pst_length, const uint8_t* srm_requirement); /* * OEMCrypto_RefreshKeys @@ -972,10 +1026,22 @@ OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session, * The following checks should be performed if is_encrypted is true. If any * check fails, an error is returned, and no decryption is performed. * - * 1. If the current key’s control block has a nonzero duration field, then - * the API shall verify that the duration is greater than the session’s - * elapsed time clock. If not, return OEMCrypto_ERROR_KEY_EXPIRED. - * + * 1. If the current key's control block has a nonzero Duration field, then + * the API shall verify that the duration is greater than the session's + * elapsed time clock before the key is used. OEMCrypto may return + * OEMCrypto_ERROR_KEY_EXPIRED from OEMCrypto_SelectKey, or SelectKey may + * return success from select key and the decrypt or generic crypto call will + * return OEMCrypto_ERROR_KEY_EXPIRED. + * 2. If the key control block has the bit Disable_Analog_Output set, then + * the device should disable analog video output. If the device has analog + * output that cannot be disabled, then the key is not selected, and + * OEMCrypto_ERROR_ANALOG_OUTPUT is returned. + * 3. If the key control block has HDCP required, and the device cannot + * enforce HDCP, then the key is not selected, and + * OEMCrypto_ERROR_INSUFFICIENT_HDCP is returned. + * 4. If the key control block has a nonzero value for HDCP_Version, and the + * device cannot enforce at least that version of HDCP, then the key is not + * selected, and OEMCrypto_ERROR_INSUFFICIENT_HDCP is returned. * * Parameters: * session (in) - crypto session identifier @@ -997,6 +1063,9 @@ OEMCryptoResult OEMCrypto_QueryKeyControl(OEMCrypto_SESSION session, * OEMCrypto_ERROR_KEY_EXPIRED * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_KEY_EXPIRED + * OEMCrypto_ERROR_ANALOG_OUTPUT + * OEMCrypto_ERROR_INSUFFICIENT_HDCP * * Version: * This method changed in API version 8. @@ -1089,6 +1158,10 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, * subsample has a length that is not a multiple of 16, the final partial * block will be in the clear. * + * A sample may be broken up into a mix of clear and encrypted subsamples. In + * order to support the VP9 standard, the breakup of a subsample into clear + * and encrypted subsamples is not always in pairs. + * * Verification: * The following checks should be performed if is_encrypted is true. If any * check fails, an error is returned, and no decryption is performed. @@ -1101,9 +1174,14 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, * the API shall verify that the output buffer is secure or direct. If not, * return OEMCrypto_ERROR_DECRYPT_FAILED. * - * 3. If the current key’s control block has the HDCP bit set, then the API + * 3. If the current key control block has the bit Disable_Analog_Output set, + * then the device should disable analog video output. If the device has + * analog output that cannot be disabled, then the key is not selected, and + * OEMCrypto_ERROR_ANALOG_OUTPUT is returned. + * + * 4. If the current key’s control block has the HDCP bit set, then the API * shall verify that the buffer will be output using HDCP only. If not, - * return OEMCrypto_ERROR_DECRYPT_FAILED. + * return OEMCrypto_ERROR_INSUFFICIENT_HDCP. * * 4. If the current key’s control block has a nonzero value for * HDCP_Version, then the current version of HDCP for the device and the @@ -1111,8 +1189,9 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, * control block. If the current version is not at least as high as that in * the control block, then return OEMCrypto_ERROR_INSUFFICIENT_HDCP. * - * 5. If the current session has an entry in the Usage Table, and the status - * of that entry is "inactive", then return OEMCrypto_ERROR_INVALID_SESSION. + * 5. If the current session has an entry in the Usage Table, and the status + * of that entry is either kInactiveUsed or kInactiveUnused, then return the + * error OEMCrypto_ERROR_LICENSE_INACTIVE. * * If the flag is_encrypted is false, then no verification is performed. This * call shall copy clear data even when there are no keys loaded, or there is @@ -1160,9 +1239,11 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, * OEMCrypto_ERROR_DECRYPT_FAILED * OEMCrypto_ERROR_KEY_EXPIRED * OEMCrypto_ERROR_INSUFFICIENT_HDCP + * OEMCrypto_ERROR_ANALOG_OUTPUT * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES * OEMCrypto_ERROR_UNKNOWN_FAILURE * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * OEMCrypto_ERROR_LICENSE_INACTIVE * * Buffer Sizes * OEMCrypto shall support subsample sizes (i.e. data_length) of at least @@ -1171,10 +1252,11 @@ OEMCryptoResult OEMCrypto_SelectKey(OEMCrypto_SESSION session, * larger than the supported size. If OEMCrypto returns * OEMCrypto_ERROR_BUFFER_TOO_LARGE, the calling function must break the buffer * into smaller chunks. For high performance devices, OEMCrypto should handle - * larger buffers. + * larger buffers. We encourage OEMCrypto implementers to not artificially + * restrict the maximum buffer size. * * Version: - * This method changed in API version 11. + * This method changed in API version 13. * This method changed its name in API version 11. */ OEMCryptoResult OEMCrypto_DecryptCENC( @@ -1245,12 +1327,13 @@ OEMCryptoResult OEMCrypto_DecryptCENC( * larger than the supported size. If OEMCrypto returns * OEMCrypto_ERROR_BUFFER_TOO_LARGE, the calling function must break the buffer * into smaller chunks. For high performance devices, OEMCrypto should handle - * larger buffers. + * larger buffers. We encourage OEMCrypto implementers to not artificially + * restrict the maximum buffer size. * * Threading * This function may be called simultaneously with any other functions. * Version - * This method is added in API version 10. + * This method changed in API version 12. */ OEMCryptoResult OEMCrypto_CopyBuffer(const uint8_t* data_addr, size_t data_length, @@ -1589,7 +1672,7 @@ OEMCryptoResult OEMCrypto_GetRandom(uint8_t* randomData, size_t dataLength); * 2. Verify that the nonce matches one generated by a previous call to * OEMCrypto_GenerateNonce(). The matching nonce shall be removed from the * nonce table. If there is no matching nonce, return - * OEMCRYPTO_ERROR_INVALID_NONCE. + * OEMCrypto_ERROR_INVALID_NONCE. * 3. Decrypt encrypted_message_key with the OEM certificate’s private RSA key * using RSA-OAEP into the buffer message_key. This message key is a 128 bit * AES key used only in step 4. This message_key should be kept in secure @@ -1716,7 +1799,7 @@ OEMCryptoResult OEMCrypto_RewrapDeviceRSAKey30( * 3. Verify that the nonce matches one generated by a previous call to * OEMCrypto_GenerateNonce(). The matching nonce shall be removed from the * nonce table. If there is no matching nonce, return - * OEMCRYPTO_ERROR_INVALID_NONCE. + * OEMCrypto_ERROR_INVALID_NONCE. * 4. Verify the message signature, using the derived signing key * (mac_key[server]) from a previous call to OEMCrypto_GenerateDerivedKeys. * 5. Decrypt enc_rsa_key in the buffer rsa_key using the derived encryption @@ -2185,7 +2268,7 @@ bool OEMCrypto_SupportsUsageTable(); bool OEMCrypto_IsAntiRollbackHwPresent(); /* - * OEMCRYPTO_GetNumberOfOpenSessions() + * OEMCrypto_GetNumberOfOpenSessions() * * Description: * Returns the current number of open OEMCrypto sessions. The CDM and @@ -2208,7 +2291,7 @@ bool OEMCrypto_IsAntiRollbackHwPresent(); OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count); /* - * OEMCRYPTO_GetMaxNumberOfSessions() + * OEMCrypto_GetMaxNumberOfSessions() * * Description: * Returns the maximum number of concurrent OEMCrypto sessions supported by @@ -2237,6 +2320,42 @@ OEMCryptoResult OEMCrypto_GetNumberOfOpenSessions(size_t* count); */ OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(size_t* max); +/* + * OEMCrypto_SupportedCertificates() + * + * Description: + * Returns the type of certificates keys that this device supports. With very + * few exceptions, all devices should support at least 2048 bit RSA keys. + * High end devices should also support 3072 bit RSA keys. Devices that are + * cast receivers should also support RSA cast receiver certificates. + * + * Parameters: + * none + * + * Threading: + * This function may be called simultaneously with any other functions. + * + * Returns: + * Returns the bitwise or of the following flags. It is likely that high + * end devices will support both 2048 and 3072 bit keys while the widevine + * servers transition to new key sizes. + * + * 0x1 = OEMCrypto_Supports_RSA_2048bit - the device can load a DRM + * certificate with a 2048 bit RSA key. + * + * 0x2 = OEMCrypto_Supports_RSA_3072bit - the device can load a DRM + * certificate with a 3072 bit RSA key. + * + * 0x10 = OEMCrypto_Supports_RSA_CAST - the device can load a CAST + * certificate. These certificate are used with + * OEMCrypto_GenerateRSASignature with padding type set to 0x2, PKCS1 with + * block type 1 padding. + * + * Version: + * This method is added in API version 13. + */ +uint32_t OEMCrypto_SupportedCertificates(); + /* * OEMCrypto_Generic_Encrypt * @@ -2484,51 +2603,23 @@ OEMCryptoResult OEMCrypto_Generic_Verify(OEMCrypto_SESSION session, const uint8_t* signature, size_t signature_length); -/* - * OEMCrypto_UpdateUsageTable - * - * Description: - * OEMCrypto should propagate values from all open sessions to the Session - * Usage Table. If any values have changed, increment the generation number, - * sign, and save the table. During playback, this function will be called - * approximately once per minute. - * - * Devices that do not implement a Session Usage Table may return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. - * - * Parameters: - * none - * - * Threading: - * This function will not be called simultaneously with any session functions. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * - * Version: - * This method changed in API version 9. - */ -OEMCryptoResult OEMCrypto_UpdateUsageTable(); - /* * OEMCrypto_DeactivateUsageEntry * * Description: - * Find the entry in the Usage Table with a matching PST. Mark the status of - * that entry as "inactive". If it corresponds to an open session, the status - * of that session will also be marked as "inactive". Then OEMCrypto will - * increment Usage Table’s generation number, sign, encrypt, and save the - * Usage Table. - * - * If no entry in the Usage Table has a matching PST, return the error - * OEMCrypto_ERROR_INVALID_CONTEXT. + * This deactivates the usage entry associated with the current session. This + * means that the state of the usage entry is changed to InactiveUsed if it + * was Active, or InactiveUnused if it was Unused. This also increments the + * entry's generation number, and the header's master generation number. The + * entry's flag ForbidReport will be set. This flag prevents an application + * from generating a report of a deactivated license without first saving the + * entry. * * Devices that do not implement a Session Usage Table may return * OEMCrypto_ERROR_NOT_IMPLEMENTED. * * Parameters: + * session (in): handle for the session to be used. * pst (in) - pointer to memory containing Provider Session Token. * pst_length (in) - length of the pst, in bytes. * @@ -2548,9 +2639,11 @@ OEMCryptoResult OEMCrypto_UpdateUsageTable(); * too large. * * Version: - * This method changed in API version 9. + * This method changed in API version 13. + * */ -OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t* pst, +OEMCryptoResult OEMCrypto_DeactivateUsageEntry(OEMCrypto_SESSION session, + const uint8_t* pst, size_t pst_length); /* @@ -2560,23 +2653,29 @@ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t* pst, * If the buffer_length is not sufficient to hold a report structure, set * buffer_length and return OEMCrypto_ERROR_SHORT_BUFFER. * - * If no entry in the Usage Table has a matching PST, return the error + * If the an entry was not loaded or created with + * OEMCrypto_CreateNewUsageEntry or OEMCrypto_LoadUsageEntry, or if the pst + * does not match that in the entry, return the error * OEMCrypto_ERROR_INVALID_CONTEXT. * - * OEMCrypto will increment Usage Table’s generation number, sign, encrypt, - * and save the Usage Table. This is done, even though the table has not - * changed, so that a single rollback cannot undo a call to - * DeactivateUsageEntry and still report that license as inactive. + * If the usage entry’s flag ForbidReport is set, indicating the entry has + * not been saved since the entry was deactivated, then the error + * OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE is returned and a report is not + * generated. Similarly, if any key in the session has been used since the + * last call to OEMCrypto_UpdateUsageEntry, then the report is not generated, + * and OEMCrypto returns the error OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE. * - * The pst_report is filled out by subtracting the times un the Usage Table - * from the current time on the secure clock. This is done in case the secure - * clock is not using UTC time, but is instead using something like seconds - * since clock installed. + * The pst_report is filled out by subtracting the times in the Usage Entry + * from the current time on the secure clock. This is done in case the + * secure clock is not using UTC time, but is instead using something like + * seconds since clock installed. * * Valid values for status are: * 0 = kUnused -- the keys have not been used to decrypt. * 1 = kActive -- the keys have been used, and have not been deactivated. - * 2 = kInactive -- the keys have been marked inactive. + * 2 = kInactive -- deprecated. Use kInactiveUsed or kInactiveUnused. + * 3 = kInactiveUsed -- the keys have been marked inactive after a decrypt. + * 4 = kInactiveUnused -- the keys have been marked inactive, no decrypt. * * The clock_security_level is reported as follows: * 0 = Insecure Clock - clock just uses system time. @@ -2619,12 +2718,14 @@ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t* pst, * Returns: * OEMCrypto_SUCCESS success * OEMCrypto_ERROR_SHORT_BUFFER if report buffer is not large enough to hold - * the output signature. + * the output report. * OEMCrypto_ERROR_INVALID_SESSION no open session with that id. - * OEMCrypto_ERROR_INVALID_CONTEXT - no entry has matching PST. * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_INVALID_CONTEXT + * OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE - if no call to UpdateUsageEntry since + * last call to Deactivate or since key use. * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * OEMCrypto_ERROR_WRONG_PST - report asked for wrong pst. * * Buffer Sizes * OEMCrypto shall support pst sizes of at least 255 bytes. @@ -2632,131 +2733,26 @@ OEMCryptoResult OEMCrypto_DeactivateUsageEntry(const uint8_t* pst, * too large. * * Version: - * This method changed in API version 9. + * This method changed in API version 13. */ OEMCryptoResult OEMCrypto_ReportUsage(OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length, - OEMCrypto_PST_Report* buffer, + uint8_t* buffer, size_t* buffer_length); /* - * OEMCrypto_DeleteUsageEntry + * OEMCrypto_DeleteOldUsageTable * * Description: - * This function verifies the signature of the given message using the - * sessions mac_key[server] and the algorithm HMAC-SHA256, and then deletes - * an entry from the session table. The session should already be associated - * with the given entry, from a previous call to OEMCrypto_ReportUsage. + * This function will delete the old usage table, if possible, freeing any + * nonvolatile secure memory. This may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED if the device did not support pre-v13 + * usage tables. * - * After performing all verification listed below, and deleting the entry - * from the Usage Table, OEMCrypto will increment Usage Table’s generation - * number, and then sign, encrypt, and save the Usage Table. - * - * The signature verification shall use a constant-time algorithm (a signature - * mismatch will always take the same time as a successful comparison). - * - * Devices that do not implement a Session Usage Table may return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. - * - * Verification: - * The following checks should be performed. If any check fails, an error is - * returned. - * 1. The pointer pst is not null, and points inside the message. If not, - * return OEMCrypto_ERROR_UNKNOWN_FAILURE. - * 2. The signature of the message shall be computed, and the API shall - * verify the computed signature matches the signature passed in. The - * signature will be computed using HMAC-SHA256 and the mac_key_server. If - * they do not match, return OEMCrypto_ERROR_SIGNATURE_FAILURE. - * 3. If the session is not associated with an entry in the Usage Table, - * return OEMCrypto_ERROR_UNKNOWN_FAILURE. - * 4. If the pst passed in as a parameter does not match that in the Usage - * Table, return OEMCrypto_ERROR_UNKNOWN_FAILURE. - * - * Parameters: - * session (in) - handle for the session to be used. - * pst (in) - pointer to memory containing Provider Session Token. - * pst_length (in) - length of the pst, in bytes. - * message (in) - pointer to memory containing message to be verified. - * message_length (in) - length of the message, in bytes. - * signature (in) - pointer to memory containing the signature. - * signature_length (in) - length of the signature, in bytes. - * - * Threading: - * This function will not be called simultaneously with any session functions. - * - * Returns: - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_INVALID_SESSION no open session with that id. - * OEMCrypto_ERROR_SIGNATURE_FAILURE - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * - * Buffer Sizes - * OEMCrypto shall support pst sizes of at least 255 bytes. - * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is - * too large. - * - * Version: - * This method changed in API version 9. - */ -OEMCryptoResult OEMCrypto_DeleteUsageEntry( - OEMCrypto_SESSION session, const uint8_t* pst, size_t pst_length, - const uint8_t* message, size_t message_length, const uint8_t* signature, - size_t signature_length); - -/* - * OEMCrypto_ForceDeleteUsageEntry - * - * Description: - * This function deletes an entry from the session usage table. This will be - * used for stale entries without a signed request from the server. - * - * After performing all verification listed below, and deleting the entry from - * the Usage Table, OEMCrypto will increment the Usage Table’s generation - * number, and then sign, encrypt, and save the Usage Table. - * - * Devices that do not implement a Session Usage Table may return - * OEMCrypto_ERROR_NOT_IMPLEMENTED. - * - * Verification - * The following checks should be performed. If any check fails, an error is - * returned. - * 1) The pointer pst is not null. If not, return - * OEMCrypto_ERROR_UNKNOWN_FAILURE. - * - * Parameters - * pst (in) - pointer to memory containing Provider Session Token. - * pst_length (in) - length of the pst, in bytes. - * - * Returns - * OEMCrypto_SUCCESS success - * OEMCrypto_ERROR_NOT_IMPLEMENTED - * OEMCrypto_ERROR_UNKNOWN_FAILURE - * OEMCrypto_ERROR_BUFFER_TOO_LARGE - * - * Buffer Sizes - * OEMCrypto shall support pst sizes of at least 255 bytes. - * OEMCrypto shall return OEMCrypto_ERROR_BUFFER_TOO_LARGE if the buffer is - * too large. - * - * Threading - * This function will not be called simultaneously with any session functions. - * - * Version - * This method changed in API version 10. - */ -OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(const uint8_t* pst, - size_t pst_length); - -/* - * OEMCrypto_DeleteUsageTable - * - * Description: - * This is called when the CDM system believes there are major problems or - * resource issues. The entire table should be cleaned and a new table should - * be created. This is the same as calling ForceDeleteUsageEntry on all - * entries. + * This is only needed for devices that are upgrading from a previous version + * of OEMCrypto to v13. Devices that have an existing usage table with + * customer’s offline licenses will use this method to move entries from the + * old table to the new one. * * Parameters: * none @@ -2770,9 +2766,500 @@ OEMCryptoResult OEMCrypto_ForceDeleteUsageEntry(const uint8_t* pst, * OEMCrypto_ERROR_UNKNOWN_FAILURE * * Version: - * This method changed in API version 9. + * This method changed in API version 13. */ -OEMCryptoResult OEMCrypto_DeleteUsageTable(); +OEMCryptoResult OEMCrypto_DeleteOldUsageTable(); + +/* + * OEMCrypto_CreateOldUsageEntry + * + * Description: + * This forces the creation of an entry in the old usage table in order to + * test OEMCrypto_CopyOldUsageTable. OEMCrypto will create a new entry, set + * the status and compute the times at license receive, first decrypt and + * last decrypt. The mac keys will be copied to the entry. The mac keys are + * not encrypted, but will only correspond to a test license. + * + * Devices that have do not support usage tables, or devices that are will + * not be field upgraded to OEMCrypto v13 may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method changed in API version 13. + */ +OEMCryptoResult OEMCrypto_CreateOldUsageEntry(uint64_t time_since_license_received, + uint64_t time_since_first_decrypt, + uint64_t time_since_last_decrypt, + OEMCrypto_Usage_Entry_Status status, + uint8_t *server_mac_key, + uint8_t *client_mac_key, + const uint8_t* pst, + size_t pst_length); + +/* + * OEMCrypto_IsSRMUpdateSupported + * + * Description: + * Returns true if the device supports SRM files and the file can be updated + * via the function OEMCrypto_LoadSRM. This also returns false for devices + * that do not support an SRM file, devices that do not support HDCP, and + * devices that have no external display support. + * + * Parameters: + * none + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * true - if LoadSRM is supported. + * false - otherwise. + * + * Version: + * This method is new in API version 13. + */ +bool OEMCrypto_IsSRMUpdateSupported(); + +/* + * OEMCrypto_GetCurrentSRMVersion + * + * Description: + * Returns the version number of the current SRM file. If the device does + * not support SRM files, this will return OEMCrypto_ERROR_NOT_IMPLEMENTED. + * If the device only supports local displays, it would return + * OEMCrypto_LOCAL_DISPLAY_ONLY. If the device has an SRM, but cannot use + * OEMCrypto to update the SRM, then this function would set version to be + * the current version number, and return OEMCrypto_SUCCESS, but it would + * return false from OEMCrypto_IsSRMUpdateSupported. + * + * Parameters: + * version (out): current SRM version number. + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_SUCCESS + * OEMCrypto_LOCAL_DISPLAY_ONLY - to indicate version was not set, and is not + * needed. + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_GetCurrentSRMVersion(uint16_t* version); + + +/* + * OEMCrypto_LoadSRM + * + * Description: + * Verify and install a new SRM file. The device shall install the new file + * only if verification passes. If verification fails, the existing SRM will + * be left in place. Verification is defined by DCP, and includes + * verification of the SRM’s signature and verification that the SRM version + * number will not be decreased. See the section HDCP SRM Update above for + * more details about the SRM. This function is for devices that support HDCP + * v2.2 or higher and wish to receive 4k content. + * + * Parameters: + * bufer (in): buffer containing the SRM + * buffer_length (in): length of the SRM, in bytes. + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS - if the file was valid and was installed. + * OEMCrypto_ERROR_INVALID_CONTEXT - if the SRM version is too low, or the + * file is corrupted. + * OEMCrypto_ERROR_SIGNATURE_FAILURE - If the signature is invalid. + * OEMCrypto_ERROR_BUFFER_TOO_LARGE - if the buffer is too large for the device. + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_LoadSRM(const uint8_t* buffer, + size_t buffer_length); + +/* + * OEMCrypto_RemoveSRM + * + * Description: + * Delete the current SRM. Any valid SRM, regardless of version number, will + * be installable after this via OEMCrypto_LoadSRM. + * + * This function should not be implemented on production devices, and will + * only be used to verify unit tests on a test device. + * + * Parameters: + * none + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_SUCCESS + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_RemoveSRM(); + +/* + * OEMCrypto_CreateUsageTableHeader + * + * Description: + * This creates a new Usage Table Header with no entries. If there is + * already a generation number stored in secure storage, it will be + * incremented by 1 and used as the new Master Generation Number. This will + * only be called if the CDM layer finds no existing usage table on the file + * system. OEMCrypto will encrypt and sign the new, empty, header and return + * it in the provided buffer. + * + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * Parameters: + * [out] header_buffer: pointer to memory where encrypted usage table header + * is written. + * [in/out] header_buffer_length: (in) length of the header_buffer, in bytes. + * (out) actual length of the header_buffer + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER - if header_buffer_length is too small. + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_CreateUsageTableHeader(uint8_t* header_buffer, + size_t* header_buffer_length); + +/* + * OEMCrypto_LoadUsageTableHeader + * + * Description: + * This loads the Usage Table Header. The buffer’s signature is verified and + * the buffer is decrypted. OEMCrypto will verify the verification + * string. If the Master Generation Number is more than 1 off, the table is + * considered bad, the headers are NOT loaded, and the error + * OEMCrypto_ERROR_GENERATION_SKEW is returned. If the generation number is + * off by 1, the warning OEMCrypto_WARNING_GENERATION_SKEW is returned but + * the header is still loaded. This warning may be logged by the CDM layer. + * + * + * Parameters: + * [in] buffer: pointer to memory containing encrypted usage table header. + * [in] buffert_length: length of the buffer, in bytes. + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER + * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage + * tables. + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_WARNING_GENERATION_SKEW - if the generation number is off by + * exactly 1. + * OEMCrypto_ERROR_GENERATION_SKEW - if the generation number is off by more + * than 1. + * OEMCrypto_ERROR_SIGNATURE_FAILURE - if the signature failed. + * OEMCrypto_ERROR_BAD_MAGIC - verification string does not match. + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_LoadUsageTableHeader(const uint8_t* buffer, + size_t buffer_length); + +/* + * OEMCrypto_CreateNewUsageEntry + * + * Description: + * This creates a new usage entry. The size of the header will be increased + * by 8 bytes, and secure volatile memory will be allocated for it. The new + * entry will be associated with the given session. The status of the new + * entry will be set to “unused”. OEMCrypto will set *usage_entry_number to + * be the index of the new entry. The first entry created will have index 0. + * The new entry will be initialized with a generation number equal to the + * master generation number, which will also be stored in the header’s new + * slot. Then the master generation number will be incremented. Since each + * entry’s generation number is less than the master generation number, the + * new entry will have a generation number that is larger than all other + * entries and larger than all previously deleted entries. This helps + * prevent a rogue application from deleting an entry and then loading an old + * version of it. + * + * Parameters: + * [in] session: handle for the session to be used. + * [out] usage_entry_number: index of new usage entry. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage tables. + * OEMCrypto_ERROR_INSUFFICIENT_RESOURCES - if there is no room in memory to + * increase the size of the usage table header. The CDM layer can delete some + * entries and then try again, or it can pass the error up to the + * application. + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_CreateNewUsageEntry(OEMCrypto_SESSION session, + uint32_t* usage_entry_number); + +/* + * OEMCrypto_LoadUsageEntry + * + * Description: + * This loads a usage table saved previously by UpdateUsageEntry. The + * signature at the beginning of the buffer is verified and the buffer will + * be decrypted. Then the verification field in the entry will be verified. + * The index in the entry must match the index passed in. The generation + * number in the entry will be compared against that in the header. If it is + * off by 1, a warning is returned, but the entry is still loaded. This + * warning may be logged by the CDM layer. If the generation number is off + * by more than 1, an error is returned and the entry is not loaded. + * + * If the entry is already loaded into another session, then this fails and + * returns OEMCrypto_ERROR_INVALID_SESSION. + * + * Parameters: + * [in] session: handle for the session to be used. + * [in] usage_entry_number: index of existing usage entry. + * [in] buffer: pointer to memory containing encrypted usage table entry. + * [in] buffer_length: length of the buffer, in bytes. + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER + * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage + * tables. + * OEMCrypto_ERROR_UNKNOWN_FAILURE - index beyond end of table. + * OEMCrypto_ERROR_INVALID_SESSION - entry associated with another session or + * the index is wrong. + * OEMCrypto_WARNING_GENERATION_SKEW - if the generation number is off by + * exactly 1. + * OEMCrypto_ERROR_GENERATION_SKEW - if the generation number is off by more + * than 1. + * OEMCrypto_ERROR_SIGNATURE_FAILURE - if the signature failed. + * OEMCrypto_ERROR_BAD_MAGIC - verification string does not match. + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_LoadUsageEntry(OEMCrypto_SESSION session, + uint32_t index, + const uint8_t* buffer, + size_t buffer_size); + +/* + * OEMCrypto_UpdateUsageEntry + * + * Description: + * Updates the session’s usage entry and fills buffers with the encrypted and + * signed entry and usage table header. OEMCrypto will update all time and + * status values in the entry, and then increment the entry’s generation + * number. The corresponding generation number in the usage table header is + * also incremented so that it matches the one in the entry. The master + * generation number in the usage table header is incremented and is copied + * to secure persistent storage. OEMCrypto will encrypt and sign the entry + * into the entry_buffer, and it will encrypt and sign the usage table header + * into the header_buffer. Some actions, such as the first decrypt and + * deactivating an entry, will also increment the entry’s generation number + * as well as changing the entry’s status and time fields. As in OEMCrypto + * v12, the first decryption will change the status from Inactive to Active, + * and it will set the time stamp "first decrypt". + * + * If the usage entry has the flag ForbidReport set, then the flag is + * cleared. It is the responsibility of the CDM layer to call this function + * and save the usage table before the next call to ReportUsage and before + * the CDM is terminated. Failure to do so will result in generation number + * skew, which will invalidate all of the usage table. + * + * If either buffer_length is not large enough, they are set to the needed + * size, and OEMCrypto_ERROR_SHORT_BUFFER. In this case, the entry is not + * updated, ForbidReport is not cleared, generation numbers are not + * incremented, and no other work is done. + * + * Parameters: + * [in] session: handle for the session to be used. + * [out] header_buffer: pointer to memory where encrypted usage table header + * is written. + * [in/out] header_buffer_length: (in) length of the header_buffer, in bytes. + * (out) actual length of the header_buffer + * [out] entry_buffer: pointer to memory where encrypted usage table entry is + * written. + * [in/out] buffer_length: (in) length of the entry_buffer, in bytes. + * (out) actual length of the entry_buffer + * + * Threading: + * This function may be called simultaneously with functions on other + * sessions, but not with other functions on this session. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER + * OEMCrypto_ERROR_NOT_IMPLEMENTED - some devices do not implement usage tables. + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_UpdateUsageEntry(OEMCrypto_SESSION session, + uint8_t* header_buffer, + size_t* header_buffer_length, + uint8_t* entry_buffer, + size_t* entry_buffer_length); + +/* + * OEMCrypto_ShrinkUsageTableHeader + * + * Description: + * This shrinks the usage table and the header. This function is used by the + * CDM layer after it has defragmented the usage table and can delete unused + * entries. It is an error if any open session is associated with an entry + * that will be erased. If new_table_size is larger than the current size, + * then the header is not changed and the error is returned. If the header + * has not been previously loaded, then an error is returned. OEMCrypto will + * increment the master generation number in the header and store the new + * value in secure persistent storage. Then, OEMCrypto will encrypt and sign + * the header into the provided buffer. The generation numbers of all + * remaining entries will remain unchanged. The next time + * OEMCrypto_CreateNewUsageEntry is called, the new entry will have an index + * of new_table_size. + * + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * If header_buffer_length is not large enough to hold the new table, it is + * set to the needed value, the generation number is not incremented, and + * OEMCrypto_ERROR_SHORT_BUFFER is returned. + * + * Parameters: + * [in] new_entry_count: number of entries in the to be in the header. + * [out] header_buffer: pointer to memory where encrypted usage table header + * is written. + * [in/out] header_buffer_length: (in) length of the header_buffer, in bytes. + * (out) actual length of the header_buffer + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_SHORT_BUFFER + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(uint32_t new_entry_count, + uint8_t* header_buffer, + size_t* header_buffer_length); + +/* + * OEMCrypto_MoveEntry + * + * Description: Moves the entry associated with the current session from one + * location in the usage table header to another. This function is used by + * the CDM layer to defragment the usage table. This does not modify any data + * in the entry, except the index and the generation number. The index in + * the session’s usage entry will be changed to new_index. The generation + * number in session’s usage entry and in the header for new_index will be + * increased to the master generation number, and then the master generation + * number is incremented. If there was an existing entry at the new location, + * it will be overwritten. It is an error to call this when the entry that + * was at new_index is associated with a currently open session. In this + * case, the error code OEMCrypto_ERROR_ENTRY_IN_USE is returned. It is the + * CDM layer’s responsibility to call UpdateUsageEntry after moving an entry. + * It is an error for new_index to be beyond the end of the existing usage + * table header. + * + * Devices that do not implement a Session Usage Table may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * Parameters: + * [in] session: handle for the session to be used. + * [in] new_index: new index to be used for the session’s usage entry + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * OEMCrypto_ERROR_BUFFER_TOO_LARGE + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_MoveEntry(OEMCrypto_SESSION session, + uint32_t new_index); + +/* + * OEMCrypto_CopyOldUsageEntry + * + * Description: + * This function copies an entry from the old v12 table to the new table. + * The new entry will already have been loaded by CreateNewUsageEntry. If + * the device did not support pre-v13 usage tables, this may return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * This is only needed for devices that are upgrading from a previous version + * of OEMCrypto to v13. Devices that have an existing usage table with + * customer’s offline licenses will use this method to move entries from the + * old table to the new one. + * + * Parameters: + * [in] session: handle for the session to be used. + * [in] pst: pointer to memory containing Provider Session Token. + * [in] pst_length: length of the pst, in bytes. + * + * Threading: + * This function will not be called simultaneously with any session functions. + * + * Returns: + * OEMCrypto_SUCCESS success + * OEMCrypto_ERROR_NOT_IMPLEMENTED + * OEMCrypto_ERROR_UNKNOWN_FAILURE + * + * Version: + * This method is new in API version 13. + */ +OEMCryptoResult OEMCrypto_CopyOldUsageEntry(OEMCrypto_SESSION session, + const uint8_t*pst, + size_t pst_length); #ifdef __cplusplus } diff --git a/oemcrypto/include/level3_file_system.h b/oemcrypto/include/level3_file_system.h new file mode 100644 index 00000000..c1474add --- /dev/null +++ b/oemcrypto/include/level3_file_system.h @@ -0,0 +1,29 @@ +// Copyright 2017 Google Inc. All Rights Reserved + +/********************************************************************* + * level3_file_system.h + * + * File system for OEMCrypto Level3 file operations. + *********************************************************************/ + +#ifndef LEVEL3_FILE_SYSTEM_H_ +#define LEVEL3_FILE_SYSTEM_H_ + +#include + +namespace wvoec3 { + +class OEMCrypto_Level3FileSystem { + public: + virtual ~OEMCrypto_Level3FileSystem() {} + virtual ssize_t Read(const char *filename, void *buffer, size_t size) = 0; + virtual ssize_t Write(const char *filename, const void *buffer, + size_t size) = 0; + virtual bool Exists(const char *filename) = 0; + virtual ssize_t FileSize(const char *filename) = 0; + virtual bool Remove(const char *filename) = 0; +}; + +} // namespace wvoec3 + +#endif diff --git a/oemcrypto/include/level3_file_system_factory.h b/oemcrypto/include/level3_file_system_factory.h new file mode 100644 index 00000000..7f1c2fd7 --- /dev/null +++ b/oemcrypto/include/level3_file_system_factory.h @@ -0,0 +1,22 @@ +// Copyright 2017 Google Inc. All Rights Reserved + +/********************************************************************* + * level3_file_system_factory.h + * + * Creates a single OEMCrypto_Level3FileSystem. + *********************************************************************/ + +#ifndef LEVEL3_FILE_SYSTEM_FACTORY_H_ +#define LEVEL3_FILE_SYSTEM_FACTORY_H_ + +#include "level3_file_system.h" + +namespace wvoec3 { + +OEMCrypto_Level3FileSystem* createLevel3FileSystem(); + +void deleteLevel3FileSystem(OEMCrypto_Level3FileSystem* file_system); + +} + +#endif // LEVEL3_FILE_SYSTEM_FACTORY_H_ \ No newline at end of file diff --git a/oemcrypto/include/oemcrypto_logging.h b/oemcrypto/include/oemcrypto_logging.h index ec09e6aa..58e688ed 100644 --- a/oemcrypto/include/oemcrypto_logging.h +++ b/oemcrypto/include/oemcrypto_logging.h @@ -6,8 +6,8 @@ #include #include -#include "log.h" #include "OEMCryptoCENC.h" +#include "log.h" namespace wvoec_mock { @@ -20,13 +20,14 @@ namespace wvoec_mock { // LogCategoryEnabled(category1 | category2) will return true if // category1 and/or category2 are set to logging. -const int kLoggingTraceOEMCryptoCalls = 0x01; +const int kLoggingTraceOEMCryptoCalls = 0x01; // All except decrypt calls. const int kLoggingDumpContentKeys = 0x02; const int kLoggingDumpKeyControlBlocks = 0x04; const int kLoggingDumpDerivedKeys = 0x08; const int kLoggingTraceNonce = 0x10; const int kLoggingTraceDecryption = 0x20; const int kLoggingTraceUsageTable = 0x40; +const int kLoggingTraceDecryptCalls = 0x80; const int kLoggingDumpTraceAll = 0xFF; void SetLoggingSettings(int level, int categories); @@ -56,10 +57,9 @@ void dump_array_part_helper(std::string& buffer, std::string array, size_t index, std::string name, const uint8_t* vector, size_t length); -void dump_array_part(std::string array, size_t index, - std::string name, const uint8_t* vector, size_t length); +void dump_array_part(std::string array, size_t index, std::string name, + const uint8_t* vector, size_t length); } // namespace wvoec_mock -#endif - +#endif // WVOEC_OEMCRYPTO_LOGGING_H_ diff --git a/oemcrypto/include/pst_report.h b/oemcrypto/include/pst_report.h new file mode 100644 index 00000000..cd21e234 --- /dev/null +++ b/oemcrypto/include/pst_report.h @@ -0,0 +1,146 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +/********************************************************************* + * pst_report.h + * + * Reference APIs needed to support Widevine's crypto algorithms. + *********************************************************************/ + +#ifndef PST_REPORT_H_ +#define PST_REPORT_H_ + +#include +#include +#include + +#include "OEMCryptoCENC.h" +#include "string_conversions.h" // needed for htonll64. + +namespace wvcdm { + +class Unpacked_PST_Report { + public: + // This object does not own the buffer, and does not check that buffer + // is not null. + Unpacked_PST_Report(uint8_t *buffer) : buffer_(buffer) {} + + // Copy and move semantics of this class is like that of a pointer. + Unpacked_PST_Report(const Unpacked_PST_Report& other) : + buffer_(other.buffer_) {} + + Unpacked_PST_Report& operator=(const Unpacked_PST_Report& other) { + buffer_ = other.buffer_; + return *this; + } + + size_t report_size() const { + return pst_length() + kraw_pst_report_size; + } + + static size_t report_size(size_t pst_length) { + return pst_length + kraw_pst_report_size; + } + + uint8_t status() const { + return static_cast(* (buffer_ + kstatus_offset)); + } + + void set_status(uint8_t value) { + buffer_[kstatus_offset] = value; + } + + uint8_t* signature() { + return buffer_ + ksignature_offset; + } + + uint8_t clock_security_level() const { + return static_cast(* (buffer_ + kclock_security_level_offset)); + } + + void set_clock_security_level(uint8_t value) { + buffer_[kclock_security_level_offset] = value; + } + + uint8_t pst_length() const { + return static_cast(* (buffer_ + kpst_length_offset)); + } + + void set_pst_length(uint8_t value) { + buffer_[kpst_length_offset] = value; + } + + uint8_t padding() const { + return static_cast(* (buffer_ + kpadding_offset)); + } + + void set_padding(uint8_t value) { + buffer_[kpadding_offset] = value; + } + + // In host byte order. + int64_t seconds_since_license_received() const { + int64_t time; + memcpy(&time, buffer_ + kseconds_since_license_received_offset, + sizeof(int64_t)); + return wvcdm::ntohll64(time); + } + + // Parameter time is in host byte order. + void set_seconds_since_license_received(int64_t time) const { + time = wvcdm::ntohll64(time); + memcpy(buffer_ + kseconds_since_license_received_offset, &time, + sizeof(int64_t)); + } + + // In host byte order. + int64_t seconds_since_first_decrypt() const { + int64_t time; + memcpy(&time, buffer_ + kseconds_since_first_decrypt_offset, + sizeof(int64_t)); + return wvcdm::ntohll64(time); + } + + // Parameter time is in host byte order. + void set_seconds_since_first_decrypt(int64_t time) const { + time = wvcdm::ntohll64(time); + memcpy(buffer_ + kseconds_since_first_decrypt_offset, &time, + sizeof(int64_t)); + } + + // In host byte order. + int64_t seconds_since_last_decrypt() const { + int64_t time; + memcpy(&time, buffer_ + kseconds_since_last_decrypt_offset, + sizeof(int64_t)); + return wvcdm::ntohll64(time); + } + + // Parameter time is in host byte order. + void set_seconds_since_last_decrypt(int64_t time) const { + time = wvcdm::ntohll64(time); + memcpy(buffer_ + kseconds_since_last_decrypt_offset, &time, + sizeof(int64_t)); + } + + uint8_t* pst() { + return (buffer_ + kpst_offset); + } + + private: + uint8_t *buffer_; + + // Size of the PST_Report without the pst string. + static const size_t kraw_pst_report_size = 48; + static const size_t ksignature_offset = 0; + static const size_t kstatus_offset = 20; + static const size_t kclock_security_level_offset = 21; + static const size_t kpst_length_offset = 22; + static const size_t kpadding_offset = 23; + static const size_t kseconds_since_license_received_offset = 24; + static const size_t kseconds_since_first_decrypt_offset = 32; + static const size_t kseconds_since_last_decrypt_offset = 40; + static const size_t kpst_offset = 48; +}; +} // namespace wvcdm + +#endif // PST_REPORT_H_ diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/0ac99ac6565414c7f57a36bcf0c212327cc88ab3 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/0ac99ac6565414c7f57a36bcf0c212327cc88ab3 new file mode 100644 index 00000000..14f9e284 Binary files /dev/null and b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/0ac99ac6565414c7f57a36bcf0c212327cc88ab3 differ diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/25adc5d13d39231afeb8ed3da76a18f9658c681a b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/25adc5d13d39231afeb8ed3da76a18f9658c681a new file mode 100644 index 00000000..ed6ddac6 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/25adc5d13d39231afeb8ed3da76a18f9658c681a @@ -0,0 +1 @@ +(c020:0d112d7ea200; \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/32556c2f870258f2b18c905c3cd017d7064927d7 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/32556c2f870258f2b18c905c3cd017d7064927d7 new file mode 100644 index 00000000..3ff95036 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/32556c2f870258f2b18c905c3cd017d7064927d7 @@ -0,0 +1 @@ +(e2!0;u \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/3cec58166305818af41d10666b1538024cfbe4ec b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/3cec58166305818af41d10666b1538024cfbe4ec new file mode 100644 index 00000000..e453ee98 Binary files /dev/null and b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/3cec58166305818af41d10666b1538024cfbe4ec differ diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/3d152b926d295b49d73a968d1668c0b9125dd2da b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/3d152b926d295b49d73a968d1668c0b9125dd2da new file mode 100644 index 00000000..b2cb12e4 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/3d152b926d295b49d73a968d1668c0b9125dd2da @@ -0,0 +1 @@ +0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202fb02570640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231180120002a0c313838363738373430350000 diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4879965ec6be329dcc7697d913b2e8971a9729d8 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4879965ec6be329dcc7697d913b2e8971a9729d8 new file mode 100644 index 00000000..b1596ccd --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4879965ec6be329dcc7697d913b2e8971a9729d8 @@ -0,0 +1 @@ +(2dea200;u \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4cd2f2c92b644ee1284cd082feb8e6773499ebe7 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4cd2f2c92b644ee1284cd082feb8e6773499ebe7 new file mode 100644 index 00000000..04a910cf --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4cd2f2c92b644ee1284cd082feb8e6773499ebe7 @@ -0,0 +1 @@ +0a4c020:0d1190d79fef02570640bd22ef44b2d7e3912250a200 diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4d39a8df58267539e4db62ef45bda5d9573b00a4 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4d39a8df58267539e4db62ef45bda5d9573b00a4 new file mode 100644 index 00000000..2a27021b --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/4d39a8df58267539e4db62ef45bda5d9573b00a4 @@ -0,0 +1 @@ +e2!0;u \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/58e6b3a414a1e090dfc6029add0f3555ccba127f b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/58e6b3a414a1e090dfc6029add0f3555ccba127f new file mode 100644 index 00000000..9cbe6ea5 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/58e6b3a414a1e090dfc6029add0f3555ccba127f @@ -0,0 +1 @@ +e \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/655668f52bfd904b9f658280d3f144491f1d2a36 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/655668f52bfd904b9f658280d3f144491f1d2a36 new file mode 100644 index 00000000..feb5341d --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/655668f52bfd904b9f658280d3f144491f1d2a36 @@ -0,0 +1 @@ +(ea200;u \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/6923a33a0bb5c0694734b3063ecb212aa7873f5d b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/6923a33a0bb5c0694734b3063ecb212aa7873f5d new file mode 100644 index 00000000..7ac6fcd2 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/6923a33a0bb5c0694734b3063ecb212aa7873f5d @@ -0,0 +1 @@ +0a(c020:0d112d7ea200; \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/71e064e7c3959c15c4b39d22e836fbfdc6b046b5 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/71e064e7c3959c15c4b39d22e836fbfdc6b046b5 new file mode 100644 index 00000000..e91578df --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/71e064e7c3959c15c4b39d22e836fbfdc6b046b5 @@ -0,0 +1 @@ +0a4c000000200:0101907d9ffde02570640bd22ef44b2d7e3912250a230a1407363534333231180120002a0c313838363738373430350000 diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/8edb36d75f26dc46aae4520b02deea1a645cfbc3 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/8edb36d75f26dc46aae4520b02deea1a645cfbc3 new file mode 100644 index 00000000..a4f7ea59 Binary files /dev/null and b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/8edb36d75f26dc46aae4520b02deea1a645cfbc3 differ diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/a121ddeb222a383990a85dfd75c93e3db6630a41 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/a121ddeb222a383990a85dfd75c93e3db6630a41 new file mode 100644 index 00000000..679be986 Binary files /dev/null and b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/a121ddeb222a383990a85dfd75c93e3db6630a41 differ diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ad95e413da295fa777257154ed40dfcd8e32ba2b b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ad95e413da295fa777257154ed40dfcd8e32ba2b new file mode 100644 index 00000000..53608711 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ad95e413da295fa777257154ed40dfcd8e32ba2b @@ -0,0 +1 @@ +0a4c000000220:01019dd79fef02570640bd22ef44b2d7e3912250a200 diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/b65818237816f961d1138ef352d0b905d3eb9330 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/b65818237816f961d1138ef352d0b905d3eb9330 new file mode 100644 index 00000000..20673e13 Binary files /dev/null and b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/b65818237816f961d1138ef352d0b905d3eb9330 differ diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/bcadfd6aacc62927bdb3e0a9f04b9aa11c192b6d b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/bcadfd6aacc62927bdb3e0a9f04b9aa11c192b6d new file mode 100644 index 00000000..8d6bcfc8 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/bcadfd6aacc62927bdb3e0a9f04b9aa11c192b6d @@ -0,0 +1 @@ +e; \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/c6f546c378e78c5a74b45ce792c88f925f34000f b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/c6f546c378e78c5a74b45ce792c88f925f34000f new file mode 100644 index 00000000..39ca47b1 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/c6f546c378e78c5a74b45ce792c88f925f34000f @@ -0,0 +1 @@ +0a4c000000200:010197d9ffde02570640bd22ef44b2d7e3912250a230a1407363534333231180120002a0c313838363738373430350000 diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d6b1a032205b2b9ddeced35d07a9d7c7f27bbef2 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d6b1a032205b2b9ddeced35d07a9d7c7f27bbef2 new file mode 100644 index 00000000..f6d90163 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d6b1a032205b2b9ddeced35d07a9d7c7f27bbef2 @@ -0,0 +1 @@ +0a4c00000020000101907d9ffde02570640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231180120002a0c313838363738373430350000 diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d7d4218b6a3c59f80d1fe0ece07ca13e5a2c209c b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d7d4218b6a3c59f80d1fe0ece07ca13e5a2c209c new file mode 100644 index 00000000..ad452495 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d7d4218b6a3c59f80d1fe0ece07ca13e5a2c209c @@ -0,0 +1 @@ +0a4c020:0d112d7e3912250a200; \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d992e60874495b187bc157e0f24d4fd8ad957094 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d992e60874495b187bc157e0f24d4fd8ad957094 new file mode 100644 index 00000000..7cea4d64 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/d992e60874495b187bc157e0f24d4fd8ad957094 @@ -0,0 +1 @@ +0a4c08001248000000020000101907d9ffde02570640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231180120002a0c313838363738373430350000 diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/e7064f0b80f61dbc65915311032d27baa569ae2a b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/e7064f0b80f61dbc65915311032d27baa569ae2a new file mode 100644 index 00000000..e8a0f876 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/e7064f0b80f61dbc65915311032d27baa569ae2a @@ -0,0 +1 @@ +) \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ead485157c8b58596faa57d6c0818e6c5b652a9e b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ead485157c8b58596faa57d6c0818e6c5b652a9e new file mode 100644 index 00000000..44f8acb5 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ead485157c8b58596faa57d6c0818e6c5b652a9e @@ -0,0 +1 @@ +e2; \ No newline at end of file diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ecca89819d61866b1f41e756edc08510f8f70747 b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ecca89819d61866b1f41e756edc08510f8f70747 new file mode 100644 index 00000000..508d4362 Binary files /dev/null and b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/ecca89819d61866b1f41e756edc08510f8f70747 differ diff --git a/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/example.txt b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/example.txt new file mode 100644 index 00000000..be5ddd83 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/GenerateSignatureCorpus/example.txt @@ -0,0 +1 @@ +0a4c08001248000000020000101907d9ffde13aa95c122678053362136bdf8408f8276e4c2d87ec52b61aa1b9f646e58734930acebe899b3e464189a14a87202fb02574e70640bd22ef44b2d7e3912250a230a14080112100915007caa9b5931b76a3a85f046523e10011a09393837363534333231180120002a0c313838363738373430350000 diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gyp b/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gyp new file mode 100644 index 00000000..f07ae140 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gyp @@ -0,0 +1,25 @@ +# Copyright 2017 Google Inc. All Rights Reserved. +# +# Builds under the CDM ./build.py (target platform) build system +# Refer to the distribution package's README for details. +{ + 'variables': { + 'oemcrypto_lib%': '', + 'oemcrypto_stubs%': '', + 'openssl_config%': 'system', + 'openssl_target%': '', + }, + 'targets': [ + { + 'target_name': 'wv_ce_cdm_oemcrypto_generate_signature_fuzz_test', + 'type': 'executable', + 'sources': [ + # The test runner and the testing device certificate. + 'oemcrypto_generate_signature.cc', + ], + 'includes': [ + 'oemcrypto_fuzztests.gypi', + ], + }, + ], +} diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gypi b/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gypi new file mode 100644 index 00000000..9c8373ca --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_fuzztests.gypi @@ -0,0 +1,54 @@ +# Copyright 2017 Google Inc. All Rights Reserved. +# +# Include this in any custom unit test targets. +# Does not include the test runner main. +{ + 'sources': [ + '../oec_device_features.cpp', + '../oec_session_util.cpp', + '../oemcrypto_session_tests_helper.cpp', + '../oemcrypto_session_tests_helper.h', + '../../../cdm/test/device_cert.cpp', + '../../../cdm/test/device_cert.h', + ], + 'include_dirs': [ + '../../../core/include', # log.h + '../../include', + '../../mock/src', # oemcrypto_key_mock.h + '../', + '../../../cdm/test', + ], + 'defines': [ + 'OEMCRYPTO_TESTS', + 'OEMCRYPTO_FUZZ_TESTS', + ], + 'libraries': [ + '../../../third_party/fuzz/platforms/x86-64/libFuzzer.a', + '-lpthread', # gtest + ], + 'dependencies': [ + '../../../cdm/cdm.gyp:widevine_ce_cdm_shared', + '../../../third_party/gmock.gyp:gmock', + '../../../third_party/gmock.gyp:gtest', + ], + 'conditions': [ + ['oemcrypto_stubs!=""', { + 'dependencies': [ + '../../stubs/stubs.gyp:oec_stubs_v<(oemcrypto_version)', + ], + }, { + 'conditions': [ + ['oemcrypto_lib==""', { + 'dependencies': [ + '../../mock/oec_mock.gyp:oec_mock', + ], + }, { + 'libraries': [ + '../../../third_party/fuzz/platforms/x86-64/libFuzzer.a', + '<(oemcrypto_lib)', + ], + }], + ], + }], + ], +} diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_generate_signature.cc b/oemcrypto/test/fuzz_tests/oemcrypto_generate_signature.cc new file mode 100644 index 00000000..f5227c3c --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_generate_signature.cc @@ -0,0 +1,38 @@ +#include "properties.h" +#include "oemcrypto_session_tests_helper.h" + +using namespace wvoec; + +static bool is_init = false; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + SessionUtil session_helper; + if (!is_init) { + bool is_cast_receiver = false; + bool force_load_test_keybox = false; + wvoec::global_features.Initialize(is_cast_receiver, force_load_test_keybox); + wvoec::global_features.RestrictFilter("*"); + wvcdm::Properties::Init(); + is_init = true; + } + + OEMCrypto_Initialize(); + session_helper.EnsureTestKeys(); + + Session s; + s.open(); + s.GenerateDerivedKeysFromKeybox(); + + static const uint32_t SignatureBufferMaxLength = size; + vector signature(SignatureBufferMaxLength); + size_t signature_length = signature.size(); + + OEMCryptoResult sts; + sts = OEMCrypto_GenerateSignature(s.session_id(), data, size, + &signature[0], &signature_length); + + s.close(); + OEMCrypto_Terminate(); + + return 0; +} diff --git a/oemcrypto/test/fuzz_tests/platforms/x86-64/fuzzer_settings.gypi b/oemcrypto/test/fuzz_tests/platforms/x86-64/fuzzer_settings.gypi new file mode 100644 index 00000000..528ab111 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/platforms/x86-64/fuzzer_settings.gypi @@ -0,0 +1,76 @@ +# Copyright 2017 Google Inc. All rights reserved. +{ + # Here you can set platform-specific compiler settings. + 'target_defaults': { + # These are flags passed to the compiler for all C & C++ files. + 'cflags': [ + '-fsanitize=address', + '-fsanitize-coverage=trace-pc-guard', + '-fPIC', + ], + + # These are flags passed to the compiler for plain C only. + 'cflags_c': [ + '-fsanitize-coverage=trace-pc-guard', + '-fsanitize=address', + '-fPIC', + ], + + # These are flags passed to the compiler for C++ only. + 'cflags_cc': [ + '-fsanitize-coverage=trace-pc-guard', + '-fsanitize=address', + '-fPIC', + ], + + # These are flags passed to the linker. + 'ldflags': [ + '-fsanitize=address', + ], + + # These are macros set by the compiler. + 'defines': [ + #'EXAMPLE_MACRO_WITH_NO_VALUE', + #'EXAMPLE_KEY=EXAMPLE_VALUE', + ], + + # These are additional include paths to search for headers. + 'include_dirs': [ + ], + + 'target_conditions': [ + ['_toolset == "host"', { + # These are settings specifically for the host toolchain. + # The extra equals sign in the key name instructs gyp to replace + # the generic settings above rather than append to them. + 'cflags=': [ + '-fsanitize-coverage=trace-pc-guard', + '-fsanitize=address', + '-fPIC', + ], + + 'cflags_c=': [ + '-fsanitize-coverage=trace-pc-guard', + '-fsanitize=address', + '-fPIC', + ], + + 'cflags_cc=': [ + '-fsanitize-coverage=trace-pc-guard', + '-fsanitize=address', + '-fPIC', + ], + + 'ldflags=': [ + '-fsanitize=address', + ], + + 'defines=': [ + ], + + 'include_dirs=': [ + ], + }], # end _toolset == "host" condition + ], # end target_conditions + }, # end target_defaults +} diff --git a/oemcrypto/test/fuzz_tests/sample_test.cc b/oemcrypto/test/fuzz_tests/sample_test.cc new file mode 100644 index 00000000..68903939 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/sample_test.cc @@ -0,0 +1,10 @@ +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size > 0 && data[0] == 'H') + if (size > 1 && data[1] == 'I') + if (size > 2 && data[2] == '!') + __builtin_trap(); + return 0; +} diff --git a/oemcrypto/test/oec_device_features.cpp b/oemcrypto/test/oec_device_features.cpp index 119c0be8..33d76024 100644 --- a/oemcrypto/test/oec_device_features.cpp +++ b/oemcrypto/test/oec_device_features.cpp @@ -8,10 +8,12 @@ #include -#include "oec_test_data.h" +#include "test_keybox.h" namespace wvoec { +using namespace wvcdm_test_auth; + DeviceFeatures global_features; void DeviceFeatures::Initialize(bool is_cast_receiver, @@ -22,6 +24,7 @@ void DeviceFeatures::Initialize(bool is_cast_receiver, loads_certificate = false; generic_crypto = false; usage_table = false; + supports_rsa_3072 = false; api_version = 0; derive_key_method = NO_METHOD; if (OEMCrypto_SUCCESS != OEMCrypto_Initialize()) { @@ -70,13 +73,24 @@ void DeviceFeatures::Initialize(bool is_cast_receiver, OEMCrypto_CloseSession(session); api_version = OEMCrypto_APIVersion(); printf("api_version = %d.\n", api_version); - usage_table = OEMCrypto_SupportsUsageTable(); + // These unit tests only work with new usage tables. We do not test v12 + // usage tables. + if (api_version > 12) usage_table = OEMCrypto_SupportsUsageTable(); printf("usage_table = %s.\n", usage_table ? "true" : "false"); if (force_load_test_keybox) { derive_key_method = FORCE_TEST_KEYBOX; } else { PickDerivedKey(); } + if (api_version >= 13) { + uint32_t supported_cert = OEMCrypto_SupportedCertificates(); + if (supported_cert & OEMCrypto_Supports_RSA_CAST) { + cast_receiver = true; + } + if (supported_cert & OEMCrypto_Supports_RSA_3072bit) { + supports_rsa_3072 = true; + } + } printf("cast_receiver = %s.\n", cast_receiver ? "true" : "false"); switch (derive_key_method) { case NO_METHOD: @@ -120,9 +134,12 @@ std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) { if (derive_key_method == NO_METHOD) FilterOut(&filter, "*SessionTest*"); if (provisioning_method != OEMCrypto_OEMCertificate) FilterOut(&filter, "*Prov30*"); + if (!supports_rsa_3072) FilterOut(&filter, "*RSAKey3072*"); + if (api_version < 9) FilterOut(&filter, "*API09*"); if (api_version < 10) FilterOut(&filter, "*API10*"); if (api_version < 11) FilterOut(&filter, "*API11*"); if (api_version < 12) FilterOut(&filter, "*API12*"); + if (api_version < 13) FilterOut(&filter, "*API13*"); // Performance tests take a long time. Filter them out if they are not // specifically requested. if (filter.find("Performance") == std::string::npos) { @@ -170,17 +187,18 @@ bool DeviceFeatures::IsTestKeyboxInstalled() { size_t key_data_len = sizeof(key_data); if (OEMCrypto_GetKeyData(key_data, &key_data_len) != OEMCrypto_SUCCESS) return false; - if (key_data_len != sizeof(kTestKeybox.data_)) return false; - if (memcmp(key_data, kTestKeybox.data_, key_data_len)) return false; + if (key_data_len != kKeyboxDataSize) return false; + if (memcmp(key_data, &kKeybox[kKeyboxDataOffset], key_data_len)) return false; uint8_t dev_id[128] = {0}; size_t dev_id_len = 128; if (OEMCrypto_GetDeviceID(dev_id, &dev_id_len) != OEMCrypto_SUCCESS) return false; // We use strncmp instead of memcmp because we don't really care about the // multiple '\0' characters at the end of the device id. - return 0 == strncmp(reinterpret_cast(dev_id), - reinterpret_cast(kTestKeybox.device_id_), - sizeof(kTestKeybox.device_id_)); + return 0 == strncmp( + reinterpret_cast(dev_id), + reinterpret_cast(&kKeybox[kKeyboxDeviceIdOffset]), + kKeyboxDeviceIdSize); } void DeviceFeatures::FilterOut(std::string* current_filter, @@ -203,6 +221,7 @@ const char* ProvisioningMethodName(OEMCrypto_ProvisioningMethod method) { case OEMCrypto_OEMCertificate: return "OEMCrypto_OEMCertificate"; } + // Not reachable return ""; } diff --git a/oemcrypto/test/oec_device_features.h b/oemcrypto/test/oec_device_features.h index c1fd2d2d..becea2af 100644 --- a/oemcrypto/test/oec_device_features.h +++ b/oemcrypto/test/oec_device_features.h @@ -26,6 +26,7 @@ class DeviceFeatures { bool generic_crypto; // Device supports generic crypto. bool cast_receiver; // Device supports alternate rsa signature padding. bool usage_table; // Device saves usage information. + bool supports_rsa_3072; // Device supports 3072 bit RSA keys. uint32_t api_version; OEMCrypto_ProvisioningMethod provisioning_method; diff --git a/oemcrypto/test/oec_session_util.cpp b/oemcrypto/test/oec_session_util.cpp index a57bf2dc..4306d49b 100644 --- a/oemcrypto/test/oec_session_util.cpp +++ b/oemcrypto/test/oec_session_util.cpp @@ -23,8 +23,8 @@ #include "OEMCryptoCENC.h" #include "log.h" #include "oec_device_features.h" -#include "oec_test_data.h" #include "oemcrypto_key_mock.h" +#include "test_rsa_key.h" #include "string_conversions.h" #include "wv_cdm_constants.h" #include "wv_cdm_types.h" @@ -35,18 +35,31 @@ using namespace std; // GTest requires PrintTo to be in the same namespace as the thing it prints, // which is std::vector in this case. namespace std { - void PrintTo(const vector& value, ostream* os) { *os << wvcdm::b2a_hex(value); } +} // namespace std -void PrintTo(const PatternTestVariant& param, ostream* os) { - *os << ((param.mode == OEMCrypto_CipherMode_CTR) ? "CTR mode" : "CBC mode") - << ", encrypt=" << param.pattern.encrypt - << ", skip=" << param.pattern.skip; +namespace { +int GetRandBytes(unsigned char* buf, int num) { + // returns 1 on success, -1 if not supported, or 0 if other failure. +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + return RAND_pseudo_bytes(buf, num); +#else + return RAND_bytes(buf, num); +#endif } -} // namespace std +#ifdef OPENSSL_IS_BORINGSSL +void DeleteX509Stack(STACK_OF(X509)* stack) { + sk_X509_pop_free(stack, X509_free); +} + +typedef size_t X509Count; +#else +typedef int X509Count; +#endif +} // namespace namespace wvoec { @@ -82,7 +95,7 @@ class openssl_ptr { T& operator*() const { return *ptr_; } T* operator->() const { return ptr_; } T* get() const { return ptr_; } - bool NotNull() { return ptr_; } + bool NotNull() const { return ptr_; } private: T* ptr_; @@ -181,8 +194,10 @@ void Session::GenerateDerivedKeysFromKeybox() { // with test keybox "installed". mac_key_server_ = wvcdm::a2b_hex( "3CFD60254786AF350B353B4FBB700AB382558400356866BA16C256BCD8C502BF"); + mac_key_client_ = wvcdm::a2b_hex( "A9DE7B3E4E199ED8D1FBC29CD6B4C772CC4538C8B0D3E208B3E76F2EC0FD6F47"); + enc_key_ = wvcdm::a2b_hex("D0BFC35DA9E33436E81C4229E78CB9F4"); } @@ -191,7 +206,7 @@ void Session::GenerateDerivedKeysFromSessionKey() { GenerateNonce(); vector session_key; vector enc_session_key; - PreparePublicKey(); + if (public_rsa_ == NULL) PreparePublicKey(); ASSERT_TRUE(GenerateRSASessionKey(&session_key, &enc_session_key)); vector mac_context; vector enc_context; @@ -222,22 +237,23 @@ void Session::LoadTestKeys(const std::string& pst, bool new_mac_keys) { &signature_[0], signature_.size(), encrypted_license().mac_key_iv, encrypted_license().mac_keys, num_keys_, - key_array_, pst_ptr, pst.length())); + key_array_, pst_ptr, pst.length(), NULL)); // Update new generated keys. memcpy(&mac_key_server_[0], license_.mac_keys, wvcdm::MAC_KEY_SIZE); memcpy(&mac_key_client_[0], license_.mac_keys + wvcdm::MAC_KEY_SIZE, wvcdm::MAC_KEY_SIZE); } else { - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_LoadKeys(session_id(), message_ptr(), message_size_, - &signature_[0], signature_.size(), NULL, NULL, - num_keys_, key_array_, pst_ptr, pst.length())); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(session_id(), message_ptr(), message_size_, + &signature_[0], signature_.size(), NULL, NULL, + num_keys_, key_array_, pst_ptr, pst.length(), NULL)); } VerifyTestKeys(); } void Session::VerifyTestKeys() { - for (int i = 0; i < num_keys_; i++) { + for (unsigned int i = 0; i < num_keys_; i++) { KeyControlBlock block; size_t size = sizeof(block); OEMCryptoResult sts = OEMCrypto_QueryKeyControl( @@ -262,6 +278,7 @@ void Session::RefreshTestKeys(const size_t key_count, uint32_t control_bits, uint32_t nonce, OEMCryptoResult expected_result) { // Note: we store the message in encrypted_license_, but the refresh key // message is not actually encrypted. It is, however, signed. + // FillRefreshMessage fills the message with a duration of kLongDuration. FillRefreshMessage(key_count, control_bits, nonce); ServerSignBuffer(reinterpret_cast(&padded_message_), message_size_, &signature_); @@ -273,9 +290,14 @@ void Session::RefreshTestKeys(const size_t key_count, uint32_t control_bits, ASSERT_EQ(expected_result, sts); ASSERT_NO_FATAL_FAILURE(TestDecryptCTR()); - sleep(kShortSleep); // Should still be valid key. + // This should still be valid key, even if the refresh failed, because this + // is before the original license duration. + sleep(kShortSleep); ASSERT_NO_FATAL_FAILURE(TestDecryptCTR(false)); - sleep(kShortSleep + kLongSleep); // Should be after first expiration. + // This should be after duration of the original license, but before the + // expiration of the refresh message. This should succeed if and only if the + // refresh succeeded. + sleep(kShortSleep + kLongSleep); if (expected_result == OEMCrypto_SUCCESS) { ASSERT_NO_FATAL_FAILURE(TestDecryptCTR(false, OEMCrypto_SUCCESS)); } else { @@ -294,21 +316,24 @@ void Session::SetKeyId(int index, const string& key_id) { void Session::FillSimpleMessage(uint32_t duration, uint32_t control, uint32_t nonce, const std::string& pst) { EXPECT_EQ( - 1, RAND_pseudo_bytes(license_.mac_key_iv, sizeof(license_.mac_key_iv))); - EXPECT_EQ(1, RAND_pseudo_bytes(license_.mac_keys, sizeof(license_.mac_keys))); - for (int i = 0; i < num_keys_; i++) { + 1, GetRandBytes(license_.mac_key_iv, sizeof(license_.mac_key_iv))); + EXPECT_EQ(1, GetRandBytes(license_.mac_keys, sizeof(license_.mac_keys))); + for (unsigned int i = 0; i < num_keys_; i++) { memset(license_.keys[i].key_id, 0, kTestKeyIdMaxLength); license_.keys[i].key_id_length = kDefaultKeyIdLength; memset(license_.keys[i].key_id, i, license_.keys[i].key_id_length); - EXPECT_EQ(1, RAND_pseudo_bytes(license_.keys[i].key_data, + EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_data, sizeof(license_.keys[i].key_data))); license_.keys[i].key_data_length = wvcdm::KEY_SIZE; - EXPECT_EQ(1, RAND_pseudo_bytes(license_.keys[i].key_iv, + EXPECT_EQ(1, GetRandBytes(license_.keys[i].key_iv, sizeof(license_.keys[i].key_iv))); - EXPECT_EQ(1, RAND_pseudo_bytes(license_.keys[i].control_iv, + EXPECT_EQ(1, GetRandBytes(license_.keys[i].control_iv, sizeof(license_.keys[i].control_iv))); - // For version 12, we require OEMCrypto to handle kc12 for all licenses. - if (global_features.api_version == 12) { + if (global_features.api_version == 13) { + // For version 13, we require OEMCrypto to handle kc13 for all licenses. + memcpy(license_.keys[i].control.verification, "kc13", 4); + } else if (global_features.api_version == 12) { + // For version 12, we require OEMCrypto to handle kc12 for all licenses. memcpy(license_.keys[i].control.verification, "kc12", 4); } else if (control & wvoec_mock::kControlSecurityPatchLevelMask) { // For versions before 12, we require the special key control block only @@ -328,6 +353,7 @@ void Session::FillSimpleMessage(uint32_t duration, uint32_t control, license_.keys[i].cipher_mode = OEMCrypto_CipherMode_CTR; } memcpy(license_.pst, pst.c_str(), min(sizeof(license_.pst), pst.length())); + pst_ = pst; } void Session::FillRefreshMessage(size_t key_count, uint32_t control_bits, @@ -336,7 +362,10 @@ void Session::FillRefreshMessage(size_t key_count, uint32_t control_bits, encrypted_license().keys[i].key_id_length = license_.keys[i].key_id_length; memcpy(encrypted_license().keys[i].key_id, license_.keys[i].key_id, encrypted_license().keys[i].key_id_length); - if (global_features.api_version == 12) { + if (global_features.api_version == 13) { + // For version 13, we require OEMCrypto to handle kc13 for all licenses. + memcpy(encrypted_license().keys[i].control.verification, "kc13", 4); + } else if (global_features.api_version == 12) { // For version 12, we require OEMCrypto to handle kc12 for all licenses. memcpy(encrypted_license().keys[i].control.verification, "kc12", 4); } else { @@ -360,7 +389,7 @@ void Session::EncryptAndSign() { AES_cbc_encrypt(&license_.mac_keys[0], &encrypted_license().mac_keys[0], 2 * wvcdm::MAC_KEY_SIZE, &aes_key, iv_buffer, AES_ENCRYPT); - for (int i = 0; i < num_keys_; i++) { + for (unsigned int i = 0; i < num_keys_; i++) { memcpy(iv_buffer, &license_.keys[i].control_iv[0], wvcdm::KEY_IV_SIZE); AES_set_encrypt_key(&license_.keys[i].key_data[0], 128, &aes_key); AES_cbc_encrypt( @@ -436,7 +465,7 @@ void Session::VerifyClientSignature(size_t data_length) { void Session::FillKeyArray(const MessageData& data, OEMCrypto_KeyObject* key_array) { - for (int i = 0; i < num_keys_; i++) { + for (unsigned int i = 0; i < num_keys_; i++) { key_array[i].key_id = data.keys[i].key_id; key_array[i].key_id_length = data.keys[i].key_id_length; key_array[i].key_data_iv = data.keys[i].key_iv; @@ -502,9 +531,9 @@ void Session::TestDecryptCTR(bool select_key_first, vector unencryptedData(256); for (size_t i = 0; i < unencryptedData.size(); i++) unencryptedData[i] = i % 256; - EXPECT_EQ(1, RAND_pseudo_bytes(&unencryptedData[0], unencryptedData.size())); + EXPECT_EQ(1, GetRandBytes(&unencryptedData[0], unencryptedData.size())); vector encryptionIv(wvcdm::KEY_IV_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], wvcdm::KEY_IV_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], wvcdm::KEY_IV_SIZE)); vector encryptedData(unencryptedData.size()); EncryptCTR(unencryptedData, license_.keys[key_index].key_data, &encryptionIv[0], &encryptedData); @@ -528,32 +557,74 @@ void Session::TestDecryptCTR(bool select_key_first, if (expected_result == OEMCrypto_SUCCESS) { // No error. ASSERT_EQ(OEMCrypto_SUCCESS, sts); ASSERT_EQ(unencryptedData, outputBuffer); - } else if (expected_result == OEMCrypto_ERROR_KEY_EXPIRED) { - // Report stale keys. - ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, sts); - ASSERT_NE(unencryptedData, outputBuffer); - } else if (expected_result == OEMCrypto_ERROR_INSUFFICIENT_HDCP) { - // Report HDCP errors. - ASSERT_EQ(OEMCrypto_ERROR_INSUFFICIENT_HDCP, sts); - ASSERT_NE(unencryptedData, outputBuffer); } else { - // OEM's can fine tune other error codes for debugging. - ASSERT_NE(OEMCrypto_SUCCESS, sts); + ASSERT_NO_FATAL_FAILURE(TestDecryptResult(expected_result, sts)); ASSERT_NE(unencryptedData, outputBuffer); } } +void Session::TestDecryptResult(OEMCryptoResult expected_result, + OEMCryptoResult actual_result) { + + if (expected_result == OEMCrypto_SUCCESS) { // No error. + ASSERT_EQ(OEMCrypto_SUCCESS, actual_result); + } else if (expected_result == OEMCrypto_ERROR_KEY_EXPIRED && + global_features.api_version >= 9) { + // Report stale keys, required in v9 and beyond. + ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, actual_result); + } else if (expected_result == OEMCrypto_ERROR_INSUFFICIENT_HDCP) { + // Report HDCP errors. + ASSERT_EQ(OEMCrypto_ERROR_INSUFFICIENT_HDCP, actual_result); + } else if (expected_result == OEMCrypto_ERROR_ANALOG_OUTPUT) { + // Report analog errors. + ASSERT_EQ(OEMCrypto_ERROR_ANALOG_OUTPUT, actual_result); + } else { + // OEM's can fine tune other error codes for debugging. + ASSERT_NE(OEMCrypto_SUCCESS, actual_result); + } +} + +void Session::TestSelectExpired(unsigned int key_index) { + if (global_features.api_version >= 13) { + OEMCryptoResult status = + OEMCrypto_SelectKey(session_id(), license().keys[key_index].key_id, + license().keys[key_index].key_id_length); + // It is OK for SelectKey to succeed with an expired key, but if there is + // an error, it must be OEMCrypto_ERROR_KEY_EXIRED. + if (status != OEMCrypto_SUCCESS) { + ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, status); + } + } +} + void Session::LoadOEMCert(bool verify_cert) { + // Get the OEM Public Cert from OEMCrypto vector public_cert; size_t public_cert_length = 0; ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, OEMCrypto_GetOEMPublicCertificate(session_id(), NULL, &public_cert_length)); - ASSERT_GT((int)public_cert_length, 0); + ASSERT_LT(0u, public_cert_length); public_cert.resize(public_cert_length); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_GetOEMPublicCertificate(session_id(), &public_cert[0], &public_cert_length)); + + // Load the certificate chain into an OpenSSL X509 Stack +#ifdef OPENSSL_IS_BORINGSSL + const openssl_ptr x509_stack( + sk_X509_new_null()); + ASSERT_TRUE(x509_stack.NotNull()) << "Unable to allocate X509 stack."; + + CBS pkcs7; + CBS_init(&pkcs7, public_cert.data(), public_cert.size()); + if (!PKCS7_get_certificates(x509_stack.get(), &pkcs7)) { + dump_openssl_error(); + FAIL() << "Unable to deserialize certificate chain."; + } + + STACK_OF(X509)* certs = x509_stack.get(); +#else // load the cert into rsa_key_. openssl_ptr bio( BIO_new_mem_buf(&public_cert[0], public_cert_length)); @@ -564,7 +635,10 @@ void Session::LoadOEMCert(bool verify_cert) { EXPECT_EQ(OBJ_obj2nid(cert->type), NID_pkcs7_signed); STACK_OF(X509)* certs = cert->d.sign->cert; - for (int i = 0; certs && i < sk_X509_num(certs); i++) { +#endif + + // Load the public cert's key into public_rsa_ and verify, if requested + for (X509Count i = 0; certs && i < sk_X509_num(certs); i++) { X509* x509_cert = sk_X509_value(certs, i); openssl_ptr pubkey(X509_get_pubkey(x509_cert)); ASSERT_TRUE(pubkey.NotNull()); @@ -589,13 +663,27 @@ void Session::LoadOEMCert(bool verify_cert) { ASSERT_TRUE(store_ctx.NotNull()); X509_STORE_CTX_init(store_ctx.get(), store.get(), x509_cert, NULL); + // TODO(fredgc): Verify cert is signed by Google. + int result = X509_verify_cert(store_ctx.get()); - ASSERT_GE(0, result) << " OEM Cert not valid. " - << X509_verify_cert_error_string(store_ctx->error); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + ASSERT_GE(0, result) << " OEM Cert not valid. " << + X509_verify_cert_error_string(store_ctx->error); +#else + ASSERT_GE(0, result) << " OEM Cert not valid. " << + X509_verify_cert_error_string( + X509_STORE_CTX_get_error(store_ctx.get())); +#endif if (result == 0) { +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) printf("Cert not verified: %s.\n", X509_verify_cert_error_string(store_ctx->error)); +#else + printf("Cert not verified: %s.\n", + X509_verify_cert_error_string( + X509_STORE_CTX_get_error(store_ctx.get()))); +#endif } } } @@ -619,7 +707,7 @@ void Session::MakeRSACertificate(struct RSAPrivateKeyMessage* encrypted, memcpy(message.rsa_key, rsa_key.data(), rsa_key.size()); message.rsa_key_length = rsa_key.size(); } - EXPECT_EQ(1, RAND_pseudo_bytes(message.rsa_key_iv, wvcdm::KEY_IV_SIZE)); + EXPECT_EQ(1, GetRandBytes(message.rsa_key_iv, wvcdm::KEY_IV_SIZE)); message.nonce = nonce_; EncryptProvisioningMessage(&message, encrypted, *encryption_key); @@ -652,10 +740,10 @@ void Session::RewrapRSAKey(const struct RSAPrivateKeyMessage& encrypted, wrapped_key->clear(); } } -void Session::RewrapRSAKey30( - const struct RSAPrivateKeyMessage& encrypted, size_t, - const std::vector& encrypted_message_key, - vector* wrapped_key, bool force) { + +void Session::RewrapRSAKey30(const struct RSAPrivateKeyMessage& encrypted, + const std::vector& encrypted_message_key, + vector* wrapped_key, bool force) { size_t wrapped_key_length = 0; ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, OEMCrypto_RewrapDeviceRSAKey30( @@ -679,8 +767,8 @@ void Session::RewrapRSAKey30( void Session::PreparePublicKey(const uint8_t* rsa_key, size_t rsa_key_length) { if (rsa_key == NULL) { - rsa_key = kTestRSAPKCS8PrivateKeyInfo2_2048; - rsa_key_length = sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048); + rsa_key = wvcdm_test_auth::kRsaPrivateKey_2048; + rsa_key_length = wvcdm_test_auth::kRsaPrivateKeySize_2048; } uint8_t* p = const_cast(rsa_key); openssl_ptr bio(BIO_new_mem_buf(p, rsa_key_length)); @@ -713,38 +801,43 @@ bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message, size_t message_length, const uint8_t* signature, size_t signature_length) { - EVP_MD_CTX ctx; - EVP_MD_CTX_init(&ctx); - EVP_PKEY_CTX* pctx = NULL; +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_MD_CTX md_ctx_struct; + EVP_MD_CTX* md_ctx = &md_ctx_struct; + EVP_MD_CTX_init(md_ctx); +#else + EVP_MD_CTX* md_ctx = EVP_MD_CTX_new(); +#endif + EVP_PKEY_CTX* pkey_ctx = NULL; - if (EVP_DigestVerifyInit(&ctx, &pctx, EVP_sha1(), NULL /* no ENGINE */, + if (EVP_DigestVerifyInit(md_ctx, &pkey_ctx, EVP_sha1(), NULL /* no ENGINE */, pkey) != 1) { LOGE("EVP_DigestVerifyInit failed in VerifyPSSSignature"); goto err; } - if (EVP_PKEY_CTX_set_signature_md(pctx, + if (EVP_PKEY_CTX_set_signature_md(pkey_ctx, const_cast(EVP_sha1())) != 1) { LOGE("EVP_PKEY_CTX_set_signature_md failed in VerifyPSSSignature"); goto err; } - if (EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) != 1) { + if (EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1) { LOGE("EVP_PKEY_CTX_set_rsa_padding failed in VerifyPSSSignature"); goto err; } - if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pctx, SHA_DIGEST_LENGTH) != 1) { + if (EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, SHA_DIGEST_LENGTH) != 1) { LOGE("EVP_PKEY_CTX_set_rsa_pss_saltlen failed in VerifyPSSSignature"); goto err; } - if (EVP_DigestVerifyUpdate(&ctx, message, message_length) != 1) { + if (EVP_DigestVerifyUpdate(md_ctx, message, message_length) != 1) { LOGE("EVP_DigestVerifyUpdate failed in VerifyPSSSignature"); goto err; } - if (EVP_DigestVerifyFinal(&ctx, const_cast(signature), + if (EVP_DigestVerifyFinal(md_ctx, const_cast(signature), signature_length) != 1) { LOGE( "EVP_DigestVerifyFinal failed in VerifyPSSSignature. (Probably a bad " @@ -752,12 +845,20 @@ bool Session::VerifyPSSSignature(EVP_PKEY* pkey, const uint8_t* message, goto err; } - EVP_MD_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_MD_CTX_cleanup(md_ctx); +#else + EVP_MD_CTX_free(md_ctx); +#endif return true; err: dump_openssl_error(); - EVP_MD_CTX_cleanup(&ctx); +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + EVP_MD_CTX_cleanup(md_ctx); +#else + EVP_MD_CTX_free(md_ctx); +#endif return false; } @@ -821,8 +922,61 @@ void Session::InstallRSASessionTestKey(const vector& wrapped_rsa_key) { GenerateDerivedKeysFromSessionKey(); } -void Session::GenerateReport(const std::string& pst, bool expect_success, +void Session::CreateNewUsageEntry() { + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_CreateNewUsageEntry(session_id(), &usage_entry_number_)); +} + +void Session::UpdateUsageEntry(std::vector* header_buffer) { + size_t header_buffer_length = 0; + size_t entry_buffer_length = 0; + ASSERT_EQ( + OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_UpdateUsageEntry(session_id(), NULL, &header_buffer_length, + NULL, &entry_buffer_length)); + ASSERT_LT(0u, header_buffer_length); + header_buffer->resize(header_buffer_length); + ASSERT_LT(0u, entry_buffer_length); + encrypted_usage_entry_.resize(entry_buffer_length); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_UpdateUsageEntry( + session_id(), &(header_buffer->front()), &header_buffer_length, + &encrypted_usage_entry_[0], &entry_buffer_length)); +} + +void Session::DeactivateUsageEntry(const std::string& pst) { + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_DeactivateUsageEntry( + session_id(), reinterpret_cast(pst.c_str()), + pst.length())); +} + +void Session::LoadUsageEntry(uint32_t index, const vector& buffer) { + usage_entry_number_ = index; + encrypted_usage_entry_ = buffer; + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageEntry(session_id(), index, &buffer[0], buffer.size())); +} + +void Session::MoveUsageEntry(uint32_t new_index, + std::vector* header_buffer, + OEMCryptoResult expect_result) { + + ASSERT_NO_FATAL_FAILURE(open()); + ASSERT_NO_FATAL_FAILURE(ReloadUsageEntry()); + ASSERT_EQ(expect_result, OEMCrypto_MoveEntry(session_id(), new_index)); + if (expect_result == OEMCrypto_SUCCESS) { + usage_entry_number_ = new_index; + ASSERT_NO_FATAL_FAILURE(UpdateUsageEntry(header_buffer)); + } + ASSERT_NO_FATAL_FAILURE(close()); +} + +void Session::GenerateReport(const std::string& pst, + OEMCryptoResult expected_result, Session* other) { + ASSERT_TRUE(open_); if (other) { // If other is specified, copy mac keys. mac_key_server_ = other->mac_key_server_; mac_key_client_ = other->mac_key_client_; @@ -830,54 +984,120 @@ void Session::GenerateReport(const std::string& pst, bool expect_success, size_t length = 0; OEMCryptoResult sts = OEMCrypto_ReportUsage( session_id(), reinterpret_cast(pst.c_str()), pst.length(), - pst_report(), &length); - if (expect_success) { + &pst_report_buffer_[0], &length); + if (expected_result == OEMCrypto_SUCCESS) { ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); } if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { - ASSERT_LE(sizeof(OEMCrypto_PST_Report), length); - pst_report_buffer_.resize(length); + ASSERT_EQ(wvcdm::Unpacked_PST_Report::report_size(pst.length()), length); + pst_report_buffer_.assign(length, 0xFF); // Fill with garbage values. } sts = OEMCrypto_ReportUsage(session_id(), reinterpret_cast(pst.c_str()), - pst.length(), pst_report(), &length); - if (!expect_success) { - ASSERT_NE(OEMCrypto_SUCCESS, sts); + pst.length(), &pst_report_buffer_[0], &length); + ASSERT_EQ(expected_result, sts); + if (expected_result != OEMCrypto_SUCCESS) { return; } - ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(pst_report_buffer_.size(), length); vector computed_signature(SHA_DIGEST_LENGTH); unsigned int sig_len = SHA_DIGEST_LENGTH; HMAC(EVP_sha1(), &mac_key_client_[0], mac_key_client_.size(), - reinterpret_cast(pst_report()) + SHA_DIGEST_LENGTH, - length - SHA_DIGEST_LENGTH, &computed_signature[0], &sig_len); - EXPECT_EQ(0, memcmp(&computed_signature[0], pst_report()->signature, + &pst_report_buffer_[SHA_DIGEST_LENGTH], length - SHA_DIGEST_LENGTH, + &computed_signature[0], &sig_len); + EXPECT_EQ(0, memcmp(&computed_signature[0], pst_report().signature(), SHA_DIGEST_LENGTH)); - EXPECT_GE(kInactive, pst_report()->status); - EXPECT_GE(kHardwareSecureClock, pst_report()->clock_security_level); - EXPECT_EQ(pst.length(), pst_report()->pst_length); - EXPECT_EQ(0, memcmp(pst.c_str(), pst_report()->pst, pst.length())); + EXPECT_GE(kInactiveUnused, pst_report().status()); + EXPECT_GE(kHardwareSecureClock, pst_report().clock_security_level()); + EXPECT_EQ(pst.length(), pst_report().pst_length()); + EXPECT_EQ(0, memcmp(pst.c_str(), pst_report().pst(), pst.length())); + // Also, we the session to be able to sign the release message with the + // correct mac keys from the usage table entry. + ASSERT_NO_FATAL_FAILURE(VerifyClientSignature()); } -OEMCrypto_PST_Report* Session::pst_report() { - return reinterpret_cast(&pst_report_buffer_[0]); +void Session::VerifyPST(const Test_PST_Report& expected) { + wvcdm::Unpacked_PST_Report computed = pst_report(); + EXPECT_EQ(expected.status, computed.status()); + char* pst_ptr = reinterpret_cast(computed.pst()); + std::string computed_pst(pst_ptr, pst_ptr + computed.pst_length()); + ASSERT_EQ(expected.pst, computed_pst); + EXPECT_NEAR(expected.seconds_since_license_received, + computed.seconds_since_license_received(), + kTimeTolerance); + // Decrypt times only valid on licenses that have been active. + if (expected.status == kActive || expected.status == kInactiveUsed) { + EXPECT_NEAR(expected.seconds_since_first_decrypt, + computed.seconds_since_first_decrypt(), + kUsageTableTimeTolerance); + EXPECT_NEAR(expected.seconds_since_last_decrypt, + computed.seconds_since_last_decrypt(), + kUsageTableTimeTolerance); + } + std::vector signature(SHA_DIGEST_LENGTH); + unsigned int md_len = SHA_DIGEST_LENGTH; + if (!HMAC(EVP_sha1(), &mac_key_client_[0], mac_key_client_.size(), + &pst_report_buffer_[0] + SHA_DIGEST_LENGTH, + pst_report_buffer_.size() - SHA_DIGEST_LENGTH, + &signature[0], &md_len)) { + cout << "Error computing HMAC.\n"; + dump_openssl_error(); + } + EXPECT_EQ(0, memcmp(computed.signature(), &signature[0], + SHA_DIGEST_LENGTH)); } -void Session::DeleteEntry(const std::string& pst) { - uint8_t* pst_ptr = encrypted_license().pst; - memcpy(pst_ptr, pst.c_str(), min(sizeof(license_.pst), pst.length())); - ServerSignBuffer(reinterpret_cast(&padded_message_), - message_size_, &signature_); - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_DeleteUsageEntry(session_id(), pst_ptr, pst.length(), - message_ptr(), message_size_, - &signature_[0], signature_.size())); +// This might adjust t to be "seconds since now". If t is small, we assume it +// is "seconds since now", but if the value of t is large, assume it is +// "absolute time" and convert to "seconds since now". +static int64_t MaybeAdjustTime(int64_t t, time_t now) { + int64_t k10Minutes = 60 * 10; // in seconds. + if (t > k10Minutes) return now - t; + return t; } -void Session::ForceDeleteEntry(const std::string& pst) { - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_ForceDeleteUsageEntry( - reinterpret_cast(pst.c_str()), pst.length())); +void Session::GenerateVerifyReport(const std::string& pst, + OEMCrypto_Usage_Entry_Status status, + int64_t time_license_received, + int64_t time_first_decrypt, + int64_t time_last_decrypt) { + ASSERT_NO_FATAL_FAILURE(GenerateReport(pst)); + Test_PST_Report expected(pst, status); + time_t now = time(NULL); + expected.seconds_since_license_received = + MaybeAdjustTime(time_license_received, now); + expected.seconds_since_first_decrypt = + MaybeAdjustTime(time_first_decrypt, now); + expected.seconds_since_last_decrypt = MaybeAdjustTime(time_last_decrypt, now); + ASSERT_NO_FATAL_FAILURE(VerifyPST(expected)); +} + +void Session::CreateOldEntry(const Test_PST_Report& report) { + OEMCryptoResult result = OEMCrypto_CreateOldUsageEntry( + report.seconds_since_license_received, + report.seconds_since_first_decrypt, + report.seconds_since_last_decrypt, + report.status, &mac_key_server_[0], + &mac_key_client_[0], + reinterpret_cast(report.pst.c_str()), + report.pst.length()); + if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) return; + ASSERT_EQ(OEMCrypto_SUCCESS, result); +} + +void Session::CopyAndVerifyOldEntry(const Test_PST_Report& report, + std::vector* header_buffer) { + ASSERT_NO_FATAL_FAILURE(CreateNewUsageEntry()); + OEMCryptoResult result = OEMCrypto_CopyOldUsageEntry( + session_id(), reinterpret_cast(report.pst.c_str()), + report.pst.length()); + if (result == OEMCrypto_ERROR_NOT_IMPLEMENTED) { + cout << "WARNING: OEMCrypto CANNOT copy old usage table to new." << endl; + return; + } + ASSERT_NO_FATAL_FAILURE(UpdateUsageEntry(header_buffer)); + ASSERT_NO_FATAL_FAILURE(GenerateReport(report.pst)); + ASSERT_NO_FATAL_FAILURE(VerifyPST(report)); } const uint8_t* Session::message_ptr() { diff --git a/oemcrypto/test/oec_session_util.h b/oemcrypto/test/oec_session_util.h index ee8d661c..2284cea8 100644 --- a/oemcrypto/test/oec_session_util.h +++ b/oemcrypto/test/oec_session_util.h @@ -10,6 +10,7 @@ #include #include "oec_device_features.h" +#include "pst_report.h" #include "wv_cdm_constants.h" using namespace std; @@ -18,19 +19,7 @@ using namespace std; // which is std::vector in this case. namespace std { -struct PatternTestVariant { - PatternTestVariant(size_t encrypt, size_t skip, OEMCryptoCipherMode mode_) { - this->pattern.encrypt = encrypt; - this->pattern.skip = skip; - this->pattern.offset = 0; - this->mode = mode_; - } - OEMCrypto_CENCEncryptPatternDesc pattern; - OEMCryptoCipherMode mode; -}; - void PrintTo(const vector& value, ostream* os); -void PrintTo(const PatternTestVariant& param, ostream* os); } // namespace std @@ -50,6 +39,7 @@ const int kLongSleep = 2 * kSpeedMultiplier; const uint32_t kDuration = 2 * kSpeedMultiplier; const uint32_t kLongDuration = 5 * kSpeedMultiplier; const int32_t kTimeTolerance = 3 * kSpeedMultiplier; +const time_t kUsageTableTimeTolerance = 10 * kSpeedMultiplier; } // namespace typedef struct { @@ -101,6 +91,18 @@ struct RSAPrivateKeyMessage { uint32_t nonce; }; +struct Test_PST_Report { + Test_PST_Report(const std::string& pst_in, + OEMCrypto_Usage_Entry_Status status_in) + : status(status_in), pst(pst_in) {} + + OEMCrypto_Usage_Entry_Status status; + int64_t seconds_since_license_received; + int64_t seconds_since_first_decrypt; + int64_t seconds_since_last_decrypt; + std::string pst; +}; + // Increment counter for AES-CTR. The CENC spec specifies we increment only // the low 64 bits of the IV counter, and leave the high 64 bits alone. This // is different from the OpenSSL implementation, so we implement the CTR loop @@ -203,6 +205,13 @@ class Session { void TestDecryptCTR(bool select_key_first = true, OEMCryptoResult expected_result = OEMCrypto_SUCCESS, int key_index = 0); + // This compares the actual result with the expected result. If OEMCrypto is + // an older version, we allow it to report an equivalent error code. + void TestDecryptResult(OEMCryptoResult expected_result, + OEMCryptoResult actual_result); + // Verify that an attempt to select an expired key either succeeds, or gives + // an actionable error code. + void TestSelectExpired(unsigned int key_index); // Calls OEMCrypto_GetOEMPublicCertificate and loads the OEM cert's public // rsa key into public_rsa_. void LoadOEMCert(bool verify_cert = false); @@ -241,27 +250,68 @@ class Session { // Calls OEMCrypto_RewrapDeviceRSAKey30 with the given provisioning response // message. If force is true, we assert that the key loads successfully. void RewrapRSAKey30(const struct RSAPrivateKeyMessage& encrypted, - size_t message_size, const std::vector& encrypted_message_key, vector* wrapped_key, bool force); // Loads the specified wrapped_rsa_key into OEMCrypto, and then runs // GenerateDerivedKeysFromSessionKey to install known encryption and mac keys. void InstallRSASessionTestKey(const vector& wrapped_rsa_key); - // Generates a usage report for the specified pst. If expect_success is true, + // Creates a new usage entry, and keeps track of the index. + void CreateNewUsageEntry(); + // Copy encrypted usage entry from other session, and then load it. + // This session must already be open. + void LoadUsageEntry(uint32_t index, const vector& buffer); + // Copy encrypted usage entry from other session. + // This session must already be open. + void LoadUsageEntry(const Session& other) { + LoadUsageEntry(other.usage_entry_number(), other.encrypted_usage_entry()); + } + // Reload previously used usage entry. + void ReloadUsageEntry() { LoadUsageEntry(*this); } + // Update the usage entry and save the header to the specified buffer. + void UpdateUsageEntry(std::vector* header_buffer); + // Deactivate this session's usage entry. + void DeactivateUsageEntry(const std::string& pst); + // The usage entry number for this session's usage entry. + uint32_t usage_entry_number() const { return usage_entry_number_; } + void set_usage_entry_number(uint32_t v) { usage_entry_number_ = v; } + // The encrypted buffer holding the recently updated and saved usage entry. + const vector& encrypted_usage_entry() const { + return encrypted_usage_entry_; + } + // Generates a usage report for the specified pst. If there is success, // the report's signature is verified, and several fields are given sanity // checks. If other is not null, then the mac keys are copied from other in // order to verify signatures. - void GenerateReport(const std::string& pst, bool expect_success = true, + void GenerateReport(const std::string& pst, + OEMCryptoResult expected_result = OEMCrypto_SUCCESS, Session* other = 0); - // Returns a pointer to the usage report generated by the previous call to - // GenerateReport. - OEMCrypto_PST_Report* pst_report(); - // Creates a signed delete usage table entry message and calls - // OEMCrypto_DeleteUsageEntry on it. - void DeleteEntry(const std::string& pst); - // Calls OEMCrypto_ForceDeleteUsageEntry to delete a usage table entry without - // a signed message. - void ForceDeleteEntry(const std::string& pst); + // Move this usage entry to a new index. + void MoveUsageEntry(uint32_t new_index, std::vector* header_buffer, + OEMCryptoResult expect_result = OEMCrypto_SUCCESS); + // PST used in FillSimpleMesage. + string pst() const { return pst_; } + // Returns a pointer-like thing to the usage report generated by the previous + // call to GenerateReport. + wvcdm::Unpacked_PST_Report pst_report() { + return wvcdm::Unpacked_PST_Report(&pst_report_buffer_[0]); + } + // Verify the values in the PST report. The signature should have been + // verified in GenerateReport, above. + void VerifyPST(const Test_PST_Report& report); + // Generate and Verify the Usage Report. If any time is greater than 10 + // minutes, it is assumed to be an absolute time, and time_since will be + // computed relative to now. + void GenerateVerifyReport(const std::string& pst, + OEMCrypto_Usage_Entry_Status status, + int64_t time_license_received = 0, + int64_t time_first_decrypt = 0, + int64_t time_last_decrypt = 0); + // Create an entry in the old usage table based on the given report. + void CreateOldEntry(const Test_PST_Report &report); + // Create a new entry and copy the old entry into it. Then very the report + // is right. + void CopyAndVerifyOldEntry(const Test_PST_Report &report, + std::vector* header_buffer); // The unencrypted license response or license renewal response. MessageData& license() { return license_; } @@ -281,8 +331,7 @@ class Session { void set_num_keys(int num_keys) { num_keys_ = num_keys; } // The current number of keys to use in the license(), encrypted_license() // and key_array(). - int num_keys() const { return num_keys_; } - size_t key_array_size() const { return num_keys_; } + unsigned int num_keys() const { return num_keys_; } // Set the size of the buffer used the encrypted license. // Must be between sizeof(MessageData) and kMaxMessageSize. @@ -307,7 +356,10 @@ class Session { size_t message_size_; // How much of the padded message to use. OEMCrypto_KeyObject key_array_[kMaxNumKeys]; std::vector signature_; - int num_keys_; + unsigned int num_keys_; + vector encrypted_usage_entry_; + uint32_t usage_entry_number_; + string pst_; }; } // namespace wvoec diff --git a/oemcrypto/test/oec_test_data.h b/oemcrypto/test/oec_test_data.h deleted file mode 100644 index 074123a8..00000000 --- a/oemcrypto/test/oec_test_data.h +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright 2016 Google Inc. All Rights Reserved. -// -// Test data for OEMCrypto unit tests. -// -#ifndef CDM_OEC_TEST_DATA_H_ -#define CDM_OEC_TEST_DATA_H_ - -#include - -#if 0 -#include "OEMCryptoCENC.h" -#include "wv_keybox.h" -#endif - -namespace wvoec { - -// These are test keyboxes. They will not be accepted by production systems. -// By using known keyboxes for these tests, the results for a given set of -// inputs to a test are predictable and can be compared to the actual results. -// The first keybox, kTestKeybox, with deviceID "TestKey01" is used for most of -// the tests. It should be loaded by OEMCrypto when OEMCrypto_LoadTestKeybox -// is called. -const wvoec_mock::WidevineKeybox kTestKeybox = { - // Sample keybox used for test vectors - { - // deviceID - 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey01 - 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - }, { - // key - 0xfb, 0xda, 0x04, 0x89, 0xa1, 0x58, 0x16, 0x0e, - 0xa4, 0x02, 0xe9, 0x29, 0xe3, 0xb6, 0x8f, 0x04, - }, { - // data - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, - 0x07, 0xd9, 0xff, 0xde, 0x13, 0xaa, 0x95, 0xc1, - 0x22, 0x67, 0x80, 0x53, 0x36, 0x21, 0x36, 0xbd, - 0xf8, 0x40, 0x8f, 0x82, 0x76, 0xe4, 0xc2, 0xd8, - 0x7e, 0xc5, 0x2b, 0x61, 0xaa, 0x1b, 0x9f, 0x64, - 0x6e, 0x58, 0x73, 0x49, 0x30, 0xac, 0xeb, 0xe8, - 0x99, 0xb3, 0xe4, 0x64, 0x18, 0x9a, 0x14, 0xa8, - 0x72, 0x02, 0xfb, 0x02, 0x57, 0x4e, 0x70, 0x64, - 0x0b, 0xd2, 0x2e, 0xf4, 0x4b, 0x2d, 0x7e, 0x39, - }, { - // magic - 0x6b, 0x62, 0x6f, 0x78, - }, { - // Crc - 0x0a, 0x7a, 0x2c, 0x35, - } -}; - -static wvoec_mock::WidevineKeybox kValidKeybox02 = { - // Sample keybox used for test vectors - { - // deviceID - 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey02 - 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - }, { - // key - 0x76, 0x5d, 0xce, 0x01, 0x04, 0x89, 0xb3, 0xd0, - 0xdf, 0xce, 0x54, 0x8a, 0x49, 0xda, 0xdc, 0xb6, - }, { - // data - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, - 0x92, 0x27, 0x0b, 0x1f, 0x1a, 0xd5, 0xc6, 0x93, - 0x19, 0x3f, 0xaa, 0x74, 0x1f, 0xdd, 0x5f, 0xb4, - 0xe9, 0x40, 0x2f, 0x34, 0xa4, 0x92, 0xf4, 0xae, - 0x9a, 0x52, 0x39, 0xbc, 0xb7, 0x24, 0x38, 0x13, - 0xab, 0xf4, 0x92, 0x96, 0xc4, 0x81, 0x60, 0x33, - 0xd8, 0xb8, 0x09, 0xc7, 0x55, 0x0e, 0x12, 0xfa, - 0xa8, 0x98, 0x62, 0x8a, 0xec, 0xea, 0x74, 0x8a, - 0x4b, 0xfa, 0x5a, 0x9e, 0xb6, 0x49, 0x0d, 0x80, - }, { - // magic - 0x6b, 0x62, 0x6f, 0x78, - }, { - // Crc - 0x2a, 0x3b, 0x3e, 0xe4, - } -}; - -static wvoec_mock::WidevineKeybox kValidKeybox03 = { - // Sample keybox used for test vectors - { - // deviceID - 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey03 - 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ - }, { - // key - 0x25, 0xe5, 0x2a, 0x02, 0x29, 0x68, 0x04, 0xa2, - 0x92, 0xfd, 0x7c, 0x67, 0x0b, 0x67, 0x1f, 0x31, - }, { - // data - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, - 0xf4, 0x0a, 0x0e, 0xa2, 0x0a, 0x71, 0xd5, 0x92, - 0xfa, 0xa3, 0x25, 0xc6, 0x4b, 0x76, 0xf1, 0x64, - 0xf4, 0x60, 0xa0, 0x30, 0x72, 0x23, 0xbe, 0x03, - 0xcd, 0xde, 0x7a, 0x06, 0xd4, 0x01, 0xeb, 0xdc, - 0xe0, 0x50, 0xc0, 0x53, 0x0a, 0x50, 0xb0, 0x37, - 0xe5, 0x05, 0x25, 0x0e, 0xa4, 0xc8, 0x5a, 0xff, - 0x46, 0x6e, 0xa5, 0x31, 0xf3, 0xdd, 0x94, 0xb7, - 0xe0, 0xd3, 0xf9, 0x04, 0xb2, 0x54, 0xb1, 0x64, - }, { - // magic - 0x6b, 0x62, 0x6f, 0x78, - }, { - // Crc - 0xa1, 0x99, 0x5f, 0x46, - } -}; - -// A 2048 bit RSA key in PKCS#8 PrivateKeyInfo format -// Used to verify the functions that manipulate RSA keys. -static const uint8_t kTestRSAPKCS8PrivateKeyInfo2_2048[] = { - 0x30, 0x82, 0x04, 0xbc, 0x02, 0x01, 0x00, 0x30, - 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, - 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, - 0x04, 0xa6, 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, - 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, - 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, - 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, - 0x94, 0x58, 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, - 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, - 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, - 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, - 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, - 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, - 0x28, 0xda, 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, - 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, - 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, - 0x29, 0xf2, 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, - 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, - 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, - 0xcd, 0x9a, 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, - 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, - 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, - 0x98, 0x56, 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, - 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, - 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, - 0xc9, 0x83, 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, - 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, - 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, - 0x2d, 0x5f, 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, - 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, - 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, - 0x82, 0x46, 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, - 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, - 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, - 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, - 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, - 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, - 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x00, 0x5e, - 0x79, 0x65, 0x49, 0xa5, 0x76, 0x79, 0xf9, 0x05, - 0x45, 0x0f, 0xf4, 0x03, 0xbd, 0xa4, 0x7d, 0x29, - 0xd5, 0xde, 0x33, 0x63, 0xd8, 0xb8, 0xac, 0x97, - 0xeb, 0x3f, 0x5e, 0x55, 0xe8, 0x7d, 0xf3, 0xe7, - 0x3b, 0x5c, 0x2d, 0x54, 0x67, 0x36, 0xd6, 0x1d, - 0x46, 0xf5, 0xca, 0x2d, 0x8b, 0x3a, 0x7e, 0xdc, - 0x45, 0x38, 0x79, 0x7e, 0x65, 0x71, 0x5f, 0x1c, - 0x5e, 0x79, 0xb1, 0x40, 0xcd, 0xfe, 0xc5, 0xe1, - 0xc1, 0x6b, 0x78, 0x04, 0x4e, 0x8e, 0x79, 0xf9, - 0x0a, 0xfc, 0x79, 0xb1, 0x5e, 0xb3, 0x60, 0xe3, - 0x68, 0x7b, 0xc6, 0xef, 0xcb, 0x71, 0x4c, 0xba, - 0xa7, 0x79, 0x5c, 0x7a, 0x81, 0xd1, 0x71, 0xe7, - 0x00, 0x21, 0x13, 0xe2, 0x55, 0x69, 0x0e, 0x75, - 0xbe, 0x09, 0xc3, 0x4f, 0xa9, 0xc9, 0x68, 0x22, - 0x0e, 0x97, 0x8d, 0x89, 0x6e, 0xf1, 0xe8, 0x88, - 0x7a, 0xd1, 0xd9, 0x09, 0x5d, 0xd3, 0x28, 0x78, - 0x25, 0x0b, 0x1c, 0x47, 0x73, 0x25, 0xcc, 0x21, - 0xb6, 0xda, 0xc6, 0x24, 0x5a, 0xd0, 0x37, 0x14, - 0x46, 0xc7, 0x94, 0x69, 0xe4, 0x43, 0x6f, 0x47, - 0xde, 0x00, 0x33, 0x4d, 0x8f, 0x95, 0x72, 0xfa, - 0x68, 0x71, 0x17, 0x66, 0x12, 0x1a, 0x87, 0x27, - 0xf7, 0xef, 0x7e, 0xe0, 0x35, 0x58, 0xf2, 0x4d, - 0x6f, 0x35, 0x01, 0xaa, 0x96, 0xe2, 0x3d, 0x51, - 0x13, 0x86, 0x9c, 0x79, 0xd0, 0xb7, 0xb6, 0x64, - 0xe8, 0x86, 0x65, 0x50, 0xbf, 0xcc, 0x27, 0x53, - 0x1f, 0x51, 0xd4, 0xca, 0xbe, 0xf5, 0xdd, 0x77, - 0x70, 0x98, 0x0f, 0xee, 0xa8, 0x96, 0x07, 0x5f, - 0x45, 0x6a, 0x7a, 0x0d, 0x03, 0x9c, 0x4f, 0x29, - 0xf6, 0x06, 0xf3, 0x5d, 0x58, 0x6c, 0x47, 0xd0, - 0x96, 0xa9, 0x03, 0x17, 0xbb, 0x4e, 0xc9, 0x21, - 0xe0, 0xac, 0xcd, 0x78, 0x78, 0xb2, 0xfe, 0x81, - 0xb2, 0x51, 0x53, 0xa6, 0x1f, 0x98, 0x45, 0x02, - 0x81, 0x81, 0x00, 0xcf, 0x73, 0x8c, 0xbe, 0x6d, - 0x45, 0x2d, 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, - 0x78, 0xcc, 0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, - 0x64, 0x60, 0x8c, 0x43, 0xeb, 0x85, 0xab, 0x04, - 0xb6, 0x7d, 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, - 0x84, 0x68, 0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, - 0xde, 0x51, 0x4b, 0xb6, 0x51, 0x86, 0x7b, 0xd0, - 0xe6, 0x4d, 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, - 0x3a, 0x83, 0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, - 0x93, 0xd7, 0x9c, 0x27, 0x80, 0xb7, 0x1e, 0x64, - 0x9e, 0xf7, 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, - 0x18, 0x6c, 0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, - 0x90, 0x8f, 0xa2, 0x16, 0x22, 0x6a, 0xcc, 0x48, - 0x06, 0x74, 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, - 0x3c, 0x2d, 0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, - 0x33, 0x85, 0x26, 0x60, 0x48, 0x16, 0xcb, 0xef, - 0xf8, 0xcd, 0x37, 0x02, 0x81, 0x81, 0x00, 0xce, - 0x15, 0x43, 0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, - 0xc3, 0x41, 0x45, 0x97, 0xb1, 0x49, 0xc2, 0x19, - 0x23, 0x87, 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, - 0xcb, 0x43, 0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, - 0xbb, 0xdb, 0xfd, 0x11, 0x9d, 0x17, 0x68, 0x78, - 0x6d, 0x61, 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, - 0xa7, 0x5b, 0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, - 0x91, 0x99, 0xe5, 0x91, 0x32, 0x2d, 0xeb, 0x3f, - 0xd8, 0x3e, 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, - 0xc1, 0xee, 0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, - 0x58, 0xf4, 0xd0, 0xb2, 0x74, 0x1d, 0x8e, 0x87, - 0x46, 0xcd, 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, - 0x0d, 0x6c, 0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, - 0xda, 0xfb, 0xed, 0x83, 0x51, 0x67, 0xa9, 0x55, - 0xab, 0x54, 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, - 0x53, 0xa8, 0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, - 0x81, 0x80, 0x67, 0x9c, 0x32, 0x83, 0x39, 0x57, - 0xff, 0x73, 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, - 0x0a, 0x2d, 0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, - 0xf3, 0x90, 0x9a, 0xab, 0x9b, 0x0b, 0x1b, 0x43, - 0x79, 0xa0, 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, - 0xeb, 0xdb, 0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, - 0xb8, 0xe1, 0xb3, 0xa1, 0x6c, 0x25, 0x92, 0xe4, - 0x33, 0xb2, 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, - 0x37, 0x43, 0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, - 0x20, 0x82, 0xa1, 0x48, 0x2c, 0x2d, 0x45, 0xdc, - 0x0f, 0x62, 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, - 0xf9, 0xca, 0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, - 0xc8, 0x28, 0x10, 0x1e, 0x08, 0x71, 0x16, 0xd8, - 0x02, 0x71, 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, - 0xb1, 0x31, 0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, - 0x35, 0x95, 0x41, 0x29, 0x40, 0x19, 0x83, 0x35, - 0x24, 0x69, 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, - 0xcc, 0x3b, 0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, - 0x50, 0x76, 0x63, 0x94, 0x49, 0x4c, 0xad, 0x10, - 0xcb, 0x47, 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, - 0x6a, 0xab, 0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, - 0x8a, 0xa2, 0xb9, 0x8f, 0xce, 0xec, 0x5e, 0x61, - 0xa8, 0xcd, 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, - 0x5f, 0xdf, 0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, - 0xe4, 0x8b, 0x01, 0x06, 0x59, 0x22, 0xfa, 0x34, - 0x4b, 0x81, 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, - 0x77, 0xe6, 0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, - 0x6c, 0xfa, 0x2b, 0xd9, 0x83, 0x5a, 0x2d, 0x0c, - 0x3b, 0x70, 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, - 0xd9, 0xbe, 0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, - 0x66, 0xff, 0x1c, 0x1b, 0xc8, 0x96, 0x76, 0xe8, - 0x6f, 0x44, 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, - 0xac, 0x21, 0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, - 0x2c, 0x7c, 0xad, 0x1e, 0x75, 0xf6, 0x69, 0x1d, - 0xe7, 0xa6, 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, - 0x28, 0x66, 0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, - 0xae, 0xb7, 0x65, 0x2c, 0x52, 0xf9, 0xe4, 0xc7, - 0x81, 0x7b, 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, - 0x70, 0xcf, 0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, - 0x75, 0x61, 0x96, 0x86, 0x4b, 0xb6, 0x2b, 0xad, - 0xf0, 0xad, 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, - 0x50, 0xcb, 0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, - 0xd3, 0x29, 0x23, 0x02, 0x60, 0xf7, 0xab, 0x30, - 0x40, 0xda, 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, - 0xa2, 0x0d, 0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, - 0xf4, 0xd4, 0x52, 0x95, 0x00, 0xae, 0x84, 0x6b, - 0x47, 0xb2, 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, - 0x72, 0x2c, 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, - 0x56, 0xfe, 0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 }; - -} // namespace wvoec - -#endif // CDM_OEC_TEST_DATA_H_ diff --git a/oemcrypto/test/oemcrypto_session_tests_helper.cpp b/oemcrypto/test/oemcrypto_session_tests_helper.cpp new file mode 100644 index 00000000..21b3aeff --- /dev/null +++ b/oemcrypto/test/oemcrypto_session_tests_helper.cpp @@ -0,0 +1,147 @@ +#include "oemcrypto_session_tests_helper.h" + +#include + +#include "test_keybox.h" + +using namespace std; +using namespace wvoec; + +namespace wvoec { + +// Make this function available when in Fuzz mode because we are not inheriting +// from OEMCryptoClientTest. +const uint8_t* find(const vector& message, + const vector& substring) { + vector::const_iterator pos = search( + message.begin(), message.end(), substring.begin(), substring.end()); + if (pos == message.end()) { + return NULL; + } + return &(*pos); +} + +// If force is true, we assert that the key loads successfully. +void SessionUtil::CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes, + bool force) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox()); + // Provisioning request would be signed by the client and verified by the + // server. + ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + struct RSAPrivateKeyMessage encrypted; + std::vector signature; + ASSERT_NO_FATAL_FAILURE( + s.MakeRSACertificate(&encrypted, sizeof(encrypted), + &signature, allowed_schemes, + encoded_rsa_key_)); + ASSERT_NO_FATAL_FAILURE(s.RewrapRSAKey( + encrypted, sizeof(encrypted), signature, &wrapped_rsa_key_, force)); + // Verify that the clear key is not contained in the wrapped key. + // It should be encrypted. + ASSERT_EQ(NULL, find(wrapped_rsa_key_, encoded_rsa_key_)); +} + +// If force is true, we assert that the key loads successfully. +void SessionUtil::CreateWrappedRSAKeyFromOEMCert( + uint32_t allowed_schemes, bool force) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); + s.GenerateNonce(); + struct RSAPrivateKeyMessage encrypted; + std::vector signature; + std::vector message_key; + std::vector encrypted_message_key; + s.GenerateRSASessionKey(&message_key, &encrypted_message_key); + ASSERT_NO_FATAL_FAILURE( + s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, + allowed_schemes, encoded_rsa_key_, &message_key)); + ASSERT_NO_FATAL_FAILURE( + s.RewrapRSAKey30(encrypted, encrypted_message_key, + &wrapped_rsa_key_, force)); + // Verify that the clear key is not contained in the wrapped key. + // It should be encrypted. + ASSERT_EQ(NULL, find(wrapped_rsa_key_, encoded_rsa_key_)); +} + +// If force is true, we assert that the key loads successfully. +void SessionUtil::CreateWrappedRSAKey(uint32_t allowed_schemes, + bool force) { + switch (global_features.provisioning_method) { + case OEMCrypto_OEMCertificate: + CreateWrappedRSAKeyFromOEMCert(allowed_schemes, force); + break; + case OEMCrypto_Keybox: + CreateWrappedRSAKeyFromKeybox(allowed_schemes, force); + break; + default: + FAIL() << "Cannot generate wrapped RSA key if provision method = " + << wvoec::ProvisioningMethodName( + global_features.provisioning_method); + } +} + +void SessionUtil::InstallKeybox(const uint8_t* keybox, bool good) { + const size_t keybox_size = wvcdm_test_auth::kKeyboxSize; + uint8_t wrapped[keybox_size]; + size_t length = keybox_size; + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_WrapKeybox(keybox, keybox_size, wrapped, &length, NULL, 0)); + OEMCryptoResult sts = OEMCrypto_InstallKeybox(wrapped, length); + if (good) { + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + } else { + // Can return error now, or return error on IsKeyboxValid. + } +} + +void SessionUtil::EnsureTestKeys() { + switch (global_features.derive_key_method) { + case DeviceFeatures::LOAD_TEST_KEYBOX: + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox()); + break; + case DeviceFeatures::LOAD_TEST_RSA_KEY: + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestRSAKey()); + break; + case DeviceFeatures::EXISTING_TEST_KEYBOX: + // already has test keybox. + break; + case DeviceFeatures::FORCE_TEST_KEYBOX: + InstallKeybox(wvcdm_test_auth::kKeybox, true); + break; + case DeviceFeatures::TEST_PROVISION_30: + // Can use oem certificate to install test rsa key. + break; + default: + FAIL() << "Cannot run test without test keybox or RSA key installed."; + } +} + +// This makes sure that the derived keys (encryption key and two mac keys) +// are installed in OEMCrypto and in the test session. +void SessionUtil::InstallTestSessionKeys(Session* s) { + if (global_features.uses_certificate) { + if (global_features.loads_certificate) { + if (wrapped_rsa_key_.size() == 0) { + // If we don't have a wrapped key yet, create one. + // This wrapped key will be shared by all sessions in the test. + ASSERT_NO_FATAL_FAILURE( + CreateWrappedRSAKey(kSign_RSASSA_PSS, true)); + } + // Load the wrapped rsa test key. + ASSERT_NO_FATAL_FAILURE( + s->InstallRSASessionTestKey(wrapped_rsa_key_)); + } + // Test RSA key should be loaded. + ASSERT_NO_FATAL_FAILURE( + s->GenerateDerivedKeysFromSessionKey()); + } else { // Just uses keybox. Test keybox should already be installed. + ASSERT_NO_FATAL_FAILURE( + s->GenerateDerivedKeysFromKeybox()); + } +} + +} diff --git a/oemcrypto/test/oemcrypto_session_tests_helper.h b/oemcrypto/test/oemcrypto_session_tests_helper.h new file mode 100644 index 00000000..dbd3fd84 --- /dev/null +++ b/oemcrypto/test/oemcrypto_session_tests_helper.h @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include + +#include "oec_session_util.h" + +#include "OEMCryptoCENC.h" +#include "test_rsa_key.h" + +namespace wvoec { + +class SessionUtil { +public: + SessionUtil() + : encoded_rsa_key_(wvcdm_test_auth::kRsaPrivateKey_2048, + wvcdm_test_auth::kRsaPrivateKey_2048 + + wvcdm_test_auth::kRsaPrivateKeySize_2048) {} + + // If force is true, we assert that the key loads successfully. + void CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes, bool force); + + // If force is true, we assert that the key loads successfully. + void CreateWrappedRSAKeyFromOEMCert(uint32_t allowed_schemes, bool force); + + // If force is true, we assert that the key loads successfully. + void CreateWrappedRSAKey(uint32_t allowed_schemes, bool force); + + void InstallKeybox(const uint8_t* keybox, bool good); + + void EnsureTestKeys(); + + void InstallTestSessionKeys(Session* s); + + std::vector encoded_rsa_key_; + std::vector wrapped_rsa_key_; +}; + +} // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_test.cpp b/oemcrypto/test/oemcrypto_test.cpp index 6728d9bd..744c6e2f 100644 --- a/oemcrypto/test/oemcrypto_test.cpp +++ b/oemcrypto/test/oemcrypto_test.cpp @@ -29,17 +29,46 @@ #include "log.h" #include "oec_device_features.h" #include "oec_session_util.h" -#include "oec_test_data.h" #include "oemcrypto_key_mock.h" +#include "oemcrypto_session_tests_helper.h" #include "properties.h" #include "string_conversions.h" +#include "test_keybox.h" +#include "test_rsa_key.h" #include "wv_cdm_constants.h" #include "wv_keybox.h" -using namespace std; -using ::testing::WithParamInterface; +using ::testing::Bool; +using ::testing::Combine; using ::testing::Range; using ::testing::Values; +using ::testing::WithParamInterface; +using namespace std; +using std::tr1::tuple; + +namespace std { // GTest wants PrintTo to be in the std namespace. +void PrintTo(const tuple& param, + ostream* os) { + OEMCrypto_CENCEncryptPatternDesc pattern = std::tr1::get<0>(param); + OEMCryptoCipherMode mode = std::tr1::get<1>(param); + bool decrypt_inplace = std::tr1::get<2>(param); + *os << ((mode == OEMCrypto_CipherMode_CTR) ? "CTR mode" : "CBC mode") + << ", encrypt=" << pattern.encrypt << ", skip=" << pattern.skip + << ", decrypt in place = " << (decrypt_inplace ? "true" : "false"); +} +} // namespace std + +namespace { +int GetRandBytes(unsigned char* buf, int num) { + // returns 1 on success, -1 if not supported, or 0 if other failure. +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + return RAND_pseudo_bytes(buf, num); +#else + return RAND_bytes(buf, num); +#endif +} +} // namespace namespace wvoec { @@ -50,11 +79,11 @@ class OEMCryptoClientTest : public ::testing::Test { virtual void SetUp() { ::testing::Test::SetUp(); wvcdm::Properties::Init(); - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); wvcdm::g_cutoff = wvcdm::LOG_INFO; const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info(); LOGD("Running test %s.%s", test_info->test_case_name(), test_info->name()); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); } virtual void TearDown() { @@ -90,7 +119,7 @@ TEST_F(OEMCryptoClientTest, VersionNumber) { cout << " OEMCrypto does not support usage tables." << endl; } ASSERT_GE(version, 8u); - ASSERT_LE(version, 12u); + ASSERT_LE(version, 13u); } TEST_F(OEMCryptoClientTest, ProvisioningDeclaredAPI12) { @@ -120,7 +149,7 @@ const char* HDCPCapabilityAsString(OEMCrypto_HDCP_Capability value) { } } -TEST_F(OEMCryptoClientTest, CheckHDCPCapability) { +TEST_F(OEMCryptoClientTest, CheckHDCPCapabilityAPI09) { OEMCryptoResult sts; OEMCrypto_HDCP_Capability current, maximum; sts = OEMCrypto_GetHDCPCapability(¤t, &maximum); @@ -131,6 +160,26 @@ TEST_F(OEMCryptoClientTest, CheckHDCPCapability) { HDCPCapabilityAsString(maximum)); } +TEST_F(OEMCryptoClientTest, CheckSRMCapabilityV13) { + // This just tests some trivial functionality of the SRM update functions. + bool supported = OEMCrypto_IsSRMUpdateSupported(); + printf(" Update SRM Supported: %s.\n", + supported ? "true" : "false"); + uint16_t version = 0; + OEMCryptoResult current_result = OEMCrypto_GetCurrentSRMVersion(&version); + if (current_result == OEMCrypto_SUCCESS) { + printf(" Current SRM Version: %d.\n", version); + EXPECT_NE(OEMCrypto_SUCCESS, OEMCrypto_GetCurrentSRMVersion(NULL)); + } else if (current_result == OEMCrypto_LOCAL_DISPLAY_ONLY) { + printf(" Current SRM Status: Local Display Only.\n"); + } else { + EXPECT_EQ(OEMCrypto_ERROR_NOT_IMPLEMENTED, current_result); + } + vector bad_srm(42); + GetRandBytes(&bad_srm[0], bad_srm.size()); + EXPECT_NE(OEMCrypto_SUCCESS, OEMCrypto_LoadSRM(&bad_srm[0], bad_srm.size())); +} + TEST_F(OEMCryptoClientTest, CheckMaxNumberOfSessionsAPI10) { size_t sessions_count; ASSERT_EQ(OEMCrypto_SUCCESS, @@ -265,7 +314,7 @@ TEST_F(OEMCryptoClientTest, GenerateTwoNonces) { ASSERT_TRUE(nonce1 != nonce2); // Very unlikely to be equal. } -TEST_F(OEMCryptoClientTest, PreventNonceFlood) { +TEST_F(OEMCryptoClientTest, PreventNonceFloodAPI09) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); int error_counter = 0; @@ -290,7 +339,7 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood) { } // Prevent a nonce flood even if each nonce is in a different session. -TEST_F(OEMCryptoClientTest, PreventNonceFlood2) { +TEST_F(OEMCryptoClientTest, PreventNonceFlood2API09) { int error_counter = 0; time_t test_start = time(NULL); // More than 20 nonces per second should generate an error. @@ -320,7 +369,7 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood2) { // is different from the test above because there are several session open at // the same time. We want to make sure you can't get a flood of nonces by // opening a flood of sessions. -TEST_F(OEMCryptoClientTest, PreventNonceFlood3) { +TEST_F(OEMCryptoClientTest, PreventNonceFlood3API09) { int request_counter = 0; int error_counter = 0; time_t test_start = time(NULL); @@ -350,7 +399,7 @@ TEST_F(OEMCryptoClientTest, PreventNonceFlood3) { TEST_F(OEMCryptoClientTest, ClearCopyTestAPI10) { const int kDataSize = 256; vector input_buffer(kDataSize); - RAND_pseudo_bytes(&input_buffer[0], input_buffer.size()); + GetRandBytes(&input_buffer[0], input_buffer.size()); vector output_buffer(kDataSize); OEMCrypto_DestBufferDesc dest_buffer; dest_buffer.type = OEMCrypto_BufferType_Clear; @@ -382,9 +431,9 @@ TEST_F(OEMCryptoClientTest, ClearCopyTestAPI10) { OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); } -TEST_F(OEMCryptoClientTest, ClearCopyTestLargeBuffer) { +TEST_F(OEMCryptoClientTest, ClearCopyTestLargeBufferAPI10) { vector input_buffer(kMaxDecryptSize); - RAND_pseudo_bytes(&input_buffer[0], input_buffer.size()); + GetRandBytes(&input_buffer[0], input_buffer.size()); vector output_buffer(kMaxDecryptSize); OEMCrypto_DestBufferDesc dest_buffer; dest_buffer.type = OEMCrypto_BufferType_Clear; @@ -501,7 +550,7 @@ TEST_F(OEMCryptoProv30Test, OEMCertSignature) { OEMCryptoResult sts; // Sign a Message vector data(500); - RAND_pseudo_bytes(&data[0], data.size()); + GetRandBytes(&data[0], data.size()); size_t signature_length = 0; vector signature(1); @@ -529,7 +578,7 @@ TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) { OEMCryptoResult sts; // Sign a Message vector data(500); - RAND_pseudo_bytes(&data[0], data.size()); + GetRandBytes(&data[0], data.size()); size_t signature_length = 0; vector signature(1); @@ -555,7 +604,7 @@ TEST_F(OEMCryptoProv30Test, OEMCertSignatureLargeBuffer) { OEMCryptoResult sts; // Sign a Message vector data(kMaxMessageSize); - RAND_pseudo_bytes(&data[0], data.size()); + GetRandBytes(&data[0], data.size()); size_t signature_length = 0; vector signature(1); @@ -581,142 +630,47 @@ TEST_F(OEMCryptoProv30Test, OEMCertSignatureLargeBuffer) { // // These tests will use either a test keybox or a test certificate to derive // session keys. -class OEMCryptoSessionTests : public OEMCryptoClientTest { +class OEMCryptoSessionTests : public OEMCryptoClientTest, + public SessionUtil { protected: - OEMCryptoSessionTests() - : encoded_rsa_key_(kTestRSAPKCS8PrivateKeyInfo2_2048, - kTestRSAPKCS8PrivateKeyInfo2_2048 + - sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048)) {} + OEMCryptoSessionTests() {} virtual void SetUp() { OEMCryptoClientTest::SetUp(); EnsureTestKeys(); - if (global_features.usage_table) OEMCrypto_DeleteUsageTable(); + if (global_features.usage_table) { + CreateUsageTableHeader(); + } } - void EnsureTestKeys() { - switch (global_features.derive_key_method) { - case DeviceFeatures::LOAD_TEST_KEYBOX: - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox()); - break; - case DeviceFeatures::LOAD_TEST_RSA_KEY: - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestRSAKey()); - break; - case DeviceFeatures::EXISTING_TEST_KEYBOX: - // already has test keybox. - break; - case DeviceFeatures::FORCE_TEST_KEYBOX: - InstallKeybox(kTestKeybox, true); - break; - case DeviceFeatures::TEST_PROVISION_30: - // Can use oem certificate to install test rsa key. - break; - default: - FAIL() << "Cannot run test without test keybox or RSA key installed."; + void CreateUsageTableHeader(bool expect_success = true) { + size_t header_buffer_length = 0; + OEMCryptoResult sts = + OEMCrypto_CreateUsageTableHeader(NULL, &header_buffer_length); + if (expect_success) { + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + } else { + ASSERT_NE(OEMCrypto_SUCCESS, sts); + if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return; + } + encrypted_usage_header_.resize(header_buffer_length); + sts = OEMCrypto_CreateUsageTableHeader(&encrypted_usage_header_[0], + &header_buffer_length); + if (expect_success) { + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + } else { + ASSERT_NE(OEMCrypto_SUCCESS, sts); } } virtual void TearDown() { // If we installed a bad keybox, end with a good one installed. if (global_features.derive_key_method == DeviceFeatures::FORCE_TEST_KEYBOX) - InstallKeybox(kTestKeybox, true); + InstallKeybox(wvcdm_test_auth::kKeybox, true); OEMCryptoClientTest::TearDown(); } - void InstallKeybox(const wvoec_mock::WidevineKeybox& keybox, bool good) { - uint8_t wrapped[sizeof(wvoec_mock::WidevineKeybox)]; - size_t length = sizeof(wvoec_mock::WidevineKeybox); - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_WrapKeybox(reinterpret_cast(&keybox), - sizeof(keybox), wrapped, &length, NULL, 0)); - OEMCryptoResult sts = OEMCrypto_InstallKeybox(wrapped, sizeof(keybox)); - if (good) { - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - } else { - // Can return error now, or return error on IsKeyboxValid. - } - } - - // This makes sure that the derived keys (encryption key and two mac keys) - // are installed in OEMCrypto and in the test session. - void InstallTestSessionKeys(Session* s) { - if (global_features.uses_certificate) { - if (global_features.loads_certificate) { - if (wrapped_rsa_key_.size() == 0) { - // If we don't have a wrapped key yet, create one. - // This wrapped key will be shared by all sessions in the test. - ASSERT_NO_FATAL_FAILURE(CreateWrappedRSAKey(kSign_RSASSA_PSS, true)); - } - // Load the wrapped rsa test key. - ASSERT_NO_FATAL_FAILURE(s->InstallRSASessionTestKey(wrapped_rsa_key_)); - } - // Test RSA key should be loaded. - ASSERT_NO_FATAL_FAILURE(s->GenerateDerivedKeysFromSessionKey()); - } else { // Just uses keybox. Test keybox should already be installed. - ASSERT_NO_FATAL_FAILURE(s->GenerateDerivedKeysFromKeybox()); - } - } - - // If force is true, we assert that the key loads successfully. - void CreateWrappedRSAKey(uint32_t allowed_schemes, bool force) { - switch (global_features.provisioning_method) { - case OEMCrypto_OEMCertificate: - CreateWrappedRSAKeyFromOEMCert(allowed_schemes, force); - break; - case OEMCrypto_Keybox: - CreateWrappedRSAKeyFromKeybox(allowed_schemes, force); - break; - default: - FAIL() << "Cannot generate wrapped RSA key if provision method = " - << wvoec::ProvisioningMethodName( - global_features.provisioning_method); - } - } - - // If force is true, we assert that the key loads successfully. - void CreateWrappedRSAKeyFromKeybox(uint32_t allowed_schemes, bool force) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.GenerateDerivedKeysFromKeybox()); - // Provisioning request would be signed by the client and verified by the - // server. - ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); - struct RSAPrivateKeyMessage encrypted; - std::vector signature; - ASSERT_NO_FATAL_FAILURE(s.MakeRSACertificate(&encrypted, sizeof(encrypted), - &signature, allowed_schemes, - encoded_rsa_key_)); - ASSERT_NO_FATAL_FAILURE(s.RewrapRSAKey( - encrypted, sizeof(encrypted), signature, &wrapped_rsa_key_, force)); - // Verify that the clear key is not contained in the wrapped key. - // It should be encrypted. - ASSERT_EQ(NULL, find(wrapped_rsa_key_, encoded_rsa_key_)); - } - - // If force is true, we assert that the key loads successfully. - void CreateWrappedRSAKeyFromOEMCert(uint32_t allowed_schemes, bool force) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); - s.GenerateNonce(); - struct RSAPrivateKeyMessage encrypted; - std::vector signature; - std::vector message_key; - std::vector encrypted_message_key; - s.GenerateRSASessionKey(&message_key, &encrypted_message_key); - ASSERT_NO_FATAL_FAILURE( - s.MakeRSACertificate(&encrypted, sizeof(encrypted), &signature, - allowed_schemes, encoded_rsa_key_, &message_key)); - ASSERT_NO_FATAL_FAILURE(s.RewrapRSAKey30(encrypted, sizeof(encrypted), - encrypted_message_key, - &wrapped_rsa_key_, force)); - // Verify that the clear key is not contained in the wrapped key. - // It should be encrypted. - ASSERT_EQ(NULL, find(wrapped_rsa_key_, encoded_rsa_key_)); - } - - std::vector encoded_rsa_key_; - std::vector wrapped_rsa_key_; + vector encrypted_usage_header_; }; class OEMCryptoSessionTestKeyboxTest : public OEMCryptoSessionTests {}; @@ -729,14 +683,12 @@ TEST_F(OEMCryptoSessionTestKeyboxTest, GoodForceKeybox) { ASSERT_EQ(DeviceFeatures::FORCE_TEST_KEYBOX, global_features.derive_key_method) << "ForceKeybox tests will modify the installed keybox."; - wvoec_mock::WidevineKeybox keybox = kValidKeybox02; OEMCryptoResult sts; - InstallKeybox(keybox, true); + InstallKeybox(wvcdm_test_auth::kKeyboxValid02, true); sts = OEMCrypto_IsKeyboxValid(); ASSERT_EQ(OEMCrypto_SUCCESS, sts); - keybox = kValidKeybox03; - InstallKeybox(keybox, true); + InstallKeybox(wvcdm_test_auth::kKeyboxValid03, true); sts = OEMCrypto_IsKeyboxValid(); ASSERT_EQ(OEMCrypto_SUCCESS, sts); } @@ -745,8 +697,9 @@ TEST_F(OEMCryptoSessionTestKeyboxTest, BadCRCForceKeybox) { ASSERT_EQ(DeviceFeatures::FORCE_TEST_KEYBOX, global_features.derive_key_method) << "ForceKeybox tests will modify the installed keybox."; - wvoec_mock::WidevineKeybox keybox = kValidKeybox02; - keybox.crc_[1] ^= 42; + uint8_t keybox[wvcdm_test_auth::kKeyboxSize]; + memcpy(keybox, wvcdm_test_auth::kKeyboxValid02, wvcdm_test_auth::kKeyboxSize); + keybox[125] ^= 42; // crc[1] OEMCryptoResult sts; InstallKeybox(keybox, false); sts = OEMCrypto_IsKeyboxValid(); @@ -757,8 +710,9 @@ TEST_F(OEMCryptoSessionTestKeyboxTest, BadMagicForceKeybox) { ASSERT_EQ(DeviceFeatures::FORCE_TEST_KEYBOX, global_features.derive_key_method) << "ForceKeybox tests will modify the installed keybox."; - wvoec_mock::WidevineKeybox keybox = kValidKeybox02; - keybox.magic_[1] ^= 42; + uint8_t keybox[wvcdm_test_auth::kKeyboxSize]; + memcpy(keybox, wvcdm_test_auth::kKeyboxValid02, wvcdm_test_auth::kKeyboxSize); + keybox[121] ^= 42; // magic[1] OEMCryptoResult sts; InstallKeybox(keybox, false); sts = OEMCrypto_IsKeyboxValid(); @@ -769,8 +723,9 @@ TEST_F(OEMCryptoSessionTestKeyboxTest, BadDataForceKeybox) { ASSERT_EQ(DeviceFeatures::FORCE_TEST_KEYBOX, global_features.derive_key_method) << "ForceKeybox tests will modify the installed keybox."; - wvoec_mock::WidevineKeybox keybox = kValidKeybox02; - keybox.data_[1] ^= 42; + uint8_t keybox[wvcdm_test_auth::kKeyboxSize]; + memcpy(keybox, wvcdm_test_auth::kKeyboxValid02, wvcdm_test_auth::kKeyboxSize); + keybox[49] ^= 42; // data[1] OEMCryptoResult sts; InstallKeybox(keybox, false); sts = OEMCrypto_IsKeyboxValid(); @@ -937,7 +892,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange1) { s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, &mac_keys[0], // Not pointing into buffer. - s.key_array_size(), s.key_array(), NULL, 0); + s.num_keys(), s.key_array(), NULL, 0, NULL); ASSERT_NE(OEMCrypto_SUCCESS, sts); } @@ -951,12 +906,12 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange2) { s.encrypted_license().mac_key_iv + sizeof(s.encrypted_license().mac_key_iv)); - OEMCryptoResult sts = OEMCrypto_LoadKeys( - s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], - s.signature().size(), - &mac_key_iv[0], // bad. - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - NULL, 0); + OEMCryptoResult sts = + OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), + &mac_key_iv[0], // bad. + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), NULL, 0, NULL); ASSERT_NE(OEMCrypto_SUCCESS, sts); } @@ -974,8 +929,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange3) { OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - NULL, 0); + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL); ASSERT_NE(OEMCrypto_SUCCESS, sts); } @@ -994,8 +949,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange4) { OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - NULL, 0); + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL); ASSERT_NE(OEMCrypto_SUCCESS, sts); } @@ -1012,8 +967,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange5) { OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - NULL, 0); + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL); ASSERT_NE(OEMCrypto_SUCCESS, sts); } @@ -1032,8 +987,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange6) { OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - NULL, 0); + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL); ASSERT_NE(OEMCrypto_SUCCESS, sts); } @@ -1052,8 +1007,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadRange7) { OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - NULL, 0); + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL); ASSERT_NE(OEMCrypto_SUCCESS, sts); } @@ -1068,8 +1023,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadNonce) { OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - NULL, 0); + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL); ASSERT_NE(OEMCrypto_SUCCESS, sts); } @@ -1094,8 +1049,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithRepeatNonce) { OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - NULL, 0); + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL); ASSERT_NE(OEMCrypto_SUCCESS, sts); } @@ -1110,8 +1065,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeyWithBadVerification) { OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - NULL, 0); + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL); ASSERT_NE(OEMCrypto_SUCCESS, sts); } @@ -1122,8 +1077,9 @@ class SessionTestAlternateVerification : public OEMCryptoSessionTests, public: virtual void SetUp() { OEMCryptoSessionTests::SetUp(); - target_api_ = GetParam(); + target_api_ = static_cast(GetParam()); } + protected: uint32_t target_api_; }; @@ -1138,21 +1094,22 @@ TEST_P(SessionTestAlternateVerification, LoadKeys) { if (target_api_ > 8 && target_api_ < 100) { snprintf(buffer, 5, "kc%02d", target_api_); } - for(int i=0; i < s.num_keys(); i++) { + for (unsigned int i = 0; i < s.num_keys(); i++) { memcpy(s.license().keys[i].control.verification, buffer, 4); } ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - NULL, 0); + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL); // If this is a future API, then LoadKeys should fail. if (global_features.api_version < target_api_) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } else { // Otherwise, LoadKeys should succeed. - ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(OEMCrypto_SUCCESS, sts) + << "LoadKeys failed for key control block kc" << target_api_; } } @@ -1160,7 +1117,7 @@ TEST_P(SessionTestAlternateVerification, LoadKeys) { // the current API + 2. We use +2 because we want to test at least 1 // future API, and the ::testing::Range is not inclusive. INSTANTIATE_TEST_CASE_P(TestAll, SessionTestAlternateVerification, - Range(8, 12 + 2)); + Range(8, 13 + 2)); TEST_F(OEMCryptoSessionTests, LoadKeysBadSignature) { Session s; @@ -1172,8 +1129,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeysBadSignature) { OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - NULL, 0); + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL); ASSERT_NE(OEMCrypto_SUCCESS, sts); } @@ -1186,8 +1143,8 @@ TEST_F(OEMCryptoSessionTests, LoadKeysWithNoDerivedKeys) { OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - NULL, 0); + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL); ASSERT_NE(OEMCrypto_SUCCESS, sts); } @@ -1204,7 +1161,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNoKeys) { OEMCrypto_SUCCESS, OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), NULL, NULL, - kNoKeys, s.key_array(), NULL, 0)); + kNoKeys, s.key_array(), NULL, 0, NULL)); } TEST_F(OEMCryptoSessionTests, LoadKeyNoKeyWithNonce) { @@ -1219,7 +1176,7 @@ TEST_F(OEMCryptoSessionTests, LoadKeyNoKeyWithNonce) { OEMCrypto_SUCCESS, OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), NULL, NULL, - kNoKeys, s.key_array(), NULL, 0)); + kNoKeys, s.key_array(), NULL, 0, NULL)); } TEST_F(OEMCryptoSessionTests, QueryKeyControl) { @@ -1259,8 +1216,8 @@ TEST_F(OEMCryptoSessionTests, AntiRollbackHardwareRequired) { OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - NULL, 0); + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL); if (OEMCrypto_IsAntiRollbackHwPresent()) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); } else { @@ -1283,8 +1240,8 @@ TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), - s.key_array(), NULL, 0)); + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), NULL, 0, NULL)); } if (patch_level < 0x3F) { Session s; @@ -1299,8 +1256,8 @@ TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), - s.key_array(), NULL, 0)); + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), NULL, 0, NULL)); } if (patch_level > 0) { Session s; @@ -1315,12 +1272,12 @@ TEST_F(OEMCryptoSessionTests, CheckMinimumPatchLevel) { OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), - s.key_array(), NULL, 0)); + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), NULL, 0, NULL)); } } -TEST_F(OEMCryptoSessionTests, Minimum20Keys) { +TEST_F(OEMCryptoSessionTests, Minimum20KeysAPI12) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -1363,7 +1320,7 @@ class SessionTestDecryptWithHDCP : public OEMCryptoSessionTests, } }; -TEST_P(SessionTestDecryptWithHDCP, Decrypt) { +TEST_P(SessionTestDecryptWithHDCP, DecryptAPI09) { // Test parameterized by HDCP version. DecryptWithHDCP(static_cast(GetParam())); } @@ -1417,7 +1374,7 @@ TEST_P(SessionTestRefreshKeyTest, RefreshNoNonce) { s.RefreshTestKeys(num_keys_, 0, 0, OEMCrypto_SUCCESS)); } -TEST_P(SessionTestRefreshKeyTest, RefreshOldNonce) { +TEST_P(SessionTestRefreshKeyTest, RefreshOldNonceAPI11) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -1434,7 +1391,7 @@ TEST_P(SessionTestRefreshKeyTest, RefreshOldNonce) { OEMCrypto_ERROR_INVALID_NONCE)); } -TEST_P(SessionTestRefreshKeyTest, RefreshBadNonce) { +TEST_P(SessionTestRefreshKeyTest, RefreshBadNonceAPI11) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -1469,6 +1426,49 @@ TEST_P(SessionTestRefreshKeyTest, RefreshLargeBuffer) { s.get_nonce(), OEMCrypto_SUCCESS)); } +// This situation would occur if an app only uses one key in the license. When +// that happens, SelectKey would be called before the first decrypt, and then +// would not need to be called again, even if the license is refreshed. +TEST_P(SessionTestRefreshKeyTest, RefreshWithNoSelectKey) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + kDuration, wvoec_mock::kControlNonceEnabled, s.get_nonce())); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys("", new_mac_keys_)); + // Call select key before the refresh. No calls below to TestDecryptCTR with + // select key set to true. + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(true)); + + s.GenerateNonce(); + // License renewal message is signed by client and verified by the server. + ASSERT_NO_FATAL_FAILURE(s.VerifyClientSignature()); + // Note: we store the message in encrypted_license_, but the refresh key + // message is not actually encrypted. It is, however, signed. + // FillRefreshMessage fills the message with a duration of kLongDuration. + ASSERT_NO_FATAL_FAILURE(s.FillRefreshMessage( + num_keys_, wvoec_mock::kControlNonceEnabled, s.get_nonce())); + s.ServerSignBuffer(reinterpret_cast(&s.encrypted_license()), + s.message_size(), &s.signature()); + OEMCrypto_KeyRefreshObject key_array[num_keys_]; + s.FillRefreshArray(key_array, num_keys_); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_RefreshKeys(s.session_id(), s.message_ptr(), + s.message_size(), &s.signature()[0], + s.signature().size(), num_keys_, key_array)); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false)); + // This should still be valid key, even if the refresh failed, because this + // is before the original license duration. + sleep(kShortSleep); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false)); + // This should be after duration of the original license, but before the + // expiration of the refresh message. This should succeed if and only if the + // refresh succeeded. + sleep(kShortSleep + kLongSleep); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false)); +} + // Of only one key control block in the refesh, we update all the keys. INSTANTIATE_TEST_CASE_P(TestRefreshAllKeys, SessionTestRefreshKeyTest, Values(std::make_pair(true, 1), @@ -1573,12 +1573,14 @@ struct SampleInitData { class OEMCryptoSessionTestsDecryptTests : public OEMCryptoSessionTests, - public WithParamInterface { + public WithParamInterface > { protected: virtual void SetUp() { OEMCryptoSessionTests::SetUp(); - pattern_ = GetParam().pattern; - cipher_mode_ = GetParam().mode; + pattern_ = std::tr1::get<0>(GetParam()); + cipher_mode_ = std::tr1::get<1>(GetParam()); + decrypt_inplace_ = std::tr1::get<2>(GetParam()); } void FindTotalSize() { @@ -1647,7 +1649,8 @@ class OEMCryptoSessionTestsDecryptTests // Partial block. Don't increment iv. Compute next block offset. block_offset = block_offset + size; } else { - EXPECT_EQ((size_t)AES_BLOCK_SIZE, block_offset + size); + EXPECT_EQ(static_cast(AES_BLOCK_SIZE), + block_offset + size); // Full block. Increment iv, and set offset to 0 for next block. ctr128_inc64(1, iv); block_offset = 0; @@ -1686,7 +1689,17 @@ class OEMCryptoSessionTestsDecryptTests ASSERT_EQ(OEMCrypto_SUCCESS, sts); // We decrypt each subsample. - vector outputBuffer(total_size_ + 16, 0xaa); + vector output_buffer(total_size_ + 16, 0xaa); + const uint8_t *input_buffer = NULL; + if (decrypt_inplace_) { // Use same buffer for input and output. + // Copy the useful data from encryptedData to output_buffer, which + // will be the same as input_buffer. Leave the 0xaa padding at the end. + for(size_t i=0; i < total_size_; i++) output_buffer[i] = encryptedData[i]; + // Now let input_buffer point to the same data. + input_buffer = &output_buffer[0]; + } else { + input_buffer = &encryptedData[0]; + } size_t buffer_offset = 0; for (size_t i = 0; i < subsample_size_.size(); i++) { OEMCrypto_CENCEncryptPatternDesc pattern = pattern_; @@ -1697,7 +1710,7 @@ class OEMCryptoSessionTestsDecryptTests uint8_t subsample_flags = 0; if (subsample_size_[i].clear_size > 0) { destBuffer.type = OEMCrypto_BufferType_Clear; - destBuffer.buffer.clear.address = &outputBuffer[buffer_offset]; + destBuffer.buffer.clear.address = &output_buffer[buffer_offset]; destBuffer.buffer.clear.max_length = total_size_ - buffer_offset; if (i == 0) subsample_flags |= OEMCrypto_FirstSubsample; if ((i == subsample_size_.size() - 1) && @@ -1705,7 +1718,7 @@ class OEMCryptoSessionTestsDecryptTests subsample_flags |= OEMCrypto_LastSubsample; } sts = - OEMCrypto_DecryptCENC(s.session_id(), &encryptedData[buffer_offset], + OEMCrypto_DecryptCENC(s.session_id(), input_buffer + buffer_offset, subsample_size_[i].clear_size, is_encrypted, sample_init_data_[i].iv, block_offset, &destBuffer, &pattern, subsample_flags); @@ -1714,7 +1727,7 @@ class OEMCryptoSessionTestsDecryptTests } if (subsample_size_[i].encrypted_size > 0) { destBuffer.type = OEMCrypto_BufferType_Clear; - destBuffer.buffer.clear.address = &outputBuffer[buffer_offset]; + destBuffer.buffer.clear.address = &output_buffer[buffer_offset]; destBuffer.buffer.clear.max_length = total_size_ - buffer_offset; is_encrypted = true; block_offset = sample_init_data_[i].block_offset; @@ -1726,7 +1739,7 @@ class OEMCryptoSessionTestsDecryptTests subsample_flags |= OEMCrypto_LastSubsample; } sts = OEMCrypto_DecryptCENC( - s.session_id(), &encryptedData[buffer_offset], + s.session_id(), input_buffer + buffer_offset, subsample_size_[i].encrypted_size, is_encrypted, sample_init_data_[i].iv, block_offset, &destBuffer, &pattern, subsample_flags); @@ -1740,13 +1753,14 @@ class OEMCryptoSessionTestsDecryptTests buffer_offset += subsample_size_[i].encrypted_size; } } - EXPECT_EQ(0xaa, outputBuffer[total_size_]) << "Buffer overrun."; - outputBuffer.resize(total_size_); - EXPECT_EQ(unencryptedData, outputBuffer); + EXPECT_EQ(0xaa, output_buffer[total_size_]) << "Buffer overrun."; + output_buffer.resize(total_size_); + EXPECT_EQ(unencryptedData, output_buffer); } OEMCrypto_CENCEncryptPatternDesc pattern_; OEMCryptoCipherMode cipher_mode_; + bool decrypt_inplace_; // If true, input and output buffers are the same. vector subsample_size_; size_t total_size_; vector sample_init_data_; @@ -1767,8 +1781,8 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, SingleLargeSubsample) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -1785,8 +1799,8 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, PatternPlusOneBlock) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -1799,8 +1813,8 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, OneBlock) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -1818,8 +1832,8 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, NoOffset) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -1839,7 +1853,7 @@ TEST_P(OEMCryptoSessionTestsPartialBlockTests, EvenOffset) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); // CTR Mode is self-inverse -- i.e. We can pick the encrypted data and // compute the unencrypted data. By picking the encrypted data to be all 0, // it is easier to re-encrypt the data and debug problems. Similarly, we @@ -1867,8 +1881,8 @@ TEST_P(OEMCryptoSessionTestsPartialBlockTests, OddOffset) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -1891,7 +1905,7 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptWithNearWrap) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); encryptionIv = wvcdm::a2b_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); @@ -1911,8 +1925,8 @@ TEST_P(OEMCryptoSessionTestsPartialBlockTests, PartialBlock) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -1932,8 +1946,8 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptLargeBuffer) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -1946,8 +1960,8 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptUnencrypted) { vector encryptedData(total_size_); vector encryptionIv(AES_BLOCK_SIZE); vector key(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); - EXPECT_EQ(1, RAND_pseudo_bytes(&key[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&key[0], AES_BLOCK_SIZE)); for (size_t i = 0; i < total_size_; i++) unencryptedData[i] = i % 256; EncryptData(key, encryptionIv, unencryptedData, &encryptedData); TestDecryptCENC(key, encryptionIv, encryptedData, unencryptedData); @@ -1963,7 +1977,7 @@ TEST_F(OEMCryptoSessionTests, DecryptUnencryptedNoKey) { vector in_buffer(256); for (size_t i = 0; i < in_buffer.size(); i++) in_buffer[i] = i % 256; vector encryptionIv(AES_BLOCK_SIZE); - EXPECT_EQ(1, RAND_pseudo_bytes(&encryptionIv[0], AES_BLOCK_SIZE)); + EXPECT_EQ(1, GetRandBytes(&encryptionIv[0], AES_BLOCK_SIZE)); // Describe the output vector out_buffer(in_buffer.size()); OEMCrypto_DestBufferDesc destBuffer; @@ -1984,107 +1998,56 @@ TEST_F(OEMCryptoSessionTests, DecryptUnencryptedNoKey) { ASSERT_EQ(in_buffer, out_buffer); } -// This test is not run by default, because it takes a long time and -// is used to measure decrypt performance, not test functionality. -// TODO(fredgc): Verify this test runs correctly. -TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptCENCPerformance) { - OEMCryptoResult sts; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - const time_t TestDuration = 5; - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(600, 0, 0)); - s.license().keys[0].cipher_mode = GetParam().mode; - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); - sts = OEMCrypto_SelectKey(s.session_id(), s.license().keys[0].key_id, - s.license().keys[0].key_id_length); - ASSERT_EQ(OEMCrypto_SUCCESS, sts); - vector encryptionIv = - wvcdm::a2b_hex("719dbcb253b2ec702bb8c1b1bc2f3bc6"); - vector input(kMaxDecryptSize); - printf("Size of input is %zd\n", input.size()); - for (unsigned int i = 0; i < kMaxDecryptSize; i++) input[i] = i % 256; - vector output(kMaxDecryptSize); - OEMCrypto_DestBufferDesc destBuffer; - destBuffer.type = OEMCrypto_BufferType_Clear; - destBuffer.buffer.clear.address = &output[0]; - OEMCrypto_CENCEncryptPatternDesc pattern = GetParam().pattern; - - const char* level = OEMCrypto_SecurityLevel(); - const int n = 10; - double x[n], y[n]; - double xsum = 0.0; - double ysum = 0.0; - double xysum = 0.0; - double xsqsum = 0.0; - printf( - "PERF:head, security, bytes, bytes/frame, time(ms)/frame, bandwidth\n"); - - for (int i = 0; i < n; i++) { - size_t length = 1024 + i * 1024; - destBuffer.buffer.clear.max_length = length; - time_t test_start = time(NULL); - time_t test_end = time(NULL); - int count = 0; - size_t total = 0; - do { - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_DecryptCENC( - s.session_id(), &input[0], length, true, &encryptionIv[0], - 0, &destBuffer, &pattern, - OEMCrypto_FirstSubsample | OEMCrypto_LastSubsample)); - count++; - total += length; - test_end = time(NULL); - } while (test_end - test_start < TestDuration); - x[i] = length; - y[i] = 1024 * (test_end - test_start) / ((double)count); - xsum += x[i]; - ysum += y[i]; - xysum += x[i] * y[i]; - xsqsum += x[i] * x[i]; - printf("PERF:stat, %s, %12zd, %12g, %12g, %12g\n", level, total, x[i], - y[i], ((double)total) / ((double)(test_end - test_start))); - } - double b = (n * xysum - xsum * ysum) / (n * xsqsum - xsum * xsum); - double a = (ysum - b * xsum) / n; - printf("PERF-FIT, security=%s fit time(ms)/frame = %g + %g * buffer_size\n", - level, a, b); - for (int i = 0; i < n; i++) { - printf("PERF-FIT, %12g, %12g, %12g\n", x[i], y[i], a + b * x[i]); - } +// Used to construct a specific pattern. +OEMCrypto_CENCEncryptPatternDesc MakePattern(size_t encrypt, size_t skip) { + OEMCrypto_CENCEncryptPatternDesc pattern; + pattern.encrypt = encrypt; + pattern.skip = skip; + pattern.offset = 0; // offset is deprecated. + return pattern; } INSTANTIATE_TEST_CASE_P(CTRTests, OEMCryptoSessionTestsPartialBlockTests, - Values(PatternTestVariant(0, 0, - OEMCrypto_CipherMode_CTR))); + Combine(Values(MakePattern(0,0)), + Values(OEMCrypto_CipherMode_CTR), + Bool())); + +// Decrypt in place for CBC tests was only required in v13. INSTANTIATE_TEST_CASE_P( - CBCTests, OEMCryptoSessionTestsPartialBlockTests, - Values(PatternTestVariant(0, 0, OEMCrypto_CipherMode_CBC), - PatternTestVariant(3, 7, OEMCrypto_CipherMode_CBC), - // HLS Edge case. We should follow the CENC spec, not HLS spec. - PatternTestVariant(9, 1, OEMCrypto_CipherMode_CBC), - PatternTestVariant(1, 9, OEMCrypto_CipherMode_CBC), - PatternTestVariant(1, 3, OEMCrypto_CipherMode_CBC), - PatternTestVariant(2, 1, OEMCrypto_CipherMode_CBC))); + CBCTestsAPI13, OEMCryptoSessionTestsPartialBlockTests, + Combine( + Values(MakePattern(0, 0), + MakePattern(3, 7), + // HLS Edge case. We should follow the CENC spec, not HLS spec. + MakePattern(9, 1), + MakePattern(1, 9), + MakePattern(1, 3), + MakePattern(2, 1)), + Values(OEMCrypto_CipherMode_CBC), Bool())); + INSTANTIATE_TEST_CASE_P( - CTRTests, OEMCryptoSessionTestsDecryptTests, - Values(PatternTestVariant(0, 0, OEMCrypto_CipherMode_CTR), - PatternTestVariant(3, 7, OEMCrypto_CipherMode_CTR), - // Pattern length should be 10, but that is not guaranteed. - PatternTestVariant(1, 3, OEMCrypto_CipherMode_CTR), - PatternTestVariant(2, 1, OEMCrypto_CipherMode_CTR))); + CTRTestsAPI11, OEMCryptoSessionTestsDecryptTests, + Combine( + Values(MakePattern(0, 0), + MakePattern(3, 7), + // Pattern length should be 10, but that is not guaranteed. + MakePattern(1, 3), + MakePattern(2, 1)), + Values(OEMCrypto_CipherMode_CTR), Bool())); + +// Decrypt in place for CBC tests was only required in v13. INSTANTIATE_TEST_CASE_P( - CBCTests, OEMCryptoSessionTestsDecryptTests, - Values(PatternTestVariant(0, 0, OEMCrypto_CipherMode_CBC), - PatternTestVariant(3, 7, OEMCrypto_CipherMode_CBC), - // HLS Edge case. We should follow the CENC spec, not HLS spec. - PatternTestVariant(9, 1, OEMCrypto_CipherMode_CBC), - PatternTestVariant(1, 9, OEMCrypto_CipherMode_CBC), - // Pattern length should be 10, but that is not guaranteed. - PatternTestVariant(1, 3, OEMCrypto_CipherMode_CBC), - PatternTestVariant(2, 1, OEMCrypto_CipherMode_CBC))); + CBCTestsAPI13, OEMCryptoSessionTestsDecryptTests, + Combine( + Values(MakePattern(0, 0), + MakePattern(3, 7), + // HLS Edge case. We should follow the CENC spec, not HLS spec. + MakePattern(9, 1), + MakePattern(1, 9), + // Pattern length should be 10, but that is not guaranteed. + MakePattern(1, 3), + MakePattern(2, 1)), + Values(OEMCrypto_CipherMode_CBC), Bool())); TEST_F(OEMCryptoSessionTests, DecryptSecureToClear) { Session s; @@ -2100,6 +2063,18 @@ TEST_F(OEMCryptoSessionTests, DecryptSecureToClear) { s.TestDecryptCTR(true, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } +TEST_F(OEMCryptoSessionTests, DecryptNoAnalogToClearAPI13) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + kDuration, wvoec_mock::kControlDisableAnalogOutput, 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + ASSERT_NO_FATAL_FAILURE( + s.TestDecryptCTR(true, OEMCrypto_ERROR_ANALOG_OUTPUT)); +} + TEST_F(OEMCryptoSessionTests, KeyDuration) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -2113,9 +2088,7 @@ TEST_F(OEMCryptoSessionTests, KeyDuration) { ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false, OEMCrypto_SUCCESS)); sleep(kLongSleep); // Should be expired key. ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR(false, OEMCrypto_ERROR_KEY_EXPIRED)); - ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, - OEMCrypto_SelectKey(s.session_id(), s.license().keys[0].key_id, - s.license().keys[0].key_id_length)); + ASSERT_NO_FATAL_FAILURE(s.TestSelectExpired(0)); } // @@ -2430,6 +2403,30 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateDecrypt) { ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); } +// Test a 3072 bit RSA key certificate. +TEST_F(OEMCryptoLoadsCertificate, TestLargeRSAKey3072) { + encoded_rsa_key_.assign(wvcdm_test_auth::kRsaPrivateKey_3072, + wvcdm_test_auth::kRsaPrivateKey_3072 + + wvcdm_test_auth::kRsaPrivateKeySize_3072); + CreateWrappedRSAKey(kSign_RSASSA_PSS, true); + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(&encoded_rsa_key_[0], + encoded_rsa_key_.size())); + ASSERT_NO_FATAL_FAILURE(s.InstallRSASessionTestKey(wrapped_rsa_key_)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(kDuration, 0, 0)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); +} + +// Devices that load certificates, should at least support RSA 2048 keys. +TEST_F(OEMCryptoLoadsCertificate, SupportsCertificatesAPI13) { + ASSERT_NE(0u, + OEMCrypto_Supports_RSA_2048bit & OEMCrypto_SupportedCertificates()) + << "Supported certificates is only " << OEMCrypto_SupportedCertificates(); +} + class OEMCryptoUsesCertificate : public OEMCryptoLoadsCertificate { protected: virtual void SetUp() { @@ -2489,7 +2486,7 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); const size_t size = 50; vector licenseRequest(size); - RAND_pseudo_bytes(&licenseRequest[0], licenseRequest.size()); + GetRandBytes(&licenseRequest[0], licenseRequest.size()); size_t signature_length = 0; sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], licenseRequest.size(), NULL, @@ -2500,6 +2497,7 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], licenseRequest.size(), signature, &signature_length, kSign_RSASSA_PSS); + delete[] signature; ASSERT_EQ(OEMCrypto_SUCCESS, sts); count++; gettimeofday(&end_time, NULL); @@ -2516,7 +2514,8 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { wrapped_rsa_key_.size())); vector session_key; vector enc_session_key; - s.PreparePublicKey(); + ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(&encoded_rsa_key_[0], + encoded_rsa_key_.size())); ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key)); vector mac_context; vector enc_context; @@ -2568,7 +2567,7 @@ TEST_F(OEMCryptoUsesCertificate, RSASignature) { OEMCryptoResult sts; // Sign a Message vector licenseRequest(500); - RAND_pseudo_bytes(&licenseRequest[0], licenseRequest.size()); + GetRandBytes(&licenseRequest[0], licenseRequest.size()); size_t signature_length = 0; uint8_t signature[500]; @@ -2587,7 +2586,8 @@ TEST_F(OEMCryptoUsesCertificate, RSASignature) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); // In the real world, the signature above would just have been used to contact // the license server to get this response. - session_.PreparePublicKey(); + ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(&encoded_rsa_key_[0], + encoded_rsa_key_.size())); ASSERT_NO_FATAL_FAILURE(session_.VerifyRSASignature( licenseRequest, signature, signature_length, kSign_RSASSA_PSS)); } @@ -2596,7 +2596,7 @@ TEST_F(OEMCryptoUsesCertificate, RSASignatureLargeBuffer) { OEMCryptoResult sts; // Sign a Message vector licenseRequest(kMaxMessageSize); - RAND_pseudo_bytes(&licenseRequest[0], licenseRequest.size()); + GetRandBytes(&licenseRequest[0], licenseRequest.size()); size_t signature_length = 0; uint8_t signature[500]; @@ -2615,7 +2615,8 @@ TEST_F(OEMCryptoUsesCertificate, RSASignatureLargeBuffer) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); // In the real world, the signature above would just have been used to contact // the license server to get this response. - session_.PreparePublicKey(); + ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(&encoded_rsa_key_[0], + encoded_rsa_key_.size())); ASSERT_NO_FATAL_FAILURE(session_.VerifyRSASignature( licenseRequest, signature, signature_length, kSign_RSASSA_PSS)); } @@ -2623,7 +2624,8 @@ TEST_F(OEMCryptoUsesCertificate, RSASignatureLargeBuffer) { TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) { vector session_key; vector enc_session_key; - session_.PreparePublicKey(); + ASSERT_NO_FATAL_FAILURE(session_.PreparePublicKey(&encoded_rsa_key_[0], + encoded_rsa_key_.size())); ASSERT_TRUE(session_.GenerateRSASessionKey(&session_key, &enc_session_key)); vector mac_context(kMaxMessageSize); vector enc_context(kMaxMessageSize); @@ -2652,7 +2654,7 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { // Sign a Message vector licenseRequest(size); - RAND_pseudo_bytes(&licenseRequest[0], licenseRequest.size()); + GetRandBytes(&licenseRequest[0], licenseRequest.size()); size_t signature_length = 256; vector signature(signature_length); sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], @@ -2683,7 +2685,7 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { ASSERT_EQ(OEMCrypto_SUCCESS, sts); vector licenseRequest(size); - RAND_pseudo_bytes(&licenseRequest[0], licenseRequest.size()); + GetRandBytes(&licenseRequest[0], licenseRequest.size()); size_t signature_length = 0; sts = OEMCrypto_GenerateRSASignature(s.session_id(), &licenseRequest[0], licenseRequest.size(), NULL, @@ -2699,7 +2701,8 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { ASSERT_EQ(OEMCrypto_SUCCESS, sts) << "Failed to sign with padding scheme=" << (int)scheme << ", size=" << (int)size; - s.PreparePublicKey(); + ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(&encoded_rsa_key_[0], + encoded_rsa_key_.size())); ASSERT_NO_FATAL_FAILURE(s.VerifyRSASignature(licenseRequest, signature, signature_length, scheme)); delete[] signature; @@ -2715,7 +2718,8 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { s.GenerateNonce(); vector session_key; vector enc_session_key; - s.PreparePublicKey(); + ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(&encoded_rsa_key_[0], + encoded_rsa_key_.size())); ASSERT_TRUE(s.GenerateRSASessionKey(&session_key, &enc_session_key)); vector mac_context; vector enc_context; @@ -2739,7 +2743,7 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { // The alternate padding is only required for cast receivers, but all devices // should forbid the alternate padding for regular certificates. -TEST_F(OEMCryptoLoadsCertificateAlternates, DisallowForbiddenPadding) { +TEST_F(OEMCryptoLoadsCertificateAlternates, DisallowForbiddenPaddingAPI09) { LoadWithAllowedSchemes(kSign_RSASSA_PSS, true); // Use default padding scheme DisallowForbiddenPadding(kSign_PKCS1_Block1, 50); } @@ -2982,7 +2986,8 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates { ASSERT_EQ(OEMCrypto_SUCCESS, sts) << "Failed to sign with padding scheme=" << (int)scheme << ", size=" << (int)message.size(); - s.PreparePublicKey(&encoded_rsa_key_[0], encoded_rsa_key_.size()); + ASSERT_NO_FATAL_FAILURE(s.PreparePublicKey(&encoded_rsa_key_[0], + encoded_rsa_key_.size())); // Verify that the signature matches the official test vector. ASSERT_EQ(correct_signature.size(), signature_length); @@ -2998,6 +3003,11 @@ class OEMCryptoCastReceiverTest : public OEMCryptoLoadsCertificateAlternates { } }; +// CAST Receivers should report that they support cast certificates. +TEST_F(OEMCryptoCastReceiverTest, SupportsCertificatesAPI13) { + ASSERT_NE(0u, OEMCrypto_Supports_RSA_CAST & OEMCrypto_SupportedCertificates()); +} + // # PKCS#1 v1.5 Signature Example 15.1 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_1) { BuildRSAKey(); @@ -3874,6 +3884,25 @@ TEST_F(GenericCryptoTest, GenericKeyBadEncrypt) { BadEncrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); } +TEST_F(GenericCryptoTest, GenericKeyEncryptSameBufferAPI12) { + EncryptAndLoadKeys(); + unsigned int key_index = 0; + vector expected_encrypted; + EncryptBuffer(key_index, clear_buffer_, &expected_encrypted); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length)); + // Input and output are same buffer: + vector buffer = clear_buffer_; + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Encrypt( + session_.session_id(), &buffer[0], buffer.size(), + iv_, OEMCrypto_AES_CBC_128_NO_PADDING, &buffer[0])); + ASSERT_EQ(expected_encrypted, buffer); +} + TEST_F(GenericCryptoTest, GenericKeyDecrypt) { EncryptAndLoadKeys(); unsigned int key_index = 1; @@ -3892,6 +3921,24 @@ TEST_F(GenericCryptoTest, GenericKeyDecrypt) { ASSERT_EQ(clear_buffer_, resultant); } +TEST_F(GenericCryptoTest, GenericKeyDecryptSameBufferAPI12) { + EncryptAndLoadKeys(); + unsigned int key_index = 1; + vector encrypted; + EncryptBuffer(key_index, clear_buffer_, &encrypted); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length)); + vector buffer = encrypted; + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Decrypt( + session_.session_id(), &buffer[0], buffer.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &buffer[0])); + ASSERT_EQ(clear_buffer_, buffer); +} + TEST_F(GenericCryptoTest, GenericSecureToClear) { session_.license().keys[1].control.control_bits |= htonl( wvoec_mock::kControlObserveDataPath | wvoec_mock::kControlDataPathSecure); @@ -4089,15 +4136,13 @@ TEST_F(GenericCryptoTest, KeyDurationEncrypt) { sleep(kLongSleep); // Should be expired key. encrypted.assign(clear_buffer_.size(), 0); - ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, - OEMCrypto_Generic_Encrypt( - session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), - iv_, OEMCrypto_AES_CBC_128_NO_PADDING, &encrypted[0])); + OEMCryptoResult status = OEMCrypto_Generic_Encrypt( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &encrypted[0]); + ASSERT_NO_FATAL_FAILURE( + session_.TestDecryptResult(OEMCrypto_ERROR_KEY_EXPIRED, status)); ASSERT_NE(encrypted, expected_encrypted); - ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, - OEMCrypto_SelectKey(session_.session_id(), - session_.license().keys[key_index].key_id, - session_.license().keys[key_index].key_id_length)); + ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); } TEST_F(GenericCryptoTest, KeyDurationDecrypt) { @@ -4124,15 +4169,13 @@ TEST_F(GenericCryptoTest, KeyDurationDecrypt) { sleep(kLongSleep); // Should be expired key. resultant.assign(encrypted.size(), 0); - ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, - OEMCrypto_Generic_Decrypt( - session_.session_id(), &encrypted[0], encrypted.size(), iv_, - OEMCrypto_AES_CBC_128_NO_PADDING, &resultant[0])); + OEMCryptoResult status = OEMCrypto_Generic_Decrypt( + session_.session_id(), &encrypted[0], encrypted.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &resultant[0]); + ASSERT_NO_FATAL_FAILURE( + session_.TestDecryptResult(OEMCrypto_ERROR_KEY_EXPIRED, status)); ASSERT_NE(clear_buffer_, resultant); - ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, - OEMCrypto_SelectKey(session_.session_id(), - session_.license().keys[key_index].key_id, - session_.license().keys[key_index].key_id_length)); + ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); } TEST_F(GenericCryptoTest, KeyDurationSign) { @@ -4161,15 +4204,13 @@ TEST_F(GenericCryptoTest, KeyDurationSign) { sleep(kLongSleep); // Should be expired key. signature.assign(SHA256_DIGEST_LENGTH, 0); - ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, - OEMCrypto_Generic_Sign(session_.session_id(), &clear_buffer_[0], - clear_buffer_.size(), OEMCrypto_HMAC_SHA256, - &signature[0], &signature_length)); + OEMCryptoResult status = OEMCrypto_Generic_Sign( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), + OEMCrypto_HMAC_SHA256, &signature[0], &signature_length); + ASSERT_NO_FATAL_FAILURE( + session_.TestDecryptResult(OEMCrypto_ERROR_KEY_EXPIRED, status)); ASSERT_NE(expected_signature, signature); - ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, - OEMCrypto_SelectKey(session_.session_id(), - session_.license().keys[key_index].key_id, - session_.license().keys[key_index].key_id_length)); + ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); } TEST_F(GenericCryptoTest, KeyDurationVerify) { @@ -4194,14 +4235,12 @@ TEST_F(GenericCryptoTest, KeyDurationVerify) { sleep(kLongSleep); // Should be expired key. - ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, - OEMCrypto_Generic_Verify( - session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), - OEMCrypto_HMAC_SHA256, &signature[0], signature.size())); - ASSERT_EQ(OEMCrypto_ERROR_KEY_EXPIRED, - OEMCrypto_SelectKey(session_.session_id(), - session_.license().keys[key_index].key_id, - session_.license().keys[key_index].key_id_length)); + OEMCryptoResult status = OEMCrypto_Generic_Verify( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), + OEMCrypto_HMAC_SHA256, &signature[0], signature.size()); + ASSERT_NO_FATAL_FAILURE( + session_.TestDecryptResult(OEMCrypto_ERROR_KEY_EXPIRED, status)); + ASSERT_NO_FATAL_FAILURE(session_.TestSelectExpired(key_index)); } const unsigned int kLongKeyId = 2; @@ -4226,14 +4265,14 @@ class GenericCryptoKeyIdLengthTest : public GenericCryptoTest { // Make all four keys have the same length. void SetUniformKeyIdLength(size_t key_id_length) { - for (int i = 0; i < session_.num_keys(); i++) { + for (unsigned int i = 0; i < session_.num_keys(); i++) { string key_id; key_id.resize(key_id_length, i + 'a'); session_.SetKeyId(i, key_id); } } - void TestWithKey(int key_index) { + void TestWithKey(unsigned int key_index) { ASSERT_LT(key_index, session_.num_keys()); EncryptAndLoadKeys(); vector encrypted; @@ -4281,10 +4320,6 @@ TEST_F(GenericCryptoKeyIdLengthTest, UniformLongKeyId) { TestWithKey(2); } -TEST_F(OEMCryptoClientTest, UpdateUsageTableTest) { - EXPECT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); -} - class UsageTableTest : public GenericCryptoTest { public: virtual void SetUp() { @@ -4292,10 +4327,15 @@ class UsageTableTest : public GenericCryptoTest { new_mac_keys_ = true; } - void DeactivatePST(const std::string& pst) { - ASSERT_EQ(OEMCrypto_SUCCESS, - OEMCrypto_DeactivateUsageEntry( - reinterpret_cast(pst.c_str()), pst.length())); + virtual void ShutDown() { + ASSERT_NO_FATAL_FAILURE(session_.close()); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Terminate()); + } + + virtual void Restart() { + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); + EnsureTestKeys(); + ASSERT_NO_FATAL_FAILURE(session_.open()); } void LoadOfflineLicense(Session& s, const std::string& pst) { @@ -4304,14 +4344,10 @@ class UsageTableTest : public GenericCryptoTest { ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec_mock::kControlNonceOrEntry, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - s.GenerateReport(pst); - s.GenerateReport(pst); - EXPECT_EQ(kUnused, s.pst_report()->status); - EXPECT_NEAR(0, - wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - kTimeTolerance); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); ASSERT_NO_FATAL_FAILURE(s.close()); } @@ -4347,30 +4383,8 @@ class UsageTableTestWithMAC : public UsageTableTest, } }; -TEST_F(UsageTableTest, PSTReportSizes) { - OEMCrypto_PST_Report report; - uint8_t* location = reinterpret_cast(&report); - EXPECT_EQ(48u, sizeof(report)); - uint8_t* field; - field = reinterpret_cast(&report.status); - EXPECT_EQ(20, field - location); - field = reinterpret_cast(&report.clock_security_level); - EXPECT_EQ(21, field - location); - field = reinterpret_cast(&report.pst_length); - EXPECT_EQ(22, field - location); - field = reinterpret_cast(&report.seconds_since_license_received); - EXPECT_EQ(24, field - location); - field = reinterpret_cast(&report.seconds_since_first_decrypt); - EXPECT_EQ(32, field - location); - field = reinterpret_cast(&report.seconds_since_last_decrypt); - EXPECT_EQ(40, field - location); - field = reinterpret_cast(&report.pst); - EXPECT_EQ(48, field - location); -} - TEST_P(UsageTableTestWithMAC, OnlineLicense) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -4378,40 +4392,59 @@ TEST_P(UsageTableTestWithMAC, OnlineLicense) { 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - s.GenerateReport(pst); - s.GenerateReport(pst); // test repeated report generation - s.GenerateReport(pst); - s.GenerateReport(pst); - EXPECT_EQ(kUnused, s.pst_report()->status); - EXPECT_NEAR(0, - wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - kTimeTolerance); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + // test repeated report generation + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - s.GenerateReport(pst); - EXPECT_EQ(kActive, s.pst_report()->status); - EXPECT_NEAR(0, - wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - kTimeTolerance); - EXPECT_NEAR(0, wvcdm::htonll64(s.pst_report()->seconds_since_first_decrypt), - kTimeTolerance); - EXPECT_NEAR(0, wvcdm::htonll64(s.pst_report()->seconds_since_last_decrypt), - kTimeTolerance); - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); - ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); - EXPECT_EQ(kInactive, s.pst_report()->status); - EXPECT_NEAR(0, - wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - kTimeTolerance); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive)); + // Flag the entry as inactive. + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + // It should report as inactive. + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUsed)); + // Decrypt should fail. + ASSERT_NO_FATAL_FAILURE( + s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); +} + +TEST_P(UsageTableTestWithMAC, ForbidReportWithNoUpdate) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + // Cannot generate a report without first updating the file. + ASSERT_NO_FATAL_FAILURE( + s.GenerateReport(pst, OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + // Now it's OK. + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive)); + // Flag the entry as inactive. + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); + // Cannot generate a report without first updating the file. + ASSERT_NO_FATAL_FAILURE( + s.GenerateReport(pst, OEMCrypto_ERROR_ENTRY_NEEDS_UPDATE)); + // Decrypt should fail. ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } TEST_P(UsageTableTestWithMAC, OnlineLicenseWithRefresh) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -4419,6 +4452,7 @@ TEST_P(UsageTableTestWithMAC, OnlineLicenseWithRefresh) { 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); time_t loaded = time(NULL); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); @@ -4430,25 +4464,16 @@ TEST_P(UsageTableTestWithMAC, OnlineLicenseWithRefresh) { kAllKeys, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, s.get_nonce(), OEMCrypto_SUCCESS)); - s.GenerateReport(pst); - time_t report_generated = time(NULL); - EXPECT_EQ(kActive, s.pst_report()->status); - // license received at LoadKeys, not at RefreshKeys. - EXPECT_NEAR(report_generated - loaded, - wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - kTimeTolerance); - // First decrypt was just after LoadKeys. - EXPECT_NEAR(report_generated - loaded, - wvcdm::htonll64(s.pst_report()->seconds_since_first_decrypt), - kTimeTolerance); - // Last decrypt just before report generated. - EXPECT_NEAR(0, wvcdm::htonll64(s.pst_report()->seconds_since_last_decrypt), - kTimeTolerance); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE( + s.GenerateVerifyReport(pst, kActive, + loaded, // when license loaded. (not refreshed) + loaded, // first decrypt. + 0)); // last decrypt is now. } TEST_F(UsageTableTest, RepeatOnlineLicense) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -4456,13 +4481,15 @@ TEST_F(UsageTableTest, RepeatOnlineLicense) { 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s.close()); Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); uint8_t* pst_ptr = s.encrypted_license().pst; + s2.LoadUsageEntry(s); // Use the same entry. // Trying to reuse a PST is bad. We use session ID for s2, everything else // reused from s. ASSERT_NE( @@ -4470,14 +4497,13 @@ TEST_F(UsageTableTest, RepeatOnlineLicense) { OEMCrypto_LoadKeys(s2.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), - s.key_array(), pst_ptr, pst.length())); + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), pst_ptr, pst.length(), NULL)); ASSERT_NO_FATAL_FAILURE(s2.close()); } // A license with non-zero replay control bits needs a valid pst.. TEST_F(UsageTableTest, OnlineEmptyPST) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -4485,37 +4511,37 @@ TEST_F(UsageTableTest, OnlineEmptyPST) { 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, s.get_nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - NULL, 0); + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL); ASSERT_NE(OEMCrypto_SUCCESS, sts); ASSERT_NO_FATAL_FAILURE(s.close()); } -TEST_F(UsageTableTest, EmptyTable) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); +// A license with non-zero replay control bits needs a valid pst.. +TEST_F(UsageTableTest, OnlineMissingEntry) { + std::string pst = "my_pst"; Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - std::string pst = "no_pst"; ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); + // ENTRY NOT CREATED: ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + OEMCryptoResult sts = OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], + s.signature().size(), s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), + s.encrypted_license().pst, pst.length(), NULL); + ASSERT_NE(OEMCrypto_SUCCESS, sts); ASSERT_NO_FATAL_FAILURE(s.close()); - OEMCrypto_DeleteUsageTable(); - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - s2.GenerateReport(pst, false); - ASSERT_NO_FATAL_FAILURE(s2.close()); } -TEST_F(UsageTableTest, FiftyEntries) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); +TEST_F(UsageTableTest, TwoHundredEntries) { Session s1; ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); @@ -4524,317 +4550,64 @@ TEST_F(UsageTableTest, FiftyEntries) { 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, s1.get_nonce(), pst1)); ASSERT_NO_FATAL_FAILURE(s1.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s1.CreateNewUsageEntry()); + ASSERT_EQ(0u, s1.usage_entry_number()); + time_t start = time(NULL); ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst1, new_mac_keys_)); - sleep(kShortSleep); + ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s1.close()); - const size_t ENTRY_COUNT = 49; // API says should hold at least 50 entries. + const size_t ENTRY_COUNT = 200; // API says should hold at least 200 entries. vector sessions(ENTRY_COUNT); for (size_t i = 0; i < ENTRY_COUNT; i++) { ASSERT_NO_FATAL_FAILURE(sessions[i].open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i])); std::string pst = "pst "; - char c = 'A' + i; - pst = pst + c; + char c1 = 'A' + (i/26); + char c2 = 'A' + (i%26); + pst = pst + c1 + c2; ASSERT_NO_FATAL_FAILURE(sessions[i].FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - sessions[i].get_nonce(), pst)); + 0, wvoec_mock::kControlNonceOrEntry, sessions[i].get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(sessions[i].EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(sessions[i].CreateNewUsageEntry()); + ASSERT_EQ(sessions[i].usage_entry_number(), i + 1); ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_)); - sessions[i].GenerateReport(pst); + ASSERT_NO_FATAL_FAILURE( + sessions[i].UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(sessions[i].close()); } - for (size_t i = 0; i < ENTRY_COUNT; i++) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - std::string pst = "pst "; - char c = 'A' + i; - pst = pst + c; - s.GenerateReport(pst, true, &sessions[i]); - EXPECT_EQ(kUnused, s.pst_report()->status); - ASSERT_NO_FATAL_FAILURE(s.close()); - } sleep(kShortSleep); - // If I add too many entries, it can delete the older ones first, except - // it shouldn't delete the one attached to an open session. (s1) for (size_t i = 0; i < ENTRY_COUNT; i++) { ASSERT_NO_FATAL_FAILURE(sessions[i].open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&sessions[i])); - std::string pst = "newer pst "; - char c = 'A' + i; - pst = pst + c; - ASSERT_NO_FATAL_FAILURE(sessions[i].FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - sessions[i].get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(sessions[i].EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_)); - sessions[i].GenerateReport(pst); + std::string pst = "pst "; + char c1 = 'A' + (i/26); + char c2 = 'A' + (i%26); + pst = pst + c1 + c2; + // Reuse license message created above. + ASSERT_NO_FATAL_FAILURE(sessions[i].ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(sessions[i].LoadTestKeys(pst, new_mac_keys_)) + << "Failed to reload license " << i << " with pst = " << pst; + ASSERT_NO_FATAL_FAILURE( + sessions[i].UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(sessions[i].close()); } - for (int i = 0; i < 49; i++) { - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - std::string pst = "newer pst "; - char c = 'A' + i; - pst = pst + c; - s.GenerateReport(pst, true, &sessions[i]); - EXPECT_EQ(kUnused, s.pst_report()->status); - ASSERT_NO_FATAL_FAILURE(s.close()); - } + // Make sure s1's entry is still in the table. + ASSERT_NO_FATAL_FAILURE(s1.open()); + ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s1.GenerateVerifyReport(pst1, kUnused, start)); ASSERT_NO_FATAL_FAILURE(s1.close()); - ASSERT_NO_FATAL_FAILURE( - s1.open()); // Make sure s1's entry is still in the table. - s1.GenerateReport(pst1); - EXPECT_EQ(kUnused, s1.pst_report()->status); - ASSERT_NO_FATAL_FAILURE(s1.close()); -} - -TEST_P(UsageTableTestWithMAC, DeleteUnusedEntry) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - std::string pst = "my pst"; - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(s.close()); - - // New session should be able to generate report and copy mac keys. - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - s2.GenerateReport(pst, true, &s); - EXPECT_EQ(kUnused, s2.pst_report()->status); - ASSERT_NO_FATAL_FAILURE(s2.DeleteEntry(pst)); - ASSERT_NO_FATAL_FAILURE(s2.close()); - - // Now that session is deleted, we can't generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - s3.GenerateReport(pst, false); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_P(UsageTableTestWithMAC, DeleteActiveEntry) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - std::string pst = "my pst"; - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - s.GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(s.close()); - - // New session should be able to generate report and copy mac keys. - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(s2.GenerateReport(pst, true, &s)); - EXPECT_EQ(kActive, s2.pst_report()->status); - ASSERT_NO_FATAL_FAILURE(s2.DeleteEntry(pst)); - ASSERT_NO_FATAL_FAILURE(s2.close()); - - // Now that session is deleted, we can't generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, false)); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_P(UsageTableTestWithMAC, ForceDeleteActiveEntry) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - std::string pst = "my pst"; - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); - ASSERT_NO_FATAL_FAILURE(s.close()); - - ASSERT_NO_FATAL_FAILURE(s.ForceDeleteEntry(pst)); - - // Now that session is deleted, we can't generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, false)); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_P(UsageTableTestWithMAC, DeleteInactiveEntry) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - std::string pst = "my pst"; - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); - ASSERT_NO_FATAL_FAILURE(s.close()); - - // New session should be able to generate report and copy mac keys. - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - s2.GenerateReport(pst, true, &s); - EXPECT_EQ(kInactive, s2.pst_report()->status); - ASSERT_NO_FATAL_FAILURE(s2.DeleteEntry(pst)); - ASSERT_NO_FATAL_FAILURE(s2.close()); - - // Now that session is deleted, we can't generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - s3.GenerateReport(pst, false); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_P(UsageTableTestWithMAC, DeleteEntryBadSignature) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - std::string pst = "my pst"; - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(s.close()); - - // New session should be able to generate report and copy mac keys. - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - s2.GenerateReport(pst, true, &s); - uint8_t* pst_ptr = s2.encrypted_license().pst; - memcpy(pst_ptr, pst.c_str(), min(sizeof(s2.license().pst), pst.length())); - vector signature(SHA256_DIGEST_LENGTH); - // Cannot delete without correct signature. - // ServerSignMessage(s2.encrypted_license(), &signature); - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_DeleteUsageEntry(s2.session_id(), pst_ptr, pst.length(), - s2.message_ptr(), s2.message_size(), - &signature[0], signature.size())); - ASSERT_NO_FATAL_FAILURE(s2.close()); - - // The session is not deleted, we can still generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - s3.GenerateReport(pst, true, &s); - EXPECT_EQ(kUnused, s3.pst_report()->status); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_P(UsageTableTestWithMAC, DeleteEntryWrongSession) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - std::string pst = "my pst"; - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(s.close()); - - // New session should not be able to delete without using GenerateReport - // to load mac keys. - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - // s2.GenerateReport(pst, true, &s); - uint8_t* pst_ptr = s2.encrypted_license().pst; - memcpy(pst_ptr, pst.c_str(), min(sizeof(s2.license().pst), pst.length())); - std::vector signature(SHA256_DIGEST_LENGTH); - s2.ServerSignBuffer(reinterpret_cast(&s2.encrypted_license()), - sizeof(s2.encrypted_license()), &signature); - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_DeleteUsageEntry(s2.session_id(), pst_ptr, pst.length(), - s2.message_ptr(), s2.message_size(), - &signature[0], signature.size())); - ASSERT_NO_FATAL_FAILURE(s2.close()); - - // The session is not deleted, we can still generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - s3.GenerateReport(pst, true, &s); - EXPECT_EQ(kUnused, s3.pst_report()->status); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_P(UsageTableTestWithMAC, DeleteEntryBadRange) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - std::string pst = "my pst"; - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(s.close()); - - // New session should not be able to delete if pst doesn't point into - // message. - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - s2.GenerateReport(pst, true, &s); - uint8_t* pst_ptr = s2.license().pst; - memcpy(pst_ptr, pst.c_str(), min(sizeof(s2.license().pst), pst.length())); - std::vector signature(SHA256_DIGEST_LENGTH); - s2.ServerSignBuffer(reinterpret_cast(&s2.encrypted_license()), - sizeof(s2.encrypted_license()), &signature); - ASSERT_NE(OEMCrypto_SUCCESS, - OEMCrypto_DeleteUsageEntry(s2.session_id(), pst_ptr, pst.length(), - s2.message_ptr(), s2.message_size(), - &signature[0], signature.size())); - ASSERT_NO_FATAL_FAILURE(s2.close()); - - // The session is not deleted, we can still generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - s3.GenerateReport(pst, true, &s); - EXPECT_EQ(kUnused, s3.pst_report()->status); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_P(UsageTableTestWithMAC, DeactivateBadPST) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - std::string pst = "nonexistant pst"; - OEMCryptoResult sts = OEMCrypto_DeactivateUsageEntry( - reinterpret_cast(pst.c_str()), pst.length()); - EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts); - std::string null_pst = ""; - sts = OEMCrypto_DeactivateUsageEntry( - reinterpret_cast(null_pst.c_str()), null_pst.length()); - EXPECT_EQ(OEMCrypto_ERROR_INVALID_CONTEXT, sts); } TEST_P(UsageTableTestWithMAC, GenericCryptoEncrypt) { std::string pst = "A PST"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); uint32_t nonce = session_.get_nonce(); MakeFourKeys( 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, nonce, pst); ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); OEMCryptoResult sts; unsigned int key_index = 0; @@ -4850,23 +4623,11 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoEncrypt) { OEMCrypto_AES_CBC_128_NO_PADDING, &encrypted[0]); ASSERT_EQ(OEMCrypto_SUCCESS, sts); EXPECT_EQ(expected_encrypted, encrypted); - session_.GenerateReport(pst); - EXPECT_EQ(kActive, session_.pst_report()->status); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_license_received), - kTimeTolerance); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_first_decrypt), - kTimeTolerance); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_last_decrypt), - kTimeTolerance); - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); - ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst)); - EXPECT_EQ(kInactive, session_.pst_report()->status); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_license_received), - kTimeTolerance); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kActive)); + ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kInactiveUsed)); encrypted.assign(clear_buffer_.size(), 0); sts = OEMCrypto_Generic_Encrypt( session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), iv_, @@ -4877,12 +4638,12 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoEncrypt) { TEST_P(UsageTableTestWithMAC, GenericCryptoDecrypt) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); uint32_t nonce = session_.get_nonce(); MakeFourKeys( 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, nonce, pst); ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); OEMCryptoResult sts; unsigned int key_index = 1; @@ -4898,23 +4659,11 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoDecrypt) { OEMCrypto_AES_CBC_128_NO_PADDING, &resultant[0]); ASSERT_EQ(OEMCrypto_SUCCESS, sts); EXPECT_EQ(clear_buffer_, resultant); - session_.GenerateReport(pst); - EXPECT_EQ(kActive, session_.pst_report()->status); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_license_received), - kTimeTolerance); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_first_decrypt), - kTimeTolerance); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_last_decrypt), - kTimeTolerance); - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); - session_.GenerateReport(pst); - EXPECT_EQ(kInactive, session_.pst_report()->status); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_license_received), - kTimeTolerance); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kActive)); + ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kInactiveUsed)); resultant.assign(encrypted.size(), 0); sts = OEMCrypto_Generic_Decrypt( session_.session_id(), &encrypted[0], encrypted.size(), iv_, @@ -4925,18 +4674,19 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoDecrypt) { TEST_P(UsageTableTestWithMAC, GenericCryptoSign) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); uint32_t nonce = session_.get_nonce(); MakeFourKeys( 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, nonce, pst); ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); OEMCryptoResult sts; unsigned int key_index = 2; vector expected_signature; - SignBuffer(key_index, clear_buffer_, &expected_signature); + ASSERT_NO_FATAL_FAILURE( + SignBuffer(key_index, clear_buffer_, &expected_signature)); sts = OEMCrypto_SelectKey(session_.session_id(), session_.license().keys[key_index].key_id, @@ -4955,24 +4705,11 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoSign) { ASSERT_EQ(OEMCrypto_SUCCESS, sts); ASSERT_EQ(expected_signature, signature); - session_.GenerateReport(pst); - EXPECT_EQ(kActive, session_.pst_report()->status); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_license_received), - kTimeTolerance); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_first_decrypt), - kTimeTolerance); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_last_decrypt), - kTimeTolerance); - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); - ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst)); - EXPECT_EQ(kInactive, session_.pst_report()->status); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_license_received), - kTimeTolerance); - + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kActive)); + ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kInactiveUsed)); signature.assign(SHA256_DIGEST_LENGTH, 0); gen_signature_length = SHA256_DIGEST_LENGTH; sts = OEMCrypto_Generic_Sign(session_.session_id(), &clear_buffer_[0], @@ -4984,12 +4721,12 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoSign) { TEST_P(UsageTableTestWithMAC, GenericCryptoVerify) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); uint32_t nonce = session_.get_nonce(); MakeFourKeys( 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, nonce, pst); ASSERT_NO_FATAL_FAILURE(session_.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(session_.LoadTestKeys(pst, new_mac_keys_)); OEMCryptoResult sts; @@ -5005,25 +4742,11 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoVerify) { clear_buffer_.size(), OEMCrypto_HMAC_SHA256, &signature[0], signature.size()); ASSERT_EQ(OEMCrypto_SUCCESS, sts); - - session_.GenerateReport(pst); - EXPECT_EQ(kActive, session_.pst_report()->status); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_license_received), - kTimeTolerance); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_first_decrypt), - kTimeTolerance); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_last_decrypt), - kTimeTolerance); - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); - ASSERT_NO_FATAL_FAILURE(session_.GenerateReport(pst)); - EXPECT_EQ(kInactive, session_.pst_report()->status); - EXPECT_NEAR( - 0, wvcdm::htonll64(session_.pst_report()->seconds_since_license_received), - kTimeTolerance); - + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kActive)); + ASSERT_NO_FATAL_FAILURE(session_.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(session_.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(session_.GenerateVerifyReport(pst, kInactiveUsed)); sts = OEMCrypto_Generic_Verify(session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), OEMCrypto_HMAC_SHA256, &signature[0], signature.size()); @@ -5032,20 +4755,19 @@ TEST_P(UsageTableTestWithMAC, GenericCryptoVerify) { TEST_P(UsageTableTestWithMAC, OfflineLicense) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); } TEST_P(UsageTableTestWithMAC, OfflineLicenseRefresh) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec_mock::kControlNonceOrEntry, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); time_t loaded = time(NULL); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); @@ -5055,25 +4777,17 @@ TEST_P(UsageTableTestWithMAC, OfflineLicenseRefresh) { size_t kAllKeys = 1; ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( kAllKeys, wvoec_mock::kControlNonceOrEntry, 0, OEMCrypto_SUCCESS)); - s.GenerateReport(pst); - time_t report_generated = time(NULL); - EXPECT_EQ(kActive, s.pst_report()->status); - // license received at LoadKeys, not at RefreshKeys. - EXPECT_NEAR(report_generated - loaded, - wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - kTimeTolerance); - // First decrypt was just after LoadKeys. - EXPECT_NEAR(report_generated - loaded, - wvcdm::htonll64(s.pst_report()->seconds_since_first_decrypt), - kTimeTolerance); - // Last decrypt just before report generated. - EXPECT_NEAR(0, wvcdm::htonll64(s.pst_report()->seconds_since_last_decrypt), - kTimeTolerance); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE( + s.GenerateVerifyReport(pst, kActive, + loaded, // license recieved. + loaded, // First decrypt when loaded, not refresh. + 0)); // last decrypt now. } TEST_P(UsageTableTestWithMAC, ReloadOfflineLicense) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); @@ -5081,29 +4795,18 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicense) { ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); // We will reuse the encrypted and signed message, so we don't call // FillSimpleMessage again. + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); - s.GenerateReport(pst); - EXPECT_EQ(kUnused, s.pst_report()->status); - EXPECT_NEAR(0, - wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - kTimeTolerance); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - s.GenerateReport(pst); - EXPECT_EQ(kActive, s.pst_report()->status); - EXPECT_NEAR(0, - wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - kTimeTolerance); - EXPECT_NEAR(0, wvcdm::htonll64(s.pst_report()->seconds_since_first_decrypt), - kTimeTolerance); - EXPECT_NEAR(0, wvcdm::htonll64(s.pst_report()->seconds_since_last_decrypt), - kTimeTolerance); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive)); ASSERT_NO_FATAL_FAILURE(s.close()); } TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); time_t loaded = time(NULL); @@ -5112,52 +4815,56 @@ TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithRefresh) { ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); // We will reuse the encrypted and signed message, so we don't call // FillSimpleMessage again. + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); - s.GenerateReport(pst); - EXPECT_EQ(kUnused, s.pst_report()->status); - EXPECT_NEAR(0, - wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - kTimeTolerance); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused, loaded, 0, 0)); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); time_t decrypt_time = time(NULL); - s.GenerateReport(pst); - time_t report_generated = time(NULL); - EXPECT_EQ(kActive, s.pst_report()->status); - // license received at first LoadKeys. - EXPECT_NEAR(report_generated - loaded, - wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - kTimeTolerance); - // First decrypt was just after second LoadKeys. - EXPECT_NEAR(report_generated - decrypt_time, - wvcdm::htonll64(s.pst_report()->seconds_since_first_decrypt), - kTimeTolerance); - EXPECT_NEAR(report_generated - decrypt_time, - wvcdm::htonll64(s.pst_report()->seconds_since_last_decrypt), - kTimeTolerance); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE( + s.GenerateVerifyReport(pst, kActive, + loaded, // license received. + decrypt_time, // first decrypt + decrypt_time)); // last decrypt size_t kAllKeys = 1; ASSERT_NO_FATAL_FAILURE(s.RefreshTestKeys( kAllKeys, wvoec_mock::kControlNonceOrEntry, 0, OEMCrypto_SUCCESS)); - s.GenerateReport(pst); - report_generated = time(NULL); - EXPECT_EQ(kActive, s.pst_report()->status); - // license received at LoadKeys, not at RefreshKeys. - EXPECT_NEAR(report_generated - loaded, - wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - kTimeTolerance); - // First decrypt was just after LoadKeys. - EXPECT_NEAR(report_generated - decrypt_time, - wvcdm::htonll64(s.pst_report()->seconds_since_first_decrypt), - kTimeTolerance); - // Last decrypt just before report generated. - EXPECT_NEAR(0, wvcdm::htonll64(s.pst_report()->seconds_since_last_decrypt), - kTimeTolerance); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive, + loaded, // license loaded. + decrypt_time, // first decrypt + 0)); // last decrypt + ASSERT_NO_FATAL_FAILURE(s.close()); +} + +TEST_P(UsageTableTestWithMAC, ReloadOfflineLicenseWithTerminate) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + ShutDown(); + Restart(); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageTableHeader(&encrypted_usage_header_[0], + encrypted_usage_header_.size())); + + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + // We will reuse the encrypted and signed message, so we don't call + // FillSimpleMessage again. + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); + ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive)); ASSERT_NO_FATAL_FAILURE(s.close()); } TEST_P(UsageTableTestWithMAC, BadReloadOfflineLicense) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); @@ -5168,31 +4875,33 @@ TEST_P(UsageTableTestWithMAC, BadReloadOfflineLicense) { ASSERT_NO_FATAL_FAILURE(s2.FillSimpleMessage( 0, wvoec_mock::kControlNonceOrEntry, s2.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s2.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); uint8_t* pst_ptr = s2.encrypted_license().pst; ASSERT_NE( OEMCrypto_SUCCESS, OEMCrypto_LoadKeys(s2.session_id(), s2.message_ptr(), s2.message_size(), &s2.signature()[0], s2.signature().size(), s2.encrypted_license().mac_key_iv, - s2.encrypted_license().mac_keys, s.key_array_size(), - s2.key_array(), pst_ptr, pst.length())); + s2.encrypted_license().mac_keys, s.num_keys(), + s2.key_array(), pst_ptr, pst.length(), NULL)); ASSERT_NO_FATAL_FAILURE(s2.close()); // Offline license with same mac keys should still be OK. ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - s.GenerateReport(pst); - EXPECT_EQ(kUnused, s.pst_report()->status); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); } // An offline license should not load on the first call if the nonce is bad. TEST_P(UsageTableTestWithMAC, OfflineBadNonce) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec_mock::kControlNonceOrEntry, 42, pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); @@ -5200,55 +4909,73 @@ TEST_P(UsageTableTestWithMAC, OfflineBadNonce) { OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - pst_ptr, pst.length()); + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), pst_ptr, + pst.length(), NULL); ASSERT_NE(OEMCrypto_SUCCESS, sts); ASSERT_NO_FATAL_FAILURE(s.close()); } // An offline license needs a valid pst. TEST_P(UsageTableTestWithMAC, OfflineEmptyPST) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE( s.FillSimpleMessage(0, wvoec_mock::kControlNonceOrEntry, s.get_nonce())); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); OEMCryptoResult sts = OEMCrypto_LoadKeys( s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), s.key_array(), - NULL, 0); + s.encrypted_license().mac_keys, s.num_keys(), s.key_array(), NULL, 0, + NULL); ASSERT_NE(OEMCrypto_SUCCESS, sts); ASSERT_NO_FATAL_FAILURE(s.close()); } +TEST_P(UsageTableTestWithMAC, ReloadOfflineWrongPST) { + std::string pst = "my_pst1"; + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + + std::string bad_pst = "my_pst2"; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); + memcpy(s.license().pst, bad_pst.c_str(), bad_pst.length()); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + uint8_t* pst_ptr = s.encrypted_license().pst; + ASSERT_NE( + OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), NULL, NULL, + s.num_keys(), s.key_array(), + pst_ptr, bad_pst.length(), NULL)); +} + TEST_P(UsageTableTestWithMAC, DeactivateOfflineLicense) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE( s.LoadTestKeys(pst, new_mac_keys_)); // Reload the license ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); // Should be able to decrypt. - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); // Then deactivate. + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); - s.GenerateReport(pst); - EXPECT_EQ(kInactive, s.pst_report()->status); - EXPECT_NEAR(0, - wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - kTimeTolerance); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUsed)); ASSERT_NO_FATAL_FAILURE(s.close()); Session s2; ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); // Offile license can not be reused if it has been deactivated. uint8_t* pst_ptr = s.encrypted_license().pst; EXPECT_NE( @@ -5256,21 +4983,24 @@ TEST_P(UsageTableTestWithMAC, DeactivateOfflineLicense) { OEMCrypto_LoadKeys(s2.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), - s.key_array(), pst_ptr, pst.length())); + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), pst_ptr, pst.length(), NULL)); + s2.close(); // But we can still generate a report. Session s3; ASSERT_NO_FATAL_FAILURE(s3.open()); - s3.GenerateReport(pst, true, &s); - EXPECT_EQ(kInactive, s3.pst_report()->status); + ASSERT_NO_FATAL_FAILURE(s3.LoadUsageEntry(s)); + ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, OEMCrypto_SUCCESS, &s)); + EXPECT_EQ(kInactiveUsed, s3.pst_report().status()); } TEST_P(UsageTableTestWithMAC, BadRange) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(session_.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( 0, wvoec_mock::kControlNonceOrEntry, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); @@ -5280,12 +5010,365 @@ TEST_P(UsageTableTestWithMAC, BadRange) { OEMCrypto_LoadKeys(s.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), - s.key_array(), pst_ptr, pst.length())); + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), pst_ptr, pst.length(), NULL)); +} + +TEST_F(UsageTableTest, UpdateFailsWithNullPtr) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, + s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + size_t header_buffer_length = encrypted_usage_header_.size(); + size_t entry_buffer_length = s.encrypted_usage_entry().size(); + vector buffer(entry_buffer_length); + // Now try to pass in null pointers for the buffers. This should fail. + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_UpdateUsageEntry( + s.session_id(), NULL, &header_buffer_length, + &buffer[0], &entry_buffer_length)); + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_UpdateUsageEntry( + s.session_id(), &encrypted_usage_header_[0], + &header_buffer_length, NULL, &entry_buffer_length)); +} + +class UsageTableDefragTest : public UsageTableTest { + protected: + void LoadFirstLicense(Session* s, uint32_t index) { + ASSERT_NO_FATAL_FAILURE(s->open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(s)); + std::string pst = "pst "; + char c1 = 'A' + (index / 26); + char c2 = 'A' + (index % 26); + pst = pst + c1 + c2; + ASSERT_NO_FATAL_FAILURE(s->FillSimpleMessage( + 0, wvoec_mock::kControlNonceOrEntry, s->get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s->EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s->CreateNewUsageEntry()); + ASSERT_EQ(s->usage_entry_number(), index); + ASSERT_NO_FATAL_FAILURE(s->LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s->TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s->UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s->close()); + } + + void ReloadLicense(Session* s, time_t start) { + ASSERT_NO_FATAL_FAILURE(s->open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(s)); + ASSERT_NO_FATAL_FAILURE(s->ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s->LoadTestKeys(s->pst(), new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s->UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s->TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s->UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s->GenerateVerifyReport(s->pst(), kActive, + start, start, 0)); + ASSERT_NO_FATAL_FAILURE(s->close()); + } + + void FailReload(Session* s, OEMCryptoResult expected_result) { + ASSERT_NO_FATAL_FAILURE(s->open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(s)); + ASSERT_EQ(expected_result, + OEMCrypto_LoadUsageEntry(s->session_id(), s->usage_entry_number(), + &s->encrypted_usage_entry()[0], + s->encrypted_usage_entry().size())); + + uint8_t* pst_ptr = s->encrypted_license().pst; + ASSERT_NE( + OEMCrypto_SUCCESS, + OEMCrypto_LoadKeys(s->session_id(), s->message_ptr(), s->message_size(), + &s->signature()[0], s->signature().size(), + s->encrypted_license().mac_key_iv, + s->encrypted_license().mac_keys, s->num_keys(), + s->key_array(), pst_ptr, s->pst().length(), NULL)); + ASSERT_NO_FATAL_FAILURE(s->close()); + } + + void ShrinkHeader(uint32_t new_size, + OEMCryptoResult expected_result = OEMCrypto_SUCCESS) { + size_t header_buffer_length = 0; + OEMCryptoResult sts = + OEMCrypto_ShrinkUsageTableHeader(new_size, NULL, &header_buffer_length); + if (expected_result == OEMCrypto_SUCCESS) { + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + } else { + ASSERT_NE(OEMCrypto_SUCCESS, sts); + if (sts != OEMCrypto_ERROR_SHORT_BUFFER) return; + } + ASSERT_LT(0u, header_buffer_length); + encrypted_usage_header_.resize(header_buffer_length); + sts = OEMCrypto_ShrinkUsageTableHeader( + new_size, &encrypted_usage_header_[0], + &header_buffer_length); + ASSERT_EQ(expected_result, sts); + } +}; + +TEST_F(UsageTableDefragTest, MoveUsageEntries) { + const size_t ENTRY_COUNT = 10; + vector sessions(ENTRY_COUNT); + vector start(ENTRY_COUNT); + for (size_t i = 0; i < ENTRY_COUNT; i++) { + ASSERT_NO_FATAL_FAILURE(LoadFirstLicense(&sessions[i], i)) + << "On license " << i << " pst=" << sessions[i].pst(); + start[i] = time(NULL); + } + for (size_t i = 0; i < ENTRY_COUNT; i++) { + ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[i], start[i])) + << "On license " << i << " pst=" << sessions[i].pst(); + } + // Move 4 to 1. + ASSERT_NO_FATAL_FAILURE( + sessions[4].MoveUsageEntry(1, &encrypted_usage_header_)); + // Shrink header to 3 entries 0, 1 was 4, 2. + ASSERT_NO_FATAL_FAILURE(ShrinkHeader(3)); + ShutDown(); + Restart(); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageTableHeader(&encrypted_usage_header_[0], + encrypted_usage_header_.size())); + ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[0], start[0])); + // Now has index 1. + ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[4], start[4])); + ASSERT_NO_FATAL_FAILURE(ReloadLicense(&sessions[2], start[2])); + // When 4 was moved to 1, it increased the gen. number in the header. + ASSERT_NO_FATAL_FAILURE( + FailReload(&sessions[1], OEMCrypto_ERROR_GENERATION_SKEW)); + // Index 3 is beyond the end of the table. + ASSERT_NO_FATAL_FAILURE( + FailReload(&sessions[3], OEMCrypto_ERROR_UNKNOWN_FAILURE)); +} + +TEST_F(UsageTableDefragTest, MoveUsageEntriesToOpenSession) { + Session s0; + Session s1; + LoadFirstLicense(&s0, 0); + LoadFirstLicense(&s1, 1); + s0.open(); + ASSERT_NO_FATAL_FAILURE(s0.ReloadUsageEntry()); + // s0 currently open on index 0. Expect this to fail: + ASSERT_NO_FATAL_FAILURE(s1.MoveUsageEntry(0, &encrypted_usage_header_, + OEMCrypto_ERROR_ENTRY_IN_USE)); +} + +TEST_F(UsageTableDefragTest, ShrinkOverOpenSessions) { + Session s0; + Session s1; + LoadFirstLicense(&s0, 0); + LoadFirstLicense(&s1, 1); + s0.open(); + ASSERT_NO_FATAL_FAILURE(s0.ReloadUsageEntry()); + s1.open(); + ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); + // Since s0 and s1 are open, we can't shrink. + ASSERT_NO_FATAL_FAILURE(ShrinkHeader(1, OEMCrypto_ERROR_ENTRY_IN_USE)); + s1.close(); // Can shrink after closing s1, even if s0 is open. + ASSERT_NO_FATAL_FAILURE(ShrinkHeader(1, OEMCrypto_SUCCESS)); +} + +TEST_F(UsageTableDefragTest, EnlargeHeader) { + Session s0; + Session s1; + LoadFirstLicense(&s0, 0); + LoadFirstLicense(&s1, 1); + // Can only shrink the header -- not make it bigger. + ASSERT_NO_FATAL_FAILURE(ShrinkHeader(4, OEMCrypto_ERROR_UNKNOWN_FAILURE)); +} + +TEST_F(UsageTableDefragTest, CreateNewHeaderWhileUsingOldOne) { + Session s0; + Session s1; + LoadFirstLicense(&s0, 0); + LoadFirstLicense(&s1, 1); + s0.open(); + ASSERT_NO_FATAL_FAILURE(s0.ReloadUsageEntry()); + const bool kExpectFailure = false; + ASSERT_NO_FATAL_FAILURE(CreateUsageTableHeader(kExpectFailure)); +} + +TEST_F(UsageTableDefragTest, ReloadUsageEntryWrongIndex) { + Session s0; + Session s1; + LoadFirstLicense(&s0, 0); + LoadFirstLicense(&s1, 1); + s0.set_usage_entry_number(1); + ASSERT_NO_FATAL_FAILURE( + FailReload(&s0, OEMCrypto_ERROR_INVALID_SESSION)); +} + +TEST_F(UsageTableDefragTest, ReloadUsageEntryBadData) { + Session s; + LoadFirstLicense(&s, 0); + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + vector data = s.encrypted_usage_entry(); + data[0] ^= 42; + // Error could be signature or verification error. + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + &data[0], data.size())); +} + +TEST_F(UsageTableTest, CopyOldEntries) { + // First create three old entries. We open sessions first to force creation + // of the mac keys. + + Session s1; + ASSERT_NO_FATAL_FAILURE(s1.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); + ASSERT_NO_FATAL_FAILURE(s1.FillSimpleMessage(0, 0, 0, "pst number 1")); + ASSERT_NO_FATAL_FAILURE(s1.EncryptAndSign()); + + Test_PST_Report report1(s1.pst(), kUnused); + report1.seconds_since_license_received = 30; + report1.seconds_since_first_decrypt = 20; + report1.seconds_since_last_decrypt = 10; + ASSERT_NO_FATAL_FAILURE(s1.CreateOldEntry(report1)); + + Session s2; + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(s2.FillSimpleMessage(0, 0, 0, "pst number 2")); + ASSERT_NO_FATAL_FAILURE(s2.EncryptAndSign()); + + Test_PST_Report report2(s2.pst(), kActive); + report2.seconds_since_license_received = 60; + report2.seconds_since_first_decrypt = 50; + report2.seconds_since_last_decrypt = 40; + ASSERT_NO_FATAL_FAILURE(s2.CreateOldEntry(report2)); + + Session s3; + ASSERT_NO_FATAL_FAILURE(s3.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s3)); + ASSERT_NO_FATAL_FAILURE(s3.FillSimpleMessage(0, 0, 0, "pst number 3")); + ASSERT_NO_FATAL_FAILURE(s3.EncryptAndSign()); + + Test_PST_Report report3(s3.pst(), kInactive); + report3.seconds_since_license_received = 90; + report3.seconds_since_first_decrypt = 80; + report3.seconds_since_last_decrypt = 70; + ASSERT_NO_FATAL_FAILURE(s3.CreateOldEntry(report3)); + + // Now we copy and verify each one. The order is changed to make + // sure there are no order dependecies. + ASSERT_NO_FATAL_FAILURE( + s2.CopyAndVerifyOldEntry(report2, &encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE( + s1.CopyAndVerifyOldEntry(report1, &encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE( + s3.CopyAndVerifyOldEntry(report3, &encrypted_usage_header_)); +} + +TEST_F(UsageTableTest, ReloadUsageTableWithSkew) { + // This also tests a few other error conditions with usage table headers. + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + + // Reload the license, and save the header. + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + // We will reuse the encrypted and signed message, so we don't call + // FillSimpleMessage again. + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + vector old_usage_header_2_ = encrypted_usage_header_; + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + vector old_usage_header_1_ = encrypted_usage_header_; + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.close()); + + ShutDown(); + Restart(); + // Null pointer generates error. + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageTableHeader(NULL, + old_usage_header_2_.size())); + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + // Cannot load an entry with if header didn't load. + ASSERT_EQ( + OEMCrypto_ERROR_UNKNOWN_FAILURE, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + &s.encrypted_usage_entry()[0], + s.encrypted_usage_entry().size())); + + ASSERT_NO_FATAL_FAILURE(s.close()); + + // Modified header generates error. + vector bad_header = encrypted_usage_header_; + bad_header[3] ^= 42; + ASSERT_NE(OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageTableHeader(&bad_header[0], + bad_header.size())); + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + // Cannot load an entry with if header didn't load. + ASSERT_EQ( + OEMCrypto_ERROR_UNKNOWN_FAILURE, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + &s.encrypted_usage_entry()[0], + s.encrypted_usage_entry().size())); + + ASSERT_NO_FATAL_FAILURE(s.close()); + + // Old by 2 generation numbers is error. + ASSERT_EQ(OEMCrypto_ERROR_GENERATION_SKEW, + OEMCrypto_LoadUsageTableHeader(&old_usage_header_2_[0], + old_usage_header_2_.size())); + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + // Cannot load an entry with if header didn't load. + ASSERT_NE( + OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + &s.encrypted_usage_entry()[0], + s.encrypted_usage_entry().size())); + + ASSERT_NO_FATAL_FAILURE(s.close()); + + // Old by 1 generation numbers is just warning. + ASSERT_EQ(OEMCrypto_WARNING_GENERATION_SKEW, + OEMCrypto_LoadUsageTableHeader(&old_usage_header_1_[0], + old_usage_header_1_.size())); + // Everything else should still work. Skew by 1 is just a warning. + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_EQ( + OEMCrypto_WARNING_GENERATION_SKEW, + OEMCrypto_LoadUsageEntry(s.session_id(), s.usage_entry_number(), + &s.encrypted_usage_entry()[0], + s.encrypted_usage_entry().size())); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + +TEST_F(UsageTableTest, GenerateReportWrongPST) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( + 0, wvoec_mock::kControlNonceOrEntry, s.get_nonce(), pst)); + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateReport("wrong_pst", + OEMCrypto_ERROR_WRONG_PST)); } TEST_F(UsageTableTest, TimingTest) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); std::string pst1 = "my_pst_1"; std::string pst2 = "my_pst_2"; std::string pst3 = "my_pst_3"; @@ -5302,12 +5385,14 @@ TEST_F(UsageTableTest, TimingTest) { sleep(kLongSleep); ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s1)); + ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s1.LoadTestKeys(pst1, new_mac_keys_)); time_t first_decrypt1 = time(NULL); ASSERT_NO_FATAL_FAILURE(s1.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s2.LoadTestKeys(pst2, new_mac_keys_)); time_t first_decrypt2 = time(NULL); ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); @@ -5318,74 +5403,55 @@ TEST_F(UsageTableTest, TimingTest) { ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); sleep(kLongSleep); - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst1)); + ASSERT_NO_FATAL_FAILURE(s1.DeactivateUsageEntry(pst1)); + ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s1.close()); ASSERT_NO_FATAL_FAILURE(s2.close()); sleep(kLongSleep); // This is as close to reboot as we can simulate in code. - ASSERT_NO_FATAL_FAILURE( - session_.close()); // Close sessions before terminate. - OEMCrypto_Terminate(); + ShutDown(); sleep(kShortSleep); - OEMCrypto_Initialize(); - EnsureTestKeys(); - // Test teardown expects session_ to be open. - ASSERT_NO_FATAL_FAILURE(session_.open()); + Restart(); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_LoadUsageTableHeader(&encrypted_usage_header_[0], + encrypted_usage_header_.size())); // After a reboot, we should be able to reload keys, and generate reports. sleep(kLongSleep); time_t third_decrypt = time(NULL); ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); + ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s2.LoadTestKeys(pst2, new_mac_keys_)); ASSERT_NO_FATAL_FAILURE(s2.TestDecryptCTR()); + ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); ASSERT_NO_FATAL_FAILURE(s2.close()); ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(s2.open()); ASSERT_NO_FATAL_FAILURE(s3.open()); + ASSERT_NO_FATAL_FAILURE(s1.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s2.ReloadUsageEntry()); + ASSERT_NO_FATAL_FAILURE(s3.ReloadUsageEntry()); sleep(kLongSleep); - - time_t report_generated1 = time(NULL); - s1.GenerateReport(pst1); - time_t report_generated2 = time(NULL); - s2.GenerateReport(pst2); - time_t report_generated3 = time(NULL); - s3.GenerateReport(pst3); - - EXPECT_EQ(kInactive, s1.pst_report()->status); - EXPECT_NEAR(report_generated1 - loaded1, - wvcdm::htonll64(s1.pst_report()->seconds_since_license_received), - kTimeTolerance); - EXPECT_NEAR(report_generated1 - first_decrypt1, - wvcdm::htonll64(s1.pst_report()->seconds_since_first_decrypt), - kTimeTolerance); - EXPECT_NEAR(report_generated1 - second_decrypt, - wvcdm::htonll64(s1.pst_report()->seconds_since_last_decrypt), - kTimeTolerance); - - EXPECT_EQ(kActive, s2.pst_report()->status); - EXPECT_NEAR(report_generated2 - loaded2, - wvcdm::htonll64(s2.pst_report()->seconds_since_license_received), - kTimeTolerance); - EXPECT_NEAR(report_generated2 - first_decrypt2, - wvcdm::htonll64(s2.pst_report()->seconds_since_first_decrypt), - kTimeTolerance); - EXPECT_NEAR(report_generated2 - third_decrypt, - wvcdm::htonll64(s2.pst_report()->seconds_since_last_decrypt), - kTimeTolerance); - - EXPECT_EQ(kUnused, s3.pst_report()->status); - EXPECT_NEAR(report_generated3 - loaded3, - wvcdm::htonll64(s3.pst_report()->seconds_since_license_received), - kTimeTolerance); - // We don't expect first or last decrypt for unused report. + ASSERT_NO_FATAL_FAILURE(s1.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s1.GenerateVerifyReport(pst1, kInactiveUsed, + loaded1, + first_decrypt1, + second_decrypt)); + ASSERT_NO_FATAL_FAILURE(s2.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s2.GenerateVerifyReport(pst2, kActive, + loaded2, + first_decrypt2, + third_decrypt)); + ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s3.GenerateVerifyReport(pst3, kUnused, loaded3)); } TEST_F(UsageTableTest, VerifyUsageTimes) { std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); @@ -5393,19 +5459,16 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, s.get_nonce(), pst)); ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + ASSERT_NO_FATAL_FAILURE(s.CreateNewUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); + time_t load_time = time(NULL); - const int kLicenseReceivedTimeTolerance = kSpeedMultiplier; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - s.GenerateReport(pst); - EXPECT_EQ(kUnused, s.pst_report()->status); - EXPECT_NEAR(wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - 0, kLicenseReceivedTimeTolerance); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused)); const time_t kDotIntervalInSeconds = 5; const time_t kIdleInSeconds = 20; const time_t kPlaybackLoopInSeconds = 2 * 60; - const time_t kUsageTableTimeTolerance = 10; cout << "This test verifies the elapsed time reported in the usage table " "for a 2 minute simulated playback." @@ -5417,11 +5480,8 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { PrintDotsWhileSleep(kIdleInSeconds, kDotIntervalInSeconds); - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - s.GenerateReport(pst); - EXPECT_EQ(kUnused, s.pst_report()->status); - EXPECT_NEAR(wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - kIdleInSeconds, kLicenseReceivedTimeTolerance); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kUnused, load_time)); cout << "Start simulated playback..." << endl; time_t dot_time = kDotIntervalInSeconds; @@ -5429,8 +5489,11 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { time_t start_time = time(NULL); do { ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - s.GenerateReport(pst); - EXPECT_EQ(kActive, s.pst_report()->status); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive, + load_time, + start_time, + 0)); // last decrypt = now. playback_time = time(NULL) - start_time; ASSERT_LE(0, playback_time); if (playback_time >= dot_time) { @@ -5441,16 +5504,13 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { } while (playback_time < kPlaybackLoopInSeconds); cout << "\nSimulated playback time = " << playback_time << " seconds.\n"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - s.GenerateReport(pst); - EXPECT_NEAR(wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - playback_time + kIdleInSeconds, kLicenseReceivedTimeTolerance); - EXPECT_NEAR(wvcdm::htonll64(s.pst_report()->seconds_since_first_decrypt), - playback_time, kUsageTableTimeTolerance); - EXPECT_NEAR(wvcdm::htonll64(s.pst_report()->seconds_since_last_decrypt), 0, - kUsageTableTimeTolerance); - EXPECT_NEAR(wvcdm::htonll64(s.pst_report()->seconds_since_first_decrypt) - - wvcdm::htonll64(s.pst_report()->seconds_since_last_decrypt), + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive, + load_time, + start_time, + 0)); // last decrypt = now. + EXPECT_NEAR(s.pst_report().seconds_since_first_decrypt() - + s.pst_report().seconds_since_last_decrypt(), playback_time, kUsageTableTimeTolerance); cout << "Wait another " << kIdleInSeconds @@ -5465,75 +5525,107 @@ TEST_F(UsageTableTest, VerifyUsageTimes) { // |<--->| = seconds_since_last_decrypt // |<----------------------------->| = seconds_since_first_decrypt // |<------------------------------------| = seconds_since_license_received - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - s.GenerateReport(pst); - EXPECT_NEAR(wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - playback_time + 2 * kIdleInSeconds, - kLicenseReceivedTimeTolerance); - EXPECT_NEAR(wvcdm::htonll64(s.pst_report()->seconds_since_first_decrypt), - playback_time + kIdleInSeconds, kUsageTableTimeTolerance); - EXPECT_NEAR(wvcdm::htonll64(s.pst_report()->seconds_since_last_decrypt), - kIdleInSeconds, kUsageTableTimeTolerance); - - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); - ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); - EXPECT_EQ(kInactive, s.pst_report()->status); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kActive, + load_time, + start_time, + kIdleInSeconds)); + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUsed, + load_time, + start_time, + kIdleInSeconds)); ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } -// This is a special case where a collection of licenses can be shared with -// multiple devices. In order for this to work, a single session must first -// load a device specific license, and then a shared content license. +// This is a special case where a group of assets can be licensed with a master +// key. In order for this to work, a single session must first load a device +// specific license, and then a shared content license. This shared license is +// sometimes called an embedded license. TEST_F(UsageTableTest, LoadSharedLicense) { - // session_.generatersasignature. - // session_.GenerateNonce - // DeriveKeysFromSessionKey - (specify enc/mac keys. - // LoadKeys replay control = 2. loads new mac keys. - // LoadKeys replay control = 0. uses same mac key. - // check second loadkeys without first fails. std::string pst = "my_pst"; - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + // We will reuse the encrypted and signed message, so we don't call + // FillSimpleMessage again. + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, true)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage(0, 0, 0)); - // The second set of keys are not loaded. - for (int i = 0; i < s.num_keys(); i++) { + + // The second set of keys are in the shared license. They will have the + // same mac keys as the original license, so we leave that alone. + // They are given different key ids so we can test that they were loaded. + // For this test, we leave the key content the same -- in real life it + // will be different. + for (unsigned int i = 0; i < s.num_keys(); i++) { memset(s.license().keys[i].key_id, 'A' + i, s.license().keys[i].key_id_length); + s.license().keys[i].control.nonce = 0; + s.license().keys[i].control.control_bits = + htonl(wvoec_mock::kSharedLicense); } - // TODO(fredgc,jfore): Decide if first set of keys need to stay loaded, or if - // they are replaced. ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys()); + ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, false)); ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); ASSERT_NO_FATAL_FAILURE(s.close()); } +TEST_F(UsageTableTest, LoadSharedLicenseWithNoMaster) { + std::string pst = "my_pst"; + Session s; + ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); + + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); + // This time, we do NOT load the master license. This should + // generate an error below. + // ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, true)); + + // The second set of keys are in the shared license. + // We given them different key ids so we can test that they were loaded. + // For this test, we leave the key content the same -- in real life it + // will be different. + for (unsigned int i = 0; i < s.num_keys(); i++) { + memset(s.license().keys[i].key_id, 'A' + i, + s.license().keys[i].key_id_length); + s.license().keys[i].control.nonce = 0; + s.license().keys[i].control.control_bits = + htonl(wvoec_mock::kSharedLicense); + } + ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); + uint8_t* pst_ptr = s.encrypted_license().pst; + ASSERT_EQ(OEMCrypto_ERROR_MISSING_MASTER, + OEMCrypto_LoadKeys( + s.session_id(), s.message_ptr(), s.message_size(), + &s.signature()[0], s.signature().size(), + s.encrypted_license().mac_key_iv, + s.encrypted_license().mac_keys, + s.num_keys(), s.key_array(), pst_ptr, pst.length(), NULL)); + ASSERT_NO_FATAL_FAILURE(s.close()); +} + TEST_F(UsageTableTest, PSTLargeBuffer) { std::string pst(kMaxPSTLength, 'a'); // A large PST. - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); Session s; ASSERT_NO_FATAL_FAILURE(LoadOfflineLicense(s, pst)); ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); + ASSERT_NO_FATAL_FAILURE(s.ReloadUsageEntry()); ASSERT_NO_FATAL_FAILURE( s.LoadTestKeys(pst, new_mac_keys_)); // Reload the license ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); // Should be able to decrypt. - ASSERT_NO_FATAL_FAILURE(DeactivatePST(pst)); // Then deactivate. + ASSERT_NO_FATAL_FAILURE(s.DeactivateUsageEntry(pst)); // Then deactivate. // After deactivate, should not be able to decrypt. ASSERT_NO_FATAL_FAILURE( s.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); - ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); - EXPECT_EQ(kInactive, s.pst_report()->status); - EXPECT_NEAR(0, - wvcdm::htonll64(s.pst_report()->seconds_since_license_received), - kTimeTolerance); + ASSERT_NO_FATAL_FAILURE(s.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s.GenerateVerifyReport(pst, kInactiveUsed)); ASSERT_NO_FATAL_FAILURE(s.close()); Session s2; @@ -5541,72 +5633,22 @@ TEST_F(UsageTableTest, PSTLargeBuffer) { ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s2)); // Offile license can not be reused if it has been deactivated. uint8_t* pst_ptr = s.encrypted_license().pst; + ASSERT_NO_FATAL_FAILURE(s2.LoadUsageEntry(s)); EXPECT_NE( OEMCrypto_SUCCESS, OEMCrypto_LoadKeys(s2.session_id(), s.message_ptr(), s.message_size(), &s.signature()[0], s.signature().size(), s.encrypted_license().mac_key_iv, - s.encrypted_license().mac_keys, s.key_array_size(), - s.key_array(), pst_ptr, pst.length())); + s.encrypted_license().mac_keys, s.num_keys(), + s.key_array(), pst_ptr, pst.length(), NULL)); + s2.close(); // But we can still generate a report. Session s3; ASSERT_NO_FATAL_FAILURE(s3.open()); - ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, true, &s)); - EXPECT_EQ(kInactive, s3.pst_report()->status); -} - -TEST_F(UsageTableTest, DeleteEntryLargeBuffer) { - std::string pst(kMaxPSTLength, 'a'); // A large PST. - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - s.GenerateReport(pst); - ASSERT_NO_FATAL_FAILURE(s.close()); - - // New session should be able to generate report and copy mac keys. - Session s2; - ASSERT_NO_FATAL_FAILURE(s2.open()); - ASSERT_NO_FATAL_FAILURE(s2.GenerateReport(pst, true, &s)); - EXPECT_EQ(kActive, s2.pst_report()->status); - ASSERT_NO_FATAL_FAILURE(s2.DeleteEntry(pst)); - ASSERT_NO_FATAL_FAILURE(s2.close()); - - // Now that session is deleted, we can't generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, false)); - ASSERT_NO_FATAL_FAILURE(s3.close()); -} - -TEST_F(UsageTableTest, ForceDeleteLargeBuffer) { - std::string pst(kMaxPSTLength, 'a'); // A large PST. - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_UpdateUsageTable()); - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(InstallTestSessionKeys(&s)); - ASSERT_NO_FATAL_FAILURE(s.FillSimpleMessage( - 0, wvoec_mock::kControlNonceEnabled | wvoec_mock::kControlNonceRequired, - s.get_nonce(), pst)); - ASSERT_NO_FATAL_FAILURE(s.EncryptAndSign()); - ASSERT_NO_FATAL_FAILURE(s.LoadTestKeys(pst, new_mac_keys_)); - ASSERT_NO_FATAL_FAILURE(s.TestDecryptCTR()); - ASSERT_NO_FATAL_FAILURE(s.GenerateReport(pst)); - ASSERT_NO_FATAL_FAILURE(s.close()); - - ASSERT_NO_FATAL_FAILURE(s.ForceDeleteEntry(pst)); - - // Now that session is deleted, we can't generate a report for it. - Session s3; - ASSERT_NO_FATAL_FAILURE(s3.open()); - ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, false)); - ASSERT_NO_FATAL_FAILURE(s3.close()); + ASSERT_NO_FATAL_FAILURE(s3.LoadUsageEntry(s)); + ASSERT_NO_FATAL_FAILURE(s3.UpdateUsageEntry(&encrypted_usage_header_)); + ASSERT_NO_FATAL_FAILURE(s3.GenerateReport(pst, OEMCrypto_SUCCESS, &s)); + EXPECT_EQ(kInactiveUsed, s3.pst_report().status()); } INSTANTIATE_TEST_CASE_P(TestUsageTables, UsageTableTestWithMAC, diff --git a/oemcrypto/test/oemcrypto_test_android.cpp b/oemcrypto/test/oemcrypto_test_android.cpp index 58c9ede1..b7719daf 100644 --- a/oemcrypto/test/oemcrypto_test_android.cpp +++ b/oemcrypto/test/oemcrypto_test_android.cpp @@ -16,6 +16,8 @@ #include "OEMCryptoCENC.h" +#include "log.h" + namespace wvoec { // These tests are required for LollyPop Android devices. @@ -101,9 +103,18 @@ TEST_F(OEMCryptoAndroidMNCTest, MinVersionNumber10) { ASSERT_GE(version, 10u); } -TEST_F(OEMCryptoAndroidMNCTest, LoadsTestKeyboxImplemented) { +TEST_F(OEMCryptoAndroidMNCTest, LoadTestKeybox) { if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_LoadTestKeybox()); + OEMCryptoResult status = OEMCrypto_LoadTestKeybox(); + // OEMCrypto may return success or not implemented. + if (status == OEMCrypto_SUCCESS) { + LOGV("OEMCrypto_LoadTestKeybox is implemented."); + } else if (status == OEMCrypto_ERROR_NOT_IMPLEMENTED) { + LOGV("OEMCrypto_LoadTestKeybox is NOT implemented."); + } else { + // Bad status - fail. + ASSERT_EQ(status, OEMCrypto_SUCCESS); + } } else { // Android should use keybox or provisioning 3.0. ASSERT_EQ(OEMCrypto_OEMCertificate, OEMCrypto_GetProvisioningMethod()); @@ -130,4 +141,12 @@ TEST_F(OEMCryptoAndroidNYCTest, MinVersionNumber11) { ASSERT_GE(version, 11u); } +// These tests are required for O MR1 Android devices. +class OEMCryptoAndroidOCTest : public OEMCryptoAndroidNYCTest {}; + +TEST_F(OEMCryptoAndroidOCTest, MinVersionNumber13) { + uint32_t version = OEMCrypto_APIVersion(); + ASSERT_GE(version, 13u); +} + } // namespace wvoec diff --git a/platforms/x86-64/environment.py b/platforms/x86-64/environment.py index d3b85474..585b901c 100644 --- a/platforms/x86-64/environment.py +++ b/platforms/x86-64/environment.py @@ -2,4 +2,6 @@ export_variables = { 'CC': 'clang', 'CXX': 'clang++', 'AR': 'ar', + 'GYP_CROSSCOMPILE': '1', + 'CLANG_BUILD': '1', } diff --git a/platforms/x86-64/settings.gypi b/platforms/x86-64/settings.gypi index aab510ca..b67a6e02 100644 --- a/platforms/x86-64/settings.gypi +++ b/platforms/x86-64/settings.gypi @@ -3,32 +3,36 @@ # Here you can override global gyp variables with platform-specific values. # See cdm.gyp for a complete list of settings you can override. 'variables': { - 'disable_cpp_11%': 0, - #'oemcrypto_version': 9, }, # end variables # Here you can set platform-specific compiler settings. 'target_defaults': { # These are flags passed to the compiler for all C & C++ files. 'cflags': [ + '-fPIC', ], # These are flags passed to the compiler for plain C only. 'cflags_c': [ '-Wbad-function-cast', ], - # These are flags passed to the compiler for C++ only. 'cflags_cc': [ - '-fPIC', - '-fno-rtti', + # Compile using the gnu'98 standard. + # C++ standards are not used becuase of use of va_copy in + # third_party/protobuf (When using std++03 it defines + # __STRICT_ANSI__, which will make clang headers not define + # va_copy.). C++11 is not used because some partner + # toolchains do not yet support it. + '-std=gnu++98', + # '-fno-rtti', '-fno-exceptions', # Enable all warnings, and treat warnings as errors. '-Wall', '-Werror', '-Wextra', '-Wunused', - '-fno-rtti', + # '-fno-rtti', '-Wcast-qual', '-Wno-long-long', '-Wno-variadic-macros', @@ -37,8 +41,8 @@ '-Wno-format-nonliteral', '-Wshadow', '-Wnon-virtual-dtor', - '-Woverloaded-virtual', - '-Wctor-dtor-privacy', + #'-Woverloaded-virtual', + #'-Wctor-dtor-privacy', '-Wcast-align', '-Wno-conversion', '-Wignored-qualifiers', @@ -46,6 +50,10 @@ #'-Wstrict-null-sentinel', #'-Wuseless-cast', '-Wno-unused-parameter', # repeated in protobufs triggers this + '-Wno-unused-local-typedefs', # metrics requires this + '-Wno-inconsistent-missing-override', + #'-Wno-maybe-uninitialized', + '-Wno-unknown-warning-option', ], # These are flags passed to the linker. @@ -54,6 +62,8 @@ # These are macros set by the compiler. 'defines': [ + # suppress use of override keyword. + 'DISABLE_OVERRIDE_KEYWORD', #'EXAMPLE_MACRO_WITH_NO_VALUE', #'EXAMPLE_KEY=EXAMPLE_VALUE', ], @@ -75,24 +85,18 @@ ], 'cflags_cc=': [ - #'-fno-rtti', '-Wcast-qual', - '-Wextra', - '-Wunused', + # '-Wextra', + '-Wno-unused', '-Wno-long-long', '-Wno-variadic-macros', '-Wno-inline', '-Wformat=2', '-Wno-format-nonliteral', - #'-Wshadow', '-Wnon-virtual-dtor', - '-Woverloaded-virtual', - #'-Wstrict-null-sentinel', - '-Wctor-dtor-privacy', - #'-Wbad-function-cast', + #'-Woverloaded-virtual', + #'-Wctor-dtor-privacy', '-Wcast-align', - #'-Wconversion', - #'-Wuseless-cast', '-Wno-ignored-qualifiers', '-Wno-return-type', ], @@ -106,23 +110,6 @@ 'include_dirs=': [ ], }], # end _toolset == "host" condition - - # Compile using C++11, unless we are forcing the use of C++03 - ['<(disable_cpp_11) == 1', { - 'defines': [ - 'DISABLE_CPP_11', - ], - 'cflags_cc': [ - # When using std++03 it defines __STRICT_ANSI__, which will make the - # clang headers not define va_copy. - '-std=gnu++98', - ], - }, { # disable_cpp_11 != 1 - 'cflags_cc': [ - '-std=c++11', - '-Wc++11-compat', - ], - }], # end disable_cpp_11 == 1 condition ], # end target_conditions 'configurations': { diff --git a/test/auth/test_keybox.cpp b/test/auth/test_keybox.cpp new file mode 100644 index 00000000..c6122f32 --- /dev/null +++ b/test/auth/test_keybox.cpp @@ -0,0 +1,113 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +// TEST ONLY: Widevine Keybox + +#include "test_keybox.h" + +namespace { + +// This is a test keybox. It (and device certificates derived from it +// through provisioning) will not be accepted by production systems. + +extern const uint32_t kKeyboxSystemId_Test = 4121; + +extern const uint8_t kKeybox_Test[] = { + // Sample keybox used for test vectors + // deviceID + 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey01 + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + // key + 0xfb, 0xda, 0x04, 0x89, 0xa1, 0x58, 0x16, 0x0e, + 0xa4, 0x02, 0xe9, 0x29, 0xe3, 0xb6, 0x8f, 0x04, + // data + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, + 0x07, 0xd9, 0xff, 0xde, 0x13, 0xaa, 0x95, 0xc1, + 0x22, 0x67, 0x80, 0x53, 0x36, 0x21, 0x36, 0xbd, + 0xf8, 0x40, 0x8f, 0x82, 0x76, 0xe4, 0xc2, 0xd8, + 0x7e, 0xc5, 0x2b, 0x61, 0xaa, 0x1b, 0x9f, 0x64, + 0x6e, 0x58, 0x73, 0x49, 0x30, 0xac, 0xeb, 0xe8, + 0x99, 0xb3, 0xe4, 0x64, 0x18, 0x9a, 0x14, 0xa8, + 0x72, 0x02, 0xfb, 0x02, 0x57, 0x4e, 0x70, 0x64, + 0x0b, 0xd2, 0x2e, 0xf4, 0x4b, 0x2d, 0x7e, 0x39, + // magic + 0x6b, 0x62, 0x6f, 0x78, + // Crc + 0x0a, 0x7a, 0x2c, 0x35, +}; + +extern const size_t kKeyboxSize_Test = 128; + +extern const uint8_t kKeyboxValid02_Test[] = { + // Sample keybox used for test vectors + // deviceID + 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey02 + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + // key + 0x76, 0x5d, 0xce, 0x01, 0x04, 0x89, 0xb3, 0xd0, + 0xdf, 0xce, 0x54, 0x8a, 0x49, 0xda, 0xdc, 0xb6, + // data + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, + 0x92, 0x27, 0x0b, 0x1f, 0x1a, 0xd5, 0xc6, 0x93, + 0x19, 0x3f, 0xaa, 0x74, 0x1f, 0xdd, 0x5f, 0xb4, + 0xe9, 0x40, 0x2f, 0x34, 0xa4, 0x92, 0xf4, 0xae, + 0x9a, 0x52, 0x39, 0xbc, 0xb7, 0x24, 0x38, 0x13, + 0xab, 0xf4, 0x92, 0x96, 0xc4, 0x81, 0x60, 0x33, + 0xd8, 0xb8, 0x09, 0xc7, 0x55, 0x0e, 0x12, 0xfa, + 0xa8, 0x98, 0x62, 0x8a, 0xec, 0xea, 0x74, 0x8a, + 0x4b, 0xfa, 0x5a, 0x9e, 0xb6, 0x49, 0x0d, 0x80, + // magic + 0x6b, 0x62, 0x6f, 0x78, + // Crc + 0x2a, 0x3b, 0x3e, 0xe4, +}; + +extern const uint8_t kKeyboxValid03_Test[] = { + // Sample keybox used for test vectors + // deviceID + 0x54, 0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x30, // TestKey03 + 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ........ + // key + 0x25, 0xe5, 0x2a, 0x02, 0x29, 0x68, 0x04, 0xa2, + 0x92, 0xfd, 0x7c, 0x67, 0x0b, 0x67, 0x1f, 0x31, + // data + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x10, 0x19, + 0xf4, 0x0a, 0x0e, 0xa2, 0x0a, 0x71, 0xd5, 0x92, + 0xfa, 0xa3, 0x25, 0xc6, 0x4b, 0x76, 0xf1, 0x64, + 0xf4, 0x60, 0xa0, 0x30, 0x72, 0x23, 0xbe, 0x03, + 0xcd, 0xde, 0x7a, 0x06, 0xd4, 0x01, 0xeb, 0xdc, + 0xe0, 0x50, 0xc0, 0x53, 0x0a, 0x50, 0xb0, 0x37, + 0xe5, 0x05, 0x25, 0x0e, 0xa4, 0xc8, 0x5a, 0xff, + 0x46, 0x6e, 0xa5, 0x31, 0xf3, 0xdd, 0x94, 0xb7, + 0xe0, 0xd3, 0xf9, 0x04, 0xb2, 0x54, 0xb1, 0x64, + // magic + 0x6b, 0x62, 0x6f, 0x78, + // Crc + 0xa1, 0x99, 0x5f, 0x46, +}; + + +} // namespace + +// Refer to the following in main modules. +// This level of indirection is present so new OEM Certificates can be +// added and then selected for use at compile time or run time. + +namespace wvcdm_test_auth { + +const uint32_t kKeyboxSystemId = kKeyboxSystemId_Test; + +const uint8_t* kKeybox = kKeybox_Test; + +const size_t kKeyboxSize = kKeyboxSize_Test; + +const uint8_t* kKeyboxValid02 = kKeyboxValid02_Test; + +const uint8_t* kKeyboxValid03 = kKeyboxValid03_Test; + +} // namespace wvcdm_test_auth diff --git a/test/auth/test_keybox.h b/test/auth/test_keybox.h new file mode 100644 index 00000000..a51d45cd --- /dev/null +++ b/test/auth/test_keybox.h @@ -0,0 +1,29 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +// TEST ONLY: Widevine Keybox + +#ifndef TEST_AUTH_KEYBOX_H_ +#define TEST_AUTH_KEYBOX_H_ + +#include +#include + +namespace wvcdm_test_auth { + +extern const uint32_t kKeyboxSystemId; + +extern const uint8_t* kKeybox; +extern const uint8_t* kKeyboxValid02; +extern const uint8_t* kKeyboxValid03; +extern const size_t kKeyboxSize; + + +const size_t kKeyboxDeviceIdOffset = 0; +const size_t kKeyboxDeviceIdSize = 32; + +const size_t kKeyboxDataOffset = 64; +const size_t kKeyboxDataSize = 48; + +} // namespace wvcdm_test_auth + +#endif // TEST_AUTH_KEYBOX_H_ diff --git a/test/auth/test_oem_cert.cpp b/test/auth/test_oem_cert.cpp new file mode 100644 index 00000000..1dbef3e3 --- /dev/null +++ b/test/auth/test_oem_cert.cpp @@ -0,0 +1,633 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +// Test-only OEM certificate. + +#include "test_oem_cert.h" + +namespace { + +extern const uint32_t kOEMSystemId_Prod = 7346; + +extern const uint8_t kOEMPrivateKey_Prod[] = { + 0x30, 0x82, 0x06, 0xfe, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x06, 0xe8, 0x30, 0x82, 0x06, 0xe4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x81, 0x00, 0xf5, 0x09, 0x64, 0x4a, 0x26, 0xfe, 0xc0, 0x98, 0x55, 0x6a, + 0x1d, 0x5d, 0x1c, 0xc7, 0x38, 0xaf, 0xfd, 0x49, 0x9e, 0x85, 0x3f, 0xd6, + 0x45, 0x0e, 0x99, 0x09, 0x85, 0x69, 0x84, 0x3c, 0xfe, 0x72, 0xa5, 0x56, + 0xfa, 0x11, 0x4f, 0x6b, 0x7d, 0x32, 0x2b, 0x0c, 0xbf, 0x8f, 0xac, 0x47, + 0x96, 0x22, 0x82, 0x3d, 0xf5, 0x64, 0x74, 0x7e, 0x62, 0x68, 0x74, 0xcd, + 0x0a, 0xec, 0x84, 0xc5, 0x15, 0x06, 0x0e, 0x5a, 0x2f, 0x20, 0xe3, 0xc9, + 0x67, 0xcd, 0xdd, 0x01, 0xb8, 0xb3, 0x18, 0x87, 0x8c, 0xa9, 0x58, 0x86, + 0x0f, 0xb6, 0xc3, 0x42, 0x7e, 0x87, 0x48, 0x5e, 0x10, 0x49, 0xc7, 0xd7, + 0xb7, 0xb8, 0xa6, 0x34, 0x08, 0x0c, 0x94, 0xf4, 0xbb, 0x2a, 0x06, 0xa4, + 0x4f, 0xec, 0xbc, 0xc4, 0x37, 0xbe, 0x99, 0x10, 0x23, 0x37, 0x24, 0xb1, + 0xdf, 0xcb, 0xe6, 0x3f, 0xc1, 0xf0, 0x0f, 0x04, 0x03, 0xc8, 0xb0, 0x1e, + 0xd6, 0xb8, 0xae, 0x77, 0xe1, 0x4d, 0x6d, 0x97, 0x69, 0x6d, 0x8a, 0x73, + 0x66, 0x32, 0x57, 0x6f, 0xcf, 0xea, 0x1e, 0x7b, 0x87, 0x03, 0x75, 0xb1, + 0xef, 0x83, 0x64, 0x26, 0xf1, 0x3f, 0xbf, 0xe6, 0x28, 0x03, 0x72, 0x57, + 0xbf, 0x47, 0x29, 0x99, 0x8f, 0x74, 0x1d, 0x01, 0x16, 0xad, 0xb2, 0xdf, + 0x80, 0xa4, 0xd3, 0x8b, 0xeb, 0x61, 0xd1, 0x40, 0x68, 0xb9, 0xa2, 0xa5, + 0xef, 0x2b, 0xe5, 0x78, 0xe8, 0x28, 0x88, 0x87, 0xb7, 0x53, 0x49, 0xbb, + 0xe4, 0xea, 0x0d, 0x5e, 0x96, 0xa5, 0xdd, 0x1f, 0x0b, 0x25, 0x8b, 0xb5, + 0x95, 0x46, 0xe7, 0xba, 0xb8, 0xc4, 0x0a, 0x36, 0xb1, 0x89, 0xeb, 0x27, + 0x5d, 0xd9, 0x97, 0x24, 0x59, 0xa3, 0x9b, 0xb0, 0x23, 0x0b, 0xd2, 0xec, + 0x65, 0x91, 0xf9, 0xf0, 0xa0, 0x74, 0x5f, 0xb4, 0xce, 0x22, 0x27, 0x18, + 0x37, 0xe2, 0x4b, 0xfc, 0x91, 0xf9, 0x09, 0x15, 0xe6, 0xdb, 0x06, 0x9b, + 0x4d, 0x82, 0xdc, 0x36, 0x14, 0x48, 0xc6, 0xd5, 0x87, 0xca, 0xec, 0x5a, + 0xa2, 0x29, 0x33, 0xef, 0x22, 0x0c, 0x4b, 0xbf, 0xe7, 0x2f, 0x95, 0xe1, + 0xd3, 0xa5, 0xd8, 0xaa, 0x44, 0x77, 0x29, 0xa3, 0x20, 0x33, 0xd2, 0x51, + 0xa2, 0xf9, 0x4a, 0x6f, 0xf7, 0x3e, 0xf7, 0x0b, 0x8a, 0xec, 0xc1, 0x99, + 0x1d, 0x47, 0xf3, 0x74, 0x02, 0x04, 0xab, 0x8e, 0x62, 0x4c, 0x9e, 0x00, + 0xc2, 0x84, 0xd7, 0xd0, 0xf8, 0xe4, 0x1c, 0x9d, 0x98, 0x15, 0xa8, 0x8f, + 0x08, 0x98, 0x4e, 0x5a, 0xfa, 0xd6, 0x60, 0x87, 0x12, 0xdc, 0x8e, 0xfd, + 0xcb, 0xb3, 0x13, 0x97, 0x7a, 0xa8, 0x8c, 0x56, 0x2e, 0x49, 0x26, 0x60, + 0xe9, 0x4a, 0xdc, 0xec, 0x3f, 0xf0, 0x94, 0xcd, 0x90, 0x8e, 0x7c, 0x21, + 0x3f, 0x80, 0x14, 0x33, 0xdd, 0xb0, 0x00, 0xe2, 0x09, 0x37, 0x06, 0xdd, + 0x17, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x81, 0x00, + 0xa1, 0xc9, 0x44, 0xad, 0x6d, 0x17, 0xd1, 0x04, 0x03, 0x89, 0x5f, 0xbf, + 0xe5, 0xcb, 0x68, 0x13, 0x52, 0xf2, 0x33, 0xb7, 0x19, 0x12, 0x19, 0x60, + 0x6d, 0x0d, 0x0b, 0x48, 0x42, 0xe6, 0x9e, 0xbe, 0x05, 0x8a, 0xea, 0xeb, + 0x58, 0xfb, 0xc8, 0x9a, 0xc2, 0x2f, 0xd5, 0x9f, 0x40, 0x09, 0xb8, 0x08, + 0x2a, 0xe4, 0x4b, 0xcc, 0xba, 0xd9, 0xe3, 0x91, 0xc2, 0x64, 0xcb, 0x6c, + 0xa4, 0xb1, 0x17, 0x93, 0x7b, 0x10, 0x72, 0x83, 0x8d, 0xc2, 0xa2, 0x46, + 0x1b, 0x41, 0x12, 0xb9, 0x35, 0x5d, 0xf2, 0x32, 0xb1, 0xdf, 0x3a, 0x2a, + 0xda, 0xbb, 0x61, 0x9d, 0x62, 0xdb, 0xb0, 0x77, 0x76, 0x7a, 0x68, 0xb4, + 0x83, 0x10, 0x61, 0xac, 0x25, 0x01, 0x7d, 0x3e, 0x5f, 0x4a, 0x47, 0xf7, + 0x30, 0x1f, 0x82, 0x0a, 0xd7, 0x36, 0xff, 0x79, 0x5e, 0x42, 0x0f, 0x58, + 0xaa, 0x3a, 0xb8, 0x8b, 0x0e, 0xef, 0x00, 0xac, 0x96, 0x14, 0x96, 0x83, + 0x33, 0xb6, 0xb5, 0x4c, 0x91, 0x2a, 0x62, 0x92, 0xcf, 0xd2, 0x27, 0xcc, + 0xdf, 0x4c, 0x55, 0x03, 0xe8, 0x82, 0x78, 0xff, 0x80, 0xcb, 0x2e, 0x30, + 0x1b, 0x85, 0x56, 0xce, 0x57, 0x9e, 0xd8, 0x16, 0x86, 0x7d, 0x87, 0x2e, + 0xae, 0x39, 0xd4, 0xac, 0xbe, 0xa5, 0xc4, 0x5a, 0x85, 0x7a, 0xea, 0x8e, + 0x69, 0x9a, 0xbd, 0x9c, 0x45, 0x33, 0xf8, 0xb1, 0x70, 0xc1, 0x8c, 0xaa, + 0xad, 0x3d, 0x76, 0x08, 0x5b, 0x7d, 0x12, 0x93, 0x03, 0x70, 0xe6, 0x5f, + 0xb2, 0xac, 0x78, 0xae, 0xbe, 0xc5, 0x31, 0xc8, 0x6d, 0x2c, 0x1c, 0x2f, + 0x41, 0x37, 0xf6, 0x88, 0xd3, 0x80, 0x93, 0xed, 0x55, 0xb1, 0xaa, 0x49, + 0xe8, 0x42, 0xd0, 0x19, 0x20, 0x58, 0xe7, 0x2d, 0x10, 0xf2, 0x69, 0x49, + 0xe9, 0x47, 0x95, 0xbb, 0xce, 0xa7, 0xe9, 0x86, 0x46, 0x1c, 0xd1, 0x1f, + 0xd4, 0xa9, 0xeb, 0x28, 0x57, 0x78, 0x6c, 0xc6, 0x6f, 0x84, 0x3b, 0xb4, + 0x8c, 0xe5, 0xd3, 0x23, 0x8b, 0xbe, 0x83, 0x75, 0x5e, 0xea, 0xcd, 0x93, + 0xd2, 0x42, 0x86, 0xfd, 0x2b, 0x67, 0x72, 0xe0, 0x46, 0x9c, 0xf7, 0xc1, + 0xe9, 0x5d, 0xad, 0xac, 0xcb, 0x57, 0xb4, 0xe7, 0x87, 0x25, 0x7d, 0x5a, + 0x43, 0x90, 0xa3, 0x2e, 0xbf, 0x36, 0xbd, 0x4c, 0xba, 0xec, 0x0f, 0x21, + 0x51, 0xda, 0x66, 0xb8, 0x1e, 0xac, 0x33, 0xeb, 0xa1, 0x3e, 0x72, 0x15, + 0x10, 0x45, 0xc0, 0xe4, 0xdb, 0x04, 0x6d, 0xaf, 0x66, 0xc2, 0xfc, 0x35, + 0x04, 0x60, 0x7d, 0x2f, 0x5e, 0x9e, 0x83, 0xf6, 0x72, 0x92, 0x6a, 0x9f, + 0xba, 0x94, 0x97, 0x33, 0xe1, 0x1d, 0x42, 0xda, 0xad, 0xa5, 0x8b, 0xad, + 0x2f, 0x2f, 0x32, 0x16, 0x88, 0x54, 0x88, 0xb2, 0x85, 0xe2, 0x33, 0x08, + 0x43, 0xc8, 0x68, 0x69, 0xaa, 0xea, 0x9a, 0xbf, 0x41, 0x12, 0xe6, 0xf1, + 0x02, 0x81, 0xc1, 0x00, 0xfe, 0x96, 0xe7, 0xc8, 0x89, 0x61, 0x2b, 0x58, + 0xaa, 0xcd, 0x37, 0x46, 0x13, 0xa1, 0x2a, 0xc8, 0x1b, 0x76, 0xde, 0x4c, + 0xb3, 0x00, 0x4f, 0x6b, 0x02, 0xc0, 0x10, 0xef, 0x87, 0xe2, 0x6d, 0x7f, + 0x10, 0x57, 0xec, 0xde, 0x70, 0x60, 0xb5, 0x8f, 0x6d, 0x17, 0x35, 0xbd, + 0xfd, 0x6a, 0x2c, 0xbb, 0xf0, 0x48, 0x5b, 0x32, 0x41, 0xf6, 0xc0, 0x62, + 0x3a, 0x88, 0xc5, 0x41, 0x83, 0x85, 0x56, 0xa7, 0x11, 0xf4, 0xd2, 0xa9, + 0xb3, 0xa3, 0xcb, 0xae, 0xca, 0xee, 0x1c, 0x52, 0x7f, 0x04, 0x34, 0x61, + 0xb9, 0x8d, 0xa3, 0x26, 0x88, 0xce, 0x3d, 0xdb, 0x9c, 0xbf, 0xcc, 0xc4, + 0x38, 0x8b, 0xf2, 0xe4, 0x7e, 0xcc, 0x46, 0x86, 0x7a, 0x3c, 0xb7, 0x95, + 0x3f, 0xbd, 0x81, 0x45, 0xc3, 0x1d, 0xbb, 0xf8, 0x56, 0x6e, 0xa5, 0x88, + 0x2d, 0xff, 0x78, 0xc0, 0xc2, 0x4a, 0x4f, 0xea, 0xb8, 0x81, 0xf3, 0x96, + 0x5e, 0xd8, 0xcb, 0x96, 0x42, 0xa3, 0x21, 0x03, 0xc8, 0x00, 0xbf, 0x95, + 0xc7, 0x83, 0x80, 0xc6, 0xc2, 0x38, 0xe4, 0xaf, 0xb2, 0x0e, 0x80, 0x92, + 0x97, 0x21, 0x16, 0xce, 0x39, 0xd3, 0x95, 0xb7, 0xc3, 0x78, 0xe1, 0x1c, + 0xc0, 0x5a, 0xbc, 0x9b, 0x68, 0x3f, 0xd6, 0x42, 0xcd, 0xca, 0x0b, 0x6d, + 0x9f, 0xde, 0x6b, 0x98, 0x3b, 0x47, 0x57, 0xb9, 0x2d, 0x92, 0x52, 0x29, + 0xc5, 0xed, 0xb5, 0x0d, 0x02, 0x81, 0xc1, 0x00, 0xf6, 0x64, 0xef, 0xef, + 0x57, 0xdb, 0x06, 0xae, 0x36, 0x86, 0x11, 0xaf, 0x96, 0xb9, 0xb1, 0x29, + 0x53, 0xce, 0x24, 0x60, 0x96, 0x8f, 0xd0, 0xb7, 0x4b, 0x60, 0x1e, 0xb3, + 0x1f, 0xae, 0x15, 0x41, 0xf1, 0x56, 0xd6, 0xf3, 0x07, 0xf8, 0xd7, 0xdd, + 0x1c, 0x82, 0xe8, 0xe0, 0xff, 0xb3, 0x41, 0x0d, 0x41, 0x96, 0x0d, 0xf2, + 0x03, 0xb0, 0x68, 0xed, 0xda, 0x8b, 0x83, 0x18, 0x4d, 0xae, 0xaf, 0x72, + 0xa1, 0x82, 0xe5, 0x5a, 0xdc, 0x2a, 0x5f, 0x93, 0x29, 0xc7, 0x24, 0xa0, + 0x8e, 0x32, 0x4c, 0x0e, 0xca, 0x6d, 0x14, 0x69, 0x5b, 0x61, 0xc5, 0xdc, + 0x0e, 0x50, 0x0c, 0x14, 0x84, 0x8b, 0xee, 0x9e, 0x1e, 0x8c, 0x3d, 0xdb, + 0x24, 0xe7, 0x99, 0x6d, 0x3c, 0xaf, 0xe6, 0x3b, 0xaa, 0xb4, 0xe4, 0x42, + 0x13, 0x53, 0x3e, 0x02, 0x47, 0x0d, 0x3a, 0x2b, 0x97, 0x71, 0x9f, 0x1b, + 0x76, 0x2d, 0xe5, 0x9c, 0x5e, 0x46, 0x5f, 0x36, 0xbf, 0x18, 0xb1, 0x1d, + 0x9a, 0xeb, 0x4d, 0x5e, 0x83, 0xd5, 0x3e, 0x5f, 0xf2, 0x6a, 0x56, 0x79, + 0x0f, 0x54, 0xec, 0x41, 0xc0, 0xdc, 0x29, 0x42, 0x45, 0xae, 0x47, 0x1c, + 0x7a, 0xd5, 0xb7, 0x92, 0x1e, 0x66, 0xb8, 0x1a, 0x2f, 0x53, 0xd2, 0x6d, + 0x42, 0x3c, 0x6c, 0x02, 0x01, 0x03, 0x9e, 0x9a, 0x95, 0x35, 0x81, 0x21, + 0x53, 0x53, 0x16, 0xda, 0x8a, 0x80, 0x8c, 0xcd, 0x02, 0x81, 0xc1, 0x00, + 0xd5, 0xba, 0x05, 0xf7, 0x7a, 0x2d, 0x52, 0xe0, 0x6a, 0xf3, 0x40, 0xd5, + 0xd9, 0xa0, 0xd1, 0x73, 0x90, 0x6a, 0xe8, 0x10, 0x67, 0xad, 0x78, 0xfe, + 0x93, 0x1e, 0x7e, 0x99, 0x37, 0xf0, 0x44, 0x90, 0x09, 0x3e, 0x67, 0x22, + 0x0e, 0x21, 0x82, 0x0a, 0x58, 0x40, 0xc5, 0xe3, 0x2b, 0x9d, 0x38, 0xd4, + 0xc5, 0xd1, 0x58, 0x8e, 0x06, 0x86, 0x89, 0xd7, 0x6c, 0xe0, 0x69, 0x08, + 0xa8, 0xcb, 0x05, 0x85, 0xd8, 0x33, 0x39, 0xaf, 0x31, 0x99, 0xee, 0x62, + 0x5d, 0x06, 0x2c, 0x4c, 0xad, 0x48, 0xf0, 0x58, 0xa2, 0x17, 0x5f, 0xc1, + 0xf7, 0xd3, 0x7c, 0x66, 0xa3, 0x5e, 0xf9, 0x1e, 0x39, 0x82, 0x73, 0x74, + 0x93, 0x66, 0x16, 0x46, 0xca, 0xd3, 0xb2, 0x22, 0xdf, 0x91, 0xcd, 0xb6, + 0xad, 0x28, 0x87, 0x26, 0xe2, 0x18, 0x9d, 0x6a, 0x87, 0x83, 0x12, 0xf2, + 0x6f, 0xa9, 0x47, 0x11, 0xfb, 0xb7, 0x4c, 0xb1, 0x0e, 0x0a, 0xde, 0x4e, + 0xd4, 0xbe, 0x71, 0xf6, 0xe4, 0xae, 0x8c, 0x27, 0xc7, 0x88, 0x84, 0x51, + 0x57, 0xb7, 0xbf, 0x74, 0x27, 0xfc, 0xb8, 0xbf, 0x57, 0x94, 0x75, 0xba, + 0xc7, 0x1c, 0xf3, 0x71, 0x83, 0xee, 0x34, 0xbd, 0x98, 0x56, 0x14, 0x44, + 0x3a, 0xee, 0x6c, 0x87, 0x44, 0x8f, 0xbb, 0xac, 0x5a, 0x2b, 0xb5, 0x13, + 0xe5, 0x9f, 0xec, 0xeb, 0x0e, 0x70, 0xe9, 0xfd, 0x1b, 0xa6, 0x84, 0xf9, + 0x02, 0x81, 0xc0, 0x4e, 0x82, 0x26, 0xf9, 0x6a, 0x52, 0xfd, 0xb3, 0xf0, + 0xe7, 0x93, 0x27, 0x11, 0xad, 0xa5, 0x47, 0x77, 0xce, 0x8d, 0x44, 0xc1, + 0x74, 0x9d, 0x9a, 0x69, 0xc7, 0xfc, 0xc0, 0x32, 0x6d, 0xf3, 0x94, 0x09, + 0x64, 0x14, 0x25, 0x67, 0xfa, 0xe0, 0x3d, 0x31, 0xe2, 0x7c, 0x75, 0x84, + 0xc4, 0x07, 0x0c, 0x44, 0x43, 0x9d, 0xb9, 0xe9, 0x77, 0x02, 0x58, 0x17, + 0x74, 0xb0, 0x96, 0xc3, 0xd9, 0xcf, 0x49, 0x85, 0x31, 0x02, 0x07, 0x8b, + 0x73, 0x6c, 0xf4, 0xa5, 0x31, 0x30, 0xf8, 0x7f, 0x96, 0x83, 0x29, 0x8b, + 0x52, 0x6a, 0x58, 0x8f, 0xa7, 0x7d, 0xb5, 0xfa, 0x51, 0x83, 0x27, 0xde, + 0x7b, 0xff, 0xd2, 0x1e, 0x05, 0xad, 0x87, 0xf0, 0x20, 0x63, 0x80, 0xac, + 0xff, 0x97, 0x2a, 0x97, 0xdf, 0xff, 0x83, 0x16, 0x49, 0x45, 0xce, 0xcf, + 0xf8, 0xe4, 0xfa, 0x12, 0xcd, 0x3f, 0x57, 0x2e, 0xb6, 0xbd, 0x1c, 0xaf, + 0xe5, 0x58, 0x5d, 0x47, 0x52, 0x84, 0xcc, 0xdc, 0x19, 0xf1, 0x93, 0x16, + 0x0a, 0x92, 0x4f, 0x5c, 0x1c, 0x89, 0xe5, 0x14, 0xff, 0x88, 0x30, 0x03, + 0x55, 0xa3, 0x47, 0xdc, 0x90, 0x05, 0x54, 0x8b, 0xc7, 0x21, 0x30, 0xcb, + 0xc3, 0x0b, 0x12, 0x3c, 0xd6, 0x46, 0x8c, 0x4d, 0xb8, 0x96, 0xe9, 0xa4, + 0x8d, 0x14, 0xb2, 0x48, 0xac, 0xbd, 0xb2, 0x72, 0xac, 0x5c, 0xf1, 0xd1, + 0x83, 0xd8, 0x59, 0x02, 0x81, 0xc0, 0x2d, 0x65, 0xc1, 0xc7, 0x0f, 0xca, + 0xbc, 0x5d, 0xa1, 0x30, 0x28, 0x27, 0x51, 0xcc, 0xc9, 0x6c, 0x7b, 0x76, + 0x43, 0xa9, 0x77, 0xd8, 0x29, 0xa3, 0x80, 0x57, 0x3b, 0xe9, 0x72, 0x1c, + 0x51, 0x37, 0xc2, 0x0b, 0x74, 0xb7, 0xaa, 0x63, 0xd5, 0xe2, 0x9b, 0x3a, + 0x3c, 0x78, 0x50, 0xcb, 0x88, 0x1a, 0xd6, 0x59, 0xdc, 0xb3, 0x05, 0xbf, + 0xe5, 0xc5, 0xb5, 0xe3, 0x9b, 0xba, 0xe7, 0xbb, 0x4d, 0x45, 0xb3, 0x4c, + 0xf2, 0x48, 0x65, 0xe7, 0x6b, 0xee, 0x12, 0xc8, 0xd5, 0xea, 0xf0, 0x89, + 0xf9, 0x03, 0xa3, 0xd9, 0x5f, 0x62, 0x2d, 0x1a, 0x55, 0x51, 0x5d, 0xf6, + 0xca, 0x6e, 0x5f, 0xf0, 0x66, 0x02, 0xf4, 0x20, 0xe6, 0xe9, 0xc4, 0xd7, + 0x36, 0x8d, 0x00, 0xce, 0x23, 0xfd, 0x90, 0xd4, 0x19, 0x5c, 0xe9, 0xcb, + 0x23, 0xe2, 0x2c, 0xf5, 0xe1, 0x6f, 0x9d, 0xb5, 0xdf, 0xea, 0x51, 0xdd, + 0x02, 0xd8, 0x40, 0xd6, 0x7f, 0x4b, 0xdb, 0x0e, 0xe2, 0x27, 0x4d, 0x0d, + 0x9b, 0x2e, 0xf7, 0xc8, 0x22, 0xca, 0x63, 0x3c, 0x2c, 0xe5, 0xa8, 0x42, + 0x26, 0xd9, 0xfc, 0xdc, 0x42, 0xc8, 0x07, 0x64, 0xa2, 0x98, 0xae, 0xb4, + 0x57, 0x02, 0x02, 0xb2, 0x2d, 0xc8, 0x59, 0x73, 0x9b, 0xee, 0xc0, 0x9a, + 0x42, 0x9a, 0xef, 0xc2, 0x27, 0x24, 0x2b, 0x20, 0x92, 0xad, 0x50, 0xfe, + 0x2c, 0x2d, 0xb6, 0xb6, 0xb5, 0xba +}; +extern const size_t kOEMPrivateKeySize_Prod = 1794; + +extern const uint8_t kOEMPublicCert_Prod[] = { + 0x30, 0x82, 0x09, 0xf7, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x07, 0x02, 0xa0, 0x82, 0x09, 0xe8, 0x30, 0x82, 0x09, 0xe4, 0x02, + 0x01, 0x01, 0x31, 0x00, 0x30, 0x0f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x02, 0x04, 0x00, 0xa0, 0x82, 0x09, + 0xc8, 0x30, 0x82, 0x04, 0x1a, 0x30, 0x82, 0x03, 0x02, 0xa0, 0x03, 0x02, + 0x01, 0x02, 0x02, 0x11, 0x00, 0xf2, 0xa1, 0x08, 0xdf, 0x12, 0x84, 0xb9, + 0x73, 0x6c, 0x23, 0x73, 0xe1, 0x1f, 0xf3, 0xac, 0x7a, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, + 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, + 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, + 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, + 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x30, 0x30, 0x2e, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x27, 0x47, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x20, 0x4f, 0x45, 0x4d, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x44, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x3b, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, + 0x6d, 0x20, 0x69, 0x64, 0x3a, 0x20, 0x37, 0x33, 0x34, 0x36, 0x30, 0x1e, + 0x17, 0x0d, 0x31, 0x37, 0x30, 0x33, 0x31, 0x33, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x32, 0x30, 0x38, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x6d, 0x31, 0x12, 0x30, 0x10, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x09, 0x37, 0x33, 0x34, 0x36, 0x2d, + 0x6c, 0x65, 0x61, 0x66, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, + 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, + 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, + 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, + 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x30, 0x82, 0x01, + 0xa2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x8f, 0x00, 0x30, 0x82, 0x01, + 0x8a, 0x02, 0x82, 0x01, 0x81, 0x00, 0xf5, 0x09, 0x64, 0x4a, 0x26, 0xfe, + 0xc0, 0x98, 0x55, 0x6a, 0x1d, 0x5d, 0x1c, 0xc7, 0x38, 0xaf, 0xfd, 0x49, + 0x9e, 0x85, 0x3f, 0xd6, 0x45, 0x0e, 0x99, 0x09, 0x85, 0x69, 0x84, 0x3c, + 0xfe, 0x72, 0xa5, 0x56, 0xfa, 0x11, 0x4f, 0x6b, 0x7d, 0x32, 0x2b, 0x0c, + 0xbf, 0x8f, 0xac, 0x47, 0x96, 0x22, 0x82, 0x3d, 0xf5, 0x64, 0x74, 0x7e, + 0x62, 0x68, 0x74, 0xcd, 0x0a, 0xec, 0x84, 0xc5, 0x15, 0x06, 0x0e, 0x5a, + 0x2f, 0x20, 0xe3, 0xc9, 0x67, 0xcd, 0xdd, 0x01, 0xb8, 0xb3, 0x18, 0x87, + 0x8c, 0xa9, 0x58, 0x86, 0x0f, 0xb6, 0xc3, 0x42, 0x7e, 0x87, 0x48, 0x5e, + 0x10, 0x49, 0xc7, 0xd7, 0xb7, 0xb8, 0xa6, 0x34, 0x08, 0x0c, 0x94, 0xf4, + 0xbb, 0x2a, 0x06, 0xa4, 0x4f, 0xec, 0xbc, 0xc4, 0x37, 0xbe, 0x99, 0x10, + 0x23, 0x37, 0x24, 0xb1, 0xdf, 0xcb, 0xe6, 0x3f, 0xc1, 0xf0, 0x0f, 0x04, + 0x03, 0xc8, 0xb0, 0x1e, 0xd6, 0xb8, 0xae, 0x77, 0xe1, 0x4d, 0x6d, 0x97, + 0x69, 0x6d, 0x8a, 0x73, 0x66, 0x32, 0x57, 0x6f, 0xcf, 0xea, 0x1e, 0x7b, + 0x87, 0x03, 0x75, 0xb1, 0xef, 0x83, 0x64, 0x26, 0xf1, 0x3f, 0xbf, 0xe6, + 0x28, 0x03, 0x72, 0x57, 0xbf, 0x47, 0x29, 0x99, 0x8f, 0x74, 0x1d, 0x01, + 0x16, 0xad, 0xb2, 0xdf, 0x80, 0xa4, 0xd3, 0x8b, 0xeb, 0x61, 0xd1, 0x40, + 0x68, 0xb9, 0xa2, 0xa5, 0xef, 0x2b, 0xe5, 0x78, 0xe8, 0x28, 0x88, 0x87, + 0xb7, 0x53, 0x49, 0xbb, 0xe4, 0xea, 0x0d, 0x5e, 0x96, 0xa5, 0xdd, 0x1f, + 0x0b, 0x25, 0x8b, 0xb5, 0x95, 0x46, 0xe7, 0xba, 0xb8, 0xc4, 0x0a, 0x36, + 0xb1, 0x89, 0xeb, 0x27, 0x5d, 0xd9, 0x97, 0x24, 0x59, 0xa3, 0x9b, 0xb0, + 0x23, 0x0b, 0xd2, 0xec, 0x65, 0x91, 0xf9, 0xf0, 0xa0, 0x74, 0x5f, 0xb4, + 0xce, 0x22, 0x27, 0x18, 0x37, 0xe2, 0x4b, 0xfc, 0x91, 0xf9, 0x09, 0x15, + 0xe6, 0xdb, 0x06, 0x9b, 0x4d, 0x82, 0xdc, 0x36, 0x14, 0x48, 0xc6, 0xd5, + 0x87, 0xca, 0xec, 0x5a, 0xa2, 0x29, 0x33, 0xef, 0x22, 0x0c, 0x4b, 0xbf, + 0xe7, 0x2f, 0x95, 0xe1, 0xd3, 0xa5, 0xd8, 0xaa, 0x44, 0x77, 0x29, 0xa3, + 0x20, 0x33, 0xd2, 0x51, 0xa2, 0xf9, 0x4a, 0x6f, 0xf7, 0x3e, 0xf7, 0x0b, + 0x8a, 0xec, 0xc1, 0x99, 0x1d, 0x47, 0xf3, 0x74, 0x02, 0x04, 0xab, 0x8e, + 0x62, 0x4c, 0x9e, 0x00, 0xc2, 0x84, 0xd7, 0xd0, 0xf8, 0xe4, 0x1c, 0x9d, + 0x98, 0x15, 0xa8, 0x8f, 0x08, 0x98, 0x4e, 0x5a, 0xfa, 0xd6, 0x60, 0x87, + 0x12, 0xdc, 0x8e, 0xfd, 0xcb, 0xb3, 0x13, 0x97, 0x7a, 0xa8, 0x8c, 0x56, + 0x2e, 0x49, 0x26, 0x60, 0xe9, 0x4a, 0xdc, 0xec, 0x3f, 0xf0, 0x94, 0xcd, + 0x90, 0x8e, 0x7c, 0x21, 0x3f, 0x80, 0x14, 0x33, 0xdd, 0xb0, 0x00, 0xe2, + 0x09, 0x37, 0x06, 0xdd, 0x17, 0x69, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, + 0x16, 0x30, 0x14, 0x30, 0x12, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, + 0xd6, 0x79, 0x04, 0x01, 0x01, 0x04, 0x04, 0x02, 0x02, 0x1c, 0xb2, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x8e, 0x2d, 0x13, 0x1e, 0x60, + 0xaa, 0xda, 0x52, 0x53, 0x55, 0x64, 0x3a, 0xdc, 0xb6, 0x7a, 0xc0, 0xba, + 0xfa, 0xeb, 0x20, 0xab, 0xb6, 0x63, 0xcf, 0xcd, 0x9b, 0xdb, 0x71, 0xf3, + 0xa0, 0xd6, 0x91, 0xbf, 0x0c, 0xc1, 0xae, 0x8f, 0x02, 0x18, 0x00, 0x54, + 0xfb, 0x49, 0x03, 0x34, 0x8d, 0x92, 0x9d, 0x5d, 0x8d, 0xa8, 0x1c, 0x20, + 0x0f, 0x85, 0x60, 0xf9, 0xf6, 0x8b, 0xbb, 0x2b, 0x82, 0xce, 0xb3, 0xe2, + 0x91, 0xe7, 0xbd, 0x91, 0x61, 0x52, 0x36, 0x40, 0x9f, 0x2f, 0x5e, 0xa6, + 0x5d, 0x2f, 0xb3, 0x81, 0xe7, 0xf1, 0x87, 0xbe, 0xc5, 0x9d, 0x67, 0x5a, + 0xf7, 0x41, 0x1e, 0x73, 0xb0, 0x1e, 0xdc, 0x4f, 0x8d, 0x53, 0x21, 0x38, + 0x1b, 0xfd, 0x92, 0x43, 0x68, 0x83, 0x03, 0xd0, 0x9a, 0xca, 0x92, 0x14, + 0x73, 0x04, 0x94, 0x2a, 0x93, 0x22, 0x60, 0x5e, 0xee, 0xb6, 0xec, 0x0f, + 0xb0, 0xc8, 0x92, 0x97, 0xfb, 0x5d, 0xed, 0x1f, 0xa0, 0x5f, 0xe4, 0x98, + 0x2f, 0xf6, 0x13, 0x78, 0x99, 0xec, 0xb3, 0xf1, 0x0d, 0x27, 0xaa, 0x19, + 0x95, 0x39, 0xdb, 0xb0, 0x7b, 0x96, 0x74, 0x03, 0x5e, 0x51, 0xf5, 0x15, + 0x27, 0xce, 0xca, 0x0b, 0x2a, 0x0d, 0x43, 0xb3, 0x68, 0x17, 0x1e, 0x11, + 0x60, 0xd9, 0x84, 0x9b, 0xc3, 0x53, 0xce, 0xbd, 0xf4, 0x61, 0x51, 0x4b, + 0x41, 0x00, 0x7e, 0xe1, 0x5f, 0x69, 0xb3, 0x4a, 0x89, 0x7e, 0x47, 0x67, + 0xfd, 0x76, 0xf8, 0x94, 0x2f, 0x72, 0xb6, 0x14, 0x08, 0x2c, 0x16, 0x4e, + 0x9d, 0x37, 0x62, 0xbf, 0x11, 0x67, 0xc0, 0x70, 0x71, 0xec, 0x55, 0x51, + 0x4e, 0x46, 0x76, 0xb4, 0xc3, 0xeb, 0x52, 0x06, 0x17, 0x06, 0xce, 0x61, + 0x43, 0xce, 0x26, 0x80, 0x68, 0xb6, 0x2d, 0x57, 0xba, 0x8c, 0x7d, 0xb7, + 0xc5, 0x05, 0x2c, 0xf8, 0xa3, 0x69, 0xf8, 0x96, 0xad, 0xac, 0xd1, 0x30, + 0x82, 0x05, 0xa6, 0x30, 0x82, 0x03, 0x8e, 0xa0, 0x03, 0x02, 0x01, 0x02, + 0x02, 0x10, 0x73, 0xd1, 0xe1, 0x1d, 0xa9, 0x75, 0xfd, 0x0c, 0xda, 0x7f, + 0xfa, 0x43, 0x3c, 0x26, 0xbd, 0x3d, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x7e, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x57, + 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x11, 0x30, + 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, + 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, + 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, + 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, + 0x76, 0x69, 0x6e, 0x65, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, + 0x03, 0x0c, 0x1a, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x65, 0x6d, 0x2d, 0x72, 0x6f, 0x6f, 0x74, + 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x37, 0x30, + 0x33, 0x31, 0x34, 0x30, 0x33, 0x30, 0x32, 0x34, 0x31, 0x5a, 0x17, 0x0d, + 0x32, 0x37, 0x30, 0x33, 0x31, 0x34, 0x30, 0x33, 0x30, 0x32, 0x34, 0x31, + 0x5a, 0x30, 0x81, 0x8b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, + 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, + 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, + 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, + 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x30, 0x30, + 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x27, 0x47, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x20, 0x4f, 0x45, 0x4d, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, + 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x3b, 0x20, 0x73, 0x79, 0x73, 0x74, + 0x65, 0x6d, 0x20, 0x69, 0x64, 0x3a, 0x20, 0x37, 0x33, 0x34, 0x36, 0x30, + 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, + 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa5, 0x45, 0x13, 0xf2, + 0xb2, 0xcb, 0x4b, 0x0f, 0xb4, 0x44, 0x25, 0x9c, 0x8a, 0x68, 0x54, 0xd5, + 0x45, 0x1e, 0x15, 0x89, 0x5b, 0xb8, 0xce, 0xda, 0x5a, 0x42, 0xe6, 0x9a, + 0x8c, 0xc1, 0xcb, 0xe8, 0xc5, 0xf5, 0x8f, 0x49, 0x0e, 0x02, 0xef, 0x5e, + 0x97, 0x1a, 0x91, 0xa4, 0x94, 0xc3, 0x50, 0x13, 0xe5, 0x13, 0xb7, 0x7f, + 0x26, 0x53, 0x19, 0xb0, 0x37, 0xa5, 0xef, 0xe6, 0x2a, 0x39, 0xdc, 0x93, + 0x37, 0xe2, 0x3d, 0x7f, 0xcb, 0x4b, 0x93, 0xa2, 0xc3, 0x69, 0x78, 0xc9, + 0x01, 0xfa, 0x68, 0x3b, 0xe0, 0xe2, 0x22, 0x6c, 0xeb, 0xe4, 0x8a, 0xa8, + 0x3e, 0xf5, 0x20, 0x82, 0xa8, 0x62, 0x68, 0x59, 0x78, 0x24, 0xde, 0xef, + 0x47, 0x43, 0xb1, 0x6c, 0x38, 0x29, 0xd3, 0x69, 0x3f, 0xae, 0x35, 0x57, + 0x75, 0x80, 0xc9, 0x21, 0xe7, 0x01, 0xb9, 0x54, 0x8b, 0x6e, 0x4e, 0x2e, + 0x5a, 0x5b, 0x77, 0xa4, 0x22, 0xc2, 0x7b, 0x95, 0xb9, 0x39, 0x2c, 0xbd, + 0xc2, 0x1e, 0x02, 0xa6, 0xb2, 0xbc, 0x0f, 0x7a, 0xcb, 0xdc, 0xbc, 0xbc, + 0x90, 0x66, 0xe3, 0xca, 0x46, 0x53, 0x3e, 0x98, 0xff, 0x2e, 0x78, 0x9f, + 0xd3, 0xa1, 0x12, 0x93, 0x66, 0x7d, 0xcc, 0x94, 0x6b, 0xec, 0x19, 0x0e, + 0x20, 0x45, 0x22, 0x57, 0x6d, 0x9e, 0xd0, 0x89, 0xf2, 0xa9, 0x34, 0xdc, + 0xab, 0xa5, 0x73, 0x47, 0x38, 0xe3, 0x7f, 0x98, 0x3a, 0x61, 0xae, 0x6c, + 0x4d, 0xf2, 0x31, 0x90, 0xcb, 0x83, 0xc1, 0xee, 0xb4, 0xf2, 0x9a, 0x28, + 0x5f, 0xbb, 0x7d, 0x89, 0xdf, 0xa2, 0x31, 0xb6, 0x1d, 0x39, 0x2b, 0x70, + 0xbf, 0x1e, 0xad, 0xe1, 0x74, 0x94, 0x1d, 0xf8, 0xc5, 0x1a, 0x8d, 0x13, + 0x45, 0xf0, 0x6a, 0x80, 0x0c, 0x5d, 0xbb, 0x46, 0x8a, 0x43, 0xd0, 0xff, + 0x21, 0x39, 0x57, 0x53, 0x5b, 0x51, 0xf8, 0xa2, 0x8f, 0x7f, 0x27, 0xc7, + 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x10, 0x30, 0x82, 0x01, + 0x0c, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, + 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, + 0x04, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, + 0xe8, 0xe9, 0xac, 0x16, 0x5c, 0x5e, 0xb2, 0xe8, 0xeb, 0xff, 0x57, 0x27, + 0x20, 0x08, 0x72, 0x63, 0x9b, 0xe5, 0xb5, 0x16, 0x30, 0x81, 0xb2, 0x06, + 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81, 0xaa, 0x30, 0x81, 0xa7, 0x80, 0x14, + 0x04, 0x94, 0x66, 0xaa, 0xf9, 0x61, 0x89, 0xb6, 0xdb, 0xb5, 0xf7, 0x13, + 0x38, 0x3d, 0x62, 0x84, 0xb8, 0x18, 0x0a, 0x8f, 0xa1, 0x81, 0x83, 0xa4, + 0x81, 0x80, 0x30, 0x7e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, + 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, + 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, + 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, + 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x23, 0x30, + 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1a, 0x77, 0x69, 0x64, 0x65, + 0x76, 0x69, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x65, 0x6d, + 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x82, 0x09, + 0x00, 0xdf, 0x86, 0x05, 0x31, 0x01, 0xbe, 0x9a, 0x9a, 0x30, 0x12, 0x06, + 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x04, 0x01, 0x01, 0x04, + 0x04, 0x02, 0x02, 0x1c, 0xb2, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x02, 0x01, + 0x00, 0x25, 0xce, 0xd2, 0x02, 0x48, 0xbb, 0xbe, 0xfc, 0xb6, 0xa4, 0x87, + 0x87, 0xe0, 0x21, 0x7d, 0xfa, 0x23, 0xc3, 0x0d, 0x73, 0x8f, 0x46, 0xe7, + 0x09, 0x59, 0xda, 0x2e, 0x55, 0x59, 0xff, 0x3c, 0x1b, 0xf6, 0xf8, 0x9a, + 0xc4, 0x1c, 0xf7, 0xac, 0xca, 0xe7, 0x63, 0xf2, 0xc7, 0xd6, 0x0c, 0x2d, + 0xa6, 0xad, 0x55, 0xf4, 0x10, 0x0e, 0xa8, 0x82, 0x0f, 0x88, 0xb5, 0x44, + 0xe8, 0x8e, 0x84, 0x08, 0xf7, 0xdd, 0xe7, 0x10, 0xce, 0x71, 0x56, 0x57, + 0x3f, 0xed, 0x48, 0xee, 0xe2, 0x5d, 0x08, 0x0a, 0x58, 0xe4, 0xfe, 0xbc, + 0x8c, 0x27, 0x1a, 0x46, 0x3f, 0xd5, 0x2d, 0xdb, 0x0b, 0x71, 0x73, 0xd1, + 0x49, 0xf3, 0x5c, 0x86, 0x4d, 0x0a, 0xe1, 0xeb, 0x53, 0x21, 0x38, 0x4f, + 0xec, 0x1e, 0xc2, 0x68, 0x1f, 0x7d, 0xa6, 0x33, 0xe9, 0xa5, 0x37, 0x2a, + 0xef, 0xcd, 0x78, 0x56, 0xb3, 0x39, 0x60, 0xf4, 0xa5, 0xf9, 0x2b, 0x85, + 0xcf, 0xe6, 0x1c, 0x7c, 0x8a, 0x5d, 0xe8, 0x26, 0x02, 0xcf, 0x7a, 0x56, + 0x1f, 0xae, 0x0d, 0x71, 0x20, 0xee, 0xec, 0x3b, 0xae, 0x95, 0x25, 0x15, + 0xc8, 0xf6, 0x92, 0x5d, 0xb8, 0x9b, 0xc2, 0xb4, 0x95, 0x33, 0x13, 0x76, + 0x45, 0xbe, 0x21, 0xe2, 0x3a, 0x69, 0x66, 0xd7, 0xff, 0x22, 0x00, 0x89, + 0xc9, 0x44, 0xb6, 0x54, 0x38, 0x1f, 0x33, 0xe4, 0xda, 0x7b, 0x87, 0xf3, + 0x23, 0xed, 0xf5, 0x16, 0x08, 0xbe, 0x4b, 0xea, 0x91, 0x8f, 0x91, 0x8b, + 0x4e, 0xd1, 0x02, 0x06, 0xa2, 0x77, 0x15, 0x03, 0x46, 0x11, 0x7d, 0x5b, + 0xea, 0x7a, 0xf6, 0x86, 0x7d, 0x96, 0xb7, 0x73, 0x9b, 0x5b, 0x32, 0xc3, + 0xf8, 0x92, 0x36, 0xe3, 0xe3, 0x2f, 0xe8, 0xf1, 0x72, 0xec, 0x0d, 0x50, + 0xd4, 0x86, 0xc5, 0x62, 0x83, 0xf1, 0x2a, 0x4c, 0xd1, 0xbf, 0x76, 0x62, + 0xd4, 0x21, 0x11, 0x68, 0xb2, 0xd6, 0x8d, 0xc4, 0xf8, 0xe4, 0x70, 0x85, + 0x19, 0xa7, 0x82, 0x27, 0x2c, 0x24, 0x21, 0x7a, 0x3b, 0xad, 0x8a, 0xd3, + 0xae, 0xda, 0x78, 0x3c, 0x6c, 0xab, 0xa2, 0xaa, 0x36, 0xf0, 0x1c, 0x58, + 0xd4, 0x72, 0x5e, 0xe8, 0x8b, 0x41, 0x08, 0xf5, 0x85, 0xdd, 0xee, 0x99, + 0x12, 0xf4, 0xd6, 0x41, 0x83, 0x69, 0xe7, 0x79, 0x19, 0xa3, 0x74, 0xc4, + 0x34, 0x2a, 0x8a, 0x7e, 0x4d, 0xbb, 0x2c, 0x49, 0x19, 0xf7, 0x98, 0x98, + 0xfc, 0x81, 0xf7, 0x9b, 0x7f, 0xff, 0xd9, 0x66, 0xf4, 0x51, 0x14, 0x29, + 0x2a, 0x14, 0x1d, 0x4f, 0xbd, 0x91, 0xba, 0x6f, 0x32, 0x34, 0x3c, 0x40, + 0x28, 0x6c, 0x97, 0xf8, 0x6d, 0x38, 0xcd, 0xa3, 0x7b, 0x18, 0xc8, 0x77, + 0x58, 0x4d, 0x53, 0x30, 0x7f, 0x4d, 0x89, 0xca, 0x95, 0x6e, 0xb5, 0xb8, + 0x8e, 0xc8, 0x2d, 0x18, 0x2f, 0x52, 0x2a, 0xde, 0xac, 0x56, 0x8d, 0x8c, + 0x67, 0x14, 0xf6, 0xb9, 0xf1, 0x65, 0xd3, 0x22, 0x43, 0xa3, 0x98, 0x42, + 0x20, 0x43, 0x4c, 0xdf, 0xf2, 0xeb, 0x31, 0x8c, 0x0e, 0x53, 0x5b, 0x99, + 0x82, 0xc3, 0x48, 0x04, 0x53, 0xad, 0x96, 0xb6, 0x9f, 0x52, 0xcc, 0x01, + 0xc8, 0xb3, 0x87, 0x6b, 0x9e, 0xea, 0xa9, 0xeb, 0xda, 0xac, 0xf9, 0x6f, + 0xde, 0xa1, 0x44, 0x32, 0x52, 0x49, 0x47, 0xff, 0x65, 0x79, 0x1e, 0xc5, + 0x73, 0x17, 0xb3, 0x36, 0xfc, 0x45, 0xca, 0x90, 0x37, 0x59, 0x1e, 0x16, + 0xab, 0x09, 0x69, 0xcf, 0xda, 0x56, 0x51, 0xfd, 0xeb, 0xcf, 0xcb, 0x8f, + 0xb1, 0xc3, 0x45, 0x2b, 0x7c, 0x0a, 0xa5, 0x9c, 0x0d, 0x2c, 0xad, 0x1c, + 0xd3, 0x33, 0xdd, 0xfe, 0x93, 0x69, 0xa2, 0x4b, 0x4b, 0xcf, 0x1d, 0x20, + 0x98, 0x4a, 0x4f, 0x5b, 0xe9, 0x24, 0xca, 0xfa, 0x18, 0x11, 0x81, 0x8b, + 0x7a, 0xb4, 0x5a, 0xc8, 0xdf, 0x6f, 0x5f, 0x21, 0x07, 0x31, 0x00 +}; +extern const size_t kOEMPublicCertSize_Prod = 2555; + +// OEM Certificate - replaces OEM cert for system id 7346. +// As soon as this certificate is known to work, remove the old one. +#if 0 +extern const uint32_t kOEMSystemId_Test_NEW = 7913; + +extern const uint8_t kOEMPrivateKey_Test_NEW[] = { + 0x30, 0x82, 0x04, 0xbe, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa8, 0x30, 0x82, 0x04, 0xa4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x01, 0x00, 0xae, 0xc8, 0x71, 0xae, 0x08, 0x0c, 0x06, 0x06, 0x2d, 0x81, + 0x7c, 0xa9, 0x8b, 0xb3, 0xd6, 0x66, 0xe4, 0xf6, 0x08, 0x5e, 0x5a, 0x75, + 0xe8, 0x74, 0x61, 0x7a, 0x88, 0xca, 0x85, 0x14, 0x0d, 0x58, 0xa4, 0x09, + 0x19, 0x6c, 0x60, 0xc9, 0xad, 0x91, 0x1c, 0xbf, 0x04, 0xb3, 0x47, 0x10, + 0x63, 0x7f, 0x02, 0x58, 0xc2, 0x1e, 0xbd, 0xcc, 0x07, 0x77, 0xaa, 0x7e, + 0x14, 0xa8, 0xc2, 0x01, 0xcd, 0xe8, 0x46, 0x60, 0x53, 0x6f, 0x2f, 0xda, + 0x17, 0x2d, 0x4d, 0x9d, 0x0e, 0x5d, 0xb5, 0x50, 0x95, 0xae, 0xab, 0x6e, + 0x43, 0xe3, 0xb0, 0x00, 0x12, 0xb4, 0x05, 0x82, 0x4a, 0x2b, 0x14, 0x63, + 0x0d, 0x1f, 0x06, 0x12, 0xaa, 0xe1, 0x9d, 0xe7, 0xba, 0xda, 0xe3, 0xfc, + 0x7c, 0x6c, 0x73, 0xae, 0x56, 0xf8, 0xab, 0xf7, 0x51, 0x93, 0x31, 0xef, + 0x8f, 0xe4, 0xb6, 0x01, 0x2c, 0xeb, 0x7b, 0xe4, 0xd8, 0xb3, 0xea, 0x70, + 0x37, 0x89, 0x05, 0xa9, 0x51, 0x57, 0x72, 0x98, 0x9e, 0xa8, 0x46, 0xdb, + 0xeb, 0x7a, 0x38, 0x2b, 0x2f, 0xc0, 0x27, 0xb7, 0xc2, 0xe1, 0x9a, 0x17, + 0xdf, 0xf5, 0xd6, 0x9c, 0xd5, 0x8c, 0xb8, 0x66, 0x42, 0xd5, 0x04, 0x1e, + 0x7c, 0x36, 0x4c, 0x1e, 0x3e, 0x45, 0x51, 0x4d, 0x41, 0x72, 0x22, 0x53, + 0x3d, 0xf4, 0x57, 0x7c, 0x6c, 0x33, 0x34, 0x24, 0x45, 0xdf, 0x84, 0x87, + 0x4a, 0xa6, 0xcb, 0x7c, 0x03, 0xa3, 0xaa, 0x8e, 0x2d, 0x82, 0x01, 0x27, + 0x87, 0x74, 0x82, 0x1a, 0xbc, 0x0f, 0x76, 0x69, 0xab, 0xe0, 0x4e, 0x70, + 0xbe, 0x37, 0xfc, 0xc8, 0x2c, 0x91, 0x17, 0x4f, 0xd5, 0x26, 0x3b, 0x7b, + 0x90, 0xb5, 0x2d, 0x64, 0xba, 0xf7, 0xd2, 0x8a, 0xb4, 0x8f, 0x38, 0x9d, + 0x8e, 0xba, 0xe7, 0x5c, 0x52, 0xf1, 0x0a, 0xb8, 0xc0, 0x1b, 0xb6, 0xb1, + 0x70, 0x7e, 0x47, 0x59, 0x94, 0x59, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, + 0x82, 0x01, 0x00, 0x3d, 0xa8, 0x2a, 0x2a, 0x19, 0x5e, 0x9c, 0x75, 0x6b, + 0x72, 0x25, 0x56, 0xee, 0x18, 0x67, 0xc6, 0xee, 0x78, 0x38, 0x65, 0x88, + 0x85, 0xd5, 0x95, 0xd0, 0x99, 0x58, 0x4e, 0x10, 0xfa, 0x0c, 0xe7, 0x3b, + 0x1e, 0x96, 0xa0, 0x5b, 0xd8, 0x69, 0x69, 0x2a, 0x79, 0xdc, 0x1f, 0x9e, + 0x2f, 0x12, 0xda, 0x48, 0x2a, 0xa5, 0x78, 0x10, 0xbe, 0x3b, 0xd8, 0x24, + 0x05, 0xcc, 0x25, 0xe8, 0x22, 0x98, 0xcf, 0x9a, 0x9b, 0xfa, 0x72, 0xdb, + 0xaf, 0x94, 0xc3, 0x7f, 0x80, 0xab, 0x03, 0xb5, 0xa5, 0x92, 0xd8, 0x06, + 0x53, 0xa4, 0x91, 0x5a, 0x91, 0xfc, 0x07, 0x13, 0x76, 0x6d, 0xd1, 0x9a, + 0xa4, 0x82, 0x89, 0x72, 0x2b, 0xfd, 0xaa, 0x0f, 0x79, 0x22, 0xba, 0x7a, + 0xf8, 0xa3, 0xaf, 0xe2, 0x73, 0x1a, 0xed, 0x28, 0x44, 0xdb, 0xbb, 0x10, + 0x3f, 0x91, 0x44, 0x3e, 0x4c, 0x55, 0xdb, 0x50, 0x5e, 0x5f, 0x6f, 0x2b, + 0x28, 0x87, 0x23, 0x49, 0x75, 0xf7, 0x85, 0x4d, 0x89, 0x01, 0xf5, 0xbb, + 0xc8, 0xe6, 0xaf, 0x6c, 0x5f, 0x78, 0xe3, 0xf8, 0xe5, 0x98, 0x3f, 0x30, + 0xe0, 0x2d, 0xe8, 0x1f, 0x21, 0xaa, 0xf7, 0x2d, 0x44, 0xb4, 0x37, 0x02, + 0xf9, 0x79, 0x66, 0xce, 0x77, 0xa8, 0xd5, 0x4a, 0xf9, 0xb7, 0x10, 0xde, + 0x65, 0xae, 0x31, 0xf1, 0x5a, 0x65, 0x60, 0x67, 0xf7, 0x3a, 0x1b, 0xa4, + 0x1e, 0x0d, 0x10, 0x00, 0x3c, 0x7a, 0xc5, 0xae, 0x6d, 0xb8, 0xa1, 0xc9, + 0x70, 0xd5, 0x20, 0x3e, 0xe0, 0xc0, 0x9a, 0x59, 0x32, 0x6c, 0x47, 0x86, + 0x09, 0x70, 0xc0, 0x1d, 0xf4, 0x67, 0xef, 0x18, 0x70, 0x12, 0x9b, 0xa5, + 0xba, 0x64, 0xdf, 0xcc, 0x37, 0x70, 0x24, 0xd4, 0x50, 0x1d, 0x7f, 0x90, + 0x0e, 0xc7, 0x54, 0xbc, 0xae, 0x99, 0x07, 0x79, 0xbe, 0x24, 0xa3, 0x60, + 0x18, 0x17, 0x89, 0x17, 0x70, 0x5c, 0x5d, 0x02, 0x81, 0x81, 0x00, 0xdb, + 0xaf, 0xdd, 0x23, 0xd2, 0x55, 0xad, 0x8f, 0x08, 0xe0, 0x47, 0xdd, 0x85, + 0x57, 0x96, 0x94, 0xee, 0x96, 0x38, 0x63, 0xc4, 0xba, 0x89, 0x47, 0x3f, + 0x18, 0x92, 0x4b, 0x97, 0x4a, 0x0a, 0x09, 0x6d, 0x23, 0xe7, 0x58, 0x55, + 0x99, 0xfa, 0x08, 0xf5, 0x34, 0x42, 0xa9, 0x04, 0x84, 0x59, 0x7a, 0xde, + 0x6d, 0x1c, 0xcb, 0x69, 0x1b, 0x10, 0x35, 0xdd, 0x0c, 0x26, 0xcc, 0x99, + 0x06, 0xf5, 0x2b, 0xb9, 0x3b, 0x85, 0x56, 0x7e, 0x31, 0xbc, 0x4e, 0x11, + 0xa6, 0xf1, 0xf1, 0x68, 0xf3, 0x89, 0xef, 0x52, 0x52, 0xfd, 0xb0, 0x05, + 0x82, 0xd4, 0x1a, 0x79, 0x6a, 0x4a, 0x07, 0x3e, 0xc7, 0x29, 0x20, 0x9f, + 0xe1, 0xb8, 0x42, 0x84, 0xc2, 0x7b, 0x2c, 0xe9, 0x8c, 0x24, 0x15, 0x4d, + 0xa6, 0x2a, 0xfc, 0x16, 0x7c, 0x54, 0xd1, 0x12, 0x28, 0x7e, 0x93, 0xd2, + 0x41, 0x39, 0x6e, 0x7f, 0xde, 0xb9, 0x03, 0x02, 0x81, 0x81, 0x00, 0xcb, + 0xac, 0x73, 0x8e, 0x38, 0x48, 0xe0, 0x7b, 0x9b, 0xe9, 0x44, 0x09, 0x77, + 0x49, 0x5f, 0x7d, 0x33, 0x01, 0x42, 0x38, 0xb3, 0x46, 0x34, 0xdd, 0xb3, + 0x72, 0x08, 0xdd, 0x3f, 0xf4, 0xf8, 0x9d, 0x91, 0x65, 0xd7, 0x68, 0xe4, + 0x53, 0xe4, 0xab, 0x6b, 0x75, 0xe9, 0x2c, 0x9e, 0x4b, 0x4a, 0xbb, 0x5a, + 0xb9, 0xc6, 0x68, 0x98, 0x7e, 0x46, 0x38, 0xd5, 0x19, 0x7e, 0x5e, 0xef, + 0x3f, 0x57, 0xf8, 0xbc, 0xb2, 0x0c, 0x61, 0x11, 0xca, 0xcb, 0xe5, 0xb9, + 0xa2, 0xba, 0x32, 0x51, 0xd3, 0x52, 0x8e, 0xc3, 0x8e, 0x32, 0xdb, 0x45, + 0xd9, 0xf6, 0x8b, 0x54, 0x2a, 0xce, 0x44, 0xe0, 0xd0, 0x91, 0xc2, 0x6f, + 0x35, 0x51, 0x1c, 0x1c, 0x2b, 0x19, 0x22, 0x20, 0xd3, 0x67, 0x41, 0x26, + 0x97, 0x6a, 0x15, 0xa6, 0xbf, 0xc0, 0x39, 0xa6, 0xef, 0xbf, 0x1d, 0xde, + 0xb5, 0x56, 0x76, 0x89, 0xcc, 0x28, 0x73, 0x02, 0x81, 0x81, 0x00, 0x93, + 0xb8, 0x79, 0x96, 0xb0, 0xc4, 0x73, 0xe4, 0x0e, 0x2f, 0xe1, 0xa3, 0x0f, + 0x1a, 0x8a, 0x62, 0xb2, 0xdb, 0xc8, 0x27, 0x2e, 0x0d, 0xd0, 0x69, 0x5c, + 0x00, 0x05, 0xbf, 0x56, 0x87, 0xae, 0xa0, 0x10, 0x07, 0x68, 0x74, 0x79, + 0x7b, 0x04, 0x2a, 0x29, 0xd7, 0x6e, 0x4b, 0x80, 0x82, 0x0e, 0x39, 0x14, + 0x7f, 0x82, 0x31, 0x35, 0x07, 0xc1, 0xaa, 0xb6, 0x6c, 0x89, 0x5a, 0x62, + 0x3b, 0xcf, 0x78, 0x0b, 0x21, 0xfe, 0xd2, 0xde, 0x20, 0x4b, 0x62, 0xa3, + 0x74, 0x02, 0x94, 0x35, 0xe2, 0x2d, 0x0a, 0x03, 0xed, 0x52, 0x30, 0x9e, + 0x44, 0x0e, 0xa3, 0x8e, 0xa8, 0xee, 0x06, 0x59, 0x2c, 0x4a, 0x77, 0x69, + 0x8a, 0xb9, 0xff, 0xe0, 0x8d, 0x75, 0x8e, 0x09, 0xac, 0xf6, 0x46, 0x6c, + 0xe4, 0x23, 0x5b, 0x47, 0x33, 0x6b, 0x44, 0xfa, 0x54, 0xc8, 0x68, 0x55, + 0xa5, 0x0e, 0x1d, 0x43, 0x56, 0x5c, 0x49, 0x02, 0x81, 0x80, 0x6d, 0xef, + 0xed, 0x03, 0xf4, 0x6f, 0xa9, 0xf4, 0x2e, 0xc9, 0x2f, 0xb7, 0x68, 0x53, + 0x0b, 0x9e, 0xf9, 0xdc, 0x11, 0x11, 0xa7, 0xfb, 0x1a, 0x68, 0x1d, 0x07, + 0x4d, 0xaf, 0x97, 0x69, 0x34, 0x95, 0x60, 0xb9, 0x37, 0x17, 0xe0, 0x3f, + 0x44, 0x6c, 0x1e, 0xfa, 0x2b, 0x5d, 0xb1, 0x27, 0xf4, 0xf3, 0xc2, 0x5a, + 0x0a, 0xb5, 0xc4, 0xb2, 0x4c, 0x57, 0x3a, 0xf7, 0xa2, 0xd5, 0xbd, 0x90, + 0x54, 0x7c, 0xe7, 0xe5, 0xb7, 0x76, 0xe1, 0xf2, 0xb4, 0x32, 0x91, 0x0c, + 0x3c, 0x33, 0x13, 0x25, 0xdd, 0x27, 0x40, 0x79, 0x4e, 0x0c, 0x3b, 0xdd, + 0x16, 0x0e, 0x48, 0xe5, 0x3e, 0x27, 0xc1, 0xad, 0xf3, 0x2f, 0x9f, 0x75, + 0x89, 0x0d, 0x82, 0x4a, 0xe5, 0x2b, 0x1d, 0xca, 0xde, 0xae, 0x35, 0x15, + 0x7f, 0xe6, 0xa5, 0xf6, 0x54, 0xdd, 0x09, 0xb5, 0xd9, 0x54, 0x57, 0x35, + 0x4c, 0xb1, 0x79, 0x59, 0xe0, 0xef, 0x02, 0x81, 0x81, 0x00, 0xae, 0x09, + 0x18, 0xa7, 0xdf, 0x3c, 0xd2, 0x46, 0x31, 0xe3, 0xe1, 0x8e, 0xff, 0x23, + 0x63, 0x14, 0x6c, 0x21, 0xd9, 0x0b, 0xc9, 0x90, 0x46, 0x96, 0xbe, 0x3b, + 0xc4, 0x96, 0x0b, 0x7f, 0x2d, 0x03, 0x39, 0x37, 0x6d, 0xea, 0xef, 0x0f, + 0x2e, 0x76, 0x61, 0xf6, 0xcd, 0x15, 0x3f, 0x6b, 0xbc, 0xf9, 0x22, 0x7f, + 0x29, 0x8f, 0x5f, 0xb1, 0x70, 0x3e, 0x54, 0x8b, 0x76, 0xe4, 0xb6, 0x2a, + 0x42, 0x85, 0xa3, 0x58, 0x1b, 0x3f, 0xd5, 0x77, 0xf6, 0x4b, 0xdb, 0xcd, + 0x8d, 0x3b, 0x6a, 0x17, 0x6d, 0xce, 0xd2, 0xe0, 0x11, 0xfe, 0xf8, 0x25, + 0x66, 0x04, 0x7c, 0xed, 0x29, 0x22, 0x74, 0x52, 0xc3, 0xd8, 0x0f, 0x38, + 0x2a, 0x0c, 0x34, 0x10, 0xd6, 0x3d, 0x84, 0x80, 0xe1, 0xc2, 0x1d, 0x8c, + 0x26, 0xc1, 0xe6, 0x0b, 0x43, 0x4b, 0x99, 0x75, 0xbf, 0x8e, 0x84, 0x1b, + 0x30, 0xec, 0xb1, 0x23, 0x4c, 0x76 +}; +extern const size_t kOEMPrivateKeySize_Test_NEW = 1218; + +extern const uint8_t kOEMPublicCert_Test_NEW[] = { + 0x30, 0x82, 0x05, 0x85, 0x30, 0x82, 0x03, 0x6d, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x03, 0xb1, 0xf7, 0x58, 0xdf, 0x1d, 0xe3, 0x25, 0x00, + 0x0b, 0x10, 0x3d, 0xd5, 0xe6, 0xe4, 0x64, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x7e, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, + 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x11, + 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, + 0x6b, 0x6c, 0x61, 0x6e, 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x0c, 0x06, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, + 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, + 0x65, 0x76, 0x69, 0x6e, 0x65, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x0c, 0x1a, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x65, 0x6d, 0x2d, 0x72, 0x6f, 0x6f, + 0x74, 0x2d, 0x70, 0x72, 0x6f, 0x64, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x37, + 0x31, 0x31, 0x31, 0x38, 0x30, 0x31, 0x31, 0x33, 0x33, 0x35, 0x5a, 0x17, + 0x0d, 0x32, 0x37, 0x31, 0x31, 0x31, 0x38, 0x30, 0x31, 0x31, 0x33, 0x31, + 0x33, 0x5a, 0x30, 0x6b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x0c, 0x02, 0x57, 0x41, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, + 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, + 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, + 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, + 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, + 0x65, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0f, + 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20, 0x69, 0x64, 0x3a, 0x20, 0x37, + 0x39, 0x31, 0x33, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, + 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, + 0xae, 0xc8, 0x71, 0xae, 0x08, 0x0c, 0x06, 0x06, 0x2d, 0x81, 0x7c, 0xa9, + 0x8b, 0xb3, 0xd6, 0x66, 0xe4, 0xf6, 0x08, 0x5e, 0x5a, 0x75, 0xe8, 0x74, + 0x61, 0x7a, 0x88, 0xca, 0x85, 0x14, 0x0d, 0x58, 0xa4, 0x09, 0x19, 0x6c, + 0x60, 0xc9, 0xad, 0x91, 0x1c, 0xbf, 0x04, 0xb3, 0x47, 0x10, 0x63, 0x7f, + 0x02, 0x58, 0xc2, 0x1e, 0xbd, 0xcc, 0x07, 0x77, 0xaa, 0x7e, 0x14, 0xa8, + 0xc2, 0x01, 0xcd, 0xe8, 0x46, 0x60, 0x53, 0x6f, 0x2f, 0xda, 0x17, 0x2d, + 0x4d, 0x9d, 0x0e, 0x5d, 0xb5, 0x50, 0x95, 0xae, 0xab, 0x6e, 0x43, 0xe3, + 0xb0, 0x00, 0x12, 0xb4, 0x05, 0x82, 0x4a, 0x2b, 0x14, 0x63, 0x0d, 0x1f, + 0x06, 0x12, 0xaa, 0xe1, 0x9d, 0xe7, 0xba, 0xda, 0xe3, 0xfc, 0x7c, 0x6c, + 0x73, 0xae, 0x56, 0xf8, 0xab, 0xf7, 0x51, 0x93, 0x31, 0xef, 0x8f, 0xe4, + 0xb6, 0x01, 0x2c, 0xeb, 0x7b, 0xe4, 0xd8, 0xb3, 0xea, 0x70, 0x37, 0x89, + 0x05, 0xa9, 0x51, 0x57, 0x72, 0x98, 0x9e, 0xa8, 0x46, 0xdb, 0xeb, 0x7a, + 0x38, 0x2b, 0x2f, 0xc0, 0x27, 0xb7, 0xc2, 0xe1, 0x9a, 0x17, 0xdf, 0xf5, + 0xd6, 0x9c, 0xd5, 0x8c, 0xb8, 0x66, 0x42, 0xd5, 0x04, 0x1e, 0x7c, 0x36, + 0x4c, 0x1e, 0x3e, 0x45, 0x51, 0x4d, 0x41, 0x72, 0x22, 0x53, 0x3d, 0xf4, + 0x57, 0x7c, 0x6c, 0x33, 0x34, 0x24, 0x45, 0xdf, 0x84, 0x87, 0x4a, 0xa6, + 0xcb, 0x7c, 0x03, 0xa3, 0xaa, 0x8e, 0x2d, 0x82, 0x01, 0x27, 0x87, 0x74, + 0x82, 0x1a, 0xbc, 0x0f, 0x76, 0x69, 0xab, 0xe0, 0x4e, 0x70, 0xbe, 0x37, + 0xfc, 0xc8, 0x2c, 0x91, 0x17, 0x4f, 0xd5, 0x26, 0x3b, 0x7b, 0x90, 0xb5, + 0x2d, 0x64, 0xba, 0xf7, 0xd2, 0x8a, 0xb4, 0x8f, 0x38, 0x9d, 0x8e, 0xba, + 0xe7, 0x5c, 0x52, 0xf1, 0x0a, 0xb8, 0xc0, 0x1b, 0xb6, 0xb1, 0x70, 0x7e, + 0x47, 0x59, 0x94, 0x59, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, + 0x10, 0x30, 0x82, 0x01, 0x0c, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, + 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, + 0x00, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, + 0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0x4b, 0xcb, 0xdf, 0xaa, 0x02, 0xde, 0x8d, 0xc3, + 0xe7, 0xe5, 0x85, 0xdb, 0x2e, 0x8a, 0xbe, 0x75, 0x6b, 0x8a, 0x67, 0x58, + 0x30, 0x81, 0xb2, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x81, 0xaa, 0x30, + 0x81, 0xa7, 0x80, 0x14, 0x04, 0x94, 0x66, 0xaa, 0xf9, 0x61, 0x89, 0xb6, + 0xdb, 0xb5, 0xf7, 0x13, 0x38, 0x3d, 0x62, 0x84, 0xb8, 0x18, 0x0a, 0x8f, + 0xa1, 0x81, 0x83, 0xa4, 0x81, 0x80, 0x30, 0x7e, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, + 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x57, 0x61, 0x73, 0x68, + 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, + 0x55, 0x04, 0x07, 0x0c, 0x08, 0x4b, 0x69, 0x72, 0x6b, 0x6c, 0x61, 0x6e, + 0x64, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, + 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, + 0x55, 0x04, 0x0b, 0x0c, 0x08, 0x57, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, + 0x65, 0x31, 0x23, 0x30, 0x21, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1a, + 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, 0x65, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x6f, 0x65, 0x6d, 0x2d, 0x72, 0x6f, 0x6f, 0x74, 0x2d, 0x70, 0x72, + 0x6f, 0x64, 0x82, 0x09, 0x00, 0xdf, 0x86, 0x05, 0x31, 0x01, 0xbe, 0x9a, + 0x9a, 0x30, 0x12, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, + 0x04, 0x01, 0x01, 0x04, 0x04, 0x02, 0x02, 0x1e, 0xe9, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, + 0x03, 0x82, 0x02, 0x01, 0x00, 0x61, 0x3f, 0x2f, 0x43, 0xe4, 0xbe, 0x66, + 0x34, 0xef, 0x92, 0x06, 0xe9, 0x88, 0xba, 0x6a, 0x1d, 0x4f, 0x54, 0x5a, + 0x97, 0xb1, 0x75, 0xd7, 0x93, 0xf8, 0x45, 0xc6, 0x83, 0x92, 0x36, 0xfd, + 0x55, 0xa9, 0x21, 0x0b, 0xdc, 0xf6, 0xae, 0x11, 0xdc, 0x62, 0x21, 0x44, + 0xbd, 0x04, 0x1d, 0x58, 0x2c, 0x03, 0xf8, 0xe4, 0xe2, 0x1e, 0xba, 0xe6, + 0xdd, 0x19, 0xdd, 0x56, 0xfd, 0xce, 0x06, 0x73, 0x5f, 0x94, 0x1e, 0xb6, + 0x03, 0xdb, 0x3d, 0x7b, 0xab, 0xab, 0x72, 0x64, 0x7b, 0xde, 0x7d, 0x4d, + 0xcf, 0x7e, 0xf0, 0x91, 0x29, 0xc1, 0x77, 0x13, 0xc2, 0x6f, 0x80, 0xab, + 0x7a, 0xa8, 0xce, 0xb0, 0x1c, 0x2a, 0xc5, 0x9c, 0xfb, 0x0b, 0xe5, 0x9f, + 0x9c, 0x1b, 0xc9, 0x4b, 0x58, 0xdf, 0x96, 0x18, 0xf7, 0x67, 0x67, 0x89, + 0xa4, 0xe9, 0x14, 0x48, 0xac, 0xfa, 0x9d, 0x86, 0x2a, 0xeb, 0x75, 0x2c, + 0x2b, 0xbf, 0x63, 0x7d, 0xc7, 0x4e, 0x7e, 0xad, 0x39, 0x2d, 0xb4, 0x7c, + 0x07, 0xa5, 0x5a, 0xe8, 0x3a, 0xd4, 0xf5, 0x0c, 0x4f, 0xf3, 0xa2, 0x9c, + 0x3c, 0x32, 0xed, 0x9d, 0x4b, 0x49, 0x05, 0xbc, 0x1f, 0xa0, 0x13, 0xe6, + 0xdd, 0x82, 0x79, 0x06, 0x31, 0x3b, 0xc6, 0x97, 0xec, 0x8d, 0xaa, 0x4f, + 0xef, 0x14, 0x3c, 0x21, 0xf6, 0x72, 0xb2, 0x09, 0x42, 0xc7, 0x74, 0xfe, + 0xef, 0x70, 0xbd, 0xe9, 0x85, 0x41, 0x30, 0x0b, 0xb3, 0x6b, 0x59, 0x0c, + 0x0f, 0x11, 0x75, 0xd4, 0xbb, 0xb1, 0xdf, 0xb1, 0xdf, 0xb3, 0xfa, 0xb3, + 0x3a, 0x43, 0x17, 0x7d, 0x8a, 0x82, 0xae, 0xa2, 0x07, 0xf8, 0x83, 0x51, + 0xfb, 0x16, 0xfb, 0x64, 0xb6, 0x46, 0xda, 0xbe, 0x32, 0x2b, 0xc0, 0xee, + 0x78, 0x2a, 0x84, 0xa9, 0x54, 0x0a, 0xf9, 0x2d, 0x61, 0x65, 0xde, 0xa5, + 0x97, 0x66, 0x79, 0x02, 0xf8, 0x97, 0x17, 0xe2, 0xd4, 0x9f, 0x9e, 0xac, + 0xcc, 0xae, 0x99, 0x9a, 0x03, 0x04, 0xbb, 0x45, 0xfe, 0xb2, 0xf5, 0x80, + 0xba, 0xbf, 0xdd, 0x24, 0xe5, 0xe6, 0x1e, 0x5d, 0x36, 0xa5, 0x87, 0x0c, + 0xdf, 0x60, 0x81, 0x6f, 0xb7, 0x5f, 0xb9, 0x1f, 0xca, 0x75, 0x3c, 0x1a, + 0x63, 0xb0, 0xeb, 0xe6, 0x95, 0x86, 0x0d, 0xae, 0xa6, 0xc9, 0x2a, 0x94, + 0xf1, 0xd0, 0xbe, 0x75, 0xc8, 0xf8, 0x07, 0xd7, 0x88, 0xff, 0xec, 0xf9, + 0xcd, 0x49, 0xc6, 0xfe, 0x4d, 0x7f, 0x44, 0x1e, 0xd8, 0xaf, 0xa9, 0x72, + 0x27, 0x98, 0xe2, 0x5a, 0x08, 0xea, 0x55, 0xd3, 0xb3, 0xea, 0xdc, 0x76, + 0x69, 0x51, 0x10, 0x01, 0x46, 0x7d, 0x33, 0x94, 0x9c, 0x94, 0xef, 0xfe, + 0x76, 0x1c, 0xc6, 0xd7, 0x15, 0x53, 0x3e, 0x8d, 0x3d, 0x29, 0x9a, 0x58, + 0x6a, 0xf1, 0x75, 0x9e, 0xea, 0x1b, 0x4c, 0xf0, 0x47, 0x76, 0xac, 0xc6, + 0xa2, 0x32, 0x44, 0x40, 0xdf, 0xfe, 0xff, 0x9d, 0xf4, 0xe2, 0xc2, 0xfa, + 0xa1, 0x5f, 0x2e, 0x66, 0xe9, 0x97, 0xcb, 0x27, 0x26, 0x6e, 0x53, 0xe4, + 0xe8, 0x86, 0x2c, 0xea, 0xd3, 0x69, 0x6c, 0x61, 0x4f, 0xfe, 0xc1, 0xc9, + 0x8b, 0x05, 0x92, 0x6f, 0x47, 0x96, 0xce, 0xf0, 0x33, 0xfa, 0x7c, 0x78, + 0x24, 0x9b, 0xd7, 0x8d, 0x36, 0x56, 0x37, 0x86, 0xbc, 0x72, 0x5a, 0xf9, + 0xb9, 0xb0, 0x93, 0xf0, 0x81, 0x78, 0x10, 0xf2, 0xb0, 0xc2, 0x79, 0x91, + 0x5e, 0xcf, 0xbc, 0x8c, 0xf2, 0x32, 0x0f, 0xf7, 0x2d, 0x30, 0xd8, 0x13, + 0x77, 0x4f, 0x78, 0x9e, 0x40, 0x8d, 0xe6, 0x3a, 0x98, 0xb2, 0xaa, 0x13, + 0x4d, 0x25, 0x49, 0x34, 0x6c, 0x80, 0x9e, 0x19, 0x03, 0xdb, 0xcd, 0xf5, + 0xb1, 0x54, 0x74, 0x1b, 0x67, 0x3c, 0x46, 0xac, 0x3e, 0x5d, 0xa2, 0xd9, + 0x13, 0x83, 0x30, 0xeb, 0x82, 0x3b, 0x06, 0xab, 0x3c, 0x39, 0x7d, 0xd0, + 0x68 +}; +extern const size_t kOEMPublicCertSize_Test_NEW = 1417; +#endif + +} // namespace + +// Refer to the following in main modules. +// This level of indirection is present so new OEM Certificates can be +// added and then selected for use at compile time or run time. + +namespace wvcdm_test_auth { + +const uint32_t kOEMSystemId = kOEMSystemId_Prod; + +const uint8_t* kOEMPrivateKey = kOEMPrivateKey_Prod; +const uint8_t* kOEMPublicCert = kOEMPublicCert_Prod; + +const size_t kOEMPrivateKeySize = kOEMPrivateKeySize_Prod; +const size_t kOEMPublicCertSize = kOEMPublicCertSize_Prod; + +} // namespace wvcdm_test_auth diff --git a/test/auth/test_oem_cert.h b/test/auth/test_oem_cert.h new file mode 100644 index 00000000..9158a612 --- /dev/null +++ b/test/auth/test_oem_cert.h @@ -0,0 +1,23 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +// Access the test OEM certificate. + +#ifndef OEM_CERT_H_ +#define OEM_CERT_H_ + +#include +#include + +namespace wvcdm_test_auth { + +extern const uint32_t kOEMSystemId; + +extern const uint8_t* kOEMPrivateKey; +extern const uint8_t* kOEMPublicCert; + +extern const size_t kOEMPrivateKeySize; +extern const size_t kOEMPublicCertSize; + +} // namespace wvcdm_test_auth + +#endif // OEM_CERT_H_ diff --git a/test/auth/test_rsa_key.cpp b/test/auth/test_rsa_key.cpp new file mode 100644 index 00000000..68c56041 --- /dev/null +++ b/test/auth/test_rsa_key.cpp @@ -0,0 +1,337 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +// Test-only RSA Keys + +#include "test_rsa_key.h" + +namespace { + +// A 2048 bit RSA key in PKCS#8 PrivateKeyInfo format +// This is the RSA Test Key. +static const uint8_t kTestRSAPKCS8PrivateKeyInfo2_2048[] = { + 0x30, 0x82, 0x04, 0xbc, 0x02, 0x01, 0x00, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x04, 0xa6, 0x30, 0x82, 0x04, 0xa2, 0x02, 0x01, + 0x00, 0x02, 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, + 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, 0x5a, 0x2a, + 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, + 0x94, 0x58, 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, + 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, 0x67, 0x5e, + 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, + 0x2a, 0xaa, 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, + 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, 0x9f, 0xe3, + 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, + 0x28, 0xda, 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, + 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, 0xaf, 0xfb, + 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, + 0x29, 0xf2, 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, + 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, 0xb5, 0xa4, + 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, + 0xcd, 0x9a, 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, + 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, 0xad, 0xda, + 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, + 0x98, 0x56, 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, + 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, 0x24, 0x03, + 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, + 0xc9, 0x83, 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, + 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, 0x61, 0x5b, + 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, + 0x2d, 0x5f, 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, + 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, 0xfb, 0x01, + 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, + 0x82, 0x46, 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, + 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, 0x5b, 0xed, + 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, + 0xd3, 0x5b, 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, + 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, 0xbb, 0xbb, + 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, + 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x00, 0x5e, + 0x79, 0x65, 0x49, 0xa5, 0x76, 0x79, 0xf9, 0x05, + 0x45, 0x0f, 0xf4, 0x03, 0xbd, 0xa4, 0x7d, 0x29, + 0xd5, 0xde, 0x33, 0x63, 0xd8, 0xb8, 0xac, 0x97, + 0xeb, 0x3f, 0x5e, 0x55, 0xe8, 0x7d, 0xf3, 0xe7, + 0x3b, 0x5c, 0x2d, 0x54, 0x67, 0x36, 0xd6, 0x1d, + 0x46, 0xf5, 0xca, 0x2d, 0x8b, 0x3a, 0x7e, 0xdc, + 0x45, 0x38, 0x79, 0x7e, 0x65, 0x71, 0x5f, 0x1c, + 0x5e, 0x79, 0xb1, 0x40, 0xcd, 0xfe, 0xc5, 0xe1, + 0xc1, 0x6b, 0x78, 0x04, 0x4e, 0x8e, 0x79, 0xf9, + 0x0a, 0xfc, 0x79, 0xb1, 0x5e, 0xb3, 0x60, 0xe3, + 0x68, 0x7b, 0xc6, 0xef, 0xcb, 0x71, 0x4c, 0xba, + 0xa7, 0x79, 0x5c, 0x7a, 0x81, 0xd1, 0x71, 0xe7, + 0x00, 0x21, 0x13, 0xe2, 0x55, 0x69, 0x0e, 0x75, + 0xbe, 0x09, 0xc3, 0x4f, 0xa9, 0xc9, 0x68, 0x22, + 0x0e, 0x97, 0x8d, 0x89, 0x6e, 0xf1, 0xe8, 0x88, + 0x7a, 0xd1, 0xd9, 0x09, 0x5d, 0xd3, 0x28, 0x78, + 0x25, 0x0b, 0x1c, 0x47, 0x73, 0x25, 0xcc, 0x21, + 0xb6, 0xda, 0xc6, 0x24, 0x5a, 0xd0, 0x37, 0x14, + 0x46, 0xc7, 0x94, 0x69, 0xe4, 0x43, 0x6f, 0x47, + 0xde, 0x00, 0x33, 0x4d, 0x8f, 0x95, 0x72, 0xfa, + 0x68, 0x71, 0x17, 0x66, 0x12, 0x1a, 0x87, 0x27, + 0xf7, 0xef, 0x7e, 0xe0, 0x35, 0x58, 0xf2, 0x4d, + 0x6f, 0x35, 0x01, 0xaa, 0x96, 0xe2, 0x3d, 0x51, + 0x13, 0x86, 0x9c, 0x79, 0xd0, 0xb7, 0xb6, 0x64, + 0xe8, 0x86, 0x65, 0x50, 0xbf, 0xcc, 0x27, 0x53, + 0x1f, 0x51, 0xd4, 0xca, 0xbe, 0xf5, 0xdd, 0x77, + 0x70, 0x98, 0x0f, 0xee, 0xa8, 0x96, 0x07, 0x5f, + 0x45, 0x6a, 0x7a, 0x0d, 0x03, 0x9c, 0x4f, 0x29, + 0xf6, 0x06, 0xf3, 0x5d, 0x58, 0x6c, 0x47, 0xd0, + 0x96, 0xa9, 0x03, 0x17, 0xbb, 0x4e, 0xc9, 0x21, + 0xe0, 0xac, 0xcd, 0x78, 0x78, 0xb2, 0xfe, 0x81, + 0xb2, 0x51, 0x53, 0xa6, 0x1f, 0x98, 0x45, 0x02, + 0x81, 0x81, 0x00, 0xcf, 0x73, 0x8c, 0xbe, 0x6d, + 0x45, 0x2d, 0x0c, 0x0b, 0x5d, 0x5c, 0x6c, 0x75, + 0x78, 0xcc, 0x35, 0x48, 0xb6, 0x98, 0xf1, 0xb9, + 0x64, 0x60, 0x8c, 0x43, 0xeb, 0x85, 0xab, 0x04, + 0xb6, 0x7d, 0x1b, 0x71, 0x75, 0x06, 0xe2, 0xda, + 0x84, 0x68, 0x2e, 0x7f, 0x4c, 0xe3, 0x73, 0xb4, + 0xde, 0x51, 0x4b, 0xb6, 0x51, 0x86, 0x7b, 0xd0, + 0xe6, 0x4d, 0xf3, 0xd1, 0xcf, 0x1a, 0xfe, 0x7f, + 0x3a, 0x83, 0xba, 0xb3, 0xe1, 0xff, 0x54, 0x13, + 0x93, 0xd7, 0x9c, 0x27, 0x80, 0xb7, 0x1e, 0x64, + 0x9e, 0xf7, 0x32, 0x2b, 0x46, 0x29, 0xf7, 0xf8, + 0x18, 0x6c, 0xf7, 0x4a, 0xbe, 0x4b, 0xee, 0x96, + 0x90, 0x8f, 0xa2, 0x16, 0x22, 0x6a, 0xcc, 0x48, + 0x06, 0x74, 0x63, 0x43, 0x7f, 0x27, 0x22, 0x44, + 0x3c, 0x2d, 0x3b, 0x62, 0xf1, 0x1c, 0xb4, 0x27, + 0x33, 0x85, 0x26, 0x60, 0x48, 0x16, 0xcb, 0xef, + 0xf8, 0xcd, 0x37, 0x02, 0x81, 0x81, 0x00, 0xce, + 0x15, 0x43, 0x6e, 0x4b, 0x0f, 0xf9, 0x3f, 0x87, + 0xc3, 0x41, 0x45, 0x97, 0xb1, 0x49, 0xc2, 0x19, + 0x23, 0x87, 0xe4, 0x24, 0x1c, 0x64, 0xe5, 0x28, + 0xcb, 0x43, 0x10, 0x14, 0x14, 0x0e, 0x19, 0xcb, + 0xbb, 0xdb, 0xfd, 0x11, 0x9d, 0x17, 0x68, 0x78, + 0x6d, 0x61, 0x70, 0x63, 0x3a, 0xa1, 0xb3, 0xf3, + 0xa7, 0x5b, 0x0e, 0xff, 0xb7, 0x61, 0x11, 0x54, + 0x91, 0x99, 0xe5, 0x91, 0x32, 0x2d, 0xeb, 0x3f, + 0xd8, 0x3e, 0xf7, 0xd4, 0xcb, 0xd2, 0xa3, 0x41, + 0xc1, 0xee, 0xc6, 0x92, 0x13, 0xeb, 0x7f, 0x42, + 0x58, 0xf4, 0xd0, 0xb2, 0x74, 0x1d, 0x8e, 0x87, + 0x46, 0xcd, 0x14, 0xb8, 0x16, 0xad, 0xb5, 0xbd, + 0x0d, 0x6c, 0x95, 0x5a, 0x16, 0xbf, 0xe9, 0x53, + 0xda, 0xfb, 0xed, 0x83, 0x51, 0x67, 0xa9, 0x55, + 0xab, 0x54, 0x02, 0x95, 0x20, 0xa6, 0x68, 0x17, + 0x53, 0xa8, 0xea, 0x43, 0xe5, 0xb0, 0xa3, 0x02, + 0x81, 0x80, 0x67, 0x9c, 0x32, 0x83, 0x39, 0x57, + 0xff, 0x73, 0xb0, 0x89, 0x64, 0x8b, 0xd6, 0xf0, + 0x0a, 0x2d, 0xe2, 0xaf, 0x30, 0x1c, 0x2a, 0x97, + 0xf3, 0x90, 0x9a, 0xab, 0x9b, 0x0b, 0x1b, 0x43, + 0x79, 0xa0, 0xa7, 0x3d, 0xe7, 0xbe, 0x8d, 0x9c, + 0xeb, 0xdb, 0xad, 0x40, 0xdd, 0xa9, 0x00, 0x80, + 0xb8, 0xe1, 0xb3, 0xa1, 0x6c, 0x25, 0x92, 0xe4, + 0x33, 0xb2, 0xbe, 0xeb, 0x4d, 0x74, 0x26, 0x5f, + 0x37, 0x43, 0x9c, 0x6c, 0x17, 0x76, 0x0a, 0x81, + 0x20, 0x82, 0xa1, 0x48, 0x2c, 0x2d, 0x45, 0xdc, + 0x0f, 0x62, 0x43, 0x32, 0xbb, 0xeb, 0x59, 0x41, + 0xf9, 0xca, 0x58, 0xce, 0x4a, 0x66, 0x53, 0x54, + 0xc8, 0x28, 0x10, 0x1e, 0x08, 0x71, 0x16, 0xd8, + 0x02, 0x71, 0x41, 0x58, 0xd4, 0x56, 0xcc, 0xf5, + 0xb1, 0x31, 0xa3, 0xed, 0x00, 0x85, 0x09, 0xbf, + 0x35, 0x95, 0x41, 0x29, 0x40, 0x19, 0x83, 0x35, + 0x24, 0x69, 0x02, 0x81, 0x80, 0x55, 0x10, 0x0b, + 0xcc, 0x3b, 0xa9, 0x75, 0x3d, 0x16, 0xe1, 0xae, + 0x50, 0x76, 0x63, 0x94, 0x49, 0x4c, 0xad, 0x10, + 0xcb, 0x47, 0x68, 0x7c, 0xf0, 0xe5, 0xdc, 0xb8, + 0x6a, 0xab, 0x8e, 0xf7, 0x9f, 0x08, 0x2c, 0x1b, + 0x8a, 0xa2, 0xb9, 0x8f, 0xce, 0xec, 0x5e, 0x61, + 0xa8, 0xcd, 0x1c, 0x87, 0x60, 0x4a, 0xc3, 0x1a, + 0x5f, 0xdf, 0x87, 0x26, 0xc6, 0xcb, 0x7c, 0x69, + 0xe4, 0x8b, 0x01, 0x06, 0x59, 0x22, 0xfa, 0x34, + 0x4b, 0x81, 0x87, 0x3c, 0x03, 0x6d, 0x02, 0x0a, + 0x77, 0xe6, 0x15, 0xd8, 0xcf, 0xa7, 0x68, 0x26, + 0x6c, 0xfa, 0x2b, 0xd9, 0x83, 0x5a, 0x2d, 0x0c, + 0x3b, 0x70, 0x1c, 0xd4, 0x48, 0xbe, 0xa7, 0x0a, + 0xd9, 0xbe, 0xdc, 0xc3, 0x0c, 0x21, 0x33, 0xb3, + 0x66, 0xff, 0x1c, 0x1b, 0xc8, 0x96, 0x76, 0xe8, + 0x6f, 0x44, 0x74, 0xbc, 0x9b, 0x1c, 0x7d, 0xc8, + 0xac, 0x21, 0xa8, 0x6e, 0x37, 0x02, 0x81, 0x80, + 0x2c, 0x7c, 0xad, 0x1e, 0x75, 0xf6, 0x69, 0x1d, + 0xe7, 0xa6, 0xca, 0x74, 0x7d, 0x67, 0xc8, 0x65, + 0x28, 0x66, 0xc4, 0x43, 0xa6, 0xbd, 0x40, 0x57, + 0xae, 0xb7, 0x65, 0x2c, 0x52, 0xf9, 0xe4, 0xc7, + 0x81, 0x7b, 0x56, 0xa3, 0xd2, 0x0d, 0xe8, 0x33, + 0x70, 0xcf, 0x06, 0x84, 0xb3, 0x4e, 0x44, 0x50, + 0x75, 0x61, 0x96, 0x86, 0x4b, 0xb6, 0x2b, 0xad, + 0xf0, 0xad, 0x57, 0xd0, 0x37, 0x0d, 0x1d, 0x35, + 0x50, 0xcb, 0x69, 0x22, 0x39, 0x29, 0xb9, 0x3a, + 0xd3, 0x29, 0x23, 0x02, 0x60, 0xf7, 0xab, 0x30, + 0x40, 0xda, 0x8e, 0x4d, 0x45, 0x70, 0x26, 0xf4, + 0xa2, 0x0d, 0xd0, 0x64, 0x5d, 0x47, 0x3c, 0x18, + 0xf4, 0xd4, 0x52, 0x95, 0x00, 0xae, 0x84, 0x6b, + 0x47, 0xb2, 0x3c, 0x82, 0xd3, 0x72, 0x53, 0xde, + 0x72, 0x2c, 0xf7, 0xc1, 0x22, 0x36, 0xd9, 0x18, + 0x56, 0xfe, 0x39, 0x28, 0x33, 0xe0, 0xdb, 0x03 +}; + +// A 3072 bit RSA key in PKCS#8 PrivateKeyInfo format +// Used to verify the functions that manipulate RSA keys. +static const uint8_t kTestRSAPKCS8PrivateKeyInfo3_3072[] = { + 0x30, 0x82, 0x06, 0xfe, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, + 0x06, 0xe8, 0x30, 0x82, 0x06, 0xe4, 0x02, 0x01, 0x00, 0x02, 0x82, 0x01, + 0x81, 0x00, 0xe3, 0x32, 0x2f, 0x0a, 0x94, 0x06, 0x46, 0x99, 0x0a, 0x58, + 0xda, 0xd0, 0x9b, 0x2b, 0xe4, 0x2a, 0x35, 0xdf, 0xb7, 0x9b, 0x5e, 0xbf, + 0xfb, 0xe5, 0x24, 0x47, 0x5a, 0x94, 0x06, 0x04, 0xe5, 0x43, 0xed, 0x37, + 0x33, 0x94, 0x09, 0xd0, 0xae, 0xad, 0x86, 0xb4, 0xc7, 0xc3, 0x56, 0x6f, + 0x88, 0x74, 0xfb, 0xab, 0xf7, 0xcf, 0xcb, 0xa6, 0x89, 0x48, 0x4a, 0x02, + 0x89, 0xcd, 0xfd, 0x83, 0x3d, 0x2a, 0x27, 0xc1, 0xa2, 0x99, 0x8e, 0xef, + 0xcf, 0x91, 0xd3, 0xb2, 0x96, 0xe7, 0x5f, 0x0c, 0xb3, 0x44, 0x6a, 0xcf, + 0xc1, 0x22, 0xb9, 0xe4, 0xd4, 0xc0, 0xf2, 0xc3, 0x8d, 0xe1, 0x43, 0x38, + 0x31, 0x9c, 0x56, 0x04, 0xd4, 0x9d, 0x41, 0x02, 0x31, 0xce, 0x7e, 0xc0, + 0x11, 0x24, 0x54, 0xb1, 0xa2, 0x99, 0x0e, 0xe2, 0x0c, 0x5b, 0x24, 0x94, + 0x85, 0xe8, 0x8c, 0x30, 0xbb, 0x12, 0x94, 0x74, 0x0f, 0x67, 0xe5, 0x69, + 0xa4, 0xc4, 0x59, 0xd6, 0x77, 0x96, 0xae, 0xc6, 0x00, 0xbe, 0xf5, 0xe6, + 0x1f, 0x71, 0x90, 0x6d, 0xdd, 0xfb, 0x7b, 0x42, 0xd0, 0xdf, 0x4b, 0x58, + 0xaf, 0x9c, 0xba, 0xcb, 0x35, 0x4b, 0xf3, 0x06, 0x3a, 0x20, 0x42, 0x97, + 0x96, 0x95, 0x47, 0xbe, 0x2d, 0xeb, 0x9a, 0xb6, 0xea, 0xe0, 0xc1, 0x1d, + 0x80, 0x61, 0x3e, 0x8e, 0x18, 0x66, 0xf4, 0x26, 0x77, 0xcf, 0x56, 0x27, + 0x8b, 0xde, 0x93, 0x94, 0x3e, 0x1d, 0xe4, 0x5f, 0x6d, 0xf2, 0x39, 0x03, + 0x15, 0x4f, 0x2e, 0x58, 0x59, 0x75, 0x19, 0xb9, 0x24, 0x87, 0xd4, 0xff, + 0x64, 0x82, 0x11, 0x10, 0x34, 0x30, 0x09, 0x39, 0x43, 0x9c, 0xd2, 0x3b, + 0x45, 0xdc, 0x85, 0x4f, 0x6d, 0xb7, 0xbb, 0x49, 0xda, 0x3b, 0x07, 0xa2, + 0x76, 0x56, 0xa0, 0xee, 0xa9, 0xa9, 0x52, 0xb7, 0xf1, 0xfd, 0xde, 0xa1, + 0x6f, 0x0e, 0x7f, 0x82, 0x3f, 0x9e, 0x3d, 0x46, 0xcd, 0x48, 0x55, 0xe8, + 0x59, 0x65, 0xd8, 0xc7, 0xe4, 0x6b, 0xe6, 0xc0, 0xdd, 0x6e, 0x5c, 0xb7, + 0x0c, 0xdb, 0x29, 0xad, 0x8e, 0xa4, 0x86, 0xe9, 0x4d, 0xad, 0x54, 0xf9, + 0x56, 0x06, 0x0e, 0xc4, 0x2b, 0x01, 0xd9, 0x86, 0x1f, 0x65, 0xbe, 0x0d, + 0x77, 0x8d, 0x9d, 0xff, 0x37, 0x97, 0x57, 0xc3, 0x06, 0x8a, 0x05, 0x80, + 0x78, 0xd3, 0xbd, 0x91, 0xa5, 0xc1, 0x11, 0x4d, 0x99, 0x1a, 0x83, 0xd7, + 0x30, 0x1c, 0x24, 0xac, 0xdf, 0x6c, 0xc1, 0x23, 0x60, 0x76, 0x54, 0xbf, + 0x2b, 0xac, 0x34, 0xf0, 0x35, 0x92, 0x0d, 0x36, 0x29, 0x09, 0x24, 0xd5, + 0x54, 0xe9, 0x68, 0x9c, 0x90, 0x07, 0x16, 0x86, 0xb1, 0xd0, 0x9b, 0xa5, + 0x86, 0x4e, 0xce, 0xbf, 0x30, 0x9d, 0x91, 0xd7, 0xd2, 0xa6, 0x4f, 0xbb, + 0xbb, 0x9d, 0x7c, 0x0f, 0x58, 0xaa, 0xf1, 0xd0, 0x90, 0x66, 0x20, 0x48, + 0x8f, 0x29, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x82, 0x01, 0x81, 0x00, + 0x88, 0x41, 0x28, 0x85, 0x27, 0x91, 0x3b, 0xf5, 0xbc, 0x86, 0xdd, 0x74, + 0x0e, 0x1b, 0x9c, 0x92, 0xd4, 0x0c, 0x7f, 0x88, 0xe6, 0xa2, 0x2c, 0xe7, + 0x97, 0x82, 0x53, 0x88, 0x42, 0xb3, 0xdc, 0xeb, 0x87, 0xf0, 0x7b, 0x36, + 0x65, 0x4c, 0x89, 0xf5, 0xf7, 0xbb, 0xa3, 0xb2, 0x3a, 0xbc, 0x59, 0x12, + 0x0f, 0x7d, 0x6a, 0xf9, 0x6c, 0x21, 0x4c, 0x63, 0xd6, 0x3e, 0xff, 0x76, + 0x52, 0x7b, 0xca, 0xca, 0xe5, 0x5c, 0xf3, 0xaf, 0x34, 0x52, 0x0e, 0x22, + 0x5e, 0xdb, 0xd4, 0x34, 0x9e, 0x84, 0x77, 0x5e, 0xa8, 0xd0, 0x3f, 0xfc, + 0x1b, 0x90, 0x69, 0x27, 0xee, 0x6f, 0xe9, 0x3f, 0x17, 0x99, 0x33, 0xe7, + 0x96, 0x8e, 0xff, 0x13, 0xf0, 0x50, 0xe8, 0x9d, 0xf6, 0xd6, 0x29, 0x71, + 0xa8, 0x79, 0x80, 0x12, 0x5b, 0x22, 0xa6, 0x56, 0x62, 0xf1, 0xcf, 0xfd, + 0x4f, 0x56, 0x4a, 0x5b, 0x32, 0x3d, 0x08, 0xa0, 0x3e, 0xad, 0xc4, 0xeb, + 0x1d, 0x15, 0xca, 0x52, 0xcc, 0x2e, 0x63, 0x74, 0x22, 0xf5, 0x08, 0x16, + 0x8b, 0x8f, 0xd8, 0x79, 0x61, 0xcb, 0x08, 0x89, 0x62, 0x1e, 0xa5, 0xf3, + 0x50, 0xf3, 0x5d, 0xdb, 0x56, 0xbc, 0x7d, 0x4f, 0xab, 0xa0, 0x4d, 0xe6, + 0xe9, 0x47, 0xdd, 0x32, 0x57, 0x6f, 0x2c, 0x1d, 0xee, 0xb3, 0x4a, 0xb3, + 0x07, 0x59, 0x20, 0xb9, 0x5d, 0xe3, 0x54, 0x27, 0x3c, 0x7c, 0x2b, 0x1d, + 0x07, 0xff, 0x49, 0x93, 0xe2, 0xe3, 0xb2, 0x65, 0xf3, 0x69, 0xc1, 0x1c, + 0x2a, 0x75, 0x80, 0x16, 0x37, 0xe6, 0x00, 0x5b, 0xd3, 0x1b, 0xac, 0xca, + 0x8b, 0x8b, 0x98, 0x77, 0x81, 0x67, 0xe3, 0xdc, 0xbb, 0xc4, 0x3a, 0x45, + 0x15, 0xec, 0xd9, 0xad, 0xdb, 0x60, 0xcf, 0xe5, 0xd8, 0xd9, 0xfc, 0xcf, + 0xbe, 0x76, 0x2f, 0x5b, 0x60, 0xdb, 0x06, 0x62, 0x5b, 0x80, 0x7e, 0x53, + 0xde, 0x74, 0xb1, 0xa3, 0xb6, 0x9b, 0x14, 0xd7, 0x09, 0x65, 0x21, 0x1d, + 0xd5, 0xd3, 0x34, 0xca, 0x89, 0xe7, 0xbc, 0xf4, 0x48, 0x81, 0x6a, 0xcf, + 0x28, 0xbe, 0x74, 0x8b, 0x40, 0xad, 0x86, 0xcd, 0xa5, 0xd6, 0xfa, 0x64, + 0x9b, 0xd2, 0xd4, 0x17, 0x20, 0xd6, 0x0d, 0xbe, 0x95, 0xd4, 0xaf, 0xa5, + 0xde, 0x31, 0x0d, 0x6a, 0x90, 0xc6, 0xd0, 0x59, 0xd4, 0x8c, 0x81, 0x2d, + 0x9d, 0x09, 0xf1, 0x22, 0xf5, 0x30, 0x2d, 0xdf, 0x85, 0x54, 0x34, 0x8a, + 0xde, 0x3c, 0xce, 0xdb, 0x36, 0x9f, 0xcf, 0x12, 0x61, 0x0e, 0x99, 0x87, + 0x70, 0x51, 0x04, 0x91, 0x74, 0xc6, 0x88, 0x22, 0x75, 0x02, 0x8f, 0x7e, + 0xb5, 0x79, 0x48, 0x2f, 0xf3, 0x3b, 0xb8, 0x82, 0x3e, 0x7e, 0x45, 0xe5, + 0xb2, 0xc8, 0x4c, 0x12, 0x73, 0xb8, 0x92, 0x04, 0xd1, 0x9a, 0xae, 0xaa, + 0x08, 0xd9, 0x23, 0x54, 0x19, 0x46, 0xc8, 0x56, 0x5f, 0x5e, 0x10, 0xa1, + 0x02, 0x81, 0xc1, 0x00, 0xf6, 0x38, 0x88, 0x31, 0x06, 0x85, 0xd9, 0x00, + 0xf0, 0x6b, 0xd8, 0x7d, 0x76, 0x08, 0xc0, 0x69, 0x6a, 0xfb, 0xa4, 0xc8, + 0xdc, 0x6b, 0x00, 0xaf, 0xae, 0x52, 0x82, 0xe6, 0xba, 0xc9, 0x5e, 0xc9, + 0xb7, 0x7f, 0xa1, 0xc4, 0xcb, 0xa0, 0xbc, 0x66, 0x3c, 0x55, 0x6a, 0xea, + 0x6e, 0x42, 0xf1, 0x6b, 0xbd, 0xc4, 0xf2, 0x6b, 0x91, 0x11, 0x82, 0x20, + 0xc2, 0xe6, 0x9e, 0x96, 0x5c, 0x9a, 0x7e, 0xb3, 0x57, 0x45, 0x9c, 0x42, + 0x60, 0x4c, 0x04, 0x4f, 0x47, 0xfb, 0xa7, 0x68, 0x4e, 0x15, 0x43, 0x5a, + 0x97, 0xb3, 0xfc, 0xd2, 0x91, 0x3c, 0x11, 0x5e, 0xaf, 0x57, 0x2a, 0xa1, + 0x45, 0xa5, 0x60, 0xf0, 0xbe, 0x31, 0xe8, 0xc4, 0x0b, 0x35, 0xe3, 0x42, + 0x9b, 0x22, 0x6b, 0xa3, 0x6c, 0x49, 0x71, 0x20, 0x34, 0x3f, 0x46, 0x0b, + 0x79, 0xc9, 0xb8, 0xb4, 0xbd, 0x9c, 0xad, 0xd3, 0xd8, 0x7e, 0x95, 0x9f, + 0x9a, 0xd4, 0x03, 0xe9, 0x5a, 0x54, 0x46, 0x94, 0x39, 0x55, 0xf1, 0x28, + 0x0d, 0xd1, 0xaa, 0xc9, 0xf8, 0x28, 0x58, 0xef, 0xb0, 0x62, 0xb6, 0x2d, + 0xc7, 0xd2, 0x09, 0x3a, 0x21, 0x0f, 0x7d, 0xa1, 0xb9, 0x59, 0xd5, 0xa7, + 0x43, 0xa9, 0x51, 0xb7, 0xbf, 0x9d, 0xf3, 0x85, 0xec, 0xb3, 0xfb, 0x51, + 0x61, 0xca, 0x81, 0x4d, 0xfa, 0xf1, 0xc3, 0x94, 0x37, 0x45, 0x91, 0xf0, + 0x4b, 0xfc, 0x8e, 0xff, 0x02, 0x81, 0xc1, 0x00, 0xec, 0x38, 0x37, 0x3b, + 0xba, 0x1b, 0x83, 0xaf, 0x3a, 0x00, 0xb9, 0x5e, 0x1f, 0xc8, 0xad, 0x57, + 0xcf, 0x7c, 0xe2, 0x94, 0x95, 0xf1, 0xec, 0x0a, 0x4b, 0x40, 0xc4, 0x48, + 0xfb, 0x47, 0x5f, 0x66, 0xc6, 0xf0, 0x70, 0x14, 0xe9, 0x08, 0xe4, 0x50, + 0x29, 0x0a, 0x24, 0x57, 0x93, 0x97, 0x21, 0xd9, 0xfb, 0xc5, 0x52, 0x0a, + 0x38, 0xb9, 0x68, 0xa3, 0x4f, 0x4b, 0xf8, 0xb8, 0x24, 0xef, 0x0c, 0x42, + 0xda, 0x57, 0x32, 0x77, 0xed, 0x9c, 0x78, 0xeb, 0x10, 0x3e, 0x70, 0x67, + 0xe9, 0x01, 0x03, 0x19, 0x19, 0xdb, 0x48, 0x9e, 0x1e, 0x52, 0x23, 0x88, + 0xb6, 0x87, 0xb8, 0x0d, 0x2d, 0x0c, 0xfc, 0x90, 0x31, 0x9f, 0xa6, 0x96, + 0x0a, 0xe1, 0x34, 0x72, 0x86, 0x0e, 0x49, 0x7c, 0xfe, 0x21, 0xaa, 0x25, + 0xdd, 0x36, 0xbb, 0x1f, 0x85, 0xfe, 0x34, 0x18, 0xc2, 0x36, 0xa2, 0x7d, + 0xee, 0xd9, 0x4f, 0x8e, 0xcb, 0x49, 0x8e, 0x7a, 0x43, 0x3c, 0x52, 0x73, + 0x18, 0x60, 0xf6, 0xb7, 0x7a, 0xc4, 0x7a, 0x8a, 0x1c, 0xf0, 0xc9, 0x2e, + 0xad, 0x54, 0xb1, 0x7b, 0x8e, 0xcb, 0x4d, 0xc2, 0xbc, 0x2a, 0x72, 0xfe, + 0x61, 0x01, 0xd8, 0xff, 0x0a, 0x22, 0x6c, 0x51, 0x7e, 0x06, 0x9e, 0x9e, + 0x3c, 0xe8, 0x31, 0x98, 0xf5, 0x08, 0x34, 0x7e, 0xfa, 0x08, 0xd1, 0x14, + 0xdf, 0xfd, 0x26, 0x2f, 0x1f, 0x5a, 0x89, 0xd7, 0x02, 0x81, 0xc0, 0x76, + 0xdd, 0xed, 0xe9, 0xf5, 0x23, 0x33, 0x13, 0x3f, 0xfe, 0x60, 0xa2, 0x99, + 0x14, 0x3a, 0x87, 0xea, 0x0d, 0x18, 0x8d, 0x9b, 0xd3, 0xd0, 0x9d, 0xff, + 0xc3, 0x77, 0xcc, 0x9a, 0x0a, 0x53, 0x47, 0x80, 0xde, 0x0e, 0x23, 0xea, + 0xc6, 0x6b, 0x8d, 0xd3, 0xbc, 0xcd, 0x03, 0xe6, 0x3d, 0x4d, 0x3d, 0xdd, + 0x7c, 0xb2, 0x27, 0xf9, 0xfe, 0x00, 0xdb, 0x7e, 0x1c, 0x46, 0x1d, 0x83, + 0x11, 0x56, 0xef, 0x8f, 0xc7, 0x5c, 0x5b, 0xb3, 0x0f, 0x9f, 0xd9, 0x02, + 0x80, 0x5c, 0x5e, 0x7f, 0xab, 0xc6, 0x3b, 0x7b, 0x17, 0x7a, 0x8b, 0xd1, + 0x6f, 0xb5, 0x57, 0x07, 0xc1, 0x46, 0x24, 0x5b, 0x72, 0x2e, 0xad, 0xaa, + 0xb4, 0x7f, 0x91, 0xfd, 0x73, 0x83, 0x86, 0x89, 0x4c, 0x81, 0xb8, 0x80, + 0xb3, 0xa7, 0xf8, 0x8b, 0x20, 0xac, 0xd9, 0x27, 0x6f, 0x9a, 0x4b, 0x2f, + 0x6a, 0xef, 0x84, 0x61, 0x75, 0x23, 0x18, 0xcd, 0x6f, 0x63, 0x80, 0x09, + 0x8a, 0xbc, 0x14, 0x1c, 0xe5, 0xff, 0xa9, 0x7d, 0x9a, 0x66, 0x20, 0x61, + 0x3c, 0x61, 0x4b, 0x3d, 0xd5, 0x39, 0xec, 0x3a, 0x16, 0x8d, 0x3b, 0xd1, + 0xf0, 0x1f, 0x8f, 0xae, 0xe2, 0xce, 0xc1, 0x94, 0x69, 0xae, 0xb8, 0xcd, + 0xba, 0x1c, 0x71, 0xe0, 0x47, 0x37, 0xa2, 0x1f, 0x5a, 0xdb, 0x37, 0xe1, + 0x59, 0x4c, 0x39, 0x46, 0xc1, 0xc0, 0x65, 0xc8, 0xd9, 0x61, 0xd3, 0x02, + 0x81, 0xc0, 0x2f, 0x63, 0xe7, 0xd0, 0xd7, 0xb9, 0x85, 0x65, 0xb6, 0x21, + 0x47, 0x0f, 0x17, 0x19, 0x4f, 0x8d, 0x7a, 0x56, 0xf7, 0xae, 0x0f, 0x97, + 0x05, 0x5f, 0xdb, 0x51, 0x17, 0x0f, 0xfd, 0x39, 0x88, 0x6e, 0x3a, 0x23, + 0x2a, 0x99, 0x47, 0x57, 0x3d, 0x56, 0xc7, 0xa4, 0xfd, 0x3d, 0x84, 0xa2, + 0xa1, 0x6b, 0xf6, 0x12, 0xd4, 0x2e, 0xb0, 0xca, 0xa1, 0xaf, 0x81, 0xcd, + 0x20, 0x0c, 0xf1, 0x7b, 0xf3, 0xdd, 0xc5, 0xa8, 0x10, 0xbb, 0xf6, 0xb3, + 0x99, 0x9e, 0xaf, 0x17, 0x97, 0xbd, 0x81, 0x05, 0x6e, 0xf5, 0xae, 0x36, + 0x4c, 0x0f, 0x4c, 0xcd, 0xf5, 0xcb, 0x0b, 0xb3, 0x96, 0xbd, 0x2d, 0xf8, + 0x99, 0x02, 0xe4, 0xb1, 0xbe, 0xde, 0x03, 0x38, 0xc3, 0x28, 0xe6, 0xb4, + 0x1f, 0x12, 0x30, 0x79, 0xd8, 0x84, 0xd8, 0x28, 0x8e, 0xc9, 0xf8, 0x3b, + 0xd3, 0x7f, 0xd4, 0x16, 0xd9, 0xea, 0xa1, 0xec, 0x7f, 0x05, 0x8a, 0xcb, + 0x2b, 0x06, 0x64, 0x4e, 0xc9, 0xcb, 0xc5, 0x6c, 0x4e, 0x92, 0xe8, 0xd2, + 0x5a, 0x33, 0x33, 0x33, 0x2b, 0x69, 0x6d, 0xe4, 0xbb, 0xe6, 0xa9, 0xf3, + 0x27, 0x9a, 0x95, 0xdd, 0x7e, 0x4c, 0x82, 0x71, 0xb8, 0x73, 0x12, 0x39, + 0x6d, 0xb9, 0xbb, 0xaa, 0xe0, 0x4f, 0xa6, 0xb0, 0x7e, 0xa2, 0xcd, 0x25, + 0xe4, 0x42, 0x45, 0x2f, 0x57, 0xa2, 0xf4, 0x7c, 0xf9, 0x18, 0x23, 0x16, + 0x2a, 0xe9, 0x02, 0x81, 0xc1, 0x00, 0xab, 0x35, 0x0d, 0x35, 0x94, 0x9d, + 0x96, 0xb2, 0xb7, 0x45, 0x16, 0xef, 0xb7, 0xea, 0xba, 0xa4, 0x32, 0xec, + 0x43, 0x05, 0xb0, 0x14, 0xbd, 0x9e, 0xd2, 0xbe, 0x0a, 0x0c, 0x4f, 0xca, + 0x4f, 0xf3, 0x11, 0xb3, 0x1f, 0xdc, 0x04, 0x18, 0x38, 0x9d, 0xb0, 0x09, + 0xb8, 0xf1, 0xcf, 0x7a, 0x89, 0x03, 0xd8, 0xed, 0x28, 0x30, 0xe8, 0xe6, + 0xbc, 0x7c, 0x1c, 0x59, 0x12, 0xf8, 0x95, 0x9b, 0x36, 0xad, 0xf2, 0xea, + 0x4a, 0x34, 0x00, 0xcf, 0x94, 0x3e, 0xeb, 0xff, 0xe2, 0x5b, 0x6c, 0x72, + 0xe3, 0x04, 0xd1, 0x10, 0x2e, 0xdd, 0x18, 0x8d, 0x9a, 0x84, 0x93, 0x55, + 0x4a, 0x80, 0x6c, 0xb5, 0x82, 0xc4, 0x16, 0x19, 0xc4, 0xba, 0xad, 0x2e, + 0x40, 0x76, 0xb3, 0xc9, 0xd4, 0x26, 0x5d, 0xc9, 0xb1, 0x05, 0x0f, 0x1f, + 0x7d, 0x59, 0x8c, 0x7b, 0xbe, 0x34, 0x09, 0x3e, 0x71, 0x0b, 0xc8, 0xf9, + 0xb3, 0x77, 0x4e, 0x4b, 0xfb, 0xbf, 0x81, 0x55, 0xa4, 0x5e, 0xc6, 0xe9, + 0xa1, 0xc3, 0x16, 0xff, 0xc8, 0x37, 0x88, 0xd5, 0x2d, 0xfb, 0x06, 0x98, + 0xe9, 0x82, 0x1b, 0x5e, 0x1e, 0xdd, 0x48, 0x5d, 0x6c, 0x59, 0xee, 0x7a, + 0xa6, 0xa4, 0x29, 0x41, 0x20, 0xb4, 0xcd, 0xf4, 0x58, 0x95, 0xfd, 0x7d, + 0xbf, 0xfc, 0x83, 0xf5, 0xe1, 0x5a, 0x5d, 0xa8, 0x08, 0x66, 0xd8, 0xa0, + 0x7f, 0xad, 0x7d, 0xcd, 0x22, 0x06 +}; + +} // namespace + +// Refer to the following in main modules. +// This level of indirection is present so new OEM Certificates can be +// added and then selected for use at compile time or run time. + +namespace wvcdm_test_auth { + +const uint8_t* kRsaPrivateKey_2048 = kTestRSAPKCS8PrivateKeyInfo2_2048; +const size_t kRsaPrivateKeySize_2048 = + sizeof(kTestRSAPKCS8PrivateKeyInfo2_2048); + +const uint8_t* kRsaPrivateKey_3072 = kTestRSAPKCS8PrivateKeyInfo3_3072; +const size_t kRsaPrivateKeySize_3072 = + sizeof(kTestRSAPKCS8PrivateKeyInfo3_3072); + +} // namespace wvcdm_test_auth diff --git a/test/auth/test_rsa_key.h b/test/auth/test_rsa_key.h new file mode 100644 index 00000000..d1870b6b --- /dev/null +++ b/test/auth/test_rsa_key.h @@ -0,0 +1,21 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +// TEST ONLY: "built-in" RSA keys. + +#ifndef TEST_AUTH_RSA_KEY_H_ +#define TEST_AUTH_RSA_KEY_H_ + +#include +#include + +namespace wvcdm_test_auth { + +extern const uint8_t* kRsaPrivateKey_2048; +extern const size_t kRsaPrivateKeySize_2048; + +extern const uint8_t* kRsaPrivateKey_3072; +extern const size_t kRsaPrivateKeySize_3072; + +} // namespace wvcdm_test_auth + +#endif // TEST_AUTH_RSA_KEY_H_ diff --git a/test/auth/test_service_cert.cpp b/test/auth/test_service_cert.cpp new file mode 100644 index 00000000..db510221 --- /dev/null +++ b/test/auth/test_service_cert.cpp @@ -0,0 +1,149 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +// Service certificates. + +#include "test_service_cert.h" + +namespace { + +static const unsigned char kServiceCertForStaging[] = { + 0x08, 0x05, 0x12, 0xc7, 0x05, 0x0a, 0xc1, 0x02, 0x08, 0x03, 0x12, 0x10, + 0x17, 0x05, 0xb9, 0x17, 0xcc, 0x12, 0x04, 0x86, 0x8b, 0x06, 0x33, 0x3a, + 0x2f, 0x77, 0x2a, 0x8c, 0x18, 0x82, 0xb4, 0x82, 0x92, 0x05, 0x22, 0x8e, + 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0x99, 0xed, + 0x5b, 0x3b, 0x32, 0x7d, 0xab, 0x5e, 0x24, 0xef, 0xc3, 0xb6, 0x2a, 0x95, + 0xb5, 0x98, 0x52, 0x0a, 0xd5, 0xbc, 0xcb, 0x37, 0x50, 0x3e, 0x06, 0x45, + 0xb8, 0x14, 0xd8, 0x76, 0xb8, 0xdf, 0x40, 0x51, 0x04, 0x41, 0xad, 0x8c, + 0xe3, 0xad, 0xb1, 0x1b, 0xb8, 0x8c, 0x4e, 0x72, 0x5a, 0x5e, 0x4a, 0x9e, + 0x07, 0x95, 0x29, 0x1d, 0x58, 0x58, 0x40, 0x23, 0xa7, 0xe1, 0xaf, 0x0e, + 0x38, 0xa9, 0x12, 0x79, 0x39, 0x30, 0x08, 0x61, 0x0b, 0x6f, 0x15, 0x8c, + 0x87, 0x8c, 0x7e, 0x21, 0xbf, 0xfb, 0xfe, 0xea, 0x77, 0xe1, 0x01, 0x9e, + 0x1e, 0x57, 0x81, 0xe8, 0xa4, 0x5f, 0x46, 0x26, 0x3d, 0x14, 0xe6, 0x0e, + 0x80, 0x58, 0xa8, 0x60, 0x7a, 0xdc, 0xe0, 0x4f, 0xac, 0x84, 0x57, 0xb1, + 0x37, 0xa8, 0xd6, 0x7c, 0xcd, 0xeb, 0x33, 0x70, 0x5d, 0x98, 0x3a, 0x21, + 0xfb, 0x4e, 0xec, 0xbd, 0x4a, 0x10, 0xca, 0x47, 0x49, 0x0c, 0xa4, 0x7e, + 0xaa, 0x5d, 0x43, 0x82, 0x18, 0xdd, 0xba, 0xf1, 0xca, 0xde, 0x33, 0x92, + 0xf1, 0x3d, 0x6f, 0xfb, 0x64, 0x42, 0xfd, 0x31, 0xe1, 0xbf, 0x40, 0xb0, + 0xc6, 0x04, 0xd1, 0xc4, 0xba, 0x4c, 0x95, 0x20, 0xa4, 0xbf, 0x97, 0xee, + 0xbd, 0x60, 0x92, 0x9a, 0xfc, 0xee, 0xf5, 0x5b, 0xba, 0xf5, 0x64, 0xe2, + 0xd0, 0xe7, 0x6c, 0xd7, 0xc5, 0x5c, 0x73, 0xa0, 0x82, 0xb9, 0x96, 0x12, + 0x0b, 0x83, 0x59, 0xed, 0xce, 0x24, 0x70, 0x70, 0x82, 0x68, 0x0d, 0x6f, + 0x67, 0xc6, 0xd8, 0x2c, 0x4a, 0xc5, 0xf3, 0x13, 0x44, 0x90, 0xa7, 0x4e, + 0xec, 0x37, 0xaf, 0x4b, 0x2f, 0x01, 0x0c, 0x59, 0xe8, 0x28, 0x43, 0xe2, + 0x58, 0x2f, 0x0b, 0x6b, 0x9f, 0x5d, 0xb0, 0xfc, 0x5e, 0x6e, 0xdf, 0x64, + 0xfb, 0xd3, 0x08, 0xb4, 0x71, 0x1b, 0xcf, 0x12, 0x50, 0x01, 0x9c, 0x9f, + 0x5a, 0x09, 0x02, 0x03, 0x01, 0x00, 0x01, 0x3a, 0x14, 0x6c, 0x69, 0x63, + 0x65, 0x6e, 0x73, 0x65, 0x2e, 0x77, 0x69, 0x64, 0x65, 0x76, 0x69, 0x6e, + 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x12, 0x80, 0x03, 0xae, 0x34, 0x73, 0x14, + 0xb5, 0xa8, 0x35, 0x29, 0x7f, 0x27, 0x13, 0x88, 0xfb, 0x7b, 0xb8, 0xcb, + 0x52, 0x77, 0xd2, 0x49, 0x82, 0x3c, 0xdd, 0xd1, 0xda, 0x30, 0xb9, 0x33, + 0x39, 0x51, 0x1e, 0xb3, 0xcc, 0xbd, 0xea, 0x04, 0xb9, 0x44, 0xb9, 0x27, + 0xc1, 0x21, 0x34, 0x6e, 0xfd, 0xbd, 0xea, 0xc9, 0xd4, 0x13, 0x91, 0x7e, + 0x6e, 0xc1, 0x76, 0xa1, 0x04, 0x38, 0x46, 0x0a, 0x50, 0x3b, 0xc1, 0x95, + 0x2b, 0x9b, 0xa4, 0xe4, 0xce, 0x0f, 0xc4, 0xbf, 0xc2, 0x0a, 0x98, 0x08, + 0xaa, 0xaf, 0x4b, 0xfc, 0xd1, 0x9c, 0x1d, 0xcf, 0xcd, 0xf5, 0x74, 0xcc, + 0xac, 0x28, 0xd1, 0xb4, 0x10, 0x41, 0x6c, 0xf9, 0xde, 0x88, 0x04, 0x30, + 0x1c, 0xbd, 0xb3, 0x34, 0xca, 0xfc, 0xd0, 0xd4, 0x09, 0x78, 0x42, 0x3a, + 0x64, 0x2e, 0x54, 0x61, 0x3d, 0xf0, 0xaf, 0xcf, 0x96, 0xca, 0x4a, 0x92, + 0x49, 0xd8, 0x55, 0xe4, 0x2b, 0x3a, 0x70, 0x3e, 0xf1, 0x76, 0x7f, 0x6a, + 0x9b, 0xd3, 0x6d, 0x6b, 0xf8, 0x2b, 0xe7, 0x6b, 0xbf, 0x0c, 0xba, 0x4f, + 0xde, 0x59, 0xd2, 0xab, 0xcc, 0x76, 0xfe, 0xb6, 0x42, 0x47, 0xb8, 0x5c, + 0x43, 0x1f, 0xbc, 0xa5, 0x22, 0x66, 0xb6, 0x19, 0xfc, 0x36, 0x97, 0x95, + 0x43, 0xfc, 0xa9, 0xcb, 0xbd, 0xbb, 0xfa, 0xfa, 0x0e, 0x1a, 0x55, 0xe7, + 0x55, 0xa3, 0xc7, 0xbc, 0xe6, 0x55, 0xf9, 0x64, 0x6f, 0x58, 0x2a, 0xb9, + 0xcf, 0x70, 0xaa, 0x08, 0xb9, 0x79, 0xf8, 0x67, 0xf6, 0x3a, 0x0b, 0x2b, + 0x7f, 0xdb, 0x36, 0x2c, 0x5b, 0xc4, 0xec, 0xd5, 0x55, 0xd8, 0x5b, 0xca, + 0xa9, 0xc5, 0x93, 0xc3, 0x83, 0xc8, 0x57, 0xd4, 0x9d, 0xaa, 0xb7, 0x7e, + 0x40, 0xb7, 0x85, 0x1d, 0xdf, 0xd2, 0x49, 0x98, 0x80, 0x8e, 0x35, 0xb2, + 0x58, 0xe7, 0x5d, 0x78, 0xea, 0xc0, 0xca, 0x16, 0xf7, 0x04, 0x73, 0x04, + 0xc2, 0x0d, 0x93, 0xed, 0xe4, 0xe8, 0xff, 0x1c, 0x6f, 0x17, 0xe6, 0x24, + 0x3e, 0x3f, 0x3d, 0xa8, 0xfc, 0x17, 0x09, 0x87, 0x0e, 0xc4, 0x5f, 0xba, + 0x82, 0x3a, 0x26, 0x3f, 0x0c, 0xef, 0xa1, 0xf7, 0x09, 0x3b, 0x19, 0x09, + 0x92, 0x83, 0x26, 0x33, 0x37, 0x05, 0x04, 0x3a, 0x29, 0xbd, 0xa6, 0xf9, + 0xb4, 0x34, 0x2c, 0xc8, 0xdf, 0x54, 0x3c, 0xb1, 0xa1, 0x18, 0x2f, 0x7c, + 0x5f, 0xff, 0x33, 0xf1, 0x04, 0x90, 0xfa, 0xca, 0x5b, 0x25, 0x36, 0x0b, + 0x76, 0x01, 0x5e, 0x9c, 0x5a, 0x06, 0xab, 0x8e, 0xe0, 0x2f, 0x00, 0xd2, + 0xe8, 0xd5, 0x98, 0x61, 0x04, 0xaa, 0xcc, 0x4d, 0xd4, 0x75, 0xfd, 0x96, + 0xee, 0x9c, 0xe4, 0xe3, 0x26, 0xf2, 0x1b, 0x83, 0xc7, 0x05, 0x85, 0x77, + 0xb3, 0x87, 0x32, 0xcd, 0xda, 0xbc, 0x6a, 0x6b, 0xed, 0x13, 0xfb, 0x0d, + 0x49, 0xd3, 0x8a, 0x45, 0xeb, 0x87, 0xa5, 0xf4, +}; + +static const unsigned char kServiceCertForUat[] = { + 0x08, 0x05, 0x12, 0xc5, 0x05, 0x0a, 0xbf, 0x02, 0x08, 0x03, 0x12, 0x10, + 0x28, 0x70, 0x34, 0x54, 0xc0, 0x08, 0xf6, 0x36, 0x18, 0xad, 0xe7, 0x44, + 0x3d, 0xb6, 0xc4, 0xc8, 0x18, 0x8b, 0xe7, 0xf9, 0x90, 0x05, 0x22, 0x8e, + 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb5, 0x21, + 0x12, 0xb8, 0xd0, 0x5d, 0x02, 0x3f, 0xcc, 0x5d, 0x95, 0xe2, 0xc2, 0x51, + 0xc1, 0xc6, 0x49, 0xb4, 0x17, 0x7c, 0xd8, 0xd2, 0xbe, 0xef, 0x35, 0x5b, + 0xb0, 0x67, 0x43, 0xde, 0x66, 0x1e, 0x3d, 0x2a, 0xbc, 0x31, 0x82, 0xb7, + 0x99, 0x46, 0xd5, 0x5f, 0xdc, 0x08, 0xdf, 0xe9, 0x54, 0x07, 0x81, 0x5e, + 0x9a, 0x62, 0x74, 0xb3, 0x22, 0xa2, 0xc7, 0xf5, 0xe0, 0x67, 0xbb, 0x5f, + 0x0a, 0xc0, 0x7a, 0x89, 0xd4, 0x5a, 0xea, 0x94, 0xb2, 0x51, 0x6f, 0x07, + 0x5b, 0x66, 0xef, 0x81, 0x1d, 0x0d, 0x26, 0xe1, 0xb9, 0xa6, 0xb8, 0x94, + 0xf2, 0xb9, 0x85, 0x79, 0x62, 0xaa, 0x17, 0x1c, 0x4f, 0x66, 0x63, 0x0d, + 0x3e, 0x4c, 0x60, 0x27, 0x18, 0x89, 0x7f, 0x5e, 0x1e, 0xf9, 0xb6, 0xaa, + 0xf5, 0xad, 0x4d, 0xba, 0x2a, 0x7e, 0x14, 0x17, 0x6d, 0xf1, 0x34, 0xa1, + 0xd3, 0x18, 0x5b, 0x5a, 0x21, 0x8a, 0xc0, 0x5a, 0x4c, 0x41, 0xf0, 0x81, + 0xef, 0xff, 0x80, 0xa3, 0xa0, 0x40, 0xc5, 0x0b, 0x09, 0xbb, 0xc7, 0x40, + 0xee, 0xdc, 0xd8, 0xf1, 0x4d, 0x67, 0x5a, 0x91, 0x98, 0x0f, 0x92, 0xca, + 0x7d, 0xdc, 0x64, 0x6a, 0x06, 0xad, 0xad, 0x51, 0x01, 0xf7, 0x4a, 0x0e, + 0x49, 0x8c, 0xc0, 0x1f, 0x00, 0x53, 0x2b, 0xac, 0x21, 0x78, 0x50, 0xbd, + 0x90, 0x5e, 0x90, 0x92, 0x36, 0x56, 0xb7, 0xdf, 0xef, 0xef, 0x42, 0x48, + 0x67, 0x67, 0xf3, 0x3e, 0xf6, 0x28, 0x3d, 0x4f, 0x42, 0x54, 0xab, 0x72, + 0x58, 0x93, 0x90, 0xbe, 0xe5, 0x58, 0x08, 0xf1, 0xd6, 0x68, 0x08, 0x0d, + 0x45, 0xd8, 0x93, 0xc2, 0xbc, 0xa2, 0xf7, 0x4d, 0x60, 0xa0, 0xc0, 0xd0, + 0xa0, 0x99, 0x3c, 0xef, 0x01, 0x60, 0x47, 0x03, 0x33, 0x4c, 0x36, 0x38, + 0x13, 0x94, 0x86, 0xbc, 0x9d, 0xaf, 0x24, 0xfd, 0x67, 0xa0, 0x7f, 0x9a, + 0xd9, 0x43, 0x02, 0x03, 0x01, 0x00, 0x01, 0x3a, 0x12, 0x73, 0x74, 0x61, + 0x67, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x63, 0x6f, 0x6d, 0x12, 0x80, 0x03, 0x98, 0x3e, 0x30, 0x35, 0x26, 0x75, + 0xf4, 0x0b, 0xa7, 0x15, 0xfc, 0x24, 0x9b, 0xda, 0xe5, 0xd4, 0xac, 0x72, + 0x49, 0xa2, 0x66, 0x65, 0x21, 0xe4, 0x36, 0x55, 0x73, 0x95, 0x29, 0x72, + 0x1f, 0xf8, 0x80, 0xe0, 0xaa, 0xef, 0xc5, 0xe2, 0x7b, 0xc9, 0x80, 0xda, + 0xea, 0xda, 0xbf, 0x3f, 0xc3, 0x86, 0xd0, 0x84, 0xa0, 0x2c, 0x82, 0x53, + 0x78, 0x48, 0xcc, 0x75, 0x3f, 0xf4, 0x97, 0xb0, 0x11, 0xa7, 0xda, 0x97, + 0x78, 0x8a, 0x00, 0xe2, 0xaa, 0x6b, 0x84, 0xcd, 0x7d, 0x71, 0xc0, 0x7a, + 0x48, 0xeb, 0xf6, 0x16, 0x02, 0xcc, 0xa5, 0xa3, 0xf3, 0x20, 0x30, 0xa7, + 0x29, 0x5c, 0x30, 0xda, 0x91, 0x5b, 0x91, 0xdc, 0x18, 0xb9, 0xbc, 0x95, + 0x93, 0xb8, 0xde, 0x8b, 0xb5, 0x0f, 0x0d, 0xed, 0xc1, 0x29, 0x38, 0xb8, + 0xe9, 0xe0, 0x39, 0xcd, 0xde, 0x18, 0xfa, 0x82, 0xe8, 0x1b, 0xb0, 0x32, + 0x63, 0x0f, 0xe9, 0x55, 0xd8, 0x5a, 0x56, 0x6c, 0xe1, 0x54, 0x30, 0x0b, + 0xf6, 0xd4, 0xc1, 0xbd, 0x12, 0x69, 0x66, 0x35, 0x6b, 0x28, 0x7d, 0x65, + 0x7b, 0x18, 0xce, 0x63, 0xd0, 0xef, 0xd4, 0x5f, 0xc5, 0x26, 0x9e, 0x97, + 0xea, 0xb1, 0x1c, 0xb5, 0x63, 0xe5, 0x56, 0x43, 0xb2, 0x6f, 0xf4, 0x9f, + 0x10, 0x9c, 0x21, 0x01, 0xaf, 0xca, 0xf3, 0x5b, 0x83, 0x2f, 0x28, 0x8f, + 0x0d, 0x9d, 0x45, 0x96, 0x0e, 0x25, 0x9e, 0x85, 0xfb, 0x5d, 0x24, 0xdb, + 0xd2, 0xcf, 0x82, 0x76, 0x4c, 0x5d, 0xd9, 0xbf, 0x72, 0x7e, 0xfb, 0xe9, + 0xc8, 0x61, 0xf8, 0x69, 0x32, 0x1f, 0x6a, 0xde, 0x18, 0x90, 0x5f, 0x4d, + 0x92, 0xf9, 0xa6, 0xda, 0x65, 0x36, 0xdb, 0x84, 0x75, 0x87, 0x1d, 0x16, + 0x8e, 0x87, 0x0b, 0xb2, 0x30, 0x3c, 0xf7, 0x0c, 0x6e, 0x97, 0x84, 0xc9, + 0x3d, 0x2d, 0xe8, 0x45, 0xad, 0x82, 0x62, 0xbe, 0x7e, 0x0d, 0x4e, 0x2e, + 0x4a, 0x07, 0x59, 0xce, 0xf8, 0x2d, 0x10, 0x9d, 0x25, 0x92, 0xc7, 0x24, + 0x29, 0xf8, 0xc0, 0x17, 0x42, 0xba, 0xe2, 0xb3, 0xde, 0xca, 0xdb, 0xc3, + 0x3c, 0x3e, 0x5f, 0x4b, 0xaf, 0x5e, 0x16, 0xec, 0xb7, 0x4e, 0xad, 0xba, + 0xfc, 0xb7, 0xc6, 0x70, 0x5f, 0x7a, 0x9e, 0x3b, 0x6f, 0x39, 0x40, 0x38, + 0x3f, 0x9c, 0x51, 0x16, 0xd2, 0x02, 0xa2, 0x0c, 0x92, 0x29, 0xee, 0x96, + 0x9c, 0x25, 0x19, 0x71, 0x83, 0x03, 0xb5, 0x0d, 0x01, 0x30, 0xc3, 0x35, + 0x2e, 0x06, 0xb0, 0x14, 0xd8, 0x38, 0x54, 0x0f, 0x8a, 0x0c, 0x22, 0x7c, + 0x00, 0x11, 0xe0, 0xf5, 0xb3, 0x8e, 0x4e, 0x29, 0x8e, 0xd2, 0xcb, 0x30, + 0x1e, 0xb4, 0x56, 0x49, 0x65, 0xf5, 0x5c, 0x5d, 0x79, 0x75, 0x7a, 0x25, + 0x0a, 0x4e, 0xb9, 0xc8, 0x4a, 0xb3, 0xe6, 0x53, 0x9f, 0x6b, 0x6f, 0xdf, + 0x56, 0x89, 0x9e, 0xa2, 0x99, 0x14, +}; + +} // namespace + +// Refer to the following in main modules. +// This level of indirection is present so new service certificates can be +// added and then selected for use at compile time or run time. + +namespace wvcdm_test_auth { + +const uint8_t* kServiceCert_Uat = kServiceCertForUat; +const uint8_t* kServiceCert_Staging = kServiceCertForStaging; + +const size_t kServiceCertSize_Uat = sizeof(kServiceCertForUat); +const size_t kServiceCertSize_Staging = sizeof(kServiceCertForStaging); + +} // namespace wvcdm_test_auth diff --git a/test/auth/test_service_cert.h b/test/auth/test_service_cert.h new file mode 100644 index 00000000..c08a63a2 --- /dev/null +++ b/test/auth/test_service_cert.h @@ -0,0 +1,21 @@ +// Copyright 2017 Google Inc. All Rights Reserved. + +// Service certificates. + +#ifndef TEST_AUTH_SERVICE_CERT_H_ +#define TEST_AUTH_SERVICE_CERT_H_ + +#include +#include + +namespace wvcdm_test_auth { + +extern const uint8_t* kServiceCert_Uat; +extern const uint8_t* kServiceCert_Staging; + +extern const size_t kServiceCertSize_Uat; +extern const size_t kServiceCertSize_Staging; + +} // namespace wvcdm_test_auth + +#endif // TEST_AUTH_SERVICE_CERT_H_ diff --git a/third_party/gmock.gyp b/third_party/gmock.gyp index b7c54b02..672a2d32 100644 --- a/third_party/gmock.gyp +++ b/third_party/gmock.gyp @@ -2,7 +2,20 @@ # This is for googletest release 1.8.0 { 'target_defaults': { - 'type': 'static_library', + # It seems that if one target uses -fPIC, then all targets will need that + # flag or else there will be linking errors. PIC means Position Independent + # Code and is needed to build the shared library version of liboemrypto.so. + # Similarly, -g is all or nothing. It turns on debugging symbols. + 'cflags': [ + '-g', + '-fPIC', + ], + 'cflags_cc': [ + '-std=gnu++98', + ], + 'ldflags': [ + '-fPIC', + ], 'include_dirs': [ 'googletest/googlemock', 'googletest/googlemock/include', @@ -15,38 +28,25 @@ 'googletest/googletest/include', ], }, - # These flags silence warnings that appear in gtest/gmock upstream. - # We will not maintain a divergent copy of gmock to fix them. - 'cflags': [ - # Ignore unknown warning options, to support both gcc & clang at once. - '-Wno-unknown-warning-option', - ], - 'cflags_cc': [ - '-Wno-sign-compare', - #'-Wno-unused-const-variable', - #'-Wno-unused-function', - #'-Wno-unused-parameter', - #'-Wno-shadow', - '-Wno-conversion', - '-Wno-sign-conversion', - #'-Wno-useless-cast', - ], }, 'targets': [ { 'target_name': 'gmock', + 'type': 'static_library', 'sources': [ 'googletest/googlemock/src/gmock-all.cc', ], }, { 'target_name': 'gmock_main', + 'type': 'static_library', 'sources': [ 'googletest/googlemock/src/gmock_main.cc', ], }, { 'target_name': 'gtest', + 'type': 'static_library', 'sources': [ 'googletest/googletest/src/gtest-all.cc', ], diff --git a/third_party/gyp/common.py b/third_party/gyp/common.py index 0f01ec64..1b245ec7 100644 --- a/third_party/gyp/common.py +++ b/third_party/gyp/common.py @@ -429,8 +429,10 @@ def GetFlavor(params): return 'netbsd' if sys.platform.startswith('aix'): return 'aix' + if sys.platform.startswith('zos'): + return 'zos' if sys.platform.startswith('os390'): - return 'os390' + return 'zos' return 'linux' diff --git a/third_party/gyp/generator/make.py b/third_party/gyp/generator/make.py index e8890474..fb4f9185 100644 --- a/third_party/gyp/generator/make.py +++ b/third_party/gyp/generator/make.py @@ -240,7 +240,7 @@ quiet_cmd_alink_thin = AR($(TOOLSET)) $@ cmd_alink_thin = rm -f $@ && $(AR.$(TOOLSET)) crsT $@ $(filter %.o,$^) quiet_cmd_link = LINK($(TOOLSET)) $@ -cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) +cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS) quiet_cmd_solink = SOLINK($(TOOLSET)) $@ cmd_solink = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS) $(LIBS) -Wl,DLL @@ -313,7 +313,7 @@ CFLAGS.host ?= $(CPPFLAGS_host) $(CFLAGS_host) CXX.host ?= %(CXX.host)s CXXFLAGS.host ?= $(CPPFLAGS_host) $(CXXFLAGS_host) LINK.host ?= %(LINK.host)s -LDFLAGS.host ?= +LDFLAGS.host ?= $(LDFLAGS_host) AR.host ?= %(AR.host)s # Define a dir function that can handle spaces. @@ -2065,7 +2065,7 @@ def GenerateOutput(target_list, target_dicts, data, params): header_params.update({ 'link_commands': LINK_COMMANDS_ANDROID, }) - elif flavor == 'os390': + elif flavor == 'zos': copy_archive_arguments = '-fPR' makedep_arguments = '-qmakedep=gcc' header_params.update({ diff --git a/third_party/gyp/generator/ninja.py b/third_party/gyp/generator/ninja.py index 3974a041..6de87b70 100644 --- a/third_party/gyp/generator/ninja.py +++ b/third_party/gyp/generator/ninja.py @@ -1195,7 +1195,10 @@ class NinjaWriter(object): is_executable = spec['type'] == 'executable' # The ldflags config key is not used on mac or win. On those platforms # linker flags are set via xcode_settings and msvs_settings, respectively. - env_ldflags = os.environ.get('LDFLAGS', '').split() + if self.toolset == 'target': + env_ldflags = os.environ.get('LDFLAGS', '').split() + elif self.toolset == 'host': + env_ldflags = os.environ.get('LDFLAGS_host', '').split() if self.flavor == 'mac': ldflags = self.xcode_settings.GetLdflags(config_name, self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']), @@ -2321,7 +2324,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params, 'copy', description='COPY $in $out', command='%s gyp-win-tool recursive-mirror $in $out' % sys.executable) - elif flavor == 'os390': + elif flavor == 'zos': master_ninja.rule( 'copy', description='COPY $in $out', diff --git a/third_party/protobuf.gypi b/third_party/protobuf.gypi index c61d3fea..59bfaa6b 100644 --- a/third_party/protobuf.gypi +++ b/third_party/protobuf.gypi @@ -32,35 +32,40 @@ ], }, 'target_defaults': { - # These flags silence warnings that appear in protobuf upstream. - # We will not maintain a divergent copy of protobuf to fix them. - 'cflags': [ - # Ignore unknown warning options, to support both gcc & clang at once. - '-Wno-unknown-warning-option', - # GCC: - '-Wno-sign-compare', - '-Wno-unused-local-typedefs', - # Clang: - '-Wno-unused-const-variable', - '-Wno-unused-function', - ], - 'cflags_cc': [ - '-frtti', - '-Wno-sign-compare', - '-Wno-unused-const-variable', - '-Wno-unused-function', - '-Wno-unused-parameter', - '-Wno-shadow', - '-Wno-tautological-undefined-compare', - '-Wno-conversion', - '-Wno-useless-cast', - ], }, 'targets': [ { 'target_name': 'protobuf_lite', 'type': 'static_library', 'toolsets': ['target'], + # These flags silence warnings that appear in protobuf upstream. + # We will not maintain a divergent copy of protobuf to fix them. + 'cflags=': [ + # Ignore unknown warning options, to support both gcc & clang at once. + '-Wno-unknown-warning-option', + # GCC: + '-Wno-sign-compare', + '-Wno-unused-local-typedefs', + # Clang: + '-Wno-unused-const-variable', + '-Wno-unused-function', + '-std=gnu++98', + ], + 'cflags_cc=': [ + # Ignore unknown warning options, to support both gcc & clang at once. + '-Wno-unknown-warning-option', + '-Wno-shadow', + # GCC: + '-Wno-sign-compare', + '-Wno-unused-local-typedefs', + '-Wno-ignored-qualifiers', + '-Wno-return-type', + '-Wno-maybe-uninitialized', + # Clang: + '-Wno-unused-const-variable', + '-Wno-unused-function', + '-std=gnu++98', + ], 'sources': [ '<@(protobuf_lite_sources)', ], @@ -79,6 +84,31 @@ 'type': 'static_library', 'toolsets': ['host'], 'includes': ['xcode_host.gypi'], # xcode workaround + # These flags silence warnings that appear in protobuf upstream. + # We will not maintain a divergent copy of protobuf to fix them. + 'cflags=': [ + # Ignore unknown warning options, to support both gcc & clang at once. + '-Wno-unknown-warning-option', + # GCC: + '-Wno-sign-compare', + '-Wno-unused-local-typedefs', + # Clang: + '-Wno-unused-const-variable', + '-Wno-unused-function', + '-Wno-maybe-uninitialized', + ], + 'cflags_cc=': [ + # Ignore unknown warning options, to support both gcc & clang at once. + '-Wno-unknown-warning-option', + '-Wno-shadow', + # GCC: + '-Wno-sign-compare', + '-Wno-unused-local-typedefs', + # Clang: + '-Wno-unused-const-variable', + '-Wno-unused-function', + '-Wno-misleading-indentation', + ], 'sources': [ '<@(protobuf_lite_sources)', @@ -118,6 +148,30 @@ 'type': 'executable', 'toolsets': ['host'], 'includes': ['xcode_host.gypi'], # xcode workaround + + # These flags silence warnings that appear in protobuf upstream. + # We will not maintain a divergent copy of protobuf to fix them. + 'cflags=': [ + # Ignore unknown warning options, to support both gcc & clang at once. + '-Wno-unknown-warning-option', + # GCC: + '-Wno-sign-compare', + '-Wno-unused-local-typedefs', + # Clang: + '-Wno-unused-const-variable', + '-Wno-unused-function', + ], + 'cflags_cc=': [ + # Ignore unknown warning options, to support both gcc & clang at once. + '-Wno-unknown-warning-option', + '-Wno-shadow', + # GCC: + '-Wno-sign-compare', + '-Wno-unused-local-typedefs', + # Clang: + '-Wno-unused-const-variable', + '-Wno-unused-function', + ], 'sources': [ '<(protobuf_source)/src/google/protobuf/compiler/code_generator.cc', '<(protobuf_source)/src/google/protobuf/compiler/command_line_interface.cc',