From 28ec8548c69d47f11ed03c1912267e1ef1673036 Mon Sep 17 00:00:00 2001 From: Matt Feddersen Date: Thu, 28 Mar 2024 19:15:22 -0700 Subject: [PATCH] Source release 18.5.0 --- CHANGELOG.md | 105 +++++- README.md | 88 ++++- cdm/cdm.gyp | 58 +++- cdm/cdm_unittests.gyp | 3 + cdm/core_unittests.gypi | 2 + cdm/include/cdm.h | 15 +- cdm/include/cdm_version.h | 2 +- cdm/platform_properties.gypi | 21 ++ cdm/src/cdm.cpp | 37 ++- cdm/test/cdm_reboot_test_main.cpp | 27 +- cdm/test/cdm_test.cpp | 56 ++-- cdm/test/perf_test.cpp | 8 + cdm/test/perf_test_dynamic.cpp | 2 +- cdm/test/test_host.cpp | 16 + cdm/test/test_host.h | 4 + core/include/cdm_engine.h | 5 + core/include/cdm_session.h | 7 +- core/include/certificate_provisioning.h | 17 +- core/include/content_key_session.h | 7 +- core/include/crypto_session.h | 35 +- core/include/key_session.h | 3 +- core/include/license.h | 13 +- core/include/license_key_status.h | 2 + core/include/policy_engine.h | 7 + core/include/privacy_crypto.h | 2 +- core/include/wv_cdm_constants.h | 11 +- core/include/wv_cdm_types.h | 8 + core/src/Android.bp | 2 +- core/src/cdm_engine.cpp | 59 +++- core/src/cdm_session.cpp | 103 ++++-- core/src/cdm_usage_table.cpp | 38 ++- core/src/certificate_provisioning.cpp | 125 ++++++-- core/src/client_identification.cpp | 10 +- core/src/content_key_session.cpp | 32 +- core/src/crypto_session.cpp | 300 ++++++++++++++++-- core/src/device_files.proto | 4 + core/src/license.cpp | 14 +- core/src/license_key_status.cpp | 25 +- core/src/license_protocol.proto | 30 ++ core/src/oemcrypto_adapter_static.cpp | 52 +-- core/src/oemcrypto_embedded_cert_stubs.cpp | 12 + core/src/oemcrypto_ota_stubs.cpp | 8 + core/src/policy_engine.cpp | 5 +- core/src/privacy_crypto_apple.cpp | 9 +- core/src/privacy_crypto_boringssl.cpp | 24 +- core/src/privacy_crypto_dummy.cpp | 4 +- core/src/service_certificate.cpp | 2 +- core/src/system_id_extractor.cpp | 2 + core/src/wv_cdm_types.cpp | 12 + core/test/cdm_engine_test.cpp | 17 +- core/test/cdm_session_unittest.cpp | 5 + .../certificate_provisioning_unittest.cpp | 293 ++++++++++++++++- core/test/core_integration_test.cpp | 279 ++++++++++++++++ core/test/crypto_session_unittest.cpp | 6 + core/test/duration_use_case_test.cpp | 21 ++ core/test/generic_crypto_unittest.cpp | 34 +- core/test/http_socket.cpp | 41 ++- core/test/http_socket.h | 5 +- core/test/keybox_ota_test.cpp | 7 + core/test/license_holder.cpp | 14 + core/test/license_holder.h | 3 + core/test/license_unittest.cpp | 83 +++-- core/test/message_dumper.cpp | 173 +++++++--- core/test/policy_engine_unittest.cpp | 11 +- core/test/policy_integration_test.cpp | 8 +- core/test/provisioning_holder.cpp | 36 +-- core/test/provisioning_holder.h | 5 + core/test/reboot_test.cpp | 223 +------------ core/test/reboot_test.h | 11 - core/test/test_base.cpp | 6 + metrics/src/Android.bp | 2 +- oemcrypto/include/OEMCryptoCENC.h | 236 +++++++++++--- oemcrypto/odk/Android.bp | 12 +- oemcrypto/odk/include/OEMCryptoCENCCommon.h | 5 +- oemcrypto/odk/include/core_message_features.h | 4 +- oemcrypto/odk/include/odk_structs.h | 4 +- oemcrypto/odk/src/core_message_features.cpp | 6 +- oemcrypto/odk/src/odk.c | 103 ++++-- oemcrypto/odk/src/odk_assert.h | 2 +- oemcrypto/odk/src/odk_structs_priv.h | 2 + oemcrypto/odk/src/odk_timer.c | 25 +- oemcrypto/odk/test/odk_test.cpp | 46 ++- oemcrypto/test/GEN_api_lock_file.c | 9 + .../oemcrypto_entitled_key_session_fuzz.cc | 136 ++++++++ .../test/fuzz_tests/oemcrypto_fuzz_helper.cc | 1 - .../fuzz_tests/oemcrypto_opk_fuzztests.gyp | 12 +- .../partner_oemcrypto_fuzztests.gyp | 12 +- oemcrypto/test/oec_device_features.cpp | 11 +- oemcrypto/test/oec_session_util.cpp | 74 +++-- oemcrypto/test/oec_session_util.h | 17 +- oemcrypto/test/oemcrypto_basic_test.cpp | 11 +- oemcrypto/test/oemcrypto_cast_test.cpp | 126 +++++--- oemcrypto/test/oemcrypto_cast_test.h | 37 +-- .../oemcrypto_corpus_generator_helper.cpp | 6 +- .../test/oemcrypto_corpus_generator_helper.h | 6 +- oemcrypto/test/oemcrypto_decrypt_test.cpp | 20 +- oemcrypto/test/oemcrypto_decrypt_test.h | 4 + oemcrypto/test/oemcrypto_license_test.cpp | 8 +- oemcrypto/test/oemcrypto_license_test.h | 8 +- .../test/oemcrypto_provisioning_test.cpp | 213 +++++++++++-- oemcrypto/test/oemcrypto_provisioning_test.h | 190 +++++++---- oemcrypto/test/oemcrypto_security_test.cpp | 153 ++++++++- oemcrypto/test/oemcrypto_test.cpp | 285 ++++++++++++++++- oemcrypto/test/oemcrypto_test_android.cpp | 13 + oemcrypto/test/oemcrypto_usage_table_test.cpp | 55 +++- oemcrypto/test/oemcrypto_usage_table_test.h | 3 + third_party/boringssl/boringssl.gyp | 19 +- third_party/generate_proto_cc.gyp | 43 +++ third_party/protobuf.gyp | 2 + 109 files changed, 3623 insertions(+), 1012 deletions(-) create mode 100644 core/src/oemcrypto_embedded_cert_stubs.cpp create mode 100644 core/test/core_integration_test.cpp create mode 100644 oemcrypto/test/fuzz_tests/oemcrypto_entitled_key_session_fuzz.cc create mode 100644 third_party/generate_proto_cc.gyp diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f619380..92894eaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,109 @@ [TOC] +## 18.5.0 (2024-03-28) + +Note: Releases v18.2-18.4 were skipped so that this release's version number +matches the OEMCrypto version. + +CE CDM v18.5.0 includes all changes from CE CDM v17.3.0 and v18.1.0. + +**It is strongly recommended** for partners to update from v18.1 to v18.5 to +address two major bugs in the CE CDM code which could result in lost offline +licenses or app crashes. See _Bug Fixes_ for 18.5.0 and 17.3.0 for details. + +## Features + + - Supports up to OEMCrypto v18.5, including new OEMCrypto tests introduced + since OEMCrypto v18.1. + - Added support for Cast provisioning 4.0 + - Cast uses a slightly different provisioning 4.0 protocol compared + to non-Cast cases. The additions to the protocol are internal + to the library, and do not require API changes for CE CDM users + - Additional tests are included, these tests will automatically + skip for device which do not use provisioning 4.0. + - Improved performance for unencrypted data provided via `Cdm::decrypt()`. + - Some apps are known to always use decrypt calls, even if parts of the + data are completely unencrypted. The CDM will now check if samples/ + sub-samples of decryption batch data can skip the decryption stage + and instead directly copied to the secure output. + - Various minor performance improvements + - Reduced internal data copying. + - Test runtime improvements: + - Skipped set up for unsupported features. + - Improved test data storage management. + - Removed unused internal testing fixtures. + - Compile time improvements by better dependency checks for third-party + libraries. + - CE CDM now compiles clean with `-Wunused-parameter`. + - Added support for new provisioning flow for baked-in certificates + on devices using Widevine's embedded device certificate. + - Affected partners should check the L3 OEMCrypto documentation for + details. + - Added new tests for CE CDM and OEMCrypto v18.5 features. + +### Bug Fixes + + - Fixed major issue with loss of offline licenses when stored license count + exceeds OEMCrypto's usage table size limit. + - Issue only affects users with 300 or more downloaded offline licenses + across all apps. + - Fixed minor issue with tests which were generating many warnings from + expected behavior. + - These warning did not cause the tests to fail, but created a lot of noise + when trying to diagnose other failures + +## 17.3.0 (2024-03-28) + +Note: Release v17.2 was skipped so that this release's version number matches +the OEMCrypto version. + +CE CDM v17.3.0 includes all changes from CE CDM v17.1.2. + +**It is strongly recommended** for partners to update from v17.1 to v17.3 to +address a major bug in the CE CDM code which could result in app crashes. See +_Bug Fixes_ for details. + +### Features + + - Supports OEMCrypto v17.3.0, including new OEMCrypto tests introduced in + v17.3.0. + - Added additional logging when license request fails, to help diagnose what + went wrong. + - Improved support for HDCP v1.0-1.4 version. + - `Cdm::getStatusForHdcpVersion()` can now accept a specific HDCP 1.x minor + version (specifically 1.0, 1.1, 1.2, 1.3, or 1.4) when checking if + content can be decrypted. + - The legacy behavior is still available via `Cdm::HdcpVersion::kHdcp1_x`. + - See _Bug Fixes_ note about related bug in previous versions. + +### Bug Fixes + + - Fixed major issue with memory corruption when apps misuse the API by making + certain key operations before license is loaded. + - Specifically, if an app generates a license request via + `Cdm::generateRequest()`, then calls `Cdm::remove()` before + `Cdm::update()` with the license response it can cause the CDM to crash. + - Fixed mistaken test failures when the CE CDM and OEMCrypto versions were not + identical. + - Fixed an issue where the OEMCrypto tests were enforcing v18 device ID + requirements on v17 integrations by mistake. + - Fixed an issue with DRM certificate serial number stability for + provisioning 4.0 devices. + - This only affected very specific devices when going through their + specific factory reset process. + - Added additional tests to catch future, similar errors. + - Fixed an issue with `Cdm::getKeyStatuses()` reporting error for certain + licenses which use specific HDCP v1.x minor versions. + - Fixed an issue with URL parsing in test code for renewal requests + - Certain tests used special URL parameters which were not properly + accounted for in the CE CDM's test utility code; this caused tests + to fail on otherwise working devices. + - Fixed the CAS tests sometimes not being skipped on non-CAS-supporting + devices. + - Removed tests that explicitly tested against Widevine's staging + provisioning servers. + ## 18.1.0 (2023-06-23) ### Features: @@ -225,7 +328,7 @@ Features: are unacceptable to you. The sample `x86-64` platform does not enable ASan or UBSan for release builds. - To maximize the benefit of UBSan, the sample `x86-64` platform now turns - on RTTI for debug builds. The Widvine CE CDM does not use RTTI and will + on RTTI for debug builds. The Widevine CE CDM does not use RTTI and will still build correctly without it. Feel free to turn it off if your platform does not support RTTI. The sample `x86-64` platform turns off RTTI for release builds. diff --git a/README.md b/README.md index 41d8d7bd..60c17bfe 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Widevine CE CDM 18.1.0 +# Widevine CE CDM 18.5.0 -Released 2023-06-23 +Released 2024-03-28 ## Getting Started @@ -10,10 +10,87 @@ following to learn more about the contents of this project and how to use them: The [Widevine Developer Site][wv-devsite] documents the CDM API and describes how to integrate the CDM into a system. -[CHANGELOG.md][changelog] lists the major changes for each release. +## New in v18.5.0 -[wv-devsite]: https://developers.google.com/widevine/drm/client/ce-cdm -[changelog]: ./CHANGELOG.md +Note: Releases v18.2-18.4 were skipped so that this release's version number +matches the OEMCrypto version. + +**It is strongly recommended** for partners to update from v18.1 to v18.5 to +address two major bugs in the CE CDM code which could result in lost offline +licenses or app crashes. See _Bug Fixes_ for details. + +## Features + + - Supports up to OEMCrypto v18.5, including new OEMCrypto tests introduced + since OEMCrypto v18.1. + - Added additional logging when license request fails, to help diagnose what + went wrong. + - Improved support for HDCP v1.0-1.4 version. + - `Cdm::getStatusForHdcpVersion()` can now accept a specific HDCP 1.x minor + version (specifically 1.0, 1.1, 1.2, 1.3, or 1.4) when checking if + content can be decrypted. + - The legacy behavior is still available via `Cdm::HdcpVersion::kHdcp1_x`. + - See _Bug Fixes_ note about related bug in previous versions. + - Added support for Cast provisioning 4.0 + - Cast uses a slightly different provisioning 4.0 protocol compared + to non-Cast cases. The additions to the protocol are internal + to the library, and do not require API changes for CE CDM users + - Additional tests are included, these tests will automatically + skip for device which do not use provisioning 4.0. + - Improved performance for unencrypted data provided via `Cdm::decrypt()`. + - Some apps are known to always use decrypt calls, even if parts of the + data are completely unencrypted. The CDM will now check if samples/ + sub-samples of decryption batch data can skip the decryption stage + and instead directly copied to the secure output. + - Various minor performance improvements + - Reduced internal data copying. + - Test runtime improvements: + - Skipped set up for unsupported features. + - Improved test data storage management. + - Removed unused internal testing fixtures. + - Compile time improvements by better dependency checks for third-party + libraries. + - CE CDM now compiles clean with `-Wunused-parameter`. + - Added support for new provisioning flow for baked-in certificates + on devices using Widevine's embedded device certificate. + - Affected partners should check the L3 OEMCrypto documentation for + details. + - Added new tests for CE CDM and OEMCrypto v18.5 features. + +### Bug Fixes + + - Fixed major issue with loss of offline licenses when stored license count + exceeds OEMCrypto's usage table size limit. + - Issue only affects users with 300 or more downloaded offline licenses + across all apps. + - Fixed major issue with memory corruption when apps misuse the API by making + certain key operations before license is loaded. + - Specifically, if an app generates a license request via + `Cdm::generateRequest()`, then calls `Cdm::remove()` before + `Cdm::update()` with the license response it can cause the CDM to crash. + - Fixed mistaken test failures when the CE CDM and OEMCrypto versions were not + identical. + - Fixed an issue with DRM certificate serial number stability for + provisioning 4.0 devices. + - This only affected very specific devices when going through their + specific factory reset process. + - Added additional tests to catch future, similar errors. + - Fixed an issue with `Cdm::getKeyStatuses()` reporting error for certain + licenses which use specific HDCP v1.x minor versions. + - Fixed an issue with URL parsing in test code for renewal requests + - Certain tests used special URL parameters which were not properly + accounted for in the CE CDM's test utility code; this caused tests + to fail on otherwise working devices. + - Fixed the CAS tests sometimes not being skipped on non-CAS-supporting + devices. + - Removed tests that explicitly tested against Widevine's staging + provisioning servers. + - Fixed minor issue with tests which were generating many warnings from + expected behavior. + - These warning did not cause the tests to fail, but created a lot of noise + when trying to diagnose other failures + +[CHANGELOG.md](./CHANGELOG.md) lists the major changes for each past release. ## Contains No OEMCrypto @@ -26,4 +103,5 @@ OEMCrypto implementation, the OEMCrypto Porting Kit. (OPK) If you are not an OEMCrypto implementer, then you will need to get an OEMCrypto implementation from your SoC manufacturer before you can use the CE CDM. +[wv-devsite]: https://developers.google.com/widevine/drm/client/ce-cdm [oec-repo]: https://widevine-partner.googlesource.com/oemcrypto/ diff --git a/cdm/cdm.gyp b/cdm/cdm.gyp index 6fa02c58..7798b56f 100644 --- a/cdm/cdm.gyp +++ b/cdm/cdm.gyp @@ -15,6 +15,7 @@ 'variables': { 'has_dual_key%': 'false', 'embedded_cert%': '', + 'support_embedded_cert_functions%': 'false', }, 'targets': [ { @@ -23,12 +24,20 @@ 'type': 'static_library', 'standalone_static_library': 1, 'hard_dependency': 1, - 'includes': ['../third_party/protoc.gypi'], + 'includes': ['../third_party/disable_warnings.gypi'], + 'dependencies': [ '<(proto_gen_gyp_path):generate_license_protocol' ], 'sources': [ - '../core/src/license_protocol.proto', + '<(proto_cc_dir)/license_protocol.pb.cc' ], - 'variables': { - 'proto_in_dir': '../core/src', + 'include_dirs': [ + '<(proto_cc_dir)', + '<(DEPTH)/third_party/protobuf/src' + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(proto_cc_dir)', + '<(DEPTH)/third_party/protobuf/src' + ] }, }, { @@ -37,10 +46,20 @@ 'type': 'static_library', 'standalone_static_library': 1, 'hard_dependency': 1, - 'includes': ['../third_party/protoc.gypi'], - 'sources': ['../core/src/device_files.proto',], - 'variables': { - 'proto_in_dir': '../core/src', + 'includes': ['../third_party/disable_warnings.gypi'], + 'dependencies': [ '<(proto_gen_gyp_path):generate_device_files' ], + 'sources': [ + '<(proto_cc_dir)/device_files.pb.cc' + ], + 'include_dirs': [ + '<(proto_cc_dir)', + '<(DEPTH)/third_party/protobuf/src' + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(proto_cc_dir)', + '<(DEPTH)/third_party/protobuf/src' + ] }, }, { @@ -49,10 +68,20 @@ 'type': 'static_library', 'standalone_static_library': 1, 'hard_dependency': 1, - 'includes': ['../third_party/protoc.gypi'], - 'sources': ['../metrics/src/wv_metrics.proto',], - 'variables': { - 'proto_in_dir': '../metrics/src', + 'includes': ['../third_party/disable_warnings.gypi'], + 'dependencies': [ '<(proto_gen_gyp_path):generate_metrics_proto' ], + 'sources': [ + '<(proto_cc_dir)/wv_metrics.pb.cc' + ], + 'include_dirs': [ + '<(proto_cc_dir)', + '<(DEPTH)/third_party/protobuf/src' + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(proto_cc_dir)', + '<(DEPTH)/third_party/protobuf/src' + ] }, }, { @@ -178,6 +207,11 @@ '../core/src/oemcrypto_ota_stubs.cpp', ], }], + ['support_embedded_cert_functions=="false"', { + 'sources': [ + '../core/src/oemcrypto_embedded_cert_stubs.cpp', + ], + }], ], }, # widevine_cdm_core target { diff --git a/cdm/cdm_unittests.gyp b/cdm/cdm_unittests.gyp index 5420a222..3ee5e1a3 100644 --- a/cdm/cdm_unittests.gyp +++ b/cdm/cdm_unittests.gyp @@ -126,8 +126,10 @@ '../core/test/license_request.cpp', '../core/test/url_request.cpp', '../util/src/string_conversions.cpp', + '../util/src/string_format.cpp', '../util/test/test_sleep.cpp', 'src/log.cpp', + 'src/logger_global.cpp', 'test/perf_test.cpp', 'test/perf_test_dynamic.cpp', 'test/test_host.cpp', @@ -136,6 +138,7 @@ 'include_dirs': [ '../core/include', '../core/test', + '../oemcrypto/include', '../util/include', '../util/test', 'include', diff --git a/cdm/core_unittests.gypi b/cdm/core_unittests.gypi index 95941b1c..c3abc38a 100644 --- a/cdm/core_unittests.gypi +++ b/cdm/core_unittests.gypi @@ -11,7 +11,9 @@ '../core/test/cdm_engine_metrics_decorator_unittest.cpp', '../core/test/cdm_session_unittest.cpp', '../core/test/cdm_usage_table_unittest.cpp', + '../core/test/core_integration_test.cpp', '../core/test/config_test_env.cpp', + '../core/test/core_integration_test.cpp', '../core/test/certificate_provisioning_unittest.cpp', '../core/test/crypto_session_unittest.cpp', '../core/test/device_files_unittest.cpp', diff --git a/cdm/include/cdm.h b/cdm/include/cdm.h index dc81eba8..6c321940 100644 --- a/cdm/include/cdm.h +++ b/cdm/include/cdm.h @@ -137,11 +137,16 @@ class CDM_EXPORT Cdm : public ITimerClient { // For ease of comparison, these values are kept in ascending order by version // number. enum HdcpVersion : int32_t { - kHdcp1_x = 0, - kHdcp2_0 = 1, - kHdcp2_1 = 2, - kHdcp2_2 = 3, - kHdcp2_3 = 4, + kHdcp1_x = 0, // Any 1.x version + kHdcp1_0 = 100, + kHdcp1_1 = 101, + kHdcp1_2 = 102, + kHdcp1_3 = 103, + kHdcp1_4 = 104, + kHdcp2_0 = 200, + kHdcp2_1 = 201, + kHdcp2_2 = 202, + kHdcp2_3 = 203 }; // Permissible usages for a key. Returned as a set of flags; multiple diff --git a/cdm/include/cdm_version.h b/cdm/include/cdm_version.h index f57cda42..0eba3f09 100644 --- a/cdm/include/cdm_version.h +++ b/cdm/include/cdm_version.h @@ -10,7 +10,7 @@ # define CDM_VERSION_MAJOR 18 #endif #ifndef CDM_VERSION_MINOR -# define CDM_VERSION_MINOR 1 +# define CDM_VERSION_MINOR 5 #endif #ifndef CDM_VERSION_PATCH # define CDM_VERSION_PATCH 0 diff --git a/cdm/platform_properties.gypi b/cdm/platform_properties.gypi index 05443cd3..046586fb 100644 --- a/cdm/platform_properties.gypi +++ b/cdm/platform_properties.gypi @@ -193,5 +193,26 @@ 'protoc_bin%': '', 'protobuf_lib_path%': '', 'protoc_host_path%': '', + + # Protobuf Generation and Build configurations: + # + # These options allow for different build steps to be specified for proto + # cc file generation than the default step. This can be used to customize + # cc file generation or add additional cc file processing steps before + # proto cc files are built. + # + # The directory where proto cc files that will be compiled are located. + 'proto_cc_dir%': '<(SHARED_INTERMEDIATE_DIR)/protoc_out', + + # The directory where proto cc files generated from proto files will be + # located. + 'proto_gen_dir%': '<(SHARED_INTERMEDIATE_DIR)/protoc_out', + + # Path to the GYP file that contains the proto cc file generation targets. + # This file must provide the following targets: + # - generate_license_protocol + # - generate_device_files + # - generate_metrics_proto + 'proto_gen_gyp_path%': '<(DEPTH)/third_party/generate_proto_cc.gyp', }, # variables } diff --git a/cdm/src/cdm.cpp b/cdm/src/cdm.cpp index 3ad12092..e02ef2a1 100644 --- a/cdm/src/cdm.cpp +++ b/cdm/src/cdm.cpp @@ -36,11 +36,6 @@ #include "cdm_version.h" #include "properties_ce.h" -#ifdef HAS_EMBEDDED_CERT -extern uint8_t kDeviceCert[]; -extern size_t kDeviceCertSize; -#endif - namespace widevine { #ifdef HAS_EMBEDDED_CERT @@ -151,6 +146,8 @@ class ReadOnlyStorage final : public Cdm::IStorage { // TODO(b/148693106): Once we have resolved the bugs causing the CDM to // erroneously write to read-only files, change this to an error instead of // a silent failure. + (void)name; + (void)data; return true; } @@ -162,6 +159,7 @@ class ReadOnlyStorage final : public Cdm::IStorage { // TODO(b/148693106): Once we have resolved the bugs causing the CDM to // erroneously write to read-only files, change this to an error instead of // a silent failure. + (void)name; return true; } @@ -576,7 +574,21 @@ Cdm::Status CdmImpl::getStatusForHdcpVersion(Cdm::HdcpVersion hdcp, result = ConvertHdcpLevel(query_value, &max_hdcp); if (result != kSuccess) return result; - *key_status = (hdcp <= max_hdcp ? Cdm::kUsable : Cdm::kOutputRestricted); + if (hdcp == kHdcp1_x && max_hdcp == kHdcp1_x) { + // Legacy case. + *key_status = Cdm::kUsable; + } else if (hdcp == kHdcp1_x && + (max_hdcp >= kHdcp1_0 && max_hdcp <= kHdcp1_4)) { + // CDM app is using legacy 1.x value, but OEMCrypto is using newer + // value. Assume usable. + *key_status = Cdm::kUsable; + } else if (max_hdcp == kHdcp1_x && (hdcp >= kHdcp1_0 && hdcp <= kHdcp1_4)) { + // CDM app is using new 1.x value, but OEMCrypto is using legacy + // value. Assume usable. + *key_status = Cdm::kUsable; + } else { + *key_status = (hdcp <= max_hdcp ? Cdm::kUsable : Cdm::kOutputRestricted); + } } return kSuccess; } @@ -1355,8 +1367,19 @@ CdmSigningAlgorithm CdmImpl::ConvertSigningAlgorithm( Cdm::Status CdmImpl::ConvertHdcpLevel(const std::string& query_value, Cdm::HdcpVersion* result) { - if (query_value == QUERY_VALUE_HDCP_V1) { + if (query_value == QUERY_VALUE_HDCP_V1_X) { + // Generic HDCP level *result = kHdcp1_x; + } else if (query_value == QUERY_VALUE_HDCP_V1_0) { + *result = kHdcp1_0; + } else if (query_value == QUERY_VALUE_HDCP_V1_1) { + *result = kHdcp1_1; + } else if (query_value == QUERY_VALUE_HDCP_V1_2) { + *result = kHdcp1_2; + } else if (query_value == QUERY_VALUE_HDCP_V1_3) { + *result = kHdcp1_3; + } else if (query_value == QUERY_VALUE_HDCP_V1_4) { + *result = kHdcp1_4; } else if (query_value == QUERY_VALUE_HDCP_V2_0) { *result = kHdcp2_0; } else if (query_value == QUERY_VALUE_HDCP_V2_1) { diff --git a/cdm/test/cdm_reboot_test_main.cpp b/cdm/test/cdm_reboot_test_main.cpp index 03cfbc90..69d999ca 100644 --- a/cdm/test/cdm_reboot_test_main.cpp +++ b/cdm/test/cdm_reboot_test_main.cpp @@ -34,10 +34,8 @@ namespace { constexpr char kGlobalDumpFileName[] = "dumped_global_filesystem.dat"; constexpr char kPerOriginDumpFileName[] = "dumped_per_origin_filesystem.dat"; -using StorageMap = TestHost::Storage::StorageMap; - // Load a TestHost file system from the real file system. -bool ReloadFileSystem(const char* file_name, StorageMap* map) { +bool ReloadFileSystem(const char* file_name, TestHost::Storage* store) { std::ifstream input(file_name); if (input.fail()) { // This is OK for first pass, but an error for later passes. @@ -46,7 +44,7 @@ bool ReloadFileSystem(const char* file_name, StorageMap* map) { } std::string dumped_file_system((std::istreambuf_iterator(input)), std::istreambuf_iterator()); - if (!wvcdm::RebootTest::ParseDump(dumped_file_system, map)) { + if (!store->LoadFromString(dumped_file_system)) { LOGE("Could not parse %s", file_name); return false; } @@ -54,31 +52,26 @@ bool ReloadFileSystem(const char* file_name, StorageMap* map) { } bool ReloadFileSystems() { - StorageMap global_map; - StorageMap per_origin_map; - if (!ReloadFileSystem(kGlobalDumpFileName, &global_map) || - !ReloadFileSystem(kPerOriginDumpFileName, &per_origin_map)) { - return false; - } - g_host->global_storage().ResetFiles(global_map); - g_host->per_origin_storage().ResetFiles(per_origin_map); - return true; + return ReloadFileSystem(kGlobalDumpFileName, &g_host->global_storage()) && + ReloadFileSystem(kPerOriginDumpFileName, + &g_host->per_origin_storage()); } // Dump a TestHost file system to the real file system. If the dump file // cannot be written, this raises an exception and crashes the program. Since // we usually build with exceptions turned off, what this means is that the test // executable will halt if the file cannot be written. -void DumpFileSystem(const char* file_name, const StorageMap& map) { - std::string dump = wvcdm::RebootTest::DumpData(map); +void DumpFileSystem(const char* file_name, const TestHost::Storage& store) { + std::string dump; + (void)store.SaveToString(&dump); std::ofstream output(file_name); output << dump; output.close(); } void DumpFileSystems() { - DumpFileSystem(kGlobalDumpFileName, g_host->global_storage().files()); - DumpFileSystem(kPerOriginDumpFileName, g_host->per_origin_storage().files()); + DumpFileSystem(kGlobalDumpFileName, g_host->global_storage()); + DumpFileSystem(kPerOriginDumpFileName, g_host->per_origin_storage()); } } // namespace diff --git a/cdm/test/cdm_test.cpp b/cdm/test/cdm_test.cpp index c7cf382e..b0c12f9e 100644 --- a/cdm/test/cdm_test.cpp +++ b/cdm/test/cdm_test.cpp @@ -235,6 +235,15 @@ class CdmTest : public WvCdmTestBase, public Cdm::IEventListener { protected: void SetUp() override { + // TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented. + // Many of these tests call EnsureProvisioned which tries to provision the + // test device using a provisioning server. Until support is added for Drm + // Reprovisioning on the server, skip these tests to avoid test failures. + if (wvoec::global_features.provisioning_method == + OEMCrypto_DrmReprovisioning) { + GTEST_SKIP() + << "Skipping until Drm Reprovisioning server support is implemented."; + } WvCdmTestBase::SetUp(); // Clear anything stored, load default device cert. @@ -1462,6 +1471,12 @@ TEST_F(CdmTest, GetExpiration) { TEST_P(CdmTestWithRemoveParam, Remove) { const bool intermediate_close = GetParam(); + // TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented. + if (wvoec::global_features.provisioning_method == + OEMCrypto_DrmReprovisioning) { + GTEST_SKIP() + << "Skipping until Drm Reprovisioning server support is implemented."; + } EnsureProvisioned(); std::string session_id; ASSERT_NO_FATAL_FAILURE( @@ -1916,27 +1931,22 @@ TEST_F(CdmTest, GetStatusForHdcpResolution) { // Unfortunately, since we cannot mock the HDCP state, we cannot validate // the validity of the values returned here, only that meaningful values are // returned. - Cdm::KeyStatus key_status; + const std::vector kSupportedHdcpVersions = { + // Legacy 1.x version + Cdm::kHdcp1_x, + // v17 1.x versions + Cdm::kHdcp1_0, Cdm::kHdcp1_1, Cdm::kHdcp1_2, Cdm::kHdcp1_3, Cdm::kHdcp1_4, + // 2.x versions. + Cdm::kHdcp2_0, Cdm::kHdcp2_1, Cdm::kHdcp2_2, Cdm::kHdcp2_3}; - ASSERT_EQ(Cdm::kSuccess, - cdm_->getStatusForHdcpVersion(Cdm::kHdcp1_x, &key_status)); - EXPECT_THAT(key_status, AnyOf(Cdm::kUsable, Cdm::kOutputRestricted)); - - ASSERT_EQ(Cdm::kSuccess, - cdm_->getStatusForHdcpVersion(Cdm::kHdcp2_0, &key_status)); - EXPECT_THAT(key_status, AnyOf(Cdm::kUsable, Cdm::kOutputRestricted)); - - ASSERT_EQ(Cdm::kSuccess, - cdm_->getStatusForHdcpVersion(Cdm::kHdcp2_1, &key_status)); - EXPECT_THAT(key_status, AnyOf(Cdm::kUsable, Cdm::kOutputRestricted)); - - ASSERT_EQ(Cdm::kSuccess, - cdm_->getStatusForHdcpVersion(Cdm::kHdcp2_2, &key_status)); - EXPECT_THAT(key_status, AnyOf(Cdm::kUsable, Cdm::kOutputRestricted)); - - ASSERT_EQ(Cdm::kSuccess, - cdm_->getStatusForHdcpVersion(Cdm::kHdcp2_3, &key_status)); - EXPECT_THAT(key_status, AnyOf(Cdm::kUsable, Cdm::kOutputRestricted)); + for (const auto version : kSupportedHdcpVersions) { + Cdm::KeyStatus key_status; + ASSERT_EQ(Cdm::kSuccess, + cdm_->getStatusForHdcpVersion(version, &key_status)) + << "HDCP version: " << static_cast(version); + EXPECT_THAT(key_status, AnyOf(Cdm::kUsable, Cdm::kOutputRestricted)) + << "HDCP version: " << static_cast(version); + } } TEST_F(CdmTest, HandlesKeyRotationWithOnlyOneLicenseRequest) { @@ -2162,6 +2172,12 @@ TEST_F(CdmTest, GetMetrics) { } TEST_P(CdmTestWithDecryptParam, DecryptToClearBuffer) { + // TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented. + if (wvoec::global_features.provisioning_method == + OEMCrypto_DrmReprovisioning) { + GTEST_SKIP() + << "Skipping until Drm Reprovisioning server support is implemented."; + } EnsureProvisioned(); DecryptParam param = GetParam(); diff --git a/cdm/test/perf_test.cpp b/cdm/test/perf_test.cpp index 9f027b0e..a9554370 100644 --- a/cdm/test/perf_test.cpp +++ b/cdm/test/perf_test.cpp @@ -20,6 +20,7 @@ #include "config_test_env.h" #include "license_request.h" +#include "logger_global.h" #include "test_host.h" #include "url_request.h" @@ -29,6 +30,7 @@ #define WALL_NOW std::chrono::high_resolution_clock::now() TestHost* g_host = nullptr; +widevine::StderrLogger g_stderr_logger; namespace widevine { @@ -145,6 +147,8 @@ class EventListener : public Cdm::IEventListener { } void onKeyStatusesChange(const std::string& session_id, bool has_new_usable_key) override {} + void onExpirationChange(const std::string& session_id, + int64_t new_expiration) override {} void onRemoveComplete(const std::string& session_id) override {} std::vector messages; @@ -182,6 +186,10 @@ class GlobalEnv : public testing::Environment { : init_func_(init_func), cert_(cert) {} void SetUp() override { + // Manually set the logger because `TestHost` makes logging calls before + // the global logger is set in |init_func_|. + g_logger = &g_stderr_logger; + g_host = new TestHost; if (!cert_.empty()) g_host->per_origin_storage().write("cert.bin", cert_); diff --git a/cdm/test/perf_test_dynamic.cpp b/cdm/test/perf_test_dynamic.cpp index c15e2bb0..9adc5d39 100644 --- a/cdm/test/perf_test_dynamic.cpp +++ b/cdm/test/perf_test_dynamic.cpp @@ -22,7 +22,7 @@ constexpr char kInitName[] = "_ZN8widevine3Cdm10initializeENS0_16SecureOutputTypeEPNS0_8IStorageEPNS0_" "6IClockEPNS0_6ITimerEPNS0_7ILoggerENS0_8LogLevelE"; constexpr char kCreateName[] = - "_ZN8widevine3Cdm6createEPNS0_14IEventListenerEPNS0_8IStorageEb"; + "_ZN8widevine3Cdm6createEPNS0_14IEventListenerEPNS0_8IStorageEbb"; bool ReadFile(const std::string& path, std::string* output) { constexpr size_t kReadSize = 8 * 1024; diff --git a/cdm/test/test_host.cpp b/cdm/test/test_host.cpp index 51ff0b62..3a4826a6 100644 --- a/cdm/test/test_host.cpp +++ b/cdm/test/test_host.cpp @@ -8,10 +8,12 @@ #include #include "cdm_version.h" +#include "device_files.pb.h" #include "file_store.h" #include "log.h" using namespace widevine; +using video_widevine_client::sdk::SavedStorage; namespace { @@ -120,6 +122,20 @@ void TestHost::Storage::Reset() { files_.clear(); } +bool TestHost::Storage::LoadFromString(const std::string& data) { + SavedStorage proto; + if (!proto.ParseFromString(data)) return false; + Reset(); + files_.insert(proto.files().begin(), proto.files().end()); + return true; +} + +bool TestHost::Storage::SaveToString(std::string* data) const { + SavedStorage proto; + proto.mutable_files()->insert(files_.begin(), files_.end()); + return proto.SerializeToString(data); +} + bool TestHost::Storage::read(const std::string& name, std::string* data) { StorageMap::iterator it = files_.find(name); bool ok = it != files_.end(); diff --git a/cdm/test/test_host.h b/cdm/test/test_host.h index 5879ddfb..29622dc3 100644 --- a/cdm/test/test_host.h +++ b/cdm/test/test_host.h @@ -28,6 +28,10 @@ class TestHost : public widevine::Cdm::IClock, ~Storage() override {} void Reset(); + // Save and load the storage from a string buffer so it can be on disk. + bool LoadFromString(const std::string& data); + bool SaveToString(std::string* data) const; + // Reset the file system to contain the specified files. void ResetFiles(const StorageMap& files) { files_ = files; }; const StorageMap& files() const { return files_; } diff --git a/core/include/cdm_engine.h b/core/include/cdm_engine.h index ce93727d..57f306fc 100644 --- a/core/include/cdm_engine.h +++ b/core/include/cdm_engine.h @@ -180,6 +180,11 @@ class CdmEngine { virtual CdmResponseType QueryOemCryptoSessionId( const CdmSessionId& session_id, CdmQueryMap* query_response); + // Query Signed CSR payload for Prov 4 device + virtual CdmResponseType QueryDeviceSignedCsrPayload( + const std::string& challenge, const std::string& device_info, + std::string* query_response); + // Generate and return a valid provisioning request. virtual CdmResponseType GetProvisioningRequest( CdmCertificateType cert_type, const std::string& cert_authority, diff --git a/core/include/cdm_session.h b/core/include/cdm_session.h index 2c6018b0..2557d0d3 100644 --- a/core/include/cdm_session.h +++ b/core/include/cdm_session.h @@ -265,6 +265,8 @@ class CdmSession { bool HasRootOfTrustBeenRenewed(); + CdmResponseType ResetCryptoSession(); + // 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); @@ -340,8 +342,9 @@ class CdmSession { bool has_license_been_loaded_ = false; bool has_license_been_restored_ = false; - bool mock_license_parser_in_use_; - bool mock_policy_engine_in_use_; + bool mock_crypto_session_in_use_ = false; + bool mock_license_parser_in_use_ = false; + bool mock_policy_engine_in_use_ = false; CORE_DISALLOW_COPY_AND_ASSIGN(CdmSession); }; diff --git a/core/include/certificate_provisioning.h b/core/include/certificate_provisioning.h index 05751485..5ca9e317 100644 --- a/core/include/certificate_provisioning.h +++ b/core/include/certificate_provisioning.h @@ -73,6 +73,9 @@ class CertificateProvisioning { static void GetProvisioningServerUrl(std::string* default_url); private: +#if defined(UNIT_TEST) + friend class CertificateProvisioningTest; +#endif CdmResponseType GetProvisioningRequestInternal( wvutil::FileSystem* file_system, RequestedSecurityLevel requested_security_level, @@ -82,7 +85,8 @@ class CertificateProvisioning { CdmResponseType GetProvisioning40RequestInternal( wvutil::FileSystem* file_system, const std::string& origin, const std::string& spoid, CdmProvisioningRequest* request, - std::string* default_url); + std::string* default_url, CdmCertificateType cert_type, + const std::string& cert_authority); CdmResponseType FillEncryptedClientId( const std::string& client_token, video_widevine::ProvisioningRequest& provisioning_request, @@ -93,7 +97,14 @@ class CertificateProvisioning { video_widevine::ProvisioningRequest& provisioning_request, const ServiceCertificate& service_certificate); CdmResponseType HandleProvisioning40Response( - wvutil::FileSystem* file_system, const std::string& response_message); + wvutil::FileSystem* file_system, + const video_widevine::SignedProvisioningMessage& signed_message, + std::string* cert, std::string* wrapped_key); + // Assign the cert type for provisioning request + // Required by Cast cert provisioning flow + CdmResponseType CertTypeAssign( + video_widevine::ProvisioningRequest& provisioning_request, + CdmCertificateType cert_type, const std::string& cert_authority); CdmResponseType SetSpoidParameter( const std::string& origin, const std::string& spoid, @@ -120,6 +131,8 @@ class CertificateProvisioning { std::string provisioning_40_wrapped_private_key_; // Key type of the generated key pair in provisioning 4. CryptoWrappedKey::Type provisioning_40_key_type_; + // Store the last provisioning request message + std::string provisioning_request_message_; CORE_DISALLOW_COPY_AND_ASSIGN(CertificateProvisioning); }; diff --git a/core/include/content_key_session.h b/core/include/content_key_session.h index e3681752..48e645a2 100644 --- a/core/include/content_key_session.h +++ b/core/include/content_key_session.h @@ -50,9 +50,10 @@ class ContentKeySession : public KeySession { CdmCipherMode cipher_mode) override; // Decrypt for ContentKeySession - OEMCryptoResult Decrypt( - const OEMCrypto_SampleDescription* samples, size_t samples_length, - const OEMCrypto_CENCEncryptPatternDesc& pattern) override; + OEMCryptoResult Decrypt(const OEMCrypto_SampleDescription* samples, + size_t samples_length, + const OEMCrypto_CENCEncryptPatternDesc& pattern, + bool is_any_subsample_protected) override; OEMCryptoResult GenericEncrypt(const std::string& in_buffer, const std::string& iv, diff --git a/core/include/crypto_session.h b/core/include/crypto_session.h index c27920fd..d0fa7b8a 100644 --- a/core/include/crypto_session.h +++ b/core/include/crypto_session.h @@ -69,6 +69,8 @@ class CryptoSession { static CryptoSession* MakeCryptoSession( metrics::CryptoMetrics* crypto_metrics); + static const char* HdcpCapabilityToString(HdcpCapability hdcp_level); + virtual ~CryptoSession(); // This method will try to terminate OEMCrypto if |session_size_| is 0. @@ -103,6 +105,9 @@ class CryptoSession { // Only valid for OEM certificate-based based devices. virtual CdmResponseType GetTokenFromOemCert( RequestedSecurityLevel requested_security_level, std::string* oem_cert); + // Retrieves the embedded public certificate from OEMCrypto. + // Only valid for L3 devices with embedded (baked-in) certificates. + virtual CdmResponseType GetTokenFromEmbeddedCertificate(std::string* token); // The overloaded methods with |requested_level| may be called // without a preceding call to Open. The other method must call Open first. @@ -194,6 +199,13 @@ class CryptoSession { std::string* additional_signature); virtual CdmResponseType GetBootCertificateChain( std::string* bcc, std::string* additional_signature); + virtual CdmResponseType GetDeviceInformation( + RequestedSecurityLevel requested_security_level, + std::string* device_info); + virtual CdmResponseType GetDeviceSignedCsrPayload( + RequestedSecurityLevel requested_security_level, + const std::string& challenge, const std::string& device_info, + std::string* signed_csr_payload); virtual CdmResponseType GenerateCertificateKeyPair( std::string* public_key, std::string* public_key_signature, std::string* wrapped_private_key, CryptoWrappedKey::Type* key_type); @@ -411,18 +423,22 @@ class CryptoSession { OEMCryptoResult DecryptMultipleSamples( const std::vector& samples, CdmCipherMode cipher_mode, - const OEMCrypto_CENCEncryptPatternDesc& pattern); - OEMCryptoResult DecryptSample( - const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode, - const OEMCrypto_CENCEncryptPatternDesc& pattern); - OEMCryptoResult LegacyDecrypt( - const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode, - const OEMCrypto_CENCEncryptPatternDesc& pattern); + const OEMCrypto_CENCEncryptPatternDesc& pattern, + bool is_any_subsample_protected); + OEMCryptoResult DecryptSample(const OEMCrypto_SampleDescription& sample, + CdmCipherMode cipher_mode, + const OEMCrypto_CENCEncryptPatternDesc& pattern, + bool is_any_subsample_protected); + OEMCryptoResult LegacyDecrypt(const OEMCrypto_SampleDescription& sample, + CdmCipherMode cipher_mode, + const OEMCrypto_CENCEncryptPatternDesc& pattern, + bool is_any_subsample_protected); OEMCryptoResult LegacyCopyBufferInChunks( const OEMCrypto_SampleDescription& sample, size_t max_chunk_size); OEMCryptoResult LegacyDecryptInChunks( const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode, - const OEMCrypto_CENCEncryptPatternDesc& pattern, size_t max_chunk_size); + const OEMCrypto_CENCEncryptPatternDesc& pattern, size_t max_chunk_size, + bool is_any_subsample_protected); // These methods should be used to take the various CryptoSession mutexes in // preference to taking the mutexes directly. @@ -545,6 +561,9 @@ class CryptoSession { // same error code in sequence of each other. A value of // OEMCrypto_SUCCESS indicates that no error have yet occurred. OEMCryptoResult last_decrypt_error_ = OEMCrypto_SUCCESS; + // Similar to |last_decrypt_error_|, but intended for calls to + // SelectKey(). + OEMCryptoResult last_select_key_error_ = OEMCrypto_SUCCESS; // In order to avoid creating a deadlock if instantiation needs to take any // of the CryptoSession static mutexes, |factory_| is protected by its own diff --git a/core/include/key_session.h b/core/include/key_session.h index 36b641fc..0b0174e6 100644 --- a/core/include/key_session.h +++ b/core/include/key_session.h @@ -39,7 +39,8 @@ class KeySession { CdmCipherMode cipher_mode) = 0; virtual OEMCryptoResult Decrypt( const OEMCrypto_SampleDescription* samples, size_t samples_length, - const OEMCrypto_CENCEncryptPatternDesc& pattern) = 0; + const OEMCrypto_CENCEncryptPatternDesc& pattern, + bool is_any_subsample_protected) = 0; virtual OEMCryptoResult GenericEncrypt(const std::string& in_buffer, const std::string& iv, OEMCrypto_Algorithm algorithm, diff --git a/core/include/license.h b/core/include/license.h index 112e58d8..3e317f91 100644 --- a/core/include/license.h +++ b/core/include/license.h @@ -90,6 +90,15 @@ class CdmLicense { const CdmKeyResponse& license_response, std::string* provider_session_token); + // Testing only. Caller retains ownership of pointers. + void set_crypto_session(CryptoSession* crypto_session) { + crypto_session_ = crypto_session; + } + + void set_policy_engine(PolicyEngine* policy_engine) { + policy_engine_ = policy_engine; + } + private: CdmResponseType HandleKeyErrorResponse( const video_widevine::SignedMessage& signed_message); @@ -129,8 +138,8 @@ class CdmLicense { bool SetTypeAndId(CdmLicenseType license_type, const std::string& request_id, T* content_id); - CryptoSession* crypto_session_; - PolicyEngine* policy_engine_; + CryptoSession* crypto_session_ = nullptr; + PolicyEngine* policy_engine_ = nullptr; std::string server_url_; std::string client_token_; const CdmSessionId session_id_; diff --git a/core/include/license_key_status.h b/core/include/license_key_status.h index 4c32d52f..6bb6e0a5 100644 --- a/core/include/license_key_status.h +++ b/core/include/license_key_status.h @@ -161,6 +161,8 @@ class LicenseKeyStatus { bool meets_security_level_constraints_ = true; CdmKeyAllowedUsage allowed_usage_; CryptoSession::HdcpCapability default_hdcp_level_ = HDCP_NONE; + CryptoSession::HdcpCapability last_reported_device_hdcp_level_ = HDCP_NONE; + CryptoSession::HdcpCapability last_reported_license_hdcp_level_ = HDCP_NONE; ConstraintList constraints_; CORE_DISALLOW_COPY_AND_ASSIGN(LicenseKeyStatus); diff --git a/core/include/policy_engine.h b/core/include/policy_engine.h index 3819cc34..506b7e91 100644 --- a/core/include/policy_engine.h +++ b/core/include/policy_engine.h @@ -106,6 +106,8 @@ class PolicyEngine { virtual const LicenseIdentification& license_id() { return license_id_; } + WvCdmEventListener* event_listener() { return event_listener_; } + bool GetSecondsSinceStarted(int64_t* seconds_since_started); bool GetSecondsSinceLastPlayed(int64_t* seconds_since_started); @@ -131,6 +133,11 @@ class PolicyEngine { return license_keys_->MeetsConstraints(key_id); } + // Testing only. Caller retains ownership. + void set_crypto_session(CryptoSession* crypto_session) { + crypto_session_ = crypto_session; + } + private: friend class PolicyEngineTest; friend class PolicyEngineConstraintsTest; diff --git a/core/include/privacy_crypto.h b/core/include/privacy_crypto.h index eea1d3bd..9904a518 100644 --- a/core/include/privacy_crypto.h +++ b/core/include/privacy_crypto.h @@ -37,7 +37,7 @@ class AesCbcKey { ~AesCbcKey(); bool Init(const std::string& key); - bool Encrypt(const std::string& in, std::string* out, std::string* iv); + bool Encrypt(const std::string& in, const std::string& iv, std::string* out); private: std::string key_; diff --git a/core/include/wv_cdm_constants.h b/core/include/wv_cdm_constants.h index 3c49ff5d..c35236aa 100644 --- a/core/include/wv_cdm_constants.h +++ b/core/include/wv_cdm_constants.h @@ -124,6 +124,7 @@ static const std::string QUERY_KEY_PRODUCTION_READY = "ProductionReady"; // Internal query key. Should not be exposed to Android apps. static const std::string QUERY_KEY_DEBUG_BOOT_CERTIFICATE_CHAIN = "DebugBootCertificateChain"; +static const std::string QUERY_KEY_DEVICE_INFORMATION = "DeviceInformation"; static const std::string QUERY_VALUE_TRUE = "True"; static const std::string QUERY_VALUE_FALSE = "False"; @@ -139,16 +140,16 @@ 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_HDCP_NO_DIGITAL_OUTPUT = "Disconnected"; static const std::string QUERY_VALUE_HDCP_NONE = "Unprotected"; -static const std::string QUERY_VALUE_HDCP_V1 = "HDCP-1.x"; -static const std::string QUERY_VALUE_HDCP_V2_0 = "HDCP-2.0"; -static const std::string QUERY_VALUE_HDCP_V2_1 = "HDCP-2.1"; -static const std::string QUERY_VALUE_HDCP_V2_2 = "HDCP-2.2"; -static const std::string QUERY_VALUE_HDCP_V2_3 = "HDCP-2.3"; +static const std::string QUERY_VALUE_HDCP_V1_X = "HDCP-1.x"; static const std::string QUERY_VALUE_HDCP_V1_0 = "HDCP-1.0"; static const std::string QUERY_VALUE_HDCP_V1_1 = "HDCP-1.1"; static const std::string QUERY_VALUE_HDCP_V1_2 = "HDCP-1.2"; static const std::string QUERY_VALUE_HDCP_V1_3 = "HDCP-1.3"; static const std::string QUERY_VALUE_HDCP_V1_4 = "HDCP-1.4"; +static const std::string QUERY_VALUE_HDCP_V2_0 = "HDCP-2.0"; +static const std::string QUERY_VALUE_HDCP_V2_1 = "HDCP-2.1"; +static const std::string QUERY_VALUE_HDCP_V2_2 = "HDCP-2.2"; +static const std::string QUERY_VALUE_HDCP_V2_3 = "HDCP-2.3"; static const std::string QUERY_VALUE_HDCP_LEVEL_UNKNOWN = "HDCP-LevelUnknown"; static const std::string QUERY_VALUE_DRM_CERTIFICATE = "DrmCertificate"; static const std::string QUERY_VALUE_KEYBOX = "Keybox"; diff --git a/core/include/wv_cdm_types.h b/core/include/wv_cdm_types.h index 55a586b3..5a5a12db 100644 --- a/core/include/wv_cdm_types.h +++ b/core/include/wv_cdm_types.h @@ -461,6 +461,9 @@ enum CdmResponseEnum : int32_t { STORE_ATSC_LICENSE_ERROR = 395, SESSION_NOT_FOUND_GENERIC_CRYPTO = 396, SESSION_NOT_FOUND_24 = 397, + GET_DEVICE_INFORMATION_ERROR = 398, + GET_DEVICE_SIGNED_CSR_PAYLOAD_ERROR = 399, + GET_TOKEN_FROM_EMBEDDED_CERT_ERROR = 400, // Don't forget to add new values to // * core/src/wv_cdm_types.cpp // * android/include/mapErrors-inl.h @@ -604,6 +607,9 @@ enum CdmClientTokenType : int32_t { kClientTokenOemCert, kClientTokenUninitialized, kClientTokenBootCertChain, + // For use by internal L3 CDMs supporting individualization of embedded + // drm certificates. + kClientTokenDrmCertificateReprovisioning, }; // kNonSecureUsageSupport - TEE does not provide any support for usage @@ -942,6 +948,8 @@ const char* IdToString(const std::string& id); // provided as string pointers. const char* IdPtrToString(const std::string* id); +const char* BoolToString(bool value); + // Logging utilities for OEMCrypto types. const char* OemCryptoResultToString(OEMCryptoResult result); } // namespace wvcdm diff --git a/core/src/Android.bp b/core/src/Android.bp index 77c84584..b001ad59 100644 --- a/core/src/Android.bp +++ b/core/src/Android.bp @@ -35,5 +35,5 @@ cc_library { static_libs: ["libcdm_metrics_protos"], whole_static_libs: ["libcdm_metrics_protos"], export_static_lib_headers: ["libcdm_metrics_protos"], - min_sdk_version: "UpsideDownCake", + min_sdk_version: "34", } diff --git a/core/src/cdm_engine.cpp b/core/src/cdm_engine.cpp index ccbe11f7..623b9e0c 100644 --- a/core/src/cdm_engine.cpp +++ b/core/src/cdm_engine.cpp @@ -38,8 +38,8 @@ std::string MapHdcpVersion(CryptoSession::HdcpCapability version) { switch (version) { case HDCP_NONE: return QUERY_VALUE_HDCP_NONE; - case HDCP_V1: - return QUERY_VALUE_HDCP_V1; + case HDCP_V1: // 1.x, not 1.0 + return QUERY_VALUE_HDCP_V1_X; case HDCP_V2: return QUERY_VALUE_HDCP_V2_0; case HDCP_V2_1: @@ -752,6 +752,7 @@ CdmResponseType CdmEngine::QueryStatus(RequestedSecurityLevel security_level, } switch (token_type) { case kClientTokenDrmCert: + case kClientTokenDrmCertificateReprovisioning: *query_response = QUERY_VALUE_DRM_CERTIFICATE; break; case kClientTokenKeybox: @@ -887,7 +888,7 @@ CdmResponseType CdmEngine::QueryStatus(RequestedSecurityLevel security_level, const CdmResponseType status = crypto_session->GetBootCertificateChain( security_level, &bcc, &signature_unused); if (status == NO_ERROR) { - LOGD("BCC length: %zu", bcc.size()); + LOGV("BCC length: %zu", bcc.size()); *query_response = std::move(bcc); return CdmResponseType(NO_ERROR); } @@ -900,6 +901,24 @@ CdmResponseType CdmEngine::QueryStatus(RequestedSecurityLevel security_level, LOGE("Failed to extract BCC: status = %d", status.ToInt()); return status; } + if (query_token == QUERY_KEY_DEVICE_INFORMATION) { + std::string device_info; + const CdmResponseType status = + crypto_session->GetDeviceInformation(security_level, &device_info); + if (status == NO_ERROR) { + LOGV("device_info length: %zu", device_info.size()); + *query_response = std::move(device_info); + return CdmResponseType(NO_ERROR); + } + if (status == NOT_IMPLEMENTED_ERROR || + status == PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR) { + LOGV("device_info not available: %s", status.ToString().c_str()); + *query_response = QUERY_VALUE_NONE; + return CdmResponseType(NO_ERROR); + } + LOGE("Failed to extract device_info: %s", status.ToString().c_str()); + return status; + } CdmResponseType status; M_TIME(status = crypto_session->Open(security_level), @@ -1038,6 +1057,34 @@ CdmResponseType CdmEngine::QueryOemCryptoSessionId( return session->QueryOemCryptoSessionId(query_response); } +CdmResponseType CdmEngine::QueryDeviceSignedCsrPayload( + const std::string& challenge, const std::string& device_info, + std::string* query_response) { + if (query_response == nullptr) { + LOGE("Output |query_response| is null"); + return CdmResponseType(PARAMETER_NULL); + } + std::unique_ptr crypto_session( + CryptoSession::MakeCryptoSession(metrics_->GetCryptoMetrics())); + + std::string signed_csr_payload; + const CdmResponseType status = crypto_session->GetDeviceSignedCsrPayload( + kLevelDefault, challenge, device_info, &signed_csr_payload); + if (status == NO_ERROR) { + LOGV("signed_csr_payload length: %zu", signed_csr_payload.size()); + *query_response = std::move(signed_csr_payload); + return CdmResponseType(NO_ERROR); + } + if (status == NOT_IMPLEMENTED_ERROR || + status == PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR) { + LOGD("signed_csr_payload not available: %s", status.ToString().c_str()); + *query_response = QUERY_VALUE_NONE; + return CdmResponseType(NO_ERROR); + } + LOGE("Failed to extract signed_csr_payload: %s", status.ToString().c_str()); + return status; +} + // static bool CdmEngine::IsSecurityLevelSupported(CdmSecurityLevel level) { LOGI("level = %s", CdmSecurityLevelToString(level)); @@ -2358,9 +2405,9 @@ CdmResponseType CdmEngine::SignRsa(const std::string& wrapped_key, { std::unique_lock lock(session_map_lock_); if (!session_map_.FindSession(session_id, &session)) { - LOGE("Session not found: session_id = %s", IdToString(session_id)); - CloseSession(session_id); - return CdmResponseType(SESSION_NOT_FOUND_24); + LOGE("Session not found: session_id = %s", IdToString(session_id)); + CloseSession(session_id); + return CdmResponseType(SESSION_NOT_FOUND_24); } } diff --git a/core/src/cdm_session.cpp b/core/src/cdm_session.cpp index 783c0416..dfe6f7df 100644 --- a/core/src/cdm_session.cpp +++ b/core/src/cdm_session.cpp @@ -81,9 +81,7 @@ CdmSession::CdmSession(wvutil::FileSystem* file_system, security_level_(kSecurityLevelUninitialized), requested_security_level_(kLevelDefault), is_initial_usage_update_(true), - is_usage_update_needed_(false), - mock_license_parser_in_use_(false), - mock_policy_engine_in_use_(false) { + is_usage_update_needed_(false) { assert(metrics_); // metrics_ must not be null. crypto_metrics_ = metrics_->GetCryptoMetrics(); crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_)); @@ -866,18 +864,10 @@ CdmResponseType CdmSession::DeleteUsageEntry(uint32_t usage_entry_index) { // The usage entry cannot be deleted if it has a crypto session handling // it, so close and reopen session. UpdateUsageEntryInformation(); - CdmResponseType sts; - crypto_session_->Close(); - crypto_session_.reset(CryptoSession::MakeCryptoSession(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_ = nullptr; - bool has_support = false; - if (crypto_session_->HasUsageTableSupport(&has_support) && has_support) { - usage_table_ = crypto_session_->GetUsageTable(); - } + crypto_session_->Close(); + CdmResponseType sts = ResetCryptoSession(); + if (sts != NO_ERROR) return sts; if (usage_table_ == nullptr) { LOGE("Usage table header unavailable"); @@ -1012,14 +1002,8 @@ bool CdmSession::StoreLicense(CdmOfflineLicenseState state, int* error_detail) { } CdmResponseType CdmSession::RemoveKeys() { - CdmResponseType sts; - crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_)); - // Ignore errors - M_TIME(sts = crypto_session_->Open(requested_security_level_), - crypto_metrics_, crypto_session_open_, sts, requested_security_level_); - policy_engine_.reset( - new PolicyEngine(session_id_, nullptr, crypto_session_.get())); - return CdmResponseType(NO_ERROR); + crypto_session_->Close(); + return ResetCryptoSession(); } CdmResponseType CdmSession::RemoveLicense() { @@ -1310,8 +1294,78 @@ CdmResponseType CdmSession::LoadCastPrivateKey( CdmResponseType CdmSession::GenerateRsaSignature(const std::string& message, std::string* signature, RSA_Padding_Scheme scheme) { - return crypto_session_->GenerateRsaSignature(message, signature, - scheme); + return crypto_session_->GenerateRsaSignature(message, signature, scheme); +} + +CdmResponseType CdmSession::ResetCryptoSession() { + LOGD("Resetting crypto session: session_id = %s, ksid = %s", + IdToString(session_id_), IdToString(key_set_id_)); + if (mock_crypto_session_in_use_) { + // If the crypto session is not reset, then there is nothing to do. + return CdmResponseType(NO_ERROR); + } + CdmResponseType sts; + crypto_session_.reset(CryptoSession::MakeCryptoSession(crypto_metrics_)); + usage_table_ = nullptr; + M_TIME(sts = crypto_session_->Open(requested_security_level_), + crypto_metrics_, crypto_session_open_, sts, requested_security_level_); + + CdmResponseType final_sts(NO_ERROR); + if (sts != NO_ERROR) { + // Challenging case, still need to reset other components. + LOGE("Failed to open crypto session: sts = %s", sts.ToString().c_str()); + final_sts = sts; + } else { + // Reset all component dependent on the crypto session. + security_level_ = crypto_session_->GetSecurityLevel(); + crypto_metrics_->crypto_session_security_level_.Record(security_level_); + + if (!file_handle_->Init(security_level_)) { + LOGE("Unable to initialize file handle"); + final_sts = CdmResponseType(SESSION_FILE_HANDLE_INIT_ERROR); + } + + if (!file_handle_->HasCertificate(atsc_mode_enabled_)) { + LOGE("Missing certificate: atsc_mode_enabled = %s", + BoolToString(atsc_mode_enabled_)); + final_sts = CdmResponseType(NEED_PROVISIONING); + } + + bool has_support = false; + if (crypto_session_->HasUsageTableSupport(&has_support) && has_support) { + usage_table_ = crypto_session_->GetUsageTable(); + } + } + + // Even if the session is not open, other members need new session pointer. + if (mock_policy_engine_in_use_) { + // Simply pass the new pointer. + policy_engine_->set_crypto_session(crypto_session_.get()); + } else { + // Attempt to maintain event listener. + WvCdmEventListener* event_listener = + policy_engine_ ? policy_engine_->event_listener() : nullptr; + policy_engine_.reset( + new PolicyEngine(session_id_, event_listener, crypto_session_.get())); + } + + if (mock_license_parser_in_use_) { + license_parser_->set_crypto_session(crypto_session_.get()); + license_parser_->set_policy_engine(policy_engine_.get()); + } else { + license_parser_.reset(new CdmLicense(session_id_)); + std::string service_certificate; + if (!Properties::GetServiceCertificate(session_id_, &service_certificate)) + service_certificate.clear(); + if (!license_parser_->Init(Properties::UsePrivacyMode(session_id_), + service_certificate, crypto_session_.get(), + policy_engine_.get())) { + LOGE("Failed to initialize license parser"); + final_sts = CdmResponseType(LICENSE_PARSER_INIT_ERROR); + } + } + + return final_sts; } // For testing only - takes ownership of pointers @@ -1323,6 +1377,7 @@ void CdmSession::set_license_parser(CdmLicense* license_parser) { void CdmSession::set_crypto_session(CryptoSession* crypto_session) { crypto_session_.reset(crypto_session); + mock_crypto_session_in_use_ = true; } void CdmSession::set_policy_engine(PolicyEngine* policy_engine) { diff --git a/core/src/cdm_usage_table.cpp b/core/src/cdm_usage_table.cpp index f560e6d1..f2dbe368 100644 --- a/core/src/cdm_usage_table.cpp +++ b/core/src/cdm_usage_table.cpp @@ -382,6 +382,9 @@ CdmResponseType CdmUsageTable::InvalidateEntryInternal( // sent back to the caller for the CDM as a whole to handle. const uint32_t pre_defrag_store_counter = store_table_counter_; const CdmResponseType status = DefragTable(device_files, metrics); + if (status != NO_ERROR) { + LOGW("Failed to defrag usage table: sts = %s", status.ToString().c_str()); + } if (pre_defrag_store_counter == store_table_counter_) { // It is possible that DefragTable() does not result in any // changes to the table, and as a result, it will not store the @@ -468,6 +471,10 @@ bool CdmUsageTable::CapacityCheck(CryptoSession* const crypto_session) { return false; } + // Session must be closed before invalidating, otherwise Shrink() will + // fail in call to InvalidateEntry(). + local_crypto_session->Close(); + status = InvalidateEntry(temporary_entry_index, /* defrag_table = */ true, device_files_.get(), metrics); @@ -480,7 +487,10 @@ bool CdmUsageTable::CapacityCheck(CryptoSession* const crypto_session) { // The entry should have been deleted from the usage table, // not just marked as type unknown. Failure to call // Shrink() may be an indicator of other issues. - LOGE("Failed to shrink table for capacity test"); + LOGE( + "Failed to shrink table for capacity test: " + "post_check_size = %zu, check_usage_entry_number = %u", + entry_info_list_.size(), temporary_entry_index); return false; } return true; @@ -949,7 +959,12 @@ CdmResponseType CdmUsageTable::DefragTable(DeviceFiles* device_files, if (entries_to_move.empty()) { LOGD("No valid entries found, shrinking entire table: size = %zu", entry_info_list_.size()); - return Shrink(metrics, static_cast(entry_info_list_.size())); + const CdmResponseType status = + Shrink(metrics, static_cast(entry_info_list_.size())); + if (status != NO_ERROR) { + LOGE("Failed to shrink table: sts = %s", status.ToString().c_str()); + } + return status; } // Step 3: Ignore invalid entries that are after the last valid @@ -973,7 +988,11 @@ CdmResponseType CdmUsageTable::DefragTable(DeviceFiles* device_files, last_valid_entry - 1; LOGD("Removing all entries after the last valid entry: count = %u", to_remove); - return Shrink(metrics, to_remove); + const CdmResponseType status = Shrink(metrics, to_remove); + if (status != NO_ERROR) { + LOGE("Failed to shrink table: sts = %s", status.ToString().c_str()); + } + return status; } // Step 4: Move the valid entries to overwrite the invalid entries. @@ -1101,7 +1120,12 @@ CdmResponseType CdmUsageTable::DefragTable(DeviceFiles* device_files, LOGD( "All entries have been invalidated, shrinking entire table: size = %zu", entry_info_list_.size()); - return Shrink(metrics, static_cast(entry_info_list_.size())); + const CdmResponseType status = + Shrink(metrics, static_cast(entry_info_list_.size())); + if (status != NO_ERROR) { + LOGE("Failed to shrink table: sts = %s", status.ToString().c_str()); + } + return status; } const UsageEntryIndex to_remove = @@ -1120,7 +1144,11 @@ CdmResponseType CdmUsageTable::DefragTable(DeviceFiles* device_files, // Step 6: Shrink table to the new size. LOGD("Clean up complete, shrinking table: count = %u", to_remove); - return Shrink(metrics, to_remove); + const CdmResponseType status = Shrink(metrics, to_remove); + if (status != NO_ERROR) { + LOGE("Failed to shrink table: sts = %s", status.ToString().c_str()); + } + return status; } // End Defrag(). CdmResponseType CdmUsageTable::ReleaseOldestEntry( diff --git a/core/src/certificate_provisioning.cpp b/core/src/certificate_provisioning.cpp index c8158b1f..f7b3b99b 100644 --- a/core/src/certificate_provisioning.cpp +++ b/core/src/certificate_provisioning.cpp @@ -151,7 +151,12 @@ CdmResponseType CertificateProvisioning::SetSpoidParameter( return status; } request->set_stable_id(device_unique_id + origin); - } // No else clause, by design. It is valid to do nothing. + } else { + // It is valid to do nothing for legacy devices. For most recently + // launched devices this is an error. For now, we will log + // but not return an error. + LOGE("No spoid/provider id/stable id set"); + } return CdmResponseType(NO_ERROR); } @@ -164,6 +169,8 @@ CertificateProvisioning::GetProvisioningType() { return SignedProvisioningMessage::PROVISIONING_40; case kClientTokenOemCert: return SignedProvisioningMessage::PROVISIONING_30; + case kClientTokenDrmCertificateReprovisioning: + return SignedProvisioningMessage::DRM_REPROVISIONING; default: return SignedProvisioningMessage::PROVISIONING_20; } @@ -210,7 +217,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal( if (crypto_session_->GetPreProvisionTokenType() == kClientTokenBootCertChain) { return GetProvisioning40RequestInternal(file_system, origin, spoid, request, - default_url); + default_url, cert_type, + cert_authority); } // Prepare device provisioning request. @@ -236,21 +244,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal( sizeof(nonce)); provisioning_request.set_nonce(encoded_nonce); - ProvisioningOptions* options = provisioning_request.mutable_options(); - switch (cert_type) { - case kCertificateWidevine: - options->set_certificate_type(ProvisioningOptions::WIDEVINE_DRM); - break; - case kCertificateX509: - options->set_certificate_type(ProvisioningOptions::X509); - break; - default: - LOGE("Unknown certificate type: %d", static_cast(cert_type)); - return CdmResponseType(CERT_PROVISIONING_INVALID_CERT_TYPE); - } - - cert_type_ = cert_type; - options->set_certificate_authority(cert_authority); + status = CertTypeAssign(provisioning_request, cert_type, cert_authority); + if (status != NO_ERROR) return status; status = SetSpoidParameter(origin, spoid, &provisioning_request); if (status != NO_ERROR) return status; @@ -309,7 +304,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal( CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal( wvutil::FileSystem* file_system, const std::string& origin, const std::string& spoid, CdmProvisioningRequest* request, - std::string* default_url) { + std::string* default_url, CdmCertificateType cert_type, + const std::string& cert_authority) { if (!crypto_session_->IsOpen()) { LOGE("Crypto session is not open"); return CdmResponseType(PROVISIONING_4_CRYPTO_SESSION_NOT_OPEN); @@ -380,9 +376,8 @@ CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal( // Since |stored_oem_cert| is empty, the client identification token will be // retrieved from OEMCrypto, which is the BCC in this case. - status = FillEncryptedClientIdWithAdditionalParameter( - stored_oem_cert, additional_parameter, provisioning_request, - wv_service_cert); + status = FillEncryptedClientId(stored_oem_cert, provisioning_request, + wv_service_cert); if (status != NO_ERROR) return status; } else { // This is the second stage provisioning. @@ -393,6 +388,29 @@ CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal( stored_oem_cert, additional_parameter, provisioning_request, *service_certificate_); if (status != NO_ERROR) return status; + + // If cert type is X509, provisioning Cast cert. + if (cert_type == kCertificateX509) { + uint32_t nonce; + status = crypto_session_->GenerateNonce(&nonce); + + if (status != NO_ERROR) { + LOGE("Failed to generate a nonce: status = %d", + static_cast(status)); + return status == NONCE_GENERATION_ERROR + ? CdmResponseType(CERT_PROVISIONING_NONCE_GENERATION_ERROR) + : status; + } + + // The provisioning server does not convert the nonce to uint32_t, it just + // passes the binary data to the response message. + const std::string encoded_nonce(reinterpret_cast(&nonce), + sizeof(nonce)); + provisioning_request.set_nonce(encoded_nonce); + + status = CertTypeAssign(provisioning_request, cert_type, cert_authority); + if (status != NO_ERROR) return status; + } } std::string public_key; @@ -415,6 +433,7 @@ CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal( std::string serialized_message; provisioning_request.SerializeToString(&serialized_message); + provisioning_request_message_ = serialized_message; SignedProvisioningMessage signed_provisioning_msg; signed_provisioning_msg.set_message(serialized_message); @@ -511,8 +530,11 @@ CertificateProvisioning::FillEncryptedClientIdWithAdditionalParameter( } CdmResponseType CertificateProvisioning::HandleProvisioning40Response( - wvutil::FileSystem* file_system, const std::string& response_message) { + wvutil::FileSystem* file_system, + const SignedProvisioningMessage& signed_response, std::string* cert, + std::string* wrapped_key) { ProvisioningResponse provisioning_response; + const std::string& response_message = signed_response.message(); if (response_message.empty() || !provisioning_response.ParseFromString(response_message)) { return CdmResponseType(PROVISIONING_4_RESPONSE_FAILED_TO_PARSE_MESSAGE); @@ -540,9 +562,44 @@ CdmResponseType CertificateProvisioning::HandleProvisioning40Response( LOGE("No private key was generated"); return CdmResponseType(PROVISIONING_4_NO_PRIVATE_KEY); } + const CryptoWrappedKey private_key(provisioning_40_key_type_, provisioning_40_wrapped_private_key_); + if (cert_type_ == kCertificateX509) { + // Load csr private key to decrypt session key + auto status = crypto_session_->LoadCertificatePrivateKey(private_key); + if (status != NO_ERROR) { + LOGE("Failed to load x509 certificate."); + return status; + } + + status = crypto_session_->GenerateDerivedKeys( + provisioning_request_message_, signed_response.session_key()); + if (status != NO_ERROR) { + LOGE("Failed to generate derived keys."); + return status; + } + + // Get wrapped private key for cast cert + CryptoWrappedKey cast_cert_private_key; + const std::string& signature = signed_response.signature(); + const std::string& core_message = signed_response.oemcrypto_core_message(); + status = crypto_session_->LoadProvisioning(response_message, core_message, + signature, + &cast_cert_private_key.key()); + if (status != NO_ERROR) { + LOGE("Failed to generate wrapped key for cast cert."); + return status; + } + + CloseSession(); + + *cert = device_certificate; + *wrapped_key = cast_cert_private_key.key(); + return CdmResponseType(NO_ERROR); + } + const CdmSecurityLevel security_level = crypto_session_->GetSecurityLevel(); CloseSession(); wvutil::FileSystem global_file_system; @@ -621,7 +678,8 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse( if (signed_response.provisioning_type() == SignedProvisioningMessage::PROVISIONING_40) { - return HandleProvisioning40Response(file_system, signed_response.message()); + return HandleProvisioning40Response(file_system, signed_response, cert, + wrapped_key); } bool error = false; @@ -841,4 +899,25 @@ CdmResponseType CertificateProvisioning::CloseSessionOnError( return status; } +CdmResponseType CertificateProvisioning::CertTypeAssign( + video_widevine::ProvisioningRequest& provisioning_request, + CdmCertificateType cert_type, const std::string& cert_authority) { + ProvisioningOptions* options = provisioning_request.mutable_options(); + switch (cert_type) { + case kCertificateWidevine: + options->set_certificate_type(ProvisioningOptions::WIDEVINE_DRM); + break; + case kCertificateX509: + options->set_certificate_type(ProvisioningOptions::X509); + break; + default: + LOGE("Unknown certificate type: %d", static_cast(cert_type)); + return CdmResponseType(CERT_PROVISIONING_INVALID_CERT_TYPE); + } + + cert_type_ = cert_type; + options->set_certificate_authority(cert_authority); + return CdmResponseType(NO_ERROR); +} + } // namespace wvcdm diff --git a/core/src/client_identification.cpp b/core/src/client_identification.cpp index d65072e1..83fac392 100644 --- a/core/src/client_identification.cpp +++ b/core/src/client_identification.cpp @@ -104,7 +104,8 @@ CdmResponseType ClientIdentification::InitForOtaKeyboxProvisioning( /* * Return the ClientIdentification message token type for provisioning request. - * NOTE: a DRM Cert should never be presented to the provisioning server. + * NOTE: a DRM Cert should never be presented to the provisioning server unless + * DRM re-provisioning is being used. */ CdmResponseType ClientIdentification::Prepare( const CdmAppParameterMap& app_parameters, @@ -145,7 +146,8 @@ CdmResponseType ClientIdentification::Prepare( } ClientIdentification_NameValue* client_info; - if (is_license_request_) { + // Include app parameters for license and provisioning requests + if (!is_okp_request_) { CdmAppParameterMap::const_iterator iter; for (iter = app_parameters.begin(); iter != app_parameters.end(); ++iter) { if (IsPropertyKeyReserved(iter->first)) { @@ -382,6 +384,10 @@ bool ClientIdentification::GetProvisioningTokenType( *token_type = video_widevine::ClientIdentification::BOOT_CERTIFICATE_CHAIN; return true; + case kClientTokenDrmCertificateReprovisioning: + *token_type = + video_widevine::ClientIdentification::DRM_DEVICE_CERTIFICATE; + return true; case kClientTokenDrmCert: default: // shouldn't happen diff --git a/core/src/content_key_session.cpp b/core/src/content_key_session.cpp index 1e391505..60e52514 100644 --- a/core/src/content_key_session.cpp +++ b/core/src/content_key_session.cpp @@ -105,18 +105,36 @@ OEMCryptoResult ContentKeySession::SelectKey(const std::string& key_id, // Decrypt for ContentKeySession OEMCryptoResult ContentKeySession::Decrypt( const OEMCrypto_SampleDescription* samples, size_t samples_length, - const OEMCrypto_CENCEncryptPatternDesc& pattern) { + const OEMCrypto_CENCEncryptPatternDesc& pattern, + bool is_any_subsample_protected) { size_t total_size = 0; for (size_t i = 0; i < samples_length; ++i) { total_size += samples[i].buffers.input_data_length; } - OEMCryptoResult sts; - M_TIME(sts = OEMCrypto_DecryptCENC(security_level_, key_handle_.data(), - key_handle_.size(), samples, - samples_length, &pattern), - metrics_, oemcrypto_decrypt_cenc_, sts, - metrics::Pow2Bucket(total_size)); + OEMCryptoResult sts = OEMCrypto_SUCCESS; + if (key_handle_.size() == 0 && !is_any_subsample_protected) { + for (size_t i = 0; i < samples_length; ++i) { + if (samples[i].subsamples != nullptr && + samples[i].subsamples_length > 0) { + const OEMCrypto_SubSampleDescription& subsample = + samples[i].subsamples[0]; + M_TIME(sts = OEMCrypto_CopyBuffer(oec_session_id_, + samples[i].buffers.input_data, + samples[i].buffers.input_data_length, + &samples[i].buffers.output_descriptor, + subsample.subsample_flags), + metrics_, oemcrypto_copy_buffer_, sts, + metrics::Pow2Bucket(samples[i].buffers.input_data_length)); + } + } + } else { + M_TIME(sts = OEMCrypto_DecryptCENC(security_level_, key_handle_.data(), + key_handle_.size(), samples, + samples_length, &pattern), + metrics_, oemcrypto_decrypt_cenc_, sts, + metrics::Pow2Bucket(total_size)); + } return sts; } diff --git a/core/src/crypto_session.cpp b/core/src/crypto_session.cpp index 0eef7f70..fb002b0c 100644 --- a/core/src/crypto_session.cpp +++ b/core/src/crypto_session.cpp @@ -62,7 +62,7 @@ OEMCryptoResult WrapIfNecessary(OEMCryptoResult ret_value) { return ret_value; } } #define RETURN_IF_NOT_OPEN(ret_value) \ - if (!open_) { \ + if (!IsOpen()) { \ LOGE("Crypto session is not open"); \ return WrapIfNecessary(ret_value); \ } @@ -70,17 +70,8 @@ OEMCryptoResult WrapIfNecessary(OEMCryptoResult ret_value) { return ret_value; } #define CRYPTO_ERROR(cdm_err, oem_err) \ CdmResponseType(cdm_err, oem_err, __func__) -#ifdef HAS_DUAL_KEY -/** - * Internal only OEMCrypto method. This is called before parsing the license - * response to indicate the response uses dual keys. If this isn't called, - * it is using single keys. - */ -extern "C" OEMCryptoResult OEMCrypto_UseSecondaryKey(OEMCrypto_SESSION session, - bool dual_key); -#endif - namespace wvcdm { + namespace { using UsageTableLock = std::unique_lock; @@ -93,6 +84,8 @@ constexpr size_t kAes128BlockSize = 16; constexpr int kMaxTerminateCountDown = 5; +constexpr char kUnavailableHdcpLevel[] = ""; + const std::string kStringNotAvailable = "NA"; // TODO(b/174412779): Remove when b/170704368 is fixed. @@ -292,6 +285,37 @@ OEMCryptoCipherMode ToOEMCryptoCipherMode(CdmCipherMode cipher_mode) { : OEMCrypto_CipherMode_CBCS; } +// static +const char* CryptoSession::HdcpCapabilityToString(HdcpCapability hdcp_level) { + switch (hdcp_level) { + case HDCP_NONE: + return "None"; + case HDCP_V1: // Means v1.x + return "v1.x"; + case HDCP_V2: // Means v2.0 + return "v2.0"; + case HDCP_V2_1: + return "v2.1"; + case HDCP_V2_2: + return "v2.2"; + case HDCP_V2_3: + return "v2.3"; + case HDCP_V1_0: + return "v1.0"; + case HDCP_V1_1: + return "v1.1"; + case HDCP_V1_2: + return "v1.2"; + case HDCP_V1_3: + return "v1.3"; + case HDCP_V1_4: + return "v1.4"; + case HDCP_NO_DIGITAL_OUTPUT: + return "No-Digital-Output"; + } + return UnknownEnumValueToString(static_cast(hdcp_level)); +} + CryptoSession::CryptoSession(metrics::CryptoMetrics* metrics) : metrics_(metrics), system_id_(NULL_SYSTEM_ID), @@ -345,6 +369,9 @@ CdmResponseType CryptoSession::GetProvisioningMethod( case OEMCrypto_BootCertificateChain: type = kClientTokenBootCertChain; break; + case OEMCrypto_DrmReprovisioning: + type = kClientTokenDrmCertificateReprovisioning; + break; case OEMCrypto_ProvisioningError: default: if (static_cast(method) == 0 && needs_keybox_provisioning_) { @@ -661,6 +688,9 @@ CdmResponseType CryptoSession::GetProvisioningToken( } else if (pre_provision_token_type_ == kClientTokenBootCertChain) { status = GetBootCertificateChain(requested_security_level, token, additional_token); + } else if (pre_provision_token_type_ == + kClientTokenDrmCertificateReprovisioning) { + status = GetTokenFromEmbeddedCertificate(token); } metrics_->crypto_session_get_token_.Increment(status); return status; @@ -1229,6 +1259,7 @@ CdmResponseType CryptoSession::PrepareAndSignProvisioningRequest( } OEMCryptoResult sts; + // TODO: b/305093063 - Refactor to a switch statement to improve readability if (pre_provision_token_type_ == kClientTokenKeybox) { should_specify_algorithm = false; const CdmResponseType status = GenerateDerivedKeys(message); @@ -1246,6 +1277,11 @@ CdmResponseType CryptoSession::PrepareAndSignProvisioningRequest( should_specify_algorithm = true; // Do nothing here. The key to signing the provisioning 4.0 request for each // stage has been loaded already when it was generated by OEMCrypto. + } else if (pre_provision_token_type_ == + kClientTokenDrmCertificateReprovisioning) { + should_specify_algorithm = false; + // Do nothing here. The baked-in certificate used as the token has already + // been loaded when the EncryptedClientId was filled in. } else { LOGE("Unknown method %d", pre_provision_token_type_); return CdmResponseType(UNKNOWN_CLIENT_TOKEN_TYPE); @@ -1417,6 +1453,153 @@ CdmResponseType CryptoSession::GetBootCertificateChain( return CdmResponseType(NO_ERROR); } +CdmResponseType CryptoSession::GetTokenFromEmbeddedCertificate( + std::string* token) { + RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED); + RETURN_IF_NULL(token, PARAMETER_NULL); + + CdmClientTokenType token_type = kClientTokenUninitialized; + const CdmResponseType sts = + GetProvisioningMethod(requested_security_level_, &token_type); + if (sts != NO_ERROR) { + LOGE("Failed to get token type"); + return sts; + } + if (token_type != kClientTokenDrmCertificateReprovisioning) { + token->clear(); + return CdmResponseType(NO_ERROR); + } + + size_t certificate_length = CERTIFICATE_DATA_SIZE; + std::string embedded_certificate(certificate_length, '\0'); + OEMCryptoResult status = + WithOecReadLock("GetTokenFromEmbeddedCertificate - attempt 1", [&] { + return OEMCrypto_GetEmbeddedDrmCertificate( + reinterpret_cast(&embedded_certificate.front()), + &certificate_length); + }); + + if (status == OEMCrypto_ERROR_SHORT_BUFFER) { + embedded_certificate.assign(certificate_length, '\0'); + status = + WithOecReadLock("GetTokenFromEmbeddedCertificate - attempt 2", [&] { + return OEMCrypto_GetEmbeddedDrmCertificate( + reinterpret_cast(&embedded_certificate.front()), + &certificate_length); + }); + } + + if (status == OEMCrypto_SUCCESS) { + // TODO: b/312782308 - Store embedded certificate as SignedDrmCertificate + video_widevine_client::sdk::HashedFile hashed_file; + video_widevine_client::sdk::File file; + embedded_certificate.resize(certificate_length); + if (hashed_file.ParseFromString(embedded_certificate) && + file.ParseFromString(hashed_file.file())) { + *token = file.device_certificate().certificate(); + return CdmResponseType(NO_ERROR); + } + } + + token->clear(); + return MapOEMCryptoResult(status, GET_TOKEN_FROM_EMBEDDED_CERT_ERROR, + "GetTokenFromEmbeddedCertificate"); +} + +CdmResponseType CryptoSession::GetDeviceInformation( + RequestedSecurityLevel requested_security_level, std::string* device_info) { + RETURN_IF_NULL(device_info, PARAMETER_NULL); + RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED); + if (GetSecurityLevel(requested_security_level) != kSecurityLevelL1) { + LOGE("CDM only supports L1 device_info"); + return CdmResponseType(NOT_IMPLEMENTED_ERROR); + } + CdmClientTokenType token_type = kClientTokenUninitialized; + const CdmResponseType status = + GetProvisioningMethod(requested_security_level, &token_type); + if (status != NO_ERROR) { + LOGE("Failed to get token type"); + return status; + } + if (token_type != kClientTokenBootCertChain) { + return CdmResponseType( + PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR); + } + + size_t device_info_length = 0; + OEMCryptoResult sts = WithOecReadLock("GetDeviceInformation Attempt 1", [&] { + return OEMCrypto_GetDeviceInformation(nullptr, &device_info_length); + }); + if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { + device_info->resize(device_info_length); + sts = WithOecReadLock("GetDeviceInformation Attempt 2", [&] { + return OEMCrypto_GetDeviceInformation( + MutableStringDataPointer(device_info), &device_info_length); + }); + } + if (sts != OEMCrypto_SUCCESS) { + LOGE("OEMCrypto_GetDeviceInformation failed: status = %d", + static_cast(sts)); + device_info->clear(); + return MapOEMCryptoResult(sts, GET_DEVICE_INFORMATION_ERROR, + "GetDeviceInformation"); + } + device_info->resize(device_info_length); + return CdmResponseType(NO_ERROR); +} + +CdmResponseType CryptoSession::GetDeviceSignedCsrPayload( + RequestedSecurityLevel requested_security_level, + const std::string& challenge, const std::string& device_info, + std::string* signed_csr_payload) { + RETURN_IF_NULL(signed_csr_payload, PARAMETER_NULL); + RETURN_IF_UNINITIALIZED(CRYPTO_SESSION_NOT_INITIALIZED); + if (GetSecurityLevel(requested_security_level) != kSecurityLevelL1) { + LOGE("CDM only supports L1 CSR payload"); + return CdmResponseType(NOT_IMPLEMENTED_ERROR); + } + CdmClientTokenType token_type = kClientTokenUninitialized; + const CdmResponseType status = + GetProvisioningMethod(requested_security_level, &token_type); + if (status != NO_ERROR) { + LOGE("Failed to get token type"); + return status; + } + if (token_type != kClientTokenBootCertChain) { + return CdmResponseType( + PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR); + } + + size_t signed_csr_payload_length = 0; + OEMCryptoResult sts = + WithOecReadLock("GetDeviceSignedCsrPayload Attempt 1", [&] { + return OEMCrypto_GetDeviceSignedCsrPayload( + reinterpret_cast(challenge.data()), + challenge.size(), + reinterpret_cast(device_info.data()), + device_info.size(), nullptr, &signed_csr_payload_length); + }); + if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { + signed_csr_payload->resize(signed_csr_payload_length); + sts = WithOecReadLock("GetDeviceSignedCsrPayload Attempt 2", [&] { + return OEMCrypto_GetDeviceSignedCsrPayload( + reinterpret_cast(challenge.data()), challenge.size(), + reinterpret_cast(device_info.data()), + device_info.size(), MutableStringDataPointer(signed_csr_payload), + &signed_csr_payload_length); + }); + } + if (sts != OEMCrypto_SUCCESS) { + LOGE("OEMCrypto_GetDeviceSignedCsrPayload failed: status = %d", + static_cast(sts)); + signed_csr_payload->clear(); + return MapOEMCryptoResult(sts, GET_DEVICE_SIGNED_CSR_PAYLOAD_ERROR, + "GetDeviceSignedCsrPayload"); + } + signed_csr_payload->resize(signed_csr_payload_length); + return CdmResponseType(NO_ERROR); +} + CdmResponseType CryptoSession::GenerateCertificateKeyPair( std::string* public_key, std::string* public_key_signature, std::string* wrapped_private_key, CryptoWrappedKey::Type* key_type) { @@ -1513,6 +1696,35 @@ CdmResponseType CryptoSession::SelectKey(const std::string& key_id, return key_session_->SelectKey(key_id, cipher_mode); }); + if (sts != last_select_key_error_) { + // Only log errors on first occurrence. Calls to SelectKey from + // the app are typically queued. If an error occurs, then it is + // expected that multiple failures will occur before the app is + // notified and stops queueing calls to the CDM. + last_select_key_error_ = sts; + if (sts == OEMCrypto_ERROR_INSUFFICIENT_HDCP || + sts == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION) { + const char* status_message = OemCryptoResultToString(sts); + HdcpCapability current_hdcp_level; + HdcpCapability max_hdcp_level; + const CdmResponseType hdcp_status = + GetHdcpCapabilities(¤t_hdcp_level, &max_hdcp_level); + const char* current_hdcp_message = kUnavailableHdcpLevel; + const char* max_hdcp_message = kUnavailableHdcpLevel; + if (hdcp_status == NO_ERROR) { + current_hdcp_message = HdcpCapabilityToString(current_hdcp_level); + max_hdcp_message = HdcpCapabilityToString(max_hdcp_level); + } + LOGE( + "oec_session_id = %u, security_level = %s, " + "status = %s, current_hdcp_level = %s, " + "max_hdcp_level = %s", + oec_session_id_, + RequestedSecurityLevelToString(requested_security_level_), + status_message, current_hdcp_message, max_hdcp_message); + } + } + switch (sts) { // SelectKey errors. case OEMCrypto_SUCCESS: @@ -1781,8 +1993,8 @@ CdmResponseType CryptoSession::Decrypt( } // Perform decrypt - const OEMCryptoResult sts = - DecryptMultipleSamples(oec_samples, params.cipher_mode, oec_pattern); + const OEMCryptoResult sts = DecryptMultipleSamples( + oec_samples, params.cipher_mode, oec_pattern, is_any_sample_protected); if (sts != OEMCrypto_SUCCESS && last_decrypt_error_ != sts) { // Decrypt errors and warnings are only logged when the error code @@ -1791,11 +2003,26 @@ CdmResponseType CryptoSession::Decrypt( // the next call. The calling application may make several more // decrypt requests before the error is handled by the app. last_decrypt_error_ = sts; - if (sts == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION) { - LOGW( - "OEMCrypto_DecryptCENC is warning of mixed HDCP output protection: " - "oec_session_id = %u", - oec_session_id_); + if (sts == OEMCrypto_WARNING_MIXED_OUTPUT_PROTECTION || + sts == OEMCrypto_ERROR_INSUFFICIENT_HDCP) { + const char* status_message = OemCryptoResultToString(sts); + HdcpCapability current_hdcp_level; + HdcpCapability max_hdcp_level; + const CdmResponseType hdcp_status = + GetHdcpCapabilities(¤t_hdcp_level, &max_hdcp_level); + const char* current_hdcp_message = kUnavailableHdcpLevel; + const char* max_hdcp_message = kUnavailableHdcpLevel; + if (hdcp_status == NO_ERROR) { + current_hdcp_message = HdcpCapabilityToString(current_hdcp_level); + max_hdcp_message = HdcpCapabilityToString(max_hdcp_level); + } + LOGE( + "OEMCrypto_DecryptCENC failed: oec_session_id = %u, " + "security_level = %s, status = %s, current_hdcp_level = %s, " + "max_hdcp_level = %s", + oec_session_id_, + RequestedSecurityLevelToString(requested_security_level_), + status_message, current_hdcp_message, max_hdcp_message); } else { LOGE( "OEMCrypto_DecryptCENC failed: oec_session_id = %u, " @@ -2914,22 +3141,24 @@ bool CryptoSession::GetAnalogOutputCapabilities(bool* can_support_output, OEMCryptoResult CryptoSession::DecryptMultipleSamples( const std::vector& samples, - CdmCipherMode cipher_mode, - const OEMCrypto_CENCEncryptPatternDesc& pattern) { + CdmCipherMode cipher_mode, const OEMCrypto_CENCEncryptPatternDesc& pattern, + bool is_any_subsample_protected) { OEMCryptoResult sts = OEMCrypto_ERROR_BUFFER_TOO_LARGE; // If there's only one sample, automatically fall through to avoid a redundant // roundtrip through OEMCrypto_DecryptCENC() if (samples.size() > 1) { WithOecSessionLock("DecryptMultipleSamples", [&] { - sts = key_session_->Decrypt(samples.data(), samples.size(), pattern); + sts = key_session_->Decrypt(samples.data(), samples.size(), pattern, + is_any_subsample_protected); }); } if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE) { // Fall back to sending each sample individually for (const OEMCrypto_SampleDescription& sample : samples) { - sts = DecryptSample(sample, cipher_mode, pattern); + sts = DecryptSample(sample, cipher_mode, pattern, + is_any_subsample_protected); if (sts != OEMCrypto_SUCCESS) break; } } @@ -2939,7 +3168,8 @@ OEMCryptoResult CryptoSession::DecryptMultipleSamples( OEMCryptoResult CryptoSession::DecryptSample( const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode, - const OEMCrypto_CENCEncryptPatternDesc& pattern) { + const OEMCrypto_CENCEncryptPatternDesc& pattern, + bool is_any_subsample_protected) { OEMCryptoResult sts = OEMCrypto_ERROR_BUFFER_TOO_LARGE; // If there's only one subsample and it contains only one type of region, @@ -2949,7 +3179,8 @@ OEMCryptoResult CryptoSession::DecryptSample( (sample.subsamples[0].num_bytes_clear > 0 && sample.subsamples[0].num_bytes_encrypted > 0)) { WithOecSessionLock("DecryptSample", [&] { - sts = key_session_->Decrypt(&sample, 1, pattern); + sts = key_session_->Decrypt(&sample, 1, pattern, + is_any_subsample_protected); }); } @@ -2981,7 +3212,8 @@ OEMCryptoResult CryptoSession::DecryptSample( fake_sample.subsamples = &clear_subsample; fake_sample.subsamples_length = 1; - sts = LegacyDecrypt(fake_sample, cipher_mode, pattern); + sts = LegacyDecrypt(fake_sample, cipher_mode, pattern, + is_any_subsample_protected); if (sts != OEMCrypto_SUCCESS) break; fake_sample.buffers.input_data += length; @@ -3007,7 +3239,8 @@ OEMCryptoResult CryptoSession::DecryptSample( fake_sample.subsamples = &encrypted_subsample; fake_sample.subsamples_length = 1; - sts = LegacyDecrypt(fake_sample, cipher_mode, pattern); + sts = LegacyDecrypt(fake_sample, cipher_mode, pattern, + is_any_subsample_protected); if (sts != OEMCrypto_SUCCESS) break; fake_sample.buffers.input_data += length; @@ -3026,7 +3259,8 @@ OEMCryptoResult CryptoSession::DecryptSample( OEMCryptoResult CryptoSession::LegacyDecrypt( const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode, - const OEMCrypto_CENCEncryptPatternDesc& pattern) { + const OEMCrypto_CENCEncryptPatternDesc& pattern, + bool is_any_subsample_protected) { const size_t max_chunk_size = GetMaxSubsampleRegionSize(); OEMCryptoResult sts = OEMCrypto_ERROR_NOT_IMPLEMENTED; @@ -3056,7 +3290,8 @@ OEMCryptoResult CryptoSession::LegacyDecrypt( } if (is_encrypted || sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) { WithOecSessionLock("LegacyDecrypt() calling key_session_->Decrypt()", [&] { - sts = key_session_->Decrypt(&sample, 1, pattern); + sts = key_session_->Decrypt(&sample, 1, pattern, + is_any_subsample_protected); }); if (sts == OEMCrypto_ERROR_BUFFER_TOO_LARGE) { @@ -3072,7 +3307,8 @@ OEMCryptoResult CryptoSession::LegacyDecrypt( : max_chunk_size; if (sample.buffers.input_data_length > chunk_size) { - sts = LegacyDecryptInChunks(sample, cipher_mode, pattern, chunk_size); + sts = LegacyDecryptInChunks(sample, cipher_mode, pattern, chunk_size, + is_any_subsample_protected); } } } @@ -3121,7 +3357,8 @@ OEMCryptoResult CryptoSession::LegacyCopyBufferInChunks( OEMCryptoResult CryptoSession::LegacyDecryptInChunks( const OEMCrypto_SampleDescription& sample, CdmCipherMode cipher_mode, - const OEMCrypto_CENCEncryptPatternDesc& pattern, size_t max_chunk_size) { + const OEMCrypto_CENCEncryptPatternDesc& pattern, size_t max_chunk_size, + bool is_any_subsample_protected) { const OEMCrypto_SubSampleDescription& subsample = sample.subsamples[0]; const bool is_protected = (subsample.num_bytes_encrypted > 0); @@ -3155,7 +3392,8 @@ OEMCryptoResult CryptoSession::LegacyDecryptInChunks( // pattern length long, which is also guaranteed to be an exact number // of AES blocks long. WithOecSessionLock("LegacyDecryptInChunks", [&] { - sts = key_session_->Decrypt(&fake_sample, 1, pattern); + sts = key_session_->Decrypt(&fake_sample, 1, pattern, + is_any_subsample_protected); }); if (sts != OEMCrypto_SUCCESS) break; diff --git a/core/src/device_files.proto b/core/src/device_files.proto index 7b4a02ef..caf93563 100644 --- a/core/src/device_files.proto +++ b/core/src/device_files.proto @@ -20,6 +20,10 @@ message NameValue { optional string value = 2; } +message SavedStorage { + map files = 1; +} + message OemCertificate { enum PrivateKeyType { RSA = 0; diff --git a/core/src/license.cpp b/core/src/license.cpp index 6f44a625..a7b5f297 100644 --- a/core/src/license.cpp +++ b/core/src/license.cpp @@ -195,9 +195,7 @@ std::vector ExtractContentKeys(const License& license) { } // namespace CdmLicense::CdmLicense(const CdmSessionId& session_id) - : crypto_session_(nullptr), - policy_engine_(nullptr), - session_id_(session_id), + : session_id_(session_id), initialized_(false), renew_with_client_id_(false), is_offline_(false), @@ -206,15 +204,17 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id) license_key_type_(kLicenseKeyTypeContent) {} CdmLicense::CdmLicense(const CdmSessionId& session_id, wvutil::Clock* clock) - : crypto_session_(nullptr), - policy_engine_(nullptr), - session_id_(session_id), + : session_id_(session_id), initialized_(false), renew_with_client_id_(false), is_offline_(false), use_privacy_mode_(false), + clock_(clock), license_key_type_(kLicenseKeyTypeContent) { - clock_.reset(clock); + if (!clock_) { + LOGW("Input |clock| is null, using default"); + clock_.reset(new wvutil::Clock()); + } } CdmLicense::~CdmLicense() {} diff --git a/core/src/license_key_status.cpp b/core/src/license_key_status.cpp index 76993358..b6224957 100644 --- a/core/src/license_key_status.cpp +++ b/core/src/license_key_status.cpp @@ -316,6 +316,7 @@ void LicenseKeyStatus::ParseContentKey(const KeyContainer& key, if (key.has_required_protection()) { default_hdcp_level_ = ProtobufHdcpToOemCryptoHdcp(key.required_protection().hdcp()); + last_reported_license_hdcp_level_ = default_hdcp_level_; } } @@ -399,14 +400,36 @@ void LicenseKeyStatus::ApplyConstraints( } } + const bool device_hdcp_level_change = + (last_reported_device_hdcp_level_ != new_hdcp_level); + last_reported_device_hdcp_level_ = new_hdcp_level; + CryptoSession::HdcpCapability desired_hdcp_level = default_hdcp_level_; if (current_constraint && current_constraint->has_required_protection()) { desired_hdcp_level = ProtobufHdcpToOemCryptoHdcp( current_constraint->required_protection().hdcp()); } + const bool license_hdcp_level_change = + (desired_hdcp_level != last_reported_license_hdcp_level_); + last_reported_license_hdcp_level_ = desired_hdcp_level; - meets_constraints_ = + const bool meets_new_constrains = MeetsHdcpRequirements(desired_hdcp_level, new_hdcp_level); + const bool meets_constraints_change = + (meets_new_constrains != meets_constraints_); + meets_constraints_ = meets_new_constrains; + + const bool change = device_hdcp_level_change || license_hdcp_level_change || + meets_constraints_change; + if (!meets_constraints_ && change) { + LOGD( + "new_hdcp_level = %s, desired_hdcp_level = %s, " + "default_hdcp_level = %s, video_pixels = %u", + CryptoSession::HdcpCapabilityToString(new_hdcp_level), + CryptoSession::HdcpCapabilityToString(desired_hdcp_level), + CryptoSession::HdcpCapabilityToString(default_hdcp_level_), + video_pixels); + } } void LicenseKeyStatus::SetConstraints(const ConstraintList& constraints) { diff --git a/core/src/license_protocol.proto b/core/src/license_protocol.proto index 25ede138..55777b69 100644 --- a/core/src/license_protocol.proto +++ b/core/src/license_protocol.proto @@ -1026,10 +1026,21 @@ message SignedProvisioningMessage { ARCPP_PROVISIONING = 4; // ChromeOS/Arc++ devices. // Android-Attestation-based OTA keyboxes. ANDROID_ATTESTATION_KEYBOX_OTA = 6; + // DRM certificate reprovisioning for individualization of embedded + // DRM certificates used by internal L3 CDMs only. + DRM_REPROVISIONING = 7; INTEL_SIGMA_101 = 101; // Intel Sigma 1.0.1 protocol. INTEL_SIGMA_210 = 210; // Intel Sigma 2.1.0 protocol. } + // Used by provisioning 4.0 to deliver cast certificates in which the server + // delivers a new rsa private key that must be encrypted and signed. + enum SessionKeyType { + UNDEFINED = 0; + WRAPPED_AES_KEY = 1; + EPHEMERAL_ECC_PUBLIC_KEY = 2; + } + // Serialized protobuf message for the corresponding protocol and stage of // the provisioning exchange. ProvisioningRequest or ProvisioningResponse // in the case of Provisioning 2.0, 3.0, 4.0 and ARCPP_PROVISIONING. Required. @@ -1056,6 +1067,21 @@ message SignedProvisioningMessage { optional HashAlgorithmProto hash_algorithm = 7; // Indicates which version of the protocol is in use. optional ProvisioningProtocolVersion protocol_version = 8; + // If populated, the contents of this field will be signaled by the + // |session_key_type| type. If the |session_key_type| is WRAPPED_AES_KEY the + // key is the bytes of an encrypted AES key. If the |session_key_type| is + // EPHEMERAL_ECC_PUBLIC_KEY the field contains the bytes of an RFC5208 ASN1 + // serialized ECC public key. + // This field is only required to be set in a success response to + // Provisioning 4.0 X509 (cast) certificate request. + optional bytes session_key = 9; + // Optional field that contains the algorithm type used to generate the + // session_key and signature in a ProvisioningResponse message. This value is + // populated in a success response to a request for a X509 (cast) certificate. + // The value used depends on the key type of the PublicKeyToCertify contained + // in Provisioning 4.0 ProvisioningMessage. + // This value must be populated if session_key is populated. + optional SessionKeyType session_key_type = 10; } // ---------------------------------------------------------------------------- @@ -1247,6 +1273,10 @@ message DrmCertificate { DEVICE = 2; SERVICE = 3; PROVISIONER = 4; + // Only used by internal L3 CDMs with baked-in (embedded) certificates that + // support the Drm Reprovisioning method for individualization of embedded + // certificates. + DEVICE_EMBEDDED = 5; } enum ServiceType { UNKNOWN_SERVICE_TYPE = 0; diff --git a/core/src/oemcrypto_adapter_static.cpp b/core/src/oemcrypto_adapter_static.cpp index de07d877..b0bd14ba 100644 --- a/core/src/oemcrypto_adapter_static.cpp +++ b/core/src/oemcrypto_adapter_static.cpp @@ -41,10 +41,12 @@ OEMCryptoResult OEMCrypto_InitializeAndCheckKeybox( } OEMCryptoResult OEMCrypto_SetDebugIgnoreKeyboxCount(uint32_t count) { + (void)count; return OEMCrypto_ERROR_NOT_IMPLEMENTED; } -OEMCryptoResult OEMCrypto_SetAllowTestKeybox(bool count) { +OEMCryptoResult OEMCrypto_SetAllowTestKeybox(bool allow) { + (void)allow; return OEMCrypto_SUCCESS; } @@ -54,7 +56,8 @@ OEMCryptoResult OEMCrypto_OpenSession(OEMCrypto_SESSION* session, } OEMCryptoResult OEMCrypto_InstallKeybox(const uint8_t* keybox, - size_t keyBoxLength, RequestedSecurityLevel) { + size_t keyBoxLength, + RequestedSecurityLevel) { return ::OEMCrypto_InstallKeybox(keybox, keyBoxLength); } @@ -104,28 +107,29 @@ OEMCryptoResult OEMCrypto_GetMaxNumberOfSessions(RequestedSecurityLevel, return ::OEMCrypto_GetMaxNumberOfSessions(maximum); } -OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod(RequestedSecurityLevel) { +OEMCrypto_ProvisioningMethod OEMCrypto_GetProvisioningMethod( + RequestedSecurityLevel) { return ::OEMCrypto_GetProvisioningMethod(); } -uint32_t OEMCrypto_SupportedCertificates(RequestedSecurityLevel level) { +uint32_t OEMCrypto_SupportedCertificates(RequestedSecurityLevel) { return ::OEMCrypto_SupportedCertificates(); } -OEMCryptoResult OEMCrypto_CreateUsageTableHeader(RequestedSecurityLevel level, +OEMCryptoResult OEMCrypto_CreateUsageTableHeader(RequestedSecurityLevel, uint8_t* header_buffer, size_t* header_buffer_length) { return ::OEMCrypto_CreateUsageTableHeader(header_buffer, header_buffer_length); } -OEMCryptoResult OEMCrypto_LoadUsageTableHeader(RequestedSecurityLevel level, +OEMCryptoResult OEMCrypto_LoadUsageTableHeader(RequestedSecurityLevel, const uint8_t* buffer, size_t buffer_length) { return ::OEMCrypto_LoadUsageTableHeader(buffer, buffer_length); } -OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(RequestedSecurityLevel level, +OEMCryptoResult OEMCrypto_ShrinkUsageTableHeader(RequestedSecurityLevel, uint32_t new_table_size, uint8_t* header_buffer, size_t* header_buffer_length) { @@ -174,46 +178,46 @@ OEMCryptoResult OEMCrypto_ProductionReady(RequestedSecurityLevel) { } OEMCryptoResult OEMCrypto_DecryptCENC( - RequestedSecurityLevel level, const uint8_t* key_handle, - size_t key_handle_length, const OEMCrypto_SampleDescription* samples, - size_t samples_length, const OEMCrypto_CENCEncryptPatternDesc* pattern) { + RequestedSecurityLevel, const uint8_t* key_handle, size_t key_handle_length, + const OEMCrypto_SampleDescription* samples, size_t samples_length, + const OEMCrypto_CENCEncryptPatternDesc* pattern) { return OEMCrypto_DecryptCENC(key_handle, key_handle_length, samples, samples_length, pattern); } OEMCryptoResult OEMCrypto_Generic_Encrypt( - RequestedSecurityLevel level, const uint8_t* key_handle, - size_t key_handle_length, const OEMCrypto_SharedMemory* in_buffer, - size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, + RequestedSecurityLevel, const uint8_t* key_handle, size_t key_handle_length, + const OEMCrypto_SharedMemory* in_buffer, size_t in_buffer_length, + const uint8_t* iv, OEMCrypto_Algorithm algorithm, OEMCrypto_SharedMemory* out_buffer) { return OEMCrypto_Generic_Encrypt(key_handle, key_handle_length, in_buffer, in_buffer_length, iv, algorithm, out_buffer); } OEMCryptoResult OEMCrypto_Generic_Decrypt( - RequestedSecurityLevel level, const uint8_t* key_handle, - size_t key_handle_length, const OEMCrypto_SharedMemory* in_buffer, - size_t in_buffer_length, const uint8_t* iv, OEMCrypto_Algorithm algorithm, + RequestedSecurityLevel, const uint8_t* key_handle, size_t key_handle_length, + const OEMCrypto_SharedMemory* in_buffer, size_t in_buffer_length, + const uint8_t* iv, OEMCrypto_Algorithm algorithm, OEMCrypto_SharedMemory* out_buffer) { return OEMCrypto_Generic_Decrypt(key_handle, key_handle_length, in_buffer, in_buffer_length, iv, algorithm, out_buffer); } OEMCryptoResult OEMCrypto_Generic_Sign( - RequestedSecurityLevel level, const uint8_t* key_handle, - size_t key_handle_length, const OEMCrypto_SharedMemory* buffer, - size_t buffer_length, OEMCrypto_Algorithm algorithm, - OEMCrypto_SharedMemory* signature, size_t* signature_length) { + RequestedSecurityLevel, const uint8_t* key_handle, size_t key_handle_length, + const OEMCrypto_SharedMemory* buffer, size_t buffer_length, + OEMCrypto_Algorithm algorithm, OEMCrypto_SharedMemory* signature, + size_t* signature_length) { return OEMCrypto_Generic_Sign(key_handle, key_handle_length, buffer, buffer_length, algorithm, signature, signature_length); } OEMCryptoResult OEMCrypto_Generic_Verify( - RequestedSecurityLevel level, const uint8_t* key_handle, - size_t key_handle_length, const OEMCrypto_SharedMemory* buffer, - size_t buffer_length, OEMCrypto_Algorithm algorithm, - const OEMCrypto_SharedMemory* signature, size_t signature_length) { + RequestedSecurityLevel, const uint8_t* key_handle, size_t key_handle_length, + const OEMCrypto_SharedMemory* buffer, size_t buffer_length, + OEMCrypto_Algorithm algorithm, const OEMCrypto_SharedMemory* signature, + size_t signature_length) { return OEMCrypto_Generic_Verify(key_handle, key_handle_length, buffer, buffer_length, algorithm, signature, signature_length); diff --git a/core/src/oemcrypto_embedded_cert_stubs.cpp b/core/src/oemcrypto_embedded_cert_stubs.cpp new file mode 100644 index 00000000..8f6362bb --- /dev/null +++ b/core/src/oemcrypto_embedded_cert_stubs.cpp @@ -0,0 +1,12 @@ +// Copyright 2024 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine Master +// License Agreement. + +#include "OEMCryptoCENC.h" + +OEMCryptoResult OEMCrypto_GetEmbeddedDrmCertificate( + uint8_t* public_cert, size_t* public_cert_length) { + (void)public_cert; + (void)public_cert_length; + return OEMCrypto_ERROR_NOT_IMPLEMENTED; +} diff --git a/core/src/oemcrypto_ota_stubs.cpp b/core/src/oemcrypto_ota_stubs.cpp index 2c0b59de..4c0784c3 100644 --- a/core/src/oemcrypto_ota_stubs.cpp +++ b/core/src/oemcrypto_ota_stubs.cpp @@ -7,6 +7,10 @@ extern "C" OEMCryptoResult OEMCrypto_GenerateOTARequest( OEMCrypto_SESSION session, uint8_t* buffer, size_t* buffer_length, uint32_t use_test_key) { + (void)session; + (void)buffer; + (void)buffer_length; + (void)use_test_key; return OEMCrypto_ERROR_NOT_IMPLEMENTED; } @@ -14,5 +18,9 @@ extern "C" OEMCryptoResult OEMCrypto_ProcessOTAKeybox(OEMCrypto_SESSION session, const uint8_t* buffer, size_t buffer_length, uint32_t use_test_key) { + (void)session; + (void)buffer; + (void)buffer_length; + (void)use_test_key; return OEMCrypto_ERROR_NOT_IMPLEMENTED; } diff --git a/core/src/policy_engine.cpp b/core/src/policy_engine.cpp index 6ee78c58..08d85524 100644 --- a/core/src/policy_engine.cpp +++ b/core/src/policy_engine.cpp @@ -39,9 +39,12 @@ PolicyEngine::PolicyEngine(CdmSessionId session_id, if (version >= kMinOemCryptoApiVersionSupportsRenewalDelayBase) { policy_timers_.reset(new PolicyTimersV18()); } + } else { + LOGW("Failed to get API version: session_id = %s", IdToString(session_id)); } - if (policy_timers_ == nullptr) { + if (!policy_timers_) { + // Use V16 policy timers if getting version failed. policy_timers_.reset(new PolicyTimersV16()); } InitDevice(crypto_session); diff --git a/core/src/privacy_crypto_apple.cpp b/core/src/privacy_crypto_apple.cpp index 788b1fad..b9b1076d 100644 --- a/core/src/privacy_crypto_apple.cpp +++ b/core/src/privacy_crypto_apple.cpp @@ -268,11 +268,10 @@ bool AesCbcKey::Init(const std::string& key) { return true; } -bool AesCbcKey::Encrypt(const std::string& in, std::string* out, - std::string* iv) { +bool AesCbcKey::Encrypt(const std::string& in, const std::string& iv, + std::string* out) { assert(!in.empty()); - assert(iv != nullptr); - assert(iv->size() == kCCBlockSizeAES128); + assert(iv.size() == kCCBlockSizeAES128); assert(out != nullptr); assert(!key_.empty()); @@ -281,7 +280,7 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out, size_t length; CCCryptorStatus result = CCCrypt(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, - key_.c_str(), key_.length(), iv->c_str(), in.c_str(), in.length(), + key_.c_str(), key_.length(), iv.c_str(), in.c_str(), in.length(), &temp[0], temp.size(), &length); if (result != kCCSuccess) { diff --git a/core/src/privacy_crypto_boringssl.cpp b/core/src/privacy_crypto_boringssl.cpp index 86ddea95..6377642b 100644 --- a/core/src/privacy_crypto_boringssl.cpp +++ b/core/src/privacy_crypto_boringssl.cpp @@ -89,18 +89,14 @@ bool AesCbcKey::Init(const std::string& key) { return true; } -bool AesCbcKey::Encrypt(const std::string& in, std::string* out, - std::string* iv) { +bool AesCbcKey::Encrypt(const std::string& in, const std::string& iv, + std::string* out) { if (in.empty()) { LOGE("No cleartext provided"); return false; } - if (iv == nullptr) { - LOGE("Initialization vector output parameter |iv| not provided"); - return false; - } - if (iv->size() != AES_BLOCK_SIZE) { - LOGE("Invalid IV size: %zu", iv->size()); + if (iv.size() != AES_BLOCK_SIZE) { + LOGE("Invalid IV size: %zu", iv.size()); return false; } if (out == nullptr) { @@ -114,8 +110,8 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out, EVP_CIPHER_CTX* evp_cipher_ctx = EVP_CIPHER_CTX_new(); if (EVP_EncryptInit(evp_cipher_ctx, EVP_aes_128_cbc(), - reinterpret_cast(&key_[0]), - reinterpret_cast(&(*iv)[0])) == 0) { + reinterpret_cast(&key_[0]), + reinterpret_cast(&iv[0])) == 0) { LOGE("AES CBC setup failure: %s", ERR_error_string(ERR_get_error(), nullptr)); EVP_CIPHER_CTX_free(evp_cipher_ctx); @@ -124,10 +120,10 @@ bool AesCbcKey::Encrypt(const std::string& in, std::string* out, out->resize(in.size() + AES_BLOCK_SIZE); int out_length = static_cast(out->size()); - if (EVP_EncryptUpdate( - evp_cipher_ctx, reinterpret_cast(&(*out)[0]), &out_length, - reinterpret_cast(const_cast(in.data())), - static_cast(in.size())) == 0) { + if (EVP_EncryptUpdate(evp_cipher_ctx, reinterpret_cast(&(*out)[0]), + &out_length, + reinterpret_cast(in.data()), + static_cast(in.size())) == 0) { LOGE("AES CBC encryption failure: %s", ERR_error_string(ERR_get_error(), nullptr)); EVP_CIPHER_CTX_free(evp_cipher_ctx); diff --git a/core/src/privacy_crypto_dummy.cpp b/core/src/privacy_crypto_dummy.cpp index 8b100c59..c016b478 100644 --- a/core/src/privacy_crypto_dummy.cpp +++ b/core/src/privacy_crypto_dummy.cpp @@ -29,8 +29,8 @@ AesCbcKey::~AesCbcKey() {} bool AesCbcKey::Init(const std::string& key) { return false; } -bool AesCbcKey::Encrypt(const std::string& in, std::string* out, - std::string* iv) { +bool AesCbcKey::Encrypt(const std::string& in, const std::string& iv, + std::string* out) { return false; } diff --git a/core/src/service_certificate.cpp b/core/src/service_certificate.cpp index c89648a1..4d6c8607 100644 --- a/core/src/service_certificate.cpp +++ b/core/src/service_certificate.cpp @@ -251,7 +251,7 @@ CdmResponseType ServiceCertificate::EncryptClientId( AesCbcKey aes; if (!aes.Init(key)) return CdmResponseType(CLIENT_ID_AES_INIT_ERROR); - if (!aes.Encrypt(id, &enc_id, &iv)) + if (!aes.Encrypt(id, iv, &enc_id)) return CdmResponseType(CLIENT_ID_AES_ENCRYPT_ERROR); CdmResponseType encrypt_result = EncryptRsaOaep(key, &enc_key); diff --git a/core/src/system_id_extractor.cpp b/core/src/system_id_extractor.cpp index edfdd3f0..72b548fd 100644 --- a/core/src/system_id_extractor.cpp +++ b/core/src/system_id_extractor.cpp @@ -59,6 +59,8 @@ bool SystemIdExtractor::ExtractSystemId(uint32_t* system_id) { bool success = false; switch (type) { case kClientTokenDrmCert: + // TODO: b/309675153 - Extract system id when using DRM reprovisioning. + case kClientTokenDrmCertificateReprovisioning: LOGW( "Cannot get a system ID from a DRM certificate, " "using null system ID: security_level = %s", diff --git a/core/src/wv_cdm_types.cpp b/core/src/wv_cdm_types.cpp index 6015f1f7..edc2edf2 100644 --- a/core/src/wv_cdm_types.cpp +++ b/core/src/wv_cdm_types.cpp @@ -12,6 +12,8 @@ namespace wvcdm { namespace { const char kEmptyIdRep[] = ""; const char kNullIdRep[] = ""; +const char kFalseRep[] = "false"; +const char kTrueRep[] = "true"; // Thread local buffer used by UnknownEnumValueToString() to represent // unknown enum values. @@ -74,6 +76,8 @@ const char* CdmClientTokenTypeToString(CdmClientTokenType type) { return "BootCertChain"; case kClientTokenUninitialized: return "Uninitialized"; + case kClientTokenDrmCertificateReprovisioning: + return "DrmCertificateReprovisioning"; } return UnknownValueRep(type); } @@ -747,6 +751,8 @@ const char* CdmResponseEnumToString(CdmResponseEnum cdm_response_enum) { return "GET_TOKEN_FROM_KEYBOX_ERROR"; case KEYBOX_TOKEN_TOO_SHORT: return "KEYBOX_TOKEN_TOO_SHORT"; + case GET_TOKEN_FROM_EMBEDDED_CERT_ERROR: + return "GET_TOKEN_FROM_EMBEDDED_CERT_ERROR"; case EXTRACT_SYSTEM_ID_FROM_OEM_CERT_ERROR: return "EXTRACT_SYSTEM_ID_FROM_OEM_CERT_ERROR"; case RSA_SIGNATURE_GENERATION_ERROR: @@ -819,6 +825,10 @@ const char* CdmResponseEnumToString(CdmResponseEnum cdm_response_enum) { return "PROVISIONING_TYPE_IS_NOT_BOOT_CERTIFICATE_CHAIN_ERROR"; case GET_BOOT_CERTIFICATE_CHAIN_ERROR: return "GET_BOOT_CERTIFICATE_CHAIN_ERROR"; + case GET_DEVICE_INFORMATION_ERROR: + return "GET_DEVICE_INFORMATION_ERROR"; + case GET_DEVICE_SIGNED_CSR_PAYLOAD_ERROR: + return "GET_DEVICE_SIGNED_CSR_PAYLOAD_ERROR"; case GENERATE_CERTIFICATE_KEY_PAIR_ERROR: return "GENERATE_CERTIFICATE_KEY_PAIR_ERROR"; case GENERATE_CERTIFICATE_KEY_PAIR_UNKNOWN_TYPE_ERROR: @@ -888,6 +898,8 @@ const char* IdPtrToString(const std::string* id) { return id->empty() ? kEmptyIdRep : id->c_str(); } +const char* BoolToString(bool value) { return value ? kTrueRep : kFalseRep; } + const char* OemCryptoResultToString(OEMCryptoResult result) { switch (result) { /* OEMCrypto return values */ diff --git a/core/test/cdm_engine_test.cpp b/core/test/cdm_engine_test.cpp index 9ab7598b..074cf343 100644 --- a/core/test/cdm_engine_test.cpp +++ b/core/test/cdm_engine_test.cpp @@ -21,6 +21,8 @@ #include "license_holder.h" #include "license_request.h" #include "log.h" +// TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented. +#include "oec_device_features.h" #include "properties.h" #include "string_conversions.h" #include "test_base.h" @@ -117,13 +119,6 @@ class WvCdmEnginePreProvTest : public WvCdmTestBaseWithEngine { std::string session_id_; }; -class WvCdmEnginePreProvTestStaging : public WvCdmEnginePreProvTest { - public: - WvCdmEnginePreProvTestStaging() { - config_ = ConfigTestEnv(kContentProtectionStagingServer); - } -}; - class WvCdmEnginePreProvTestProd : public WvCdmEnginePreProvTest { public: WvCdmEnginePreProvTestProd() { @@ -152,6 +147,12 @@ class WvCdmEngineTest : public WvCdmEnginePreProvTest { WvCdmEngineTest() {} void SetUp() override { + // TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented. + if (wvoec::global_features.provisioning_method == + OEMCrypto_DrmReprovisioning) { + GTEST_SKIP() + << "Skipping until Drm Reprovisioning server support is implemented."; + } WvCdmEnginePreProvTest::SetUp(); session_opened_ = false; WvCdmEnginePreProvTest::OpenSession(); @@ -334,8 +335,6 @@ TEST_F(WvCdmEngineTest, SetLicensingServiceInvalidCertificate) { NO_ERROR); }; -TEST_F(WvCdmEnginePreProvTestStaging, ProvisioningTest) { EnsureProvisioned(); } - TEST_F(WvCdmEnginePreProvTestUatBinary, ProvisioningTest) { EnsureProvisioned(); } diff --git a/core/test/cdm_session_unittest.cpp b/core/test/cdm_session_unittest.cpp index 0e553f53..876c5f7e 100644 --- a/core/test/cdm_session_unittest.cpp +++ b/core/test/cdm_session_unittest.cpp @@ -12,6 +12,7 @@ #include "cdm_usage_table.h" #include "crypto_key.h" #include "crypto_wrapped_key.h" +#include "oec_device_features.h" #include "properties.h" #include "service_certificate.h" #include "string_conversions.h" @@ -215,6 +216,10 @@ class CdmSessionTest : public WvCdmTestBase { protected: void SetUp() override { WvCdmTestBase::SetUp(); + if (wvoec::global_features.derive_key_method == + wvoec::DeviceFeatures::NO_METHOD) { + GTEST_SKIP() << "Test for devices that can derive session keys only."; + } metrics_ = std::make_shared(); cdm_session_.reset(new CdmSession(nullptr, metrics_)); // Inject testing mocks. diff --git a/core/test/certificate_provisioning_unittest.cpp b/core/test/certificate_provisioning_unittest.cpp index 9f80b2be..944c7af2 100644 --- a/core/test/certificate_provisioning_unittest.cpp +++ b/core/test/certificate_provisioning_unittest.cpp @@ -10,12 +10,20 @@ #include #include "crypto_session.h" +#include "license_protocol.pb.h" #include "metrics_collections.h" +#include "string_conversions.h" +#include "string_format.h" #include "test_base.h" #include "wv_cdm_types.h" namespace { +using video_widevine::DrmCertificate; +using video_widevine::ProvisioningResponse; +using video_widevine::SignedDrmCertificate; +using video_widevine::SignedProvisioningMessage; + const std::string kSignedDeviceCertificate = wvutil::a2bs_hex( "0A3D0802121B7769646576696E655F746573745F73657269616C5F6E756D62657218C09A0C" "28D2093A11746573742E7769646576696E652E636F6D6080B51812097369676E617475726" @@ -50,11 +58,108 @@ const int64_t kCreationTime = 200000; const int64_t kExpirationTime = 400000; const std::string kSerialNumber = "widevine_test_serial_number"; const uint32_t kSystemId = 1234; +const uint32_t kNonce = 0x49e81305; +const std::string kNonceString = "\x49\xe8\x13\x05"; +const std::string kEmptyString; +const std::string kFakeCoreMessage = wvutil::a2bs_hex("DEADBEEF"); +const std::string kFakeProvisioningRequestSignature = + "a very real provisioning request signature"; +const std::string kFakeBuildInfo = "Mock Crypto Session - License Test"; +const std::string kFakeAuthority = "a fake authority"; +const std::string kOrigin = "an origin for device id"; +const std::string kSpoid = "a spoid for identifying a device"; +const std::string kFakeCertificateToken = "a testing certificate token"; +const std::string kFakePublicKey = "a very real public key for a certificate"; +const std::string kFakeSignature = "a very real certificate signature"; +const std::string kWrappedPrivateKey = "a wrapped private key"; + +bool MakeSignedDrmCertificate(const std::string& public_key, + const std::string& serial_number, + uint32_t system_id, const std::string& signature, + std::string* certificate) { + DrmCertificate drm_certificate; + drm_certificate.set_public_key(public_key); + drm_certificate.set_serial_number(serial_number); + drm_certificate.set_system_id(system_id); + drm_certificate.set_type(DrmCertificate::DEVICE); + drm_certificate.set_creation_time_seconds(kCreationTime); + std::string serialized_drm_certificate; + drm_certificate.SerializeToString(&serialized_drm_certificate); + + SignedDrmCertificate signed_drm_certificate; + signed_drm_certificate.set_drm_certificate(serialized_drm_certificate); + signed_drm_certificate.set_signature(signature); + return signed_drm_certificate.SerializeToString(certificate); +} + +bool MakeProvisioningResponseJson( + const std::string& certificate, const std::string& nonce, + ProvisioningResponse::ProvisioningStatus status, + const std::string& signature, const std::string& core_message, + std::string* response) { + ProvisioningResponse provisioning_response; + provisioning_response.set_device_certificate(certificate); + provisioning_response.set_nonce(nonce); + provisioning_response.set_status(status); + std::string provisioning_response_string; + if (!provisioning_response.SerializeToString(&provisioning_response_string)) { + return false; + } + + SignedProvisioningMessage signed_response; + signed_response.set_message(provisioning_response_string); + signed_response.set_signature(signature); + signed_response.set_oemcrypto_core_message(core_message); + signed_response.set_protocol_version(SignedProvisioningMessage::VERSION_1_1); + signed_response.set_provisioning_type( + SignedProvisioningMessage::DRM_REPROVISIONING); + std::string signed_response_string; + if (!signed_response.SerializeToString(&signed_response_string)) { + return false; + } + + std::string response_base64 = + wvutil::Base64SafeEncode(signed_response_string); + return wvutil::FormatString(response, "{ \"signedResponse\": \"%s\" }", + response_base64.c_str()); +} } // unnamed namespace namespace wvcdm { +using ::testing::_; +using ::testing::ByMove; +using ::testing::DoAll; +using ::testing::NiceMock; +using ::testing::NotNull; +using ::testing::Return; +using ::testing::ReturnArg; +using ::testing::SaveArg; +using ::testing::SetArgPointee; +using ::testing::SetArgReferee; +using ::testing::StrEq; + +using wvutil::File; + +class MockFile : public File { + public: + MockFile() {} + ~MockFile() override {} + + MOCK_METHOD(ssize_t, Read, (char*, size_t), (override)); + MOCK_METHOD(ssize_t, Write, (const char*, size_t), (override)); +}; + +class MockFileSystem : public wvutil::FileSystem { + public: + MockFileSystem() {} + ~MockFileSystem() override {} + + MOCK_METHOD(std::unique_ptr, Open, (const std::string&, int), + (override)); +}; + class MockCryptoSession : public TestCryptoSession { public: MockCryptoSession(metrics::CryptoMetrics* metrics) @@ -78,17 +183,39 @@ class MockCryptoSession : public TestCryptoSession { MOCK_METHOD(CdmResponseType, UpdateUsageEntry, (UsageTableHeader*, UsageEntry*), (override)); MOCK_METHOD(CdmResponseType, MoveUsageEntry, (UsageEntryIndex), (override)); + + MOCK_METHOD(bool, IsOpen, (), (override)); + MOCK_METHOD(CdmClientTokenType, GetPreProvisionTokenType, (), (override)); + MOCK_METHOD(CdmResponseType, GetProvisioningToken, + (std::string*, std::string*), (override)); + MOCK_METHOD(CdmResponseType, GenerateNonce, (uint32_t*), (override)); + MOCK_METHOD(CdmResponseType, PrepareAndSignProvisioningRequest, + (const std::string&, std::string*, std::string*, bool&, + OEMCrypto_SignatureHashAlgorithm&), + (override)); + MOCK_METHOD(bool, GetSupportedCertificateTypes, (SupportedCertificateTypes*), + (override)); + MOCK_METHOD(bool, GetApiVersion, (uint32_t*), (override)); + MOCK_METHOD(bool, GetResourceRatingTier, (uint32_t*), (override)); + MOCK_METHOD(bool, GetBuildInformation, (std::string*), (override)); + MOCK_METHOD(CdmSecurityLevel, GetSecurityLevel, (), (override)); + MOCK_METHOD(CdmResponseType, LoadProvisioning, + (const std::string&, const std::string&, const std::string&, + std::string*), + (override)); }; class TestStubCryptoSessionFactory : public CryptoSessionFactory { CryptoSession* MakeCryptoSession( metrics::CryptoMetrics* crypto_metrics) override { - return new MockCryptoSession(crypto_metrics); + return new NiceMock(crypto_metrics); } }; -class CertificateProvisioningTest : public WvCdmTestBase { - protected: +class CertificateProvisioningTest + : public WvCdmTestBase, + public testing::WithParamInterface { + public: void SetUp() override { WvCdmTestBase::SetUp(); CryptoSession::SetCryptoSessionFactory(new TestStubCryptoSessionFactory()); @@ -96,20 +223,52 @@ class CertificateProvisioningTest : public WvCdmTestBase { metrics_.reset(new metrics::CryptoMetrics()); certificate_provisioning_.reset( new CertificateProvisioning(metrics_.get())); + GetCryptoSession()->pre_provision_token_type_ = GetParam(); } void TearDown() override {} + protected: + MockCryptoSession* GetCryptoSession() { + return static_cast( + certificate_provisioning_->crypto_session_.get()); + } + std::unique_ptr metrics_; std::unique_ptr certificate_provisioning_; }; +void MockValidCryptoSession(MockCryptoSession* crypto_session, + CdmClientTokenType token_type) { + const uint32_t crypto_session_api_version = 18; + const uint32_t resource_rating_tier = RESOURCE_RATING_TIER_LOW; + + ON_CALL(*crypto_session, IsOpen()).WillByDefault(Return(true)); + ON_CALL(*crypto_session, Open(_)) + .WillByDefault(Return(CdmResponseType(NO_ERROR))); + ON_CALL(*crypto_session, GenerateNonce(NotNull())) + .WillByDefault( + DoAll(SetArgPointee<0>(kNonce), Return(CdmResponseType(NO_ERROR)))); + ON_CALL(*crypto_session, GetApiVersion(NotNull())) + .WillByDefault( + DoAll(SetArgPointee<0>(crypto_session_api_version), Return(true))); + ON_CALL(*crypto_session, GetResourceRatingTier(NotNull())) + .WillByDefault( + DoAll(SetArgPointee<0>(resource_rating_tier), Return(true))); + ON_CALL(*crypto_session, GetBuildInformation(NotNull())) + .WillByDefault(DoAll(SetArgPointee<0>(kFakeBuildInfo), Return(true))); + ON_CALL(*crypto_session, GetSupportedCertificateTypes(NotNull())) + .WillByDefault(Return(false)); + ON_CALL(*crypto_session, GetPreProvisionTokenType()) + .WillByDefault(Return(token_type)); +} + // Tests ExtractDeviceInfo failure scenarios // * invalid output parmeters // * invalid signed drm device certificate // * signed drm device certificate contains no drm certificate // * drm certificate has an invalid certificate type -TEST_F(CertificateProvisioningTest, ExtractDeviceInfo_InvalidInput) { +TEST_P(CertificateProvisioningTest, ExtractDeviceInfo_InvalidInput) { std::string serial_number; uint32_t system_id; @@ -134,7 +293,7 @@ TEST_F(CertificateProvisioningTest, ExtractDeviceInfo_InvalidInput) { // * able to extract both |serial_number| and |system_id| // * able to extract only |serial_number| // * able to extract only |system_id| -TEST_F(CertificateProvisioningTest, ExtractDeviceInfo) { +TEST_P(CertificateProvisioningTest, ExtractDeviceInfo) { std::string serial_number; uint32_t system_id; int64_t creation_time_seconds, expiration_time_seconds; @@ -205,4 +364,128 @@ TEST_F(CertificateProvisioningTest, ExtractDeviceInfo) { EXPECT_EQ(kExpirationTime, expiration_time_seconds); } +TEST_P(CertificateProvisioningTest, ProvisioningRequestIsValid) { + certificate_provisioning_->Init(""); + MockFileSystem file_system; + MockCryptoSession* crypto_session = GetCryptoSession(); + MockValidCryptoSession(crypto_session, GetParam()); + ON_CALL(*crypto_session, GetProvisioningToken(NotNull(), _)) + .WillByDefault(DoAll(SetArgPointee<0>(kFakeCertificateToken), + Return(CdmResponseType(NO_ERROR)))); + EXPECT_CALL(*crypto_session, + PrepareAndSignProvisioningRequest(_, NotNull(), NotNull(), _, _)) + .WillOnce(DoAll(SetArgPointee<1>(kFakeCoreMessage), + SetArgPointee<2>(kFakeProvisioningRequestSignature), + SetArgReferee<3>(false), + Return(CdmResponseType(NO_ERROR)))); + + CdmProvisioningRequest request; + std::string default_url; + EXPECT_EQ(NO_ERROR, + certificate_provisioning_->GetProvisioningRequest( + &file_system, kLevelDefault, kCertificateWidevine, + kFakeAuthority, kOrigin, kSpoid, &request, &default_url)); +} + +TEST_P(CertificateProvisioningTest, ProvisioningRequestFailsEmptySignature) { + certificate_provisioning_->Init(""); + MockFileSystem file_system; + MockCryptoSession* crypto_session = GetCryptoSession(); + MockValidCryptoSession(crypto_session, GetParam()); + ON_CALL(*crypto_session, GetProvisioningToken(NotNull(), _)) + .WillByDefault(DoAll(SetArgPointee<0>(kFakeCertificateToken), + Return(CdmResponseType(NO_ERROR)))); + EXPECT_CALL(*crypto_session, + PrepareAndSignProvisioningRequest(_, NotNull(), NotNull(), _, _)) + .WillOnce(DoAll(SetArgPointee<1>(kFakeCoreMessage), + SetArgPointee<2>(kEmptyString), SetArgReferee<3>(false), + Return(CdmResponseType(NO_ERROR)))); + + CdmProvisioningRequest request; + std::string default_url; + EXPECT_EQ(CERT_PROVISIONING_REQUEST_ERROR_4, + certificate_provisioning_->GetProvisioningRequest( + &file_system, kLevelDefault, kCertificateWidevine, + kFakeAuthority, kOrigin, kSpoid, &request, &default_url)); +} + +TEST_P(CertificateProvisioningTest, + ProvisioningResponseFailsWithEmptyResponse) { + certificate_provisioning_->Init(""); + + MockFileSystem file_system; + std::string certificate; + std::string wrapped_key; + EXPECT_EQ(CERT_PROVISIONING_RESPONSE_ERROR_1, + certificate_provisioning_->HandleProvisioningResponse( + &file_system, /*response=*/"", &certificate, &wrapped_key)); +} + +TEST_P(CertificateProvisioningTest, + ProvisioningResponseFailsIfDeviceIsRevoked) { + certificate_provisioning_->Init(""); + + MockFileSystem file_system; + std::string response_certificate; + std::string response; + ASSERT_TRUE(MakeSignedDrmCertificate(kFakePublicKey, kSerialNumber, kSystemId, + kFakeSignature, &response_certificate)); + ASSERT_TRUE(MakeProvisioningResponseJson( + response_certificate, kNonceString, + ProvisioningResponse::REVOKED_DEVICE_CREDENTIALS, kFakeSignature, + kFakeCoreMessage, &response)); + + std::string certificate; + std::string wrapped_key; + EXPECT_EQ(DEVICE_REVOKED, + certificate_provisioning_->HandleProvisioningResponse( + &file_system, response, &certificate, &wrapped_key)); +} + +TEST_P(CertificateProvisioningTest, ProvisioningResponseSuccess) { + certificate_provisioning_->Init(""); + std::string expected_certificate; + std::string response; + ASSERT_TRUE(MakeSignedDrmCertificate(kFakePublicKey, kSerialNumber, kSystemId, + kFakeSignature, &expected_certificate)); + ASSERT_TRUE(MakeProvisioningResponseJson( + expected_certificate, kNonceString, ProvisioningResponse::NO_ERROR, + kFakeSignature, kFakeCoreMessage, &response)); + + MockCryptoSession* crypto_session = GetCryptoSession(); + ON_CALL(*crypto_session, IsOpen()).WillByDefault(Return(true)); + ON_CALL(*crypto_session, GetSecurityLevel()) + .WillByDefault(Return(kSecurityLevelL3)); + EXPECT_CALL(*crypto_session, LoadProvisioning) + .Times(1) + .WillOnce(DoAll(SetArgPointee<3>(kWrappedPrivateKey), + Return(CdmResponseType(NO_ERROR)))); + + MockFile* file = new MockFile(); + std::string stored_certificate; + EXPECT_CALL(*file, Write(_, _)) + .Times(1) + .WillOnce(DoAll(SaveArg<0>(&stored_certificate), ReturnArg<1>())); + + MockFileSystem file_system; + EXPECT_CALL(file_system, Open(StrEq(wvutil::kLegacyCertificateFileName), _)) + .Times(1) + .WillOnce(Return(ByMove(std::unique_ptr(file)))); + + std::string certificate; + std::string wrapped_key; + EXPECT_EQ(NO_ERROR, certificate_provisioning_->HandleProvisioningResponse( + &file_system, response, &certificate, &wrapped_key)); + EXPECT_NE(std::string::npos, stored_certificate.find(expected_certificate)); +} + +INSTANTIATE_TEST_SUITE_P( + CertificateProvisioningTests, CertificateProvisioningTest, + testing::Values(kClientTokenKeybox, kClientTokenOemCert, + kClientTokenDrmCertificateReprovisioning), + [](const testing::TestParamInfo& + param_type) { + return CdmClientTokenTypeToString(param_type.param); + }); + } // namespace wvcdm diff --git a/core/test/core_integration_test.cpp b/core/test/core_integration_test.cpp new file mode 100644 index 00000000..ef3657f2 --- /dev/null +++ b/core/test/core_integration_test.cpp @@ -0,0 +1,279 @@ +// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine License +// Agreement. + +#include "certificate_provisioning.h" +#include "license_holder.h" +#include "log.h" +#include "provisioning_holder.h" +#include "test_base.h" +#include "wv_cdm_types.h" + +static const std::string kTestAppPackageName1 = "test_app_package_1"; +static const std::string kTestAppPackageName2 = "test_app_package_2"; +static const std::string kTestOrigin1 = "test_origin_1"; +static const std::string kTestOrigin2 = "test_origin_2"; + +namespace wvcdm { + +class CoreIntegrationTest : public WvCdmTestBaseWithEngine { + public: + void SetUp() override { + WvCdmTestBase::SetUp(); + EnsureProvisioned(); + } + + using WvCdmTestBase::Provision; + virtual bool Provision(const std::string app_package_name, std::string* spoid, + std::string* drm_certificate_serial_number) { + if (spoid == nullptr) { + ADD_FAILURE() << "|spoid| not provided"; + return false; + } + + if (drm_certificate_serial_number == nullptr) { + ADD_FAILURE() << "|drm_certificate_serial_number| not provided"; + return false; + } + + std::string device_id; + CdmResponseType sts = + cdm_engine_.QueryStatus(kLevelDefault, QUERY_KEY_DEVICE_ID, &device_id); + + if (!sts.IsOk()) { + ADD_FAILURE() << "Query Device ID failed: " << sts.code(); + return false; + } + + std::string value; + sts = cdm_engine_.QueryStatus(kLevelDefault, QUERY_KEY_SYSTEM_ID, &value); + + if (!sts.IsOk()) { + ADD_FAILURE() << "Query System ID failed: " << sts.code(); + return false; + } + + long val = std::strtol(value.c_str(), nullptr, 10); + if (val <= 0 || val > std::numeric_limits::max()) { + ADD_FAILURE() << "System ID invalid: " << val; + return false; + } + + uint32_t system_id = static_cast(val); + + *spoid = device_id + app_package_name; + cdm_engine_.SetSpoid(*spoid); + + ProvisioningHolder provisioner(&cdm_engine_, config_); + if (system_id == wvcdm::NULL_SYSTEM_ID) { + // Either first stage prov 4.0 or OKP + provisioner.Provision(binary_provisioning_); + } + provisioner.Provision(binary_provisioning_); + + if (!GetDrmCertificateSerialNumber(provisioner.response(), + drm_certificate_serial_number)) { + ADD_FAILURE() << "Failed to get DRM Certificate Serial Number: " + << sts.code(); + return false; + } + + return true; + } + + virtual bool Provision(const std::string app_package_name, + const std::string origin, std::string* spoid, + std::string* drm_certificate_serial_number) { + std::string name = app_package_name + origin; + return Provision(name, spoid, drm_certificate_serial_number); + } + + private: + bool GetDrmCertificateSerialNumber( + const wvcdm::CdmProvisioningResponse& response, + std::string* serial_number) { + if (serial_number == nullptr) { + ADD_FAILURE() << "|serial_number| not provided"; + return false; + } + + wvcdm::CdmProvisioningResponse provisioning_response; + if (!wvcdm::CertificateProvisioning::ExtractAndDecodeSignedMessage( + response, &provisioning_response)) { + ADD_FAILURE() << "Failed to decode signed provisioning message"; + return false; + } + + video_widevine::SignedProvisioningMessage signed_response; + if (!signed_response.ParseFromString(provisioning_response) || + !signed_response.has_message()) { + ADD_FAILURE() << "Failed to parse signed provisioning response"; + return false; + } + + video_widevine::ProvisioningResponse prov_response; + if (!prov_response.ParseFromString(signed_response.message()) || + !prov_response.has_device_certificate()) { + ADD_FAILURE() << "Failed to parse provisioning response"; + return false; + } + + if (!wvcdm::CertificateProvisioning::ExtractDeviceInfo( + prov_response.device_certificate(), serial_number, nullptr, nullptr, + nullptr)) { + ADD_FAILURE() << "Failed to extract device information"; + return false; + } + + return true; + } +}; + +/** + * Verify that SPOIDs and DRM certificate serial number are stable between + * factory resets/provisioning attempts for the same app and different between + * different apps. Test using two different apps and origins. + */ +TEST_F(CoreIntegrationTest, ProvisioningStableSpoidTest) { + std::string level; + ASSERT_EQ( + NO_ERROR, + cdm_engine_.QueryStatus(kLevelDefault, QUERY_KEY_SECURITY_LEVEL, &level) + .code()); + + ASSERT_TRUE(level == QUERY_VALUE_SECURITY_LEVEL_L1 || + level == QUERY_VALUE_SECURITY_LEVEL_L3) + << "Unknown security level: " << level; + + CdmSecurityLevel security_level = level == QUERY_VALUE_SECURITY_LEVEL_L1 + ? kSecurityLevelL1 + : kSecurityLevelL3; + + // App 1, first provisioning attempt + std::string spoid_app_1[2]; + std::string drm_cert_serial_number_app_1[2]; + ASSERT_TRUE(Provision(kTestAppPackageName1, &spoid_app_1[0], + &drm_cert_serial_number_app_1[0])); + + cdm_engine_.Unprovision(security_level); + + // second provisioning attempt + ASSERT_TRUE(Provision(kTestAppPackageName1, &spoid_app_1[1], + &drm_cert_serial_number_app_1[1])); + + // App 2, first provisioning attempt + std::string spoid_app_2[2]; + std::string drm_cert_serial_number_app_package_name_2[2]; + ASSERT_TRUE(Provision(kTestAppPackageName2, &spoid_app_2[0], + &drm_cert_serial_number_app_package_name_2[0])); + + cdm_engine_.Unprovision(security_level); + + // second provisioning attempt + ASSERT_TRUE(Provision(kTestAppPackageName2, &spoid_app_2[1], + &drm_cert_serial_number_app_package_name_2[1])); + + // App 1 and Origin 1, first provisioning attempt + std::string spoid_app_1_origin_1[2]; + std::string drm_cert_serial_number_app_1_origin_1[2]; + ASSERT_TRUE(Provision(kTestAppPackageName1, kTestOrigin1, + &spoid_app_1_origin_1[0], + &drm_cert_serial_number_app_1_origin_1[0])); + + cdm_engine_.Unprovision(security_level); + + // second provisioning attempt + ASSERT_TRUE(Provision(kTestAppPackageName1, kTestOrigin1, + &spoid_app_1_origin_1[1], + &drm_cert_serial_number_app_1_origin_1[1])); + + // App 1 and Origin 2, first provisioning attempt + std::string spoid_app_1_origin_2[2]; + std::string drm_cert_serial_number_app_1_origin_2[2]; + ASSERT_TRUE(Provision(kTestAppPackageName1, kTestOrigin2, + &spoid_app_1_origin_2[0], + &drm_cert_serial_number_app_1_origin_2[0])); + + cdm_engine_.Unprovision(security_level); + + // second provisioning attempt + ASSERT_TRUE(Provision(kTestAppPackageName1, kTestOrigin2, + &spoid_app_1_origin_2[1], + &drm_cert_serial_number_app_1_origin_2[1])); + + // Verify that SPOID and DRM cert are stable + ASSERT_EQ(spoid_app_1[0], spoid_app_1[1]); + ASSERT_EQ(spoid_app_2[0], spoid_app_2[1]); + ASSERT_EQ(spoid_app_1_origin_1[0], spoid_app_1_origin_1[1]); + ASSERT_EQ(spoid_app_1_origin_2[0], spoid_app_1_origin_2[1]); + + ASSERT_EQ(drm_cert_serial_number_app_1[0], drm_cert_serial_number_app_1[1]); + ASSERT_EQ(drm_cert_serial_number_app_package_name_2[0], + drm_cert_serial_number_app_package_name_2[1]); + ASSERT_EQ(drm_cert_serial_number_app_1_origin_1[0], + drm_cert_serial_number_app_1_origin_1[1]); + ASSERT_EQ(drm_cert_serial_number_app_1_origin_2[0], + drm_cert_serial_number_app_1_origin_2[1]); + + // Verify that SPOID and DRM cert do not match different apps/origins + ASSERT_NE(spoid_app_1[0], spoid_app_2[0]); + ASSERT_NE(spoid_app_1[0], spoid_app_1_origin_1[0]); + ASSERT_NE(spoid_app_1[0], spoid_app_1_origin_2[0]); + ASSERT_NE(spoid_app_2[0], spoid_app_1_origin_1[0]); + ASSERT_NE(spoid_app_2[0], spoid_app_1_origin_2[0]); + ASSERT_NE(spoid_app_1_origin_1[0], spoid_app_1_origin_2[0]); + + ASSERT_NE(drm_cert_serial_number_app_1[0], + drm_cert_serial_number_app_package_name_2[0]); + ASSERT_NE(drm_cert_serial_number_app_1[0], + drm_cert_serial_number_app_1_origin_1[0]); + ASSERT_NE(drm_cert_serial_number_app_1[0], + drm_cert_serial_number_app_1_origin_2[0]); + ASSERT_NE(drm_cert_serial_number_app_package_name_2[0], + drm_cert_serial_number_app_1_origin_1[0]); + ASSERT_NE(drm_cert_serial_number_app_package_name_2[0], + drm_cert_serial_number_app_1_origin_2[0]); + ASSERT_NE(drm_cert_serial_number_app_1_origin_1[0], + drm_cert_serial_number_app_1_origin_2[0]); +} + +/** + * A clear lead without a license loaded. + */ +TEST_F(CoreIntegrationTest, ClearLead) { + LicenseHolder holder("CDM_Streaming", &cdm_engine_, config_); + const KeyId key_id = ""; + + ASSERT_NO_FATAL_FAILURE(holder.OpenSession()); + ASSERT_NO_FATAL_FAILURE(holder.FetchLicense()); + EXPECT_EQ(NO_ERROR, holder.DecryptClearLead(key_id)); + ASSERT_NO_FATAL_FAILURE(holder.CloseSession()); +} + +/** + * Playback clear lead with a license loaded. Playback should succeed. + */ +TEST_F(CoreIntegrationTest, ClearLeadAfterLicenseLoad) { + LicenseHolder holder("CDM_Streaming", &cdm_engine_, config_); + const KeyId key_id = ""; + + ASSERT_NO_FATAL_FAILURE(holder.OpenSession()); + ASSERT_NO_FATAL_FAILURE(holder.FetchLicense()); + ASSERT_NO_FATAL_FAILURE(holder.LoadLicense()); + EXPECT_EQ(NO_ERROR, holder.DecryptClearLead(key_id)); + ASSERT_NO_FATAL_FAILURE(holder.CloseSession()); +} + +/** + * Decrypt without a license loaded. Decrypt should fail with a NEED_KEY error. + */ +TEST_F(CoreIntegrationTest, NeedKeyBeforeLicenseLoad) { + LicenseHolder holder("CDM_Streaming", &cdm_engine_, config_); + const KeyId key_id = "0000000000000000"; + + ASSERT_NO_FATAL_FAILURE(holder.OpenSession()); + ASSERT_NO_FATAL_FAILURE(holder.FetchLicense()); + EXPECT_EQ(NEED_KEY, holder.Decrypt(key_id)); + ASSERT_NO_FATAL_FAILURE(holder.CloseSession()); +} +} // namespace wvcdm diff --git a/core/test/crypto_session_unittest.cpp b/core/test/crypto_session_unittest.cpp index 0a5545a2..a668481d 100644 --- a/core/test/crypto_session_unittest.cpp +++ b/core/test/crypto_session_unittest.cpp @@ -99,6 +99,9 @@ TEST_F(CryptoSessionMetricsTest, OpenSessionValidMetrics) { } else if (token_type == kClientTokenBootCertChain) { EXPECT_EQ(OEMCrypto_BootCertificateChain, metrics_proto.oemcrypto_provisioning_method().int_value()); + } else if (token_type == kClientTokenDrmCertificateReprovisioning) { + EXPECT_EQ(OEMCrypto_DrmReprovisioning, + metrics_proto.oemcrypto_provisioning_method().int_value()); } else { FAIL() << "Unexpected token type: " << token_type; } @@ -140,6 +143,9 @@ TEST_F(CryptoSessionMetricsTest, GetProvisioningTokenValidMetrics) { } else if (token_type == kClientTokenBootCertChain) { EXPECT_EQ(OEMCrypto_BootCertificateChain, metrics_proto.oemcrypto_provisioning_method().int_value()); + } else if (token_type == kClientTokenDrmCertificateReprovisioning) { + EXPECT_EQ(OEMCrypto_DrmReprovisioning, + metrics_proto.oemcrypto_provisioning_method().int_value()); } else { ASSERT_EQ(0, metrics_proto.crypto_session_get_token().size()); } diff --git a/core/test/duration_use_case_test.cpp b/core/test/duration_use_case_test.cpp index d2ffd2f3..b36e814f 100644 --- a/core/test/duration_use_case_test.cpp +++ b/core/test/duration_use_case_test.cpp @@ -128,6 +128,12 @@ class CdmDurationTest : public WvCdmTestBaseWithEngine, // appended to it. void SetUp() override { WvCdmTestBase::SetUp(); + // TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented. + if (wvoec::global_features.provisioning_method == + OEMCrypto_DrmReprovisioning) { + GTEST_SKIP() + << "Skipping until Drm Reprovisioning server support is implemented."; + } EnsureProvisioned(); license_holder_.set_can_persist(GetParam()); ASSERT_NO_FATAL_FAILURE(license_holder_.OpenSession()); @@ -152,6 +158,14 @@ class CdmDurationTest : public WvCdmTestBaseWithEngine, } void TearDown() override { + // TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented. + if (wvoec::global_features.provisioning_method == + OEMCrypto_DrmReprovisioning) { + // Since the session was not opened above. We can skip closing the session + // here too. This should be removed when EnsureProvisioning no longer + // skips this test. + return; + } license_holder_.CloseSession(); // Log the time used in this test suite. When this comment was written, // these tests took over three hours. If we want to improve that, we need to @@ -1048,6 +1062,7 @@ class CdmUseCase_LicenseWithRenewal : public RenewalTest { void SetUp() override { RenewalTest::SetUp(); + if(Test::IsSkipped()) return; const uint64_t next_renewal = start_of_playback_ + initial_policy_.renewal_delay; // Allow playback within the initial renewal window. @@ -1253,6 +1268,7 @@ class CdmUseCase_LicenseWithRenewalPlayback : public RenewalTest { void SetUp() override { RenewalTest::SetUp(); + if(Test::IsSkipped()) return; uint64_t next_renewal = start_of_playback_ + initial_policy_.renewal_delay; // Allow playback within the initial renewal window. SleepUntil(start_of_playback_); @@ -1570,12 +1586,16 @@ class CdmUseCase_RenewOnLicenseLoad : public RenewalTest { void SetUp() override { RenewalTest::SetUp(); + if(Test::IsSkipped()) return; // The Renew on License Load feature is only supported on v18+ servers. if (config_.ServerOlderThan(18) || wvoec::global_features.api_version < 18) { GTEST_SKIP() << "Renew on License Load supported on v18+ servers and " "devices only."; } + if (license_holder_.can_persist() && !wvoec::global_features.usage_table) { + GTEST_SKIP() << "Renew on License Load requires a usage table for offline licenses."; + } } uint64_t renewal_cutoff_; @@ -1714,6 +1734,7 @@ class CdmUseCase_Heartbeat : public RenewalTest { void SetUp() override { RenewalTest::SetUp(); + if(Test::IsSkipped()) return; const uint64_t next_renewal = start_of_playback_ + initial_policy_.renewal_delay; // Allow playback within the initial renewal window. diff --git a/core/test/generic_crypto_unittest.cpp b/core/test/generic_crypto_unittest.cpp index 1b8422bc..94fe365c 100644 --- a/core/test/generic_crypto_unittest.cpp +++ b/core/test/generic_crypto_unittest.cpp @@ -37,9 +37,19 @@ class WvGenericCryptoTest : public WvCdmTestBaseWithEngine { void SetUp() override { WvCdmTestBase::SetUp(); + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } + // TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented. + if (wvoec::global_features.provisioning_method == + OEMCrypto_DrmReprovisioning) { + GTEST_SKIP() + << "Skipping until Drm Reprovisioning server support is implemented."; + } EnsureProvisioned(); ASSERT_NO_FATAL_FAILURE(holder_.OpenSession()); ASSERT_NO_FATAL_FAILURE(holder_.FetchLicense()); + ASSERT_NO_FATAL_FAILURE(holder_.LoadLicense()); ency_id_ = "encrypt-key-----"; ency_key_ = a2b_hex("0102030405060708090a0b0c0d0e0f10"); @@ -64,7 +74,11 @@ class WvGenericCryptoTest : public WvCdmTestBaseWithEngine { iv_ = std::string(iv_vector_.begin(), iv_vector_.end()); } - void TearDown() override { holder_.CloseSession(); } + void TearDown() override { + // TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented. + if (IsSkipped()) return; + holder_.CloseSession(); + } protected: LicenseHolder holder_; @@ -89,12 +103,7 @@ class WvGenericCryptoTest : public WvCdmTestBaseWithEngine { std::string iv_; }; -TEST_F(WvGenericCryptoTest, LoadSpecialKeys) { - ASSERT_NO_FATAL_FAILURE(holder_.LoadLicense()); -} - TEST_F(WvGenericCryptoTest, GenericEncryptGood) { - ASSERT_NO_FATAL_FAILURE(holder_.LoadLicense()); std::string encrypted = Aes128CbcEncrypt(ency_key_, in_vector_, iv_vector_); std::string out_buffer; EXPECT_EQ(NO_ERROR, cdm_engine_.GenericEncrypt( @@ -104,7 +113,6 @@ TEST_F(WvGenericCryptoTest, GenericEncryptGood) { } TEST_F(WvGenericCryptoTest, GenericEncryptNoKey) { - ASSERT_NO_FATAL_FAILURE(holder_.LoadLicense()); std::string encrypted = Aes128CbcEncrypt(ency_key_, in_vector_, iv_vector_); std::string out_buffer; KeyId key_id("no_key"); @@ -115,7 +123,6 @@ TEST_F(WvGenericCryptoTest, GenericEncryptNoKey) { } TEST_F(WvGenericCryptoTest, GenericEncryptKeyNotAllowed) { - ASSERT_NO_FATAL_FAILURE(holder_.LoadLicense()); // Trying to use Decrypt key to encrypt, which is not allowed. KeyId key_id = dency_id_; std::string encrypted = Aes128CbcEncrypt(dency_key_, in_vector_, iv_vector_); @@ -128,7 +135,6 @@ TEST_F(WvGenericCryptoTest, GenericEncryptKeyNotAllowed) { } TEST_F(WvGenericCryptoTest, GenericDecryptGood) { - ASSERT_NO_FATAL_FAILURE(holder_.LoadLicense()); std::string decrypted = Aes128CbcDecrypt(dency_key_, in_vector_, iv_vector_); std::string out_buffer; EXPECT_EQ(NO_ERROR, cdm_engine_.GenericDecrypt( @@ -138,7 +144,6 @@ TEST_F(WvGenericCryptoTest, GenericDecryptGood) { } TEST_F(WvGenericCryptoTest, GenericDecryptNoKey) { - ASSERT_NO_FATAL_FAILURE(holder_.LoadLicense()); std::string decrypted = Aes128CbcDecrypt(dency_key_, in_vector_, iv_vector_); std::string out_buffer; KeyId key_id = "no_key"; @@ -149,7 +154,6 @@ TEST_F(WvGenericCryptoTest, GenericDecryptNoKey) { } TEST_F(WvGenericCryptoTest, GenericDecryptKeyNotAllowed) { - ASSERT_NO_FATAL_FAILURE(holder_.LoadLicense()); // Trying to use Encrypt key to decrypt, which is not allowed. KeyId key_id = ency_id_; std::string decrypted = Aes128CbcDecrypt(ency_key_, in_vector_, iv_vector_); @@ -162,7 +166,6 @@ TEST_F(WvGenericCryptoTest, GenericDecryptKeyNotAllowed) { } TEST_F(WvGenericCryptoTest, GenericSignGood) { - ASSERT_NO_FATAL_FAILURE(holder_.LoadLicense()); std::string out_buffer; std::string signature = SignHMAC(in_buffer_, siggy_key_); EXPECT_EQ(NO_ERROR, cdm_engine_.GenericSign( @@ -172,7 +175,6 @@ TEST_F(WvGenericCryptoTest, GenericSignGood) { } TEST_F(WvGenericCryptoTest, GenericSignKeyNotAllowed) { - ASSERT_NO_FATAL_FAILURE(holder_.LoadLicense()); // Wrong key std::string key_id = vou_id_; std::string out_buffer; @@ -185,7 +187,6 @@ TEST_F(WvGenericCryptoTest, GenericSignKeyNotAllowed) { } TEST_F(WvGenericCryptoTest, GenericVerifyGood) { - ASSERT_NO_FATAL_FAILURE(holder_.LoadLicense()); std::string signature = SignHMAC(in_buffer_, vou_key_); EXPECT_EQ(NO_ERROR, cdm_engine_.GenericVerify( holder_.session_id(), in_buffer_, vou_id_, @@ -193,7 +194,6 @@ TEST_F(WvGenericCryptoTest, GenericVerifyGood) { } TEST_F(WvGenericCryptoTest, GenericVerifyKeyNotAllowed) { - ASSERT_NO_FATAL_FAILURE(holder_.LoadLicense()); // Wrong key std::string key_id = siggy_id_; std::string signature = SignHMAC(in_buffer_, siggy_key_); @@ -203,7 +203,6 @@ TEST_F(WvGenericCryptoTest, GenericVerifyKeyNotAllowed) { } TEST_F(WvGenericCryptoTest, GenericVerifyBadSignature) { - ASSERT_NO_FATAL_FAILURE(holder_.LoadLicense()); std::string signature(MAC_KEY_SIZE, 's'); // OEMCrypto error is OEMCrypto_ERROR_SIGNATURE_FAILURE EXPECT_EQ(UNKNOWN_ERROR, cdm_engine_.GenericVerify( @@ -212,7 +211,6 @@ TEST_F(WvGenericCryptoTest, GenericVerifyBadSignature) { } TEST_F(WvGenericCryptoTest, GenericEncryptDecrypt) { - ASSERT_NO_FATAL_FAILURE(holder_.LoadLicense()); std::string encrypted = Aes128CbcEncrypt(both_key_, in_vector_, iv_vector_); std::string out_buffer; EXPECT_EQ(NO_ERROR, cdm_engine_.GenericEncrypt( @@ -228,8 +226,6 @@ TEST_F(WvGenericCryptoTest, GenericEncryptDecrypt) { } TEST_F(WvGenericCryptoTest, GenericSignVerify) { - ASSERT_NO_FATAL_FAILURE(holder_.LoadLicense()); - std::string out_buffer; std::string signature = SignHMAC(in_buffer_, sign_and_verify_key_); EXPECT_EQ(NO_ERROR, cdm_engine_.GenericSign( diff --git a/core/test/http_socket.cpp b/core/test/http_socket.cpp index 51d0436d..f1a8649e 100644 --- a/core/test/http_socket.cpp +++ b/core/test/http_socket.cpp @@ -165,12 +165,25 @@ bool HttpSocket::ParseUrl(const std::string& url, std::string* scheme, return false; } + // Strip off the domain name and port. In the url it will be terminated by + // either a splash or a question mark: + // like this example.com?key=value + // or this example.com/path/to/resource if (!Tokenize(url, "/", offset, domain_name, &offset)) { - // The rest of the URL belongs to the domain name. - domain_name->assign(url, offset, std::string::npos); - // No explicit path after the domain name. - path->assign("/"); + if (Tokenize(url, "?", offset, domain_name, &offset)) { + // url had no '/', but it did have '?'. Use the default path but + // keep the extra parameters. i.e. turn '?extra' into '/?extra'. + path->assign("/"); + path->append(url, offset - 1, std::string::npos); + } else { + // url had no '/' or '?'. + // The rest of the URL belongs to the domain name. + domain_name->assign(url, offset, std::string::npos); + // Use the default path. + path->assign("/"); + } } else { + // url had a '/'. // The rest of the URL, including the preceding slash, belongs to the path. path->assign(url, offset - 1, std::string::npos); } @@ -192,7 +205,7 @@ bool HttpSocket::ParseUrl(const std::string& url, std::string* scheme, } HttpSocket::HttpSocket(const std::string& url) - : socket_fd_(-1), ssl_(nullptr), ssl_ctx_(nullptr) { + : url_(url), socket_fd_(-1), ssl_(nullptr), ssl_ctx_(nullptr) { valid_url_ = ParseUrl(url, &scheme_, &secure_connect_, &domain_name_, &port_, &resource_path_); create_time_ = @@ -280,10 +293,12 @@ bool HttpSocket::Connect(int timeout_in_ms) { if (ret == EAI_SYSTEM) { // EAI_SYSTEM implies an underlying system issue. Error is // specified by |errno|. - LOGE("getaddrinfo failed due to system error: errno = %d", GetError()); + LOGE("getaddrinfo %s (port %s) failed due to system error: errno = %d", + domain_name_.c_str(), port_.c_str(), GetError()); } else { // Error is specified by return value. - LOGE("getaddrinfo failed: ret = %d", ret); + LOGE("getaddrinfo %s (port %s) failed: ret = %d", domain_name_.c_str(), + port_.c_str(), ret); } return false; } @@ -292,7 +307,8 @@ bool HttpSocket::Connect(int timeout_in_ms) { socket_fd_ = socket(addr_info->ai_family, addr_info->ai_socktype, addr_info->ai_protocol); if (socket_fd_ < 0) { - LOGE("Cannot open socket: errno = %d", GetError()); + LOGE("Cannot open socket %s (port %s): errno = %d", domain_name_.c_str(), + port_.c_str(), GetError()); return false; } @@ -300,19 +316,22 @@ bool HttpSocket::Connect(int timeout_in_ms) { #ifdef _WIN32 u_long mode = 1; // Non-blocking mode. if (ioctlsocket(socket_fd_, FIONBIO, &mode) != 0) { - LOGE("ioctlsocket error, wsa error = %d", WSAGetLastError()); + LOGE("ioctlsocket error %s (port %s), wsa error = %d", domain_name_.c_str(), + port_.c_str(), WSAGetLastError()); CloseSocket(); return false; } #else const int original_flags = fcntl(socket_fd_, F_GETFL, 0); if (original_flags == -1) { - LOGE("fcntl error, errno = %d", errno); + LOGE("fcntl error %s (port %s), errno = %d", domain_name_.c_str(), + port_.c_str(), errno); CloseSocket(); return false; } if (fcntl(socket_fd_, F_SETFL, original_flags | O_NONBLOCK) == -1) { - LOGE("fcntl error, errno = %d", errno); + LOGE("fcntl error %s (port %s), errno = %d", domain_name_.c_str(), + port_.c_str(), errno); CloseSocket(); return false; } diff --git a/core/test/http_socket.h b/core/test/http_socket.h index ae5761d6..72c5f0f9 100644 --- a/core/test/http_socket.h +++ b/core/test/http_socket.h @@ -32,9 +32,7 @@ class HttpSocket { const std::string& domain_name() const { return domain_name_; } int port() const { return atoi(port_.c_str()); } const std::string& resource_path() const { return resource_path_; } - std::string url() const { - return scheme_ + "://" + domain_name_ + ":" + port_ + resource_path_; - } + const std::string& url() const { return url_; } int ReadAndLogErrors(char* data, int len, int timeout_in_ms); int WriteAndLogErrors(const char* data, int len, int timeout_in_ms); @@ -57,6 +55,7 @@ class HttpSocket { bool Wait(bool for_read, int timeout_in_ms); FRIEND_TEST(HttpSocketTest, ParseUrlTest); + std::string url_; std::string scheme_; bool secure_connect_; std::string domain_name_; diff --git a/core/test/keybox_ota_test.cpp b/core/test/keybox_ota_test.cpp index efffd785..fc3c76ee 100644 --- a/core/test/keybox_ota_test.cpp +++ b/core/test/keybox_ota_test.cpp @@ -9,6 +9,7 @@ #include "create_test_file_system.h" #include "crypto_session.h" +#include "oec_device_features.h" #include "properties.h" #include "provisioning_holder.h" #include "test_base.h" @@ -22,8 +23,14 @@ namespace wvcdm { class CdmOtaKeyboxTest : public ::testing::Test { public: void SetUp() override { + if (wvoec::global_features.provisioning_method != OEMCrypto_Keybox) { + GTEST_SKIP() << "Test for Prov 2.0 devices only."; + } ::testing::Test::SetUp(); Properties::Init(); + if (wvoec::global_features.provisioning_method != OEMCrypto_Keybox) { + GTEST_SKIP() << "Test for Prov 2.0 devices only."; + } 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()); diff --git a/core/test/license_holder.cpp b/core/test/license_holder.cpp index ceef5315..f5bec604 100644 --- a/core/test/license_holder.cpp +++ b/core/test/license_holder.cpp @@ -127,6 +127,20 @@ CdmResponseType LicenseHolder::Decrypt(const std::string& key_id) { return cdm_engine_->DecryptV16(session_id_, params); } +CdmResponseType LicenseHolder::DecryptClearLead(const std::string& key_id) { + constexpr size_t buffer_size = 500; + const std::vector input(buffer_size, 0); + std::vector output(buffer_size, 0); + const std::vector iv(KEY_IV_SIZE, 0); + CdmDecryptionParametersV16 params(key_id); + params.is_secure = false; + CdmDecryptionSample sample(input.data(), output.data(), 0, input.size(), iv); + CdmDecryptionSubsample subsample(input.size(), 0); + sample.subsamples.push_back(subsample); + params.samples.push_back(sample); + return cdm_engine_->DecryptV16(session_id_, params); +} + void LicenseHolder::DecryptSecure(const KeyId& key_id) { ASSERT_TRUE(wvoec::global_features.test_secure_buffers); constexpr size_t buffer_size = 500; diff --git a/core/test/license_holder.h b/core/test/license_holder.h index 914e8d22..6c5174dc 100644 --- a/core/test/license_holder.h +++ b/core/test/license_holder.h @@ -78,6 +78,9 @@ class LicenseHolder { // Try to decrypt some random data. This does not verify that the data is // decrypted correctly. Returns the result of the decrypt operation. CdmResponseType Decrypt(const std::string& key_id); + // Try to copy the clear lead to a secure buffer. Returns the result of the + // copy buffer operation. + CdmResponseType DecryptClearLead(const std::string& key_id); // Try to decrypt some random data to a secure buffer. If the test harness // does not allow creating a secure buffer, then this function fails // immediately. Otherwise, a secure buffer is created and used for a diff --git a/core/test/license_unittest.cpp b/core/test/license_unittest.cpp index c45804a0..c57ca87e 100644 --- a/core/test/license_unittest.cpp +++ b/core/test/license_unittest.cpp @@ -136,6 +136,7 @@ const std::string kFakeKeyTooLong = const std::string kFakeKeyTooShort = a2bs_hex("06e247e7f924208011"); const std::string kFakeIv = a2bs_hex("3d515a3ee0be1687080ac59da9e0d69a"); const std::string kFakeBuildInfo = "Mock Crypto Session - License Test"; +const uint32_t kDefaultOemCryptoVersion = 18; class MockCryptoSession : public TestCryptoSession { public: @@ -215,84 +216,85 @@ class CdmLicenseTest : public WvCdmTestBase { protected: CdmLicenseTest(const std::string& pssh = (kCencInitDataHdr + kCencPssh)) : pssh_(pssh) {} + void SetUp() override { WvCdmTestBase::SetUp(); - clock_ = new MockClock(); - crypto_session_ = new MockCryptoSession(&crypto_metrics_); - init_data_ = new InitializationData(CENC_INIT_DATA_FORMAT, pssh_); - policy_engine_ = new MockPolicyEngine(crypto_session_); - + crypto_session_.reset(new MockCryptoSession(&crypto_metrics_)); ON_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull())) .WillByDefault( DoAll(SetArgPointee<0>(kDefaultSupportedCertTypes), Return(true))); + // PolicyEngine will call GetApiVersion() on creation. + EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull())) + .WillRepeatedly( + DoAll(SetArgPointee<0>(kDefaultOemCryptoVersion), Return(true))); + + policy_engine_.reset(new MockPolicyEngine(crypto_session_.get())); + + init_data_ = InitializationData(CENC_INIT_DATA_FORMAT, pssh_); + + clock_ = new MockClock(); + cdm_license_.reset(new CdmLicenseTestPeer(kCdmSessionId, clock_)); } void TearDown() override { - delete cdm_license_; - delete policy_engine_; - delete init_data_; - delete crypto_session_; - delete clock_; - } - - virtual void CreateCdmLicense() { - cdm_license_ = new CdmLicenseTestPeer(kCdmSessionId, clock_); + // Nullify pointers for objects owned by CdmLicense. clock_ = nullptr; + + cdm_license_.reset(); + + // Release mock objects used by the CdmLicense. + // Order is important. + policy_engine_.reset(); + crypto_session_.reset(); } - CdmLicenseTestPeer* cdm_license_ = nullptr; - MockClock* clock_ = nullptr; metrics::CryptoMetrics crypto_metrics_; - MockCryptoSession* crypto_session_ = nullptr; - InitializationData* init_data_ = nullptr; - MockPolicyEngine* policy_engine_ = nullptr; + MockClock* clock_ = nullptr; // Owned by |cdm_license_|. + std::unique_ptr cdm_license_; + std::unique_ptr policy_engine_; + std::unique_ptr crypto_session_; + + InitializationData init_data_; std::string pssh_; }; TEST_F(CdmLicenseTest, InitSuccess) { EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true)); - - CreateCdmLicense(); EXPECT_TRUE(cdm_license_->Init(false, kEmptyServiceCertificate, - crypto_session_, policy_engine_)); + crypto_session_.get(), policy_engine_.get())); } TEST_F(CdmLicenseTest, InitFail_CryptoSessionNull) { - CreateCdmLicense(); EXPECT_FALSE(cdm_license_->Init(false, kEmptyServiceCertificate, nullptr, - policy_engine_)); + policy_engine_.get())); } TEST_F(CdmLicenseTest, InitFail_PolicyEngineNull) { EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true)); - CreateCdmLicense(); EXPECT_FALSE(cdm_license_->Init(false, kEmptyServiceCertificate, - crypto_session_, nullptr)); + crypto_session_.get(), nullptr)); } TEST_F(CdmLicenseTest, InitWithEmptyServiceCert) { EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true)); - CreateCdmLicense(); EXPECT_TRUE(cdm_license_->Init(true, kEmptyServiceCertificate, - crypto_session_, policy_engine_)); + crypto_session_.get(), policy_engine_.get())); } TEST_F(CdmLicenseTest, InitWithInvalidServiceCert) { EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true)); - CreateCdmLicense(); EXPECT_FALSE(cdm_license_->Init(true, kInvalidServiceCertificate, - crypto_session_, policy_engine_)); + crypto_session_.get(), policy_engine_.get())); } TEST_F(CdmLicenseTest, InitWithServiceCert) { EXPECT_CALL(*crypto_session_, IsOpen()).WillOnce(Return(true)); - CreateCdmLicense(); EXPECT_TRUE(cdm_license_->Init(true, kDefaultServiceCertificate, - crypto_session_, policy_engine_)); + crypto_session_.get(), policy_engine_.get())); } TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) { @@ -315,7 +317,7 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) { // Supported certificates set by SetUp(). EXPECT_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull())); EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull())) - .WillOnce( + .WillRepeatedly( DoAll(SetArgPointee<0>(crypto_session_api_version), Return(true))); EXPECT_CALL(*crypto_session_, GetResourceRatingTier(NotNull())) .WillOnce(DoAll(SetArgPointee<0>(resource_rating_tier), Return(true))); @@ -335,15 +337,14 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidation) { .WillOnce( DoAll(SetArgPointee<0>(kWatermarkingConfigurable), Return(true))); - CreateCdmLicense(); EXPECT_TRUE(cdm_license_->Init(true, kDefaultServiceCertificate, - crypto_session_, policy_engine_)); + crypto_session_.get(), policy_engine_.get())); CdmAppParameterMap app_parameters; CdmKeyMessage signed_request; std::string server_url; EXPECT_EQ(cdm_license_->PrepareKeyRequest( - *init_data_, kToken, kLicenseTypeStreaming, app_parameters, + init_data_, kToken, kLicenseTypeStreaming, app_parameters, &signed_request, &server_url), KEY_MESSAGE); @@ -449,7 +450,7 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidationV15) { Return(CdmResponseType(NO_ERROR)))); EXPECT_CALL(*crypto_session_, GetSupportedCertificateTypes(NotNull())); EXPECT_CALL(*crypto_session_, GetApiVersion(NotNull())) - .WillOnce( + .WillRepeatedly( DoAll(SetArgPointee<0>(crypto_session_api_version), Return(true))); EXPECT_CALL(*crypto_session_, GetResourceRatingTier(NotNull())) .WillOnce(DoAll(SetArgPointee<0>(resource_rating_tier), Return(true))); @@ -469,15 +470,14 @@ TEST_F(CdmLicenseTest, PrepareKeyRequestValidationV15) { .WillOnce( DoAll(SetArgPointee<0>(kWatermarkingNotSupported), Return(true))); - CreateCdmLicense(); EXPECT_TRUE(cdm_license_->Init(true, kDefaultServiceCertificate, - crypto_session_, policy_engine_)); + crypto_session_.get(), policy_engine_.get())); CdmAppParameterMap app_parameters; CdmKeyMessage signed_request; std::string server_url; EXPECT_EQ(cdm_license_->PrepareKeyRequest( - *init_data_, kToken, kLicenseTypeStreaming, app_parameters, + init_data_, kToken, kLicenseTypeStreaming, app_parameters, &signed_request, &server_url), KEY_MESSAGE); @@ -614,9 +614,8 @@ TEST_P(CdmLicenseEntitledKeyTest, LoadsEntitledKeys) { } // Set up the CdmLicense with the mocks and fake entitlement key - CreateCdmLicense(); EXPECT_TRUE(cdm_license_->Init(true, kDefaultServiceCertificate, - crypto_session_, policy_engine_)); + crypto_session_.get(), policy_engine_.get())); cdm_license_->set_entitlement_keys(entitlement_license); // Call the function under test and check its return value diff --git a/core/test/message_dumper.cpp b/core/test/message_dumper.cpp index 65289fba..ee3fa8ef 100644 --- a/core/test/message_dumper.cpp +++ b/core/test/message_dumper.cpp @@ -48,6 +48,63 @@ void DumpHex(std::ofstream* out, const std::string& name, << " sizeof(" << name << "_raw));\n"; *out << std::dec; // Turn off hex when we're done. } + +void LogTimer(const char* field, bool set, int64_t time) { + long long total_seconds = static_cast(time); + char sign = ' '; + if (total_seconds < 0) { + total_seconds = -total_seconds; + sign = '-'; + } + const unsigned long long seconds = total_seconds % 60; + unsigned long long minutes = total_seconds / 60; + unsigned long long hours = minutes / 60; + minutes = minutes % 60; + unsigned long long days = hours / 24; + hours = hours % 24; + if (set) { + LOGD("%25.25s: %c%llu seconds = %llu day %02llu:%02llu:%02llu HMS", field, + sign, total_seconds, days, hours, minutes, seconds); + } else { + LOGD("%25.25s: %llu seconds (unset)", field, total_seconds); + } +} + +void PrintRequestProto(const char* label, + const video_widevine::LicenseRequest& request) { + switch (request.type()) { + case video_widevine::LicenseRequest_RequestType_NEW: + LOGD("%s type: NEW", label); + break; + case video_widevine::LicenseRequest_RequestType_RENEWAL: + LOGD("%s type: RENEWAL", label); + break; + case video_widevine::LicenseRequest_RequestType_RELEASE: + LOGD("%s type: RELEASE", label); + break; + } +} +void PrintLicenseProto(const char* label, + const video_widevine::License& license) { + LOGD("%s: can_play = %s, can_persist = %s, can_renew = %s", label, + license.policy().can_play() ? "true" : "false", + license.policy().can_persist() ? "true" : "false", + license.policy().can_renew() ? "true" : "false"); +#define QUOTE_DEFINE(A) #A +#define QUOTE(A) QUOTE_DEFINE(A) +#define LOG_TIMER(NAME) \ + LogTimer(QUOTE(NAME), license.policy().has_##NAME(), license.policy().NAME()) + LOG_TIMER(rental_duration_seconds); + LOG_TIMER(playback_duration_seconds); + LOG_TIMER(license_duration_seconds); + LOG_TIMER(renewal_recovery_duration_seconds); + LOG_TIMER(renewal_delay_seconds); + LOG_TIMER(renewal_retry_interval_seconds); + LOGD("renewal_server_url: %s", license.policy().renewal_server_url().c_str()); + LOGD("renew_with_usage: %s", + license.policy().renew_with_usage() ? "true" : "false"); + LOG_TIMER(play_start_grace_period_seconds); +} } // namespace std::ofstream MessageDumper::license_file; @@ -75,44 +132,54 @@ void MessageDumper::DumpLicenseRequest(const CdmKeyRequest& request) { SignedMessage signed_message; DumpHeader(&license_file, "License"); EXPECT_TRUE(signed_message.ParseFromString(request.message)); - EXPECT_TRUE(signed_message.has_oemcrypto_core_message()); - DumpHex(&license_file, "core_request", - signed_message.oemcrypto_core_message()); + if (wvoec::global_features.api_version >= wvoec::kCoreMessagesAPI) { + EXPECT_TRUE(signed_message.has_oemcrypto_core_message()); + DumpHex(&license_file, "core_request", + signed_message.oemcrypto_core_message()); + } // Since this is run within a test, we can also verify that the // request is valid. video_widevine::LicenseRequest license_request; EXPECT_TRUE(license_request.ParseFromString(signed_message.msg())); + // TODO(fredgc): figure out if we can build tests with full protobufs instead + // of proto lite, so that we can use TextFormat. + PrintRequestProto("License Request", license_request); } void MessageDumper::DumpLicense(const std::string& response) { SignedMessage signed_response; EXPECT_TRUE(signed_response.ParseFromString(response)); - EXPECT_TRUE(signed_response.has_oemcrypto_core_message()); - DumpHex(&license_file, "core_response", - signed_response.oemcrypto_core_message()); + if (wvoec::global_features.api_version >= wvoec::kCoreMessagesAPI) { + EXPECT_TRUE(signed_response.has_oemcrypto_core_message()); + DumpHex(&license_file, "core_response", + signed_response.oemcrypto_core_message()); + } video_widevine::License license; EXPECT_TRUE(license.ParseFromString(signed_response.msg())); + PrintLicenseProto("License", license); DumpHex(&license_file, "serialized_license", signed_response.msg()); std::string message = signed_response.oemcrypto_core_message() + signed_response.msg(); - ODK_Message odk_msg = ODK_Message_Create( - reinterpret_cast(const_cast(message.c_str())), - message.length()); - ODK_Message_SetSize(&odk_msg, - signed_response.oemcrypto_core_message().length()); - ODK_ParsedLicense odk_parsed_license = {}; - ODK_LicenseResponse odk_license_response = {}; - odk_license_response.parsed_license = &odk_parsed_license; - Unpack_ODK_LicenseResponse(&odk_msg, &odk_license_response); - EXPECT_EQ(ODK_Message_GetStatus(&odk_msg), MESSAGE_STATUS_OK); - // Valid hash is only needed for v16 messages. - std::string hash(ODK_SHA256_HASH_SIZE, ' '); - DumpHex(&license_file, "core_request_sha256", hash); - license_file << " nonce_required_ = " - << (odk_parsed_license.nonce_required ? "true" : "false") - << ";\n"; + if (wvoec::global_features.api_version >= wvoec::kCoreMessagesAPI) { + ODK_Message odk_msg = ODK_Message_Create( + reinterpret_cast(const_cast(message.c_str())), + message.length()); + ODK_Message_SetSize(&odk_msg, + signed_response.oemcrypto_core_message().length()); + ODK_ParsedLicense odk_parsed_license = {}; + ODK_LicenseResponse odk_license_response = {}; + odk_license_response.parsed_license = &odk_parsed_license; + Unpack_ODK_LicenseResponse(&odk_msg, &odk_license_response); + EXPECT_EQ(ODK_Message_GetStatus(&odk_msg), MESSAGE_STATUS_OK); + // Valid hash is only needed for v16 messages. + std::string hash(ODK_SHA256_HASH_SIZE, ' '); + DumpHex(&license_file, "core_request_sha256", hash); + license_file << " nonce_required_ = " + << (odk_parsed_license.nonce_required ? "true" : "false") + << ";\n"; + } license_file << " RunTest();\n"; license_file << "}\n\n"; } @@ -121,38 +188,44 @@ void MessageDumper::DumpRenewalRequest(const CdmKeyRequest& request) { DumpHeader(&renewal_file, "Renewal"); SignedMessage signed_message; EXPECT_TRUE(signed_message.ParseFromString(request.message)); - EXPECT_TRUE(signed_message.has_oemcrypto_core_message()); - DumpHex(&renewal_file, "core_request", - signed_message.oemcrypto_core_message()); - + if (wvoec::global_features.api_version >= wvoec::kCoreMessagesAPI) { + EXPECT_TRUE(signed_message.has_oemcrypto_core_message()); + DumpHex(&renewal_file, "core_request", + signed_message.oemcrypto_core_message()); + } video_widevine::LicenseRequest renewal_request; EXPECT_TRUE(renewal_request.ParseFromString(signed_message.msg())); + PrintRequestProto("Renewal Request", renewal_request); } void MessageDumper::DumpRenewal(const std::string& response) { SignedMessage signed_response; EXPECT_TRUE(signed_response.ParseFromString(response)) << "Response = " << wvutil::b2a_hex(response); - EXPECT_TRUE(signed_response.has_oemcrypto_core_message()); - DumpHex(&renewal_file, "core_response", - signed_response.oemcrypto_core_message()); - + if (wvoec::global_features.api_version >= wvoec::kCoreMessagesAPI) { + EXPECT_TRUE(signed_response.has_oemcrypto_core_message()); + DumpHex(&renewal_file, "core_response", + signed_response.oemcrypto_core_message()); + } video_widevine::License renewal; EXPECT_TRUE(renewal.ParseFromString(signed_response.msg())); + PrintLicenseProto("Renewal", renewal); DumpHex(&renewal_file, "renewal", signed_response.msg()); - std::string message = - signed_response.oemcrypto_core_message() + signed_response.msg(); - ODK_Message odk_msg = ODK_Message_Create( - reinterpret_cast(const_cast(message.c_str())), - message.length()); - ODK_Message_SetSize(&odk_msg, - signed_response.oemcrypto_core_message().length()); - ODK_RenewalResponse odk_renewal_response = {}; - Unpack_ODK_RenewalResponse(&odk_msg, &odk_renewal_response); - EXPECT_EQ(ODK_Message_GetStatus(&odk_msg), MESSAGE_STATUS_OK); - renewal_file << " renewal_duration_seconds_ = " - << odk_renewal_response.renewal_duration_seconds << ";\n"; + if (wvoec::global_features.api_version >= wvoec::kCoreMessagesAPI) { + std::string message = + signed_response.oemcrypto_core_message() + signed_response.msg(); + ODK_Message odk_msg = ODK_Message_Create( + reinterpret_cast(const_cast(message.c_str())), + message.length()); + ODK_Message_SetSize(&odk_msg, + signed_response.oemcrypto_core_message().length()); + ODK_RenewalResponse odk_renewal_response = {}; + Unpack_ODK_RenewalResponse(&odk_msg, &odk_renewal_response); + EXPECT_EQ(ODK_Message_GetStatus(&odk_msg), MESSAGE_STATUS_OK); + renewal_file << " renewal_duration_seconds_ = " + << odk_renewal_response.renewal_duration_seconds << ";\n"; + } renewal_file << " RunTest();\n"; renewal_file << "}\n\n"; } @@ -167,9 +240,11 @@ void MessageDumper::DumpProvisioningRequest( SignedProvisioningMessage signed_message; EXPECT_TRUE(signed_message.ParseFromString(request)) << "Request = " << wvutil::b2a_hex(request); - EXPECT_TRUE(signed_message.has_oemcrypto_core_message()); - DumpHex(&provision_file, "core_request", - signed_message.oemcrypto_core_message()); + if (wvoec::global_features.api_version >= wvoec::kCoreMessagesAPI) { + EXPECT_TRUE(signed_message.has_oemcrypto_core_message()); + DumpHex(&provision_file, "core_request", + signed_message.oemcrypto_core_message()); + } } } @@ -186,9 +261,11 @@ void MessageDumper::DumpProvisioning(const CdmProvisioningResponse& response) { response, &extracted_message)); EXPECT_TRUE(signed_response.ParseFromString(extracted_message)); } - EXPECT_TRUE(signed_response.has_oemcrypto_core_message()); - DumpHex(&provision_file, "core_response", - signed_response.oemcrypto_core_message()); + if (wvoec::global_features.api_version >= wvoec::kCoreMessagesAPI) { + EXPECT_TRUE(signed_response.has_oemcrypto_core_message()); + DumpHex(&provision_file, "core_response", + signed_response.oemcrypto_core_message()); + } DumpHex(&provision_file, "provisioning_response", signed_response.message()); // The choice of ECC or RSA key is decided at the server, based on diff --git a/core/test/policy_engine_unittest.cpp b/core/test/policy_engine_unittest.cpp index 677502a4..f9c17dc7 100644 --- a/core/test/policy_engine_unittest.cpp +++ b/core/test/policy_engine_unittest.cpp @@ -131,7 +131,6 @@ using video_widevine::STREAMING; // gmock methods using ::testing::_; using ::testing::InSequence; -using ::testing::Invoke; using ::testing::MockFunction; using ::testing::Pair; using ::testing::Return; @@ -250,7 +249,7 @@ class PolicyEngineTestV16 : public PolicyEngineTest { protected: void SetUp() override { EXPECT_CALL(*crypto_session_, GetApiVersion(_)) - .WillOnce(DoAll(SetArgPointee<0>(kOemCryptoV16), Return(true))); + .WillRepeatedly(DoAll(SetArgPointee<0>(kOemCryptoV16), Return(true))); PolicyEngineTest::SetUp(); } }; @@ -267,7 +266,7 @@ class PolicyEngineTestV18 : public PolicyEngineTest { } }; -TEST_F(PolicyEngineTest, NoLicense) { +TEST_F(PolicyEngineTestV16, NoLicense) { EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); } @@ -1045,7 +1044,7 @@ TEST_F(PolicyEngineKeyAllowedUsageTest, AllowedUsageGeneric) { class PolicyEngineQueryTest : public PolicyEngineTestV16 { protected: void SetUp() override { - PolicyEngineTest::SetUp(); + PolicyEngineTestV16::SetUp(); policy_engine_.reset( new PolicyEngine(kSessionId, nullptr, crypto_session_.get())); InjectMockClock(); @@ -2826,6 +2825,10 @@ TEST_F(PolicyEngineTestV16, PlaybackOk_RestoreWithoutPlaybackTimes) { EXPECT_TRUE(policy_engine_->CanDecryptContent(kKeyId)); } +TEST_F(PolicyEngineTestV18, NoLicense) { + EXPECT_FALSE(policy_engine_->CanDecryptContent(kKeyId)); +} + // These tests exercise license policy when OEMCrypto supports v18. // The following scenarios are from the duration-and-renewal doc. // Verifies correct reporting of events, OnSessionRenewalNeeded, diff --git a/core/test/policy_integration_test.cpp b/core/test/policy_integration_test.cpp index 2f6f8d1c..258df3e8 100644 --- a/core/test/policy_integration_test.cpp +++ b/core/test/policy_integration_test.cpp @@ -34,7 +34,9 @@ class CorePIGTest : public WvCdmTestBaseWithEngine { } }; -// An offline license with nonce not required. +/** + * An offline license with nonce not required. + */ TEST_F(CorePIGTest, OfflineNoNonce) { LicenseHolder holder("CDM_OfflineNoNonce", &cdm_engine_, config_); holder.set_can_persist(true); @@ -53,7 +55,9 @@ TEST_F(CorePIGTest, OfflineNoNonce) { ASSERT_NO_FATAL_FAILURE(holder.CloseSession()); } -// An offline license with nonce and provider session token. +/** + * An offline license with nonce and provider session token. + */ TEST_F(CorePIGTest, OfflineWithPST) { LicenseHolder holder("CDM_OfflineWithPST", &cdm_engine_, config_); holder.set_can_persist(true); diff --git a/core/test/provisioning_holder.cpp b/core/test/provisioning_holder.cpp index 963b8670..954b2eff 100644 --- a/core/test/provisioning_holder.cpp +++ b/core/test/provisioning_holder.cpp @@ -69,17 +69,16 @@ void ProvisioningHolder::Provision(CdmCertificateType cert_type, url_request.PostCertRequestInQueryString(request); // Receive and parse response. - std::string response; - ASSERT_NO_FATAL_FAILURE(url_request.AssertOkResponse(&response)) + ASSERT_NO_FATAL_FAILURE(url_request.AssertOkResponse(&response_)) << "Failed to fetch provisioning response. " - << DumpProvAttempt(request, response, cert_type); + << DumpProvAttempt(request, response_, cert_type); if (binary_provisioning) { // extract provisioning response from received message // Extracts signed response from JSON string, result is serialized // protobuf. std::string protobuf_response; - const bool extract_ok = ExtractSignedMessage(response, &protobuf_response); + const bool extract_ok = ExtractSignedMessage(response_, &protobuf_response); ASSERT_TRUE(extract_ok) << "Failed to extract signed serialized " "response from JSON response"; LOGV("Extracted response message: \n%s\n", protobuf_response.c_str()); @@ -94,26 +93,17 @@ void ProvisioningHolder::Provision(CdmCertificateType cert_type, << "Failed to decode base64 of response: response = " << protobuf_response; - const std::string binary_protobuf_response(response_vec.begin(), - response_vec.end()); + response_.assign(response_vec.begin(), response_vec.end()); + } - ASSERT_EQ(NO_ERROR, cdm_engine_->HandleProvisioningResponse( - binary_protobuf_response, kLevelDefault, - &certificate_, &wrapped_key_)) - << "Binary provisioning failed. " - << DumpProvAttempt(request, response, cert_type); - if (config_.dump_golden_data()) { - MessageDumper::DumpProvisioning(binary_protobuf_response); - } - } else { - ASSERT_EQ(NO_ERROR, - cdm_engine_->HandleProvisioningResponse( - response, kLevelDefault, &certificate_, &wrapped_key_)) - << "Non-binary provisioning failed. " - << DumpProvAttempt(request, response, cert_type); - if (config_.dump_golden_data()) { - MessageDumper::DumpProvisioning(response); - } + ASSERT_EQ(NO_ERROR, + cdm_engine_->HandleProvisioningResponse( + response_, kLevelDefault, &certificate_, &wrapped_key_)) + << (binary_provisioning ? "Binary provisioning failed. " + : "Non-binary provisioning failed. ") + << DumpProvAttempt(request, response_, cert_type); + if (config_.dump_golden_data()) { + MessageDumper::DumpProvisioning(response_); } } diff --git a/core/test/provisioning_holder.h b/core/test/provisioning_holder.h index f42bacba..1b68c9cc 100644 --- a/core/test/provisioning_holder.h +++ b/core/test/provisioning_holder.h @@ -20,6 +20,10 @@ class ProvisioningHolder { provisioning_service_certificate_( config.provisioning_service_certificate()) {} void Provision(CdmCertificateType cert_type, bool binary_provisioning); + void Provision(bool binary_provisioning) { + Provision(kCertificateWidevine, binary_provisioning); + } + std::string response() const { return response_; } std::string certificate() const { return certificate_; } std::string wrapped_key() const { return wrapped_key_; } @@ -28,6 +32,7 @@ class ProvisioningHolder { const ConfigTestEnv& config_; std::string provisioning_server_url_; std::string provisioning_service_certificate_; + std::string response_; std::string certificate_; std::string wrapped_key_; diff --git a/core/test/reboot_test.cpp b/core/test/reboot_test.cpp index 3e5ce5fe..1e4f61f2 100644 --- a/core/test/reboot_test.cpp +++ b/core/test/reboot_test.cpp @@ -11,14 +11,14 @@ #include #include "create_test_file_system.h" +#include "device_files.pb.h" #include "license_holder.h" #include "log.h" #include "test_sleep.h" -using wvutil::a2b_hex; +using video_widevine_client::sdk::SavedStorage; using wvutil::FileSystem; using wvutil::TestSleep; -using wvutil::unlimited_b2a_hex; namespace wvcdm { FileSystem* RebootTest::file_system_; @@ -27,179 +27,8 @@ namespace { // How much fudge or round off error do we allow in license durations for reboot // tests. constexpr int64_t kFudge = 10; - -// We will encode a value string by wrapping it in braces, or as hex. -// If the string is not printable, or if it has unmatched braces, then we use -// hex. Otherwise, we surround the whole string with braces. -std::string EncodeString(const std::string& data) { - int braces_count = 0; - for (size_t i = 0; i < data.length(); i++) { - if (data[i] == '{') braces_count++; - if (data[i] == '}') braces_count--; - // If printable or whitespace (because '\n' is not printable?!?). - bool printable = isprint(data[i]) || isspace(data[i]); - // If there are any unprintable characters, except whitespace, or if we - // close a brace before we open it, then just use hex. - if (!printable || braces_count < 0) { - return "0x" + unlimited_b2a_hex(data) + ","; - } - } - // If we left any braces open, then use hex. - if (braces_count != 0) return "0x" + unlimited_b2a_hex(data) + ","; - return "{" + data + "},"; -} - -// Encode a map key for dumping. When we encode a map, we expect the keys to be -// like filenames, so we can separate them with colons and whitespace. If the -// key has these special characters, we will encode as hex. -std::string EncodeKey(const std::string& data) { - if (data.length() == 0) { - LOGE("Encoding empty string as key!"); - return "EMPTY:"; - } - // When decoding, we assume that a key starting with "0x" is in hex. So we - // can't have any keys that start with "0x". - if (data.substr(0, 2) == "0x") return "0x" + unlimited_b2a_hex(data) + ":"; - // If the key is just is not printable, or if it has unmatched braces, then - // we use hex. Otherwise, we surround the whole string with braces. - for (size_t i = 0; i < data.length(); i++) { - if (!isprint(data[i]) || (data[i] == ':')) { - return "0x" + unlimited_b2a_hex(data) + ":"; - } - } - return data + ":"; -} - -// In between keys and values, we will ignore whitespace. This allows a human to -// edit the persistent data a little bit without breaking anything. -void SkipSpace(const std::string& encoded, size_t* index) { - if (!index) return; - while (*index < encoded.length() && isspace(encoded[*index])) (*index)++; -} - -// Decode a string that was encoded using EncodeString. -std::string DecodeString(const std::string& encoded, size_t* index) { - if (!index) return ""; - SkipSpace(encoded, index); - if (*index + 2 >= - encoded.length()) { // Encoded string has at least 3 characters. - LOGE("Error decoding short string from %s at %zd", encoded.c_str(), *index); - *index = encoded.length(); - return ""; - } - if (encoded[*index] == '{') { - (*index)++; - size_t start = *index; - int braces_count = 1; - while (*index < encoded.length()) { - if (encoded[*index] == '{') braces_count++; - if (encoded[*index] == '}') braces_count--; - if (braces_count == 0) { - size_t end = *index; - (*index) += 2; // absorb the comma and the '}', too. - return encoded.substr(start, end - start); - } - (*index)++; - } - std::string tail = encoded.substr(start); - LOGE("Non-terminated brace %s at %zd: %s", encoded.c_str(), start, - tail.c_str()); - *index = encoded.length(); - return ""; - } - if (encoded[*index] != '0' || encoded[*index + 1] != 'x') { - std::string tail = encoded.substr(*index); - LOGE("Hex should start with 0x in %s at %zd: %s", encoded.c_str(), *index, - tail.c_str()); - *index = encoded.length(); - return ""; - } - *index += 2; - size_t start = *index; - while (*index < encoded.length()) { - if (encoded[*index] == ',') { - size_t end = *index; - std::vector result = a2b_hex(encoded.substr(start, end - start)); - (*index)++; // absorb the comma. - return std::string(result.begin(), result.end()); - } - (*index)++; - } - std::string tail = encoded.substr(start); - LOGE("Bad encoding in %s at %zd: %s", encoded.c_str(), start, tail.c_str()); - *index = encoded.length(); - return ""; -} - -// Decode a string that was encoded with EncodeKey. -std::string DecodeKey(const std::string& encoded, size_t* index) { - if (!index) return ""; - SkipSpace(encoded, index); - if (*index + 1 >= encoded.length()) { - LOGE("Error decoding key from %s at %zd", encoded.c_str(), *index); - *index = encoded.length(); - return ""; - } - // If it starts with 0x, then it is in hex. - if (encoded[*index] == '0' && encoded[*index + 1] == 'x') { - size_t start = *index + 2; - while (*index < encoded.length() && encoded[*index] != ':') (*index)++; - size_t end = *index; - std::vector result = a2b_hex(encoded.substr(start, end - start)); - (*index)++; // skip the colon. - return std::string(result.begin(), result.end()); - } - size_t start = *index; - while (*index < encoded.length() && encoded[*index] != ':') (*index)++; - size_t end = *index; - (*index)++; // skip the colon. - return encoded.substr(start, end - start); -} } // namespace -std::string RebootTest::DumpData( - const std::map& data) { - std::ostringstream output; - output << "{\n"; - for (const auto& entry : data) { - output << " " << EncodeKey(entry.first) << " " - << EncodeString(entry.second) + "\n"; - } - output << "}\n"; - return output.str(); -} - -bool RebootTest::ParseDump(const std::string& dump, - std::map* data) { - size_t index = 0; - SkipSpace(dump, &index); - if (index >= dump.length()) return false; - if (dump[index] != '{') { - LOGE("Dump does not start with '{'"); - return false; - } - index++; // absorb '{' - while (true) { - SkipSpace(dump, &index); - if (index >= dump.length()) return false; - if (dump[index] == '}') { - index++; // absorb '}' - SkipSpace(dump, &index); - if (index != dump.length()) { - std::string tail = dump.substr(index); - LOGE("Trailing data in dump. %s at %zd: %s", dump.c_str(), index, - tail.c_str()); - return false; - } - return true; - } - std::string tail = dump.substr(index); - std::string key = DecodeKey(dump, &index); - std::string value = DecodeString(dump, &index); - (*data)[key] = value; - } -} - void RebootTest::SetUp() { WvCdmTestBase::SetUp(); if (!file_system_) file_system_ = CreateTestFileSystem(); @@ -221,7 +50,10 @@ void RebootTest::SetUp() { std::string dump(file_size, ' '); ssize_t read = file->Read(&dump[0], dump.size()); EXPECT_EQ(read, file_size) << "Error reading persistent data file."; - EXPECT_TRUE(ParseDump(dump, &persistent_data_)); + + SavedStorage proto; + EXPECT_TRUE(proto.ParseFromString(dump)); + persistent_data_.insert(proto.files().begin(), proto.files().end()); } TestSleep::SyncFakeClock(); } @@ -231,7 +63,13 @@ void RebootTest::TearDown() { auto file = file_system_->Open(persistent_data_filename_, FileSystem::kCreate | FileSystem::kTruncate); ASSERT_TRUE(file) << "Failed to open file: " << persistent_data_filename_; - std::string dump = DumpData(persistent_data_); + + SavedStorage proto; + proto.mutable_files()->insert(persistent_data_.begin(), + persistent_data_.end()); + std::string dump; + ASSERT_TRUE(proto.SerializeToString(&dump)); + const ssize_t bytes_written = file->Write(dump.data(), dump.length()); EXPECT_EQ(bytes_written, static_cast(dump.length())); WvCdmTestBase::TearDown(); @@ -254,41 +92,6 @@ void RebootTest::SaveTime(const std::string& key, int64_t time) { persistent_data_[key] = std::to_string(time); } -/** Test the dump and restore functions above. This does not test CDM - functionality. */ -TEST_F(RebootTest, TestDumpUtil) { - // Check that an empty map can be saved. - std::map map1; - const std::string dump = DumpData(map1); - std::map map2; - EXPECT_TRUE(ParseDump(dump, &map2)); - EXPECT_EQ(map1, map2); - // Now fill it with some data and try again. - map1["key1"] = "this is a string. "; - map1["key2"] = "mismatch } {"; - map1["key3"] = "mismatch } "; - map1["key4"] = "mismatch {"; - map1["key5"] = "this: { has { matched } } braces { /.,)(**&^$&^% }"; - map1["key6"] = ""; - map1["00 whitespace in key 00"] = "value is ok"; - // This key looks like it might be hex. It should show up as hex in the - // save file. - map1["0x_bad_key_00"] = "value is ok"; - std::string big_string = "start with something {binary"; - // Double big_string 8 times, i.e. times 256, so it's bigger than 2k: - for (int i = 0; i < 8; i++) big_string = big_string + big_string; - map1["big_file"] = big_string; - const std::string dump2 = DumpData(map1); - std::map map3; - EXPECT_TRUE(ParseDump(dump2, &map3)); - EXPECT_EQ(map1, map3); - if (test_pass() == 0) { - persistent_data_ = map1; - } else { - EXPECT_EQ(persistent_data_, map1); - } -} - /** Verify that the file system stores files from one test pass to the next. */ TEST_F(RebootTest, FilesArePersistent) { const std::string key = "saved_value"; diff --git a/core/test/reboot_test.h b/core/test/reboot_test.h index fae4993e..5d9b097e 100644 --- a/core/test/reboot_test.h +++ b/core/test/reboot_test.h @@ -22,17 +22,6 @@ class RebootTest : public WvCdmTestBaseWithEngine { static void set_file_system(wvutil::FileSystem* file_system) { file_system_ = file_system; } - // Dump a map to a std string in an almost human readable way so that the map - // can be rebuilt using ParseDump below. The keys in the map must be standard - // identifier strings, which means no special characters or whitespace. By - // "almost human readable", we mean that a human debugging the dump will be - // able to find the keys, and see the values if they are printable or see a - // hex dump of the values if they are not. - static std::string DumpData(const std::map& data); - // Parse a dump generated by DumpData and recreate the original data map. - // Returns true on success. - static bool ParseDump(const std::string& dump, - std::map* data); static int test_pass() { return default_config_->test_pass(); } diff --git a/core/test/test_base.cpp b/core/test/test_base.cpp index f5c13e8e..26d44167 100644 --- a/core/test/test_base.cpp +++ b/core/test/test_base.cpp @@ -330,6 +330,12 @@ void WvCdmTestBase::Provision() { } void WvCdmTestBase::EnsureProvisioned() { + // TODO: b/305093063 - Remove when Drm Reprovisioning server is implemented. + if (wvoec::global_features.provisioning_method == + OEMCrypto_DrmReprovisioning) { + GTEST_SKIP() + << "Skipping until Drm Reprovisioning server support is implemented."; + } CdmSessionId session_id; std::unique_ptr file_system(CreateTestFileSystem()); // OpenSession will check if a DRM certificate exists, while diff --git a/metrics/src/Android.bp b/metrics/src/Android.bp index 83f3dcd0..61754e58 100644 --- a/metrics/src/Android.bp +++ b/metrics/src/Android.bp @@ -67,5 +67,5 @@ cc_library { type: "full", }, - min_sdk_version: "UpsideDownCake", + min_sdk_version: "34", } diff --git a/oemcrypto/include/OEMCryptoCENC.h b/oemcrypto/include/OEMCryptoCENC.h index fcfdd3bb..e349200a 100644 --- a/oemcrypto/include/OEMCryptoCENC.h +++ b/oemcrypto/include/OEMCryptoCENC.h @@ -3,7 +3,7 @@ // License Agreement. /** - * @mainpage OEMCrypto API v18.2 + * @mainpage OEMCrypto API v18.5 * * OEMCrypto is the low level library implemented by the OEM to provide key and * content protection, usually in a separate secure memory or process space. The @@ -136,6 +136,7 @@ * license to be reloaded. * * @defgroup entitled Entitlement License API + * Functions that are needed for entitled and entitlement licenses. * * [Entitlement licensing](../../index#entitlement) is a way to provide access * to content keys that may be stored elsewhere, such as in the content itself. @@ -366,7 +367,8 @@ typedef struct { } OEMCrypto_CENCEncryptPatternDesc; /** - * OEMCryptoCipherMode is used in SelectKey to prepare a key for decryption. + * OEMCryptoCipherMode is used in OEMCrypto_GetKeyHandle() to prepare a key for + * decryption. */ typedef enum OEMCryptoCipherMode { // explicit cipher modes used for modular DRM @@ -511,7 +513,9 @@ typedef enum OEMCrypto_ProvisioningMethod { // Device has factory installed OEM certificate. OEMCrypto_OEMCertificate = 3, // Device has Boot Certificate Chain (BCC). - OEMCrypto_BootCertificateChain = 4 + OEMCrypto_BootCertificateChain = 4, + // Device has baked in DRM certificate with reprovisioning (level 3 only). + OEMCrypto_DrmReprovisioning = 5 } OEMCrypto_ProvisioningMethod; /** @@ -712,6 +716,9 @@ typedef enum OEMCrypto_SignatureHashAlgorithm { #define OEMCrypto_GetSignatureHashAlgorithm _oecc139 #define OEMCrypto_EnterTestMode _oecc140 #define OEMCrypto_GetDeviceSignedCsrPayload _oecc141 +#define OEMCrypto_FactoryInstallBCCSignature _oecc142 +#define OEMCrypto_GetEmbeddedDrmCertificate _oecc143 +#define OEMCrypto_UseSecondaryKey _oecc144 // clang-format on /// @addtogroup initcontrol @@ -1198,6 +1205,8 @@ OEMCryptoResult OEMCrypto_GenerateNonce(OEMCrypto_SESSION session, * @retval OEMCrypto_ERROR_BUFFER_TOO_LARGE * @retval OEMCrypto_ERROR_SESSION_LOST_STATE * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * @retval OEMCrypto_ERROR_INVALID_KEY if the session's private key is not a + * DRM key. * * @buffer_size * OEMCrypto shall support message sizes as described in the section @@ -1508,9 +1517,9 @@ OEMCryptoResult OEMCrypto_PrepAndSignRenewalRequest( * Note: if the current SRM version requirement is not met, LoadLicense will * still succeed and the keys will be loaded. However, those keys with the * SRMVersionRequired bit set will have their HDCP_Version increased to 0xF - - * local display only. Any future call to SelectKey for these keys while - * there is an external display will return OEMCrypto_ERROR_INSUFFICIENT_HDCP - * at that time. + * local display only. Any future call to OEMCrypto_GetKeyHandle() for these + * keys while there is an external display will return + * OEMCrypto_ERROR_INSUFFICIENT_HDCP at that time. * * @param[in] session: crypto session identifier. * @param[in] message: pointer to memory containing data. @@ -1774,7 +1783,7 @@ OEMCryptoResult OEMCrypto_RemoveEntitledKeySession( * * For devices that use a hardware key ladder, it may be more convenient to * store the encrypted content key data in the key table, and decrypt it when - * the function SelectKey is called. + * the function OEMCrypto_GetKeyHandle() is called. * * @param[in] session: handle for the entitled key session to be used. * @param[in] message: pointer to memory containing message to be verified. @@ -2000,8 +2009,8 @@ OEMCryptoResult OEMCrypto_GetOEMKeyToken(OEMCrypto_SESSION key_session, * For platforms that do not need to support Bypass Decrypt, a mode compatible * with previous versions of OEMCrypto is available. These devices may latch the * key to the session and continue to use this key for this session until - * OEMCrypto_SelectKey() is called again, or until OEMCrypto_CloseSession() is - * called. + * OEMCrypto_GetKeyHandle() is called again, or until OEMCrypto_CloseSession() + * is called. * * The "key handle" in this mode is the session ID. Platforms should request a * 4-byte key handle buffer and copy the session ID into it. @@ -2014,13 +2023,13 @@ OEMCryptoResult OEMCrypto_GetOEMKeyToken(OEMCrypto_SESSION key_session, * the device should disable analog video output. If the device has * analog video output that cannot be disabled, then the key is not * selected, and OEMCrypto_ERROR_ANALOG_OUTPUT is returned. This step is - * optional -- SelectKey may return OEMCrypto_SUCCESS and delay the - * error until a call to OEMCrypto_DecryptCENC(). + * optional -- OEMCrypto_GetKeyHandle() may return OEMCrypto_SUCCESS and + * delay the error until a call to OEMCrypto_DecryptCENC(). * 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. This step is optional - * -- SelectKey may return OEMCrypto_SUCCESS and delay the error until a - * call to OEMCrypto_DecryptCENC(). + * -- OEMCrypto_GetKeyHandle() may return OEMCrypto_SUCCESS and delay the + * error until a call to OEMCrypto_DecryptCENC(). * 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. @@ -2090,9 +2099,9 @@ OEMCryptoResult OEMCrypto_GetKeyHandle(OEMCrypto_SESSION session, * ISO-CENC standard. * * Decryption mode is AES-128-CTR or AES-128-CBC depending on the value of - * cipher_mode previously passed in to OEMCrypto_SelectKey(). For the encrypted - * portion of subsamples, the content key associated with the handle is - * latched in the active hardware key ladder and is used for the decryption + * cipher_mode previously passed in to OEMCrypto_GetKeyHandle(). For the + * encrypted portion of subsamples, the content key associated with the handle + * is latched in the active hardware key ladder and is used for the decryption * operation. For the clear portion of subsamples, the data is simply copied. * * After decryption, all the input_data bytes are copied to the location @@ -2190,8 +2199,8 @@ OEMCryptoResult OEMCrypto_GetKeyHandle(OEMCrypto_SESSION session, * * The decryption mode, either OEMCrypto_CipherMode_CENC or * OEMCrypto_CipherMode_CBCS, was already specified in the call to - * OEMCrypto_SelectKey(). The encryption pattern is specified by the fields in - * the parameter pattern. A description of partial encryption patterns for + * OEMCrypto_GetKeyHandle(). The encryption pattern is specified by the fields + * in the parameter pattern. A description of partial encryption patterns for * 'cbcs' can be found in the ISO-CENC standard, section 10.4. * * 'cenc' SCHEME: @@ -2996,6 +3005,41 @@ OEMCryptoResult OEMCrypto_WrapKeyboxOrOEMCert( OEMCryptoResult OEMCrypto_InstallKeyboxOrOEMCert(const uint8_t* keybox_or_cert, size_t keybox_or_cert_length); +/** + * Install a factory generated signature for the BCC. This is for devices that + * use Provisioning 4.0, with the signing option in the factory. With the + * signing option, the BCC is extracted from the device in the factory. Instead + * of being uploaded to the Widevine server, the BCC is signed by a certificate + * that the manufacturer shares with Widevine. The signature is then installed + * on the device is a secure location. The signature must not be erased during + * factory reset. + * + * This signature should be returned as `addition_signature` in a call to the + * function `OEMCrypto_GetBootCertificateChain()`. + * + * Devices that do not support Provisioning 4.0, or only support Provisioning + * 4.0 Option 1 should return OEMCrypto_ERROR_NOT_IMPLEMENTED. + * + * + * @param[in] signature: pointer to data as input + * @param[in] signature_length: length of the data in bytes + * + * @retval OEMCrypto_SUCCESS success + * @retval OEMCrypto_ERROR_INSUFFICIENT_RESOURCES + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + * @retval OEMCrypto_ERROR_SYSTEM_INVALIDATED + * + * @threading + * This is an "Initialization and Termination Function" and will not be + * called simultaneously with any other function, as if the CDM holds a write + * lock on the OEMCrypto system. + * + * @version + * This method is new in API version 18.3. + */ +OEMCryptoResult OEMCrypto_FactoryInstallBCCSignature(const uint8_t* signature, + size_t signature_length); + /** * This function is for OEMCrypto to tell the layer above what provisioning * method it uses: keybox or OEM certificate. @@ -3319,6 +3363,8 @@ uint32_t OEMCrypto_MinorAPIVersion(void); * different TA builds. * - "build_timestamp" [string]: ISO 8601 formatted timestamp of the time the * TA was compiled, eg "YYYY-MM-DDTHH:MM:SS" + * - "is_factory_mode" [bool]: Whether this was built with FACTORY_MODE_ONLY + * defined * * While not required, another optional top level struct can be added to the * build information string to provide information about liboemcrypto.so: @@ -3812,7 +3858,7 @@ OEMCryptoResult OEMCrypto_ProductionReady(void); * the most recent license should be honored. The watermarking feature should * be turned on or off when a license is loaded. If this conflicts with a * license that had been loaded earlier, then keys from the earlier license may - * not be used. In this case, either OEMCrypto_SelectKey or + * not be used. In this case, either OEMCrypto_GetKeyHandle or * OEMCrypto_DecryptCENC will return OEMCrypto_ERROR_INSUFFICIENT_PRIVILEGE to * indicate that the watermarking status has changed and the license is no * longer usable. @@ -4838,6 +4884,7 @@ OEMCryptoResult OEMCrypto_GetBootCertificateChain( * 4.4, as well as Android IRemotelyProvisionedComponent.aidl (under * "SignedData") * + * ~~~ * |public_key_signature|: COSE_Sign1 CBOR array * [ * protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 / @@ -4847,6 +4894,7 @@ OEMCryptoResult OEMCrypto_GetBootCertificateChain( * signature: bstr ; PureEd25519(priv_key, Sig_structure) / * ; ECDSA(priv_key, Sig_structure) * ] + * ~~~ * * Notes: * 1. The payload field in the COSE_Sign1 struct is the public key generated @@ -4861,6 +4909,7 @@ OEMCryptoResult OEMCrypto_GetBootCertificateChain( * field in Sig_structure is the same as the payload in the above * COSE_Sign1 CBOR array. * + * ~~~ * Sig_structure: CBOR array * [ * context: "Signature1", @@ -4869,6 +4918,7 @@ OEMCryptoResult OEMCrypto_GetBootCertificateChain( * external_aad: bstr .size 0, * payload: bstr .cbor Data / nil, * ] + * ~~~ * * If an OEM private key is available, the request is assumed to be for DRM * certificate provisioning and the public key is signed by the OEM private key. @@ -4877,6 +4927,19 @@ OEMCryptoResult OEMCrypto_GetBootCertificateChain( * key is an ECC key, then |public_key_signature| is the ASN.1 DER-encoded (R,S) * signature as specified in RFC 3279 2.2.3. * + * After this function completes successfully, the session will hold a private + * key and will be ready for a call to + * OEMCrypto_PrepAndSignProvisioningRequest(). In particular, when this + * function is used to generate a DRM Certificate key pair, the session will be + * ready to sign a provisioning request with the DRM Cert private key. When this + * function is used to generate an OEM Certificate key pair, the session will be + * ready to sign a provisioning request with the OEM Cert private key. + * + * The public key shall be an ASN.1 DER-encoded SubjectPublicKeyInfo as + * specified in RFC 5280. Widevine recommends ECC keys for Provisioning 4.0, but + * an RSA key may also be used. If the key is an RSA key, then the encoding + * should use "rsaEncryption" (OID 1.2.840.113549.1.1.1), and not RSASSA-PSS. + * * @param[in] session: session id. * @param[out] public_key: pointer to the buffer that receives the public key * that is to be certified by the server. The key must be an ASN.1 @@ -4920,7 +4983,9 @@ OEMCryptoResult OEMCrypto_GenerateCertificateKeyPair( OEMCrypto_PrivateKeyType* key_type); /** - * Get the serialized device information in CBOR map format. + * Get the serialized device information in CBOR map format. This is for devices + * that use Provisioning 4.0, with the device key uploading option in the + * factory. * * The device * information may contain, for example, device make and model, "fused" status, @@ -4929,7 +4994,9 @@ OEMCryptoResult OEMCrypto_GenerateCertificateKeyPair( * provisioning request is coming from the expected device in the fields, based * on the values previously uploaded and registered. * - * This method is used by provisioning 4 only. + * Devices that do not support Provisioning 4.0, or do not support + * Provisioning 4.0 Uploading Option should return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * * @param[out] device_info: pointer to the buffer that receives the serialized * device information in CBOR map format. @@ -4955,24 +5022,87 @@ OEMCryptoResult OEMCrypto_GetDeviceInformation(uint8_t* device_info, size_t* device_info_length); /** - * Get the serialized signed Certificate Signing Request (Csr) payload in - * COSE_Sign1 format. + * Get the serialized signed Certificate Signing Request (CSR) payload in + * COSE_Sign1 format. This is for devices that use Provisioning 4.0, with the + * device key uploading option in the factory. * - * The signed CSR payload contains challenge and device information. It is - * signed by the leaf cert of the boot certificate chain (BCC). It is only used - * in the factory, uploaded and validated during device registration. + * With the uploading option, the RKP factory extraction tool provided by Google + * makes a call to this function to collect the signed CSR payload for + * generating the CSR to be uploaded to the device database. The CSR payload is + * signed by the leaf cert of the Boot Certificate Chain. * - * This method is used by provisioning 4 only. + * The format of a CSR payload before COSE_Sign1 is a CBOR array described in + * Android IRemotelyProvisionedComponent.aidl (under "CsrPayload"): + * + * ~~~ + * CsrPayload = [ ; CBOR Array defining the payload for CSR. + * version: 3, ; The CsrPayload CDDL Schema version. + * CertificateType: "widevine" ; The type of certificate being requested. + * DeviceInfo, ; Defined in Android DeviceInfo.aidl + * KeysToSign: [] ; Empty list + * ] + * ~~~ + * + * The type of CertificateType is tstr and the value should always be + * "widevine". The type of KeysToSign is CBOR array and the value is not used, + * which should be left as an empty list. Note that the DeviceInfo above is a + * CBOR map structure defined in DeviceInfo.aidl, which can be constructed from + * the input |encoded_device_info|. DeviceInfo must be canonicalized according + * to the specification in RFC 7049. The required fields from DeviceInfo.aidl + * are: brand, manufacturer, product, model, device, vb_state, bootloader_state, + * vbmeta_digest, security_level. + * + * Once CsrPayload is prepared, together with |challenge| it is signed by the + * leaf cert of BCC, in the format of: + * + * ~~~ + * |signed_csr_payload| = SignedData<[ + * challenge: bstr .size (0..64), + * bstr .cbor CsrPayload, + * ]> + * ~~~ + * + * This function should output |signed_csr_payload| in the format of + * SignedData, which is a COSE_Sign1 CBOR and is defined in Android + * IRemotelyProvisionedComponent.aidl (under "SignedData"): + * + * ~~~ + * SignedData = [ + * protected: bstr .cbor { 1 : AlgorithmEdDSA / AlgorithmES256 / + * AlgorithmES384 }, + * unprotected: {}, + * payload: bstr .cbor Data / nil, + * signature: bstr ; PureEd25519(priv_key, Sig_structure) / + * ; ECDSA(priv_key, Sig_structure) + * ] + * ~~~ + * + * Also see OEMCrypto_GenerateCertificateKeyPair() for more details of + * SignedData and Sig_structure. + * + * Data in the payload field of SignedData is a CBOR array: + * + * ~~~ + * Data = [ + * challenge: bstr .size (0..64), + * bstr .cbor CsrPayload, + * ] + * ~~~ + * + * Devices that do not support Provisioning 4.0, or do not support + * Provisioning 4.0 Uploading Option should return + * OEMCrypto_ERROR_NOT_IMPLEMENTED. * * @param[in] challenge: pointer to the buffer containing a byte string to be - * signed. + * signed. It is generated by the RKP factory extraction tool. * @param[in] challenge_length: size of the challenge buffer. * @param[in] encoded_device_info: pointer to the buffer containing the - * serialized device information in CBOR map format. + * serialized device information in CBOR map format. It should be returned as + * `device_info` in a call to the function `OEMCrypto_GetDeviceInformation()`. * @param[in] encoded_device_info_length: size of the encoded_device_info * buffer. * @param[out] signed_csr_payload: pointer to the buffer that receives the - * serialized CSR payload in COSE_Sign1 format. + * serialized signed CSR payload in COSE_Sign1 format. * @param[in,out] signed_csr_payload_length: on input, size of the caller's * signed_csr_payload buffer. On output, the number of bytes written into the * buffer. @@ -5121,14 +5251,13 @@ uint32_t OEMCrypto_SupportsDecryptHash(void); * length of the hash will be at most 128 bytes, and will be 4 bytes (32 * bits) for the default CRC32 hash. * - * This may be called before the first call to SelectKey. In that case, this - * function cannot verify that the key control block allows hash - * verification. The function DecryptCENC should verify that the key control - * bit allows hash verification when it is called. If an attempt is made to - * compute a hash when the selected key does not have the bit - * Allow_Hash_Verification set, then a hash should not be computed, and - * OEMCrypto_GetHashErrorCode() should return the error - * OEMCrypto_ERROR_UNKNOWN_FAILURE. + * This may be called before the first call to OEMCrypto_GetKeyHandle. In that + * case, this function cannot verify that the key control block allows hash + * verification. The function DecryptCENC should verify that the key control bit + * allows hash verification when it is called. If an attempt is made to compute + * a hash when the selected key does not have the bit Allow_Hash_Verification + * set, then a hash should not be computed, and OEMCrypto_GetHashErrorCode() + * should return the error OEMCrypto_ERROR_UNKNOWN_FAILURE. * * OEMCrypto should compute the hash of the frame and then compare it with * the correct value. If the values differ, then OEMCrypto should latch in an @@ -5255,7 +5384,7 @@ OEMCryptoResult OEMCrypto_AllocateSecureBuffer( * will cause the unit test using secure buffers to fail. * * @param[in] session: session id for operation. - * @param[out] output_descriptor: the buffer descriptor modified by + * @param[in,out] output_descriptor: the buffer descriptor modified by * OEMCrypto_AllocateSecureBuffer() * @param[in] secure_fd: The integer returned by * OEMCrypto_AllocateSecureBuffer() @@ -5717,6 +5846,35 @@ OEMCryptoResult OEMCrypto_Generic_Verify_V17( /****************************************************************************/ /****************************************************************************/ +/* The following functions are used by internal L3 CDMs and are not required by + * other CDM implementations. + */ + +/** + * Get the embedded Drm Certificate used by internal L3 CDMs. + * + * @param[out] public_cert where the certificate is stored. + * @param[in,out] public_cert_length the length, in bytes, of the certificate. + * + * @retval OEMCrypto_SUCCESS on success + * @retval OEMCrypto_ERROR_SHORT_BUFFER if public_cert_length is too small. + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + */ +OEMCryptoResult OEMCrypto_GetEmbeddedDrmCertificate(uint8_t* public_cert, + size_t* public_cert_length); + +/** + * Marks the given session as using a secondary key. + * + * @param[in] session_id: handle for the session to be used. + * @param[in] dual_key: whether this session uses a secondary key. + * + * @ignore + * @retval OEMCrypto_SUCCESS on success + * @retval OEMCrypto_ERROR_NOT_IMPLEMENTED + */ +OEMCryptoResult OEMCrypto_UseSecondaryKey(OEMCrypto_SESSION session_id, + bool dual_key); #ifdef __cplusplus } diff --git a/oemcrypto/odk/Android.bp b/oemcrypto/odk/Android.bp index 544c838f..f0e918e6 100644 --- a/oemcrypto/odk/Android.bp +++ b/oemcrypto/odk/Android.bp @@ -25,6 +25,11 @@ cc_library_static { "vendor/widevine/libwvdrmengine/oemcrypto/odk/include", "vendor/widevine/libwvdrmengine/oemcrypto/odk/src", ], + header_libs: [ + "jni_headers", + "libbase_headers", + "liblog_headers", + ], srcs: [ "src/odk.c", @@ -38,7 +43,7 @@ cc_library_static { proprietary: true, owner: "widevine", - min_sdk_version: "UpsideDownCake", + min_sdk_version: "34", } // ---------------------------------------------------------------- @@ -51,6 +56,11 @@ cc_library_static { "vendor/widevine/libwvdrmengine/oemcrypto/odk/include", "vendor/widevine/libwvdrmengine/oemcrypto/odk/src", ], + header_libs: [ + "jni_headers", + "libbase_headers", + "liblog_headers", + ], srcs: [ "src/core_message_deserialize.cpp", diff --git a/oemcrypto/odk/include/OEMCryptoCENCCommon.h b/oemcrypto/odk/include/OEMCryptoCENCCommon.h index a4e54380..7da8b45a 100644 --- a/oemcrypto/odk/include/OEMCryptoCENCCommon.h +++ b/oemcrypto/odk/include/OEMCryptoCENCCommon.h @@ -122,6 +122,7 @@ typedef enum OEMCrypto_Usage_Entry_Status { kInactiveUnused = 4, } OEMCrypto_Usage_Entry_Status; +/* Not used publicly. Not documented with Doxygen. */ typedef enum OEMCrypto_ProvisioningRenewalType { OEMCrypto_NoRenewal = 0, OEMCrypto_RenewalACert = 1, @@ -137,7 +138,9 @@ typedef enum OEMCrypto_LicenseType { OEMCrypto_LicenseType_MaxValue = OEMCrypto_EntitlementLicense, } OEMCrypto_LicenseType; -/* Private key type used in the provisioning response. */ +/** + * Private key type used in the provisioning response. + */ typedef enum OEMCrypto_PrivateKeyType { OEMCrypto_RSA_Private_Key = 0, OEMCrypto_ECC_Private_Key = 1, diff --git a/oemcrypto/odk/include/core_message_features.h b/oemcrypto/odk/include/core_message_features.h index 01cdda8f..06396d58 100644 --- a/oemcrypto/odk/include/core_message_features.h +++ b/oemcrypto/odk/include/core_message_features.h @@ -26,9 +26,9 @@ struct CoreMessageFeatures { // This is the published version of the ODK Core Message library. The default // behavior is for the server to restrict messages to at most this version - // number. The default is 18.2. + // number. The default is 18.5. uint32_t maximum_major_version = 18; - uint32_t maximum_minor_version = 2; + uint32_t maximum_minor_version = 5; bool operator==(const CoreMessageFeatures &other) const; bool operator!=(const CoreMessageFeatures &other) const { diff --git a/oemcrypto/odk/include/odk_structs.h b/oemcrypto/odk/include/odk_structs.h index 457617e0..b7d8ca02 100644 --- a/oemcrypto/odk/include/odk_structs.h +++ b/oemcrypto/odk/include/odk_structs.h @@ -16,10 +16,10 @@ extern "C" { /* The version of this library. */ #define ODK_MAJOR_VERSION 18 -#define ODK_MINOR_VERSION 2 +#define ODK_MINOR_VERSION 5 /* ODK Version string. Date changed automatically on each release. */ -#define ODK_RELEASE_DATE "ODK v18.2 2023-06-14" +#define ODK_RELEASE_DATE "ODK v18.5 2024-03-21" /* The lowest version number for an ODK message. */ #define ODK_FIRST_VERSION 16 diff --git a/oemcrypto/odk/src/core_message_features.cpp b/oemcrypto/odk/src/core_message_features.cpp index 6ee2a250..94a3d953 100644 --- a/oemcrypto/odk/src/core_message_features.cpp +++ b/oemcrypto/odk/src/core_message_features.cpp @@ -19,7 +19,9 @@ CoreMessageFeatures CoreMessageFeatures::DefaultFeatures( uint32_t maximum_major_version) { CoreMessageFeatures features; features.maximum_major_version = maximum_major_version; - // The default minor version is the highest for each major version. + // The default minor version is the highest for each major version. This also + // needs to be updated with new version releases in + // ODK_InitializeSessionValues() when the minor version is being set. switch (maximum_major_version) { case 16: features.maximum_minor_version = 5; // 16.5 @@ -28,7 +30,7 @@ CoreMessageFeatures CoreMessageFeatures::DefaultFeatures( features.maximum_minor_version = 2; // 17.2 break; case 18: - features.maximum_minor_version = 2; // 18.2 + features.maximum_minor_version = 5; // 18.5 break; default: features.maximum_minor_version = 0; diff --git a/oemcrypto/odk/src/odk.c b/oemcrypto/odk/src/odk.c index fafa0ab6..3ade4a76 100644 --- a/oemcrypto/odk/src/odk.c +++ b/oemcrypto/odk/src/odk.c @@ -9,6 +9,7 @@ #include #include +#include "odk_message.h" #include "odk_overflow.h" #include "odk_serialize.h" #include "odk_structs.h" @@ -45,12 +46,23 @@ static OEMCryptoResult ODK_PrepareRequest( * message buffer has been correctly initialized by the caller. */ switch (message_type) { case ODK_License_Request_Type: { - core_message->message_length = ODK_LICENSE_REQUEST_SIZE; - if (sizeof(ODK_PreparedLicenseRequest) > prepared_request_buffer_length) { - return ODK_ERROR_CORE_MESSAGE; + if (nonce_values->api_major_version > 17) { + core_message->message_length = ODK_LICENSE_REQUEST_SIZE; + if (sizeof(ODK_PreparedLicenseRequest) > + prepared_request_buffer_length) { + return ODK_ERROR_CORE_MESSAGE; + } + Pack_ODK_PreparedLicenseRequest( + &msg, (ODK_PreparedLicenseRequest*)prepared_request_buffer); + } else { + core_message->message_length = ODK_LICENSE_REQUEST_SIZE_V17; + if (sizeof(ODK_PreparedLicenseRequestV17) > + prepared_request_buffer_length) { + return ODK_ERROR_CORE_MESSAGE; + } + Pack_ODK_PreparedLicenseRequestV17( + &msg, (ODK_PreparedLicenseRequestV17*)prepared_request_buffer); } - Pack_ODK_PreparedLicenseRequest( - &msg, (ODK_PreparedLicenseRequest*)prepared_request_buffer); break; } case ODK_Renewal_Request_Type: { @@ -63,13 +75,23 @@ static OEMCryptoResult ODK_PrepareRequest( break; } case ODK_Provisioning_Request_Type: { - core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE; - if (sizeof(ODK_PreparedProvisioningRequest) > - prepared_request_buffer_length) { - return ODK_ERROR_CORE_MESSAGE; + if (nonce_values->api_major_version > 17) { + core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE; + if (sizeof(ODK_PreparedProvisioningRequest) > + prepared_request_buffer_length) { + return ODK_ERROR_CORE_MESSAGE; + } + Pack_ODK_PreparedProvisioningRequest( + &msg, (ODK_PreparedProvisioningRequest*)prepared_request_buffer); + } else { + core_message->message_length = ODK_PROVISIONING_REQUEST_SIZE_V17; + if (sizeof(ODK_PreparedProvisioningRequestV17) > + prepared_request_buffer_length) { + return ODK_ERROR_CORE_MESSAGE; + } + Pack_ODK_PreparedProvisioningRequestV17( + &msg, (ODK_PreparedProvisioningRequestV17*)prepared_request_buffer); } - Pack_ODK_PreparedProvisioningRequest( - &msg, (ODK_PreparedProvisioningRequest*)prepared_request_buffer); break; } case ODK_Provisioning40_Request_Type: { @@ -186,12 +208,19 @@ OEMCryptoResult ODK_PrepareCoreLicenseRequest( counter_info == NULL) { return ODK_ERROR_CORE_MESSAGE; } - ODK_PreparedLicenseRequest license_request = {0}; - memcpy(&license_request.counter_info, counter_info, - sizeof(license_request.counter_info)); - return ODK_PrepareRequest( - message, message_length, core_message_size, ODK_License_Request_Type, - nonce_values, &license_request, sizeof(ODK_PreparedLicenseRequest)); + if (nonce_values->api_major_version > 17) { + ODK_PreparedLicenseRequest license_request = {0}; + memcpy(&license_request.counter_info, counter_info, + sizeof(license_request.counter_info)); + return ODK_PrepareRequest( + message, message_length, core_message_size, ODK_License_Request_Type, + nonce_values, &license_request, sizeof(ODK_PreparedLicenseRequest)); + } else { + ODK_PreparedLicenseRequestV17 license_request = {0}; + return ODK_PrepareRequest( + message, message_length, core_message_size, ODK_License_Request_Type, + nonce_values, &license_request, sizeof(ODK_PreparedLicenseRequestV17)); + } } OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message, @@ -250,14 +279,22 @@ OEMCryptoResult ODK_PrepareCoreProvisioningRequest( counter_info == NULL) { return ODK_ERROR_CORE_MESSAGE; } - ODK_PreparedProvisioningRequest provisioning_request = {0}; - memcpy(&provisioning_request.counter_info, counter_info, - sizeof(ODK_MessageCounterInfo)); + if (nonce_values->api_major_version > 17) { + ODK_PreparedProvisioningRequest provisioning_request = {0}; + memcpy(&provisioning_request.counter_info, counter_info, + sizeof(ODK_MessageCounterInfo)); - return ODK_PrepareRequest(message, message_length, core_message_length, - ODK_Provisioning_Request_Type, nonce_values, - &provisioning_request, - sizeof(ODK_PreparedProvisioningRequest)); + return ODK_PrepareRequest(message, message_length, core_message_length, + ODK_Provisioning_Request_Type, nonce_values, + &provisioning_request, + sizeof(ODK_PreparedProvisioningRequest)); + } else { + ODK_PreparedProvisioningRequestV17 provisioning_request = {0}; + return ODK_PrepareRequest(message, message_length, core_message_length, + ODK_Provisioning_Request_Type, nonce_values, + &provisioning_request, + sizeof(ODK_PreparedProvisioningRequestV17)); + } } OEMCryptoResult ODK_PrepareCoreProvisioning40Request( @@ -434,10 +471,14 @@ OEMCryptoResult ODK_ParseRenewal(const uint8_t* message, size_t message_length, */ /* If a renewal request is lost in transit, we should throw it out and create * a new one. We use the timestamp to make sure we have the latest request. - * We only do this if playback has already started. This allows us to reload - * an offline license and also reload a renewal before starting playback. + * We only do this if a renewal has been requested for this session. This + * allows us to reload an offline license and also reload a renewal from a + * previous session before starting playback. + * TODO: b/290249855 - This is reversed. It should be "!=" instead of "<". + * We will not fix this in the current release, because it is already in + * production code. Instead, this will be fixed in v19. */ - if (clock_values->timer_status != ODK_CLOCK_TIMER_STATUS_LICENSE_LOADED && + if (clock_values->time_of_renewal_request > 0 && clock_values->time_of_renewal_request < renewal_response.request.playback_time) { return ODK_STALE_RENEWAL; @@ -489,14 +530,6 @@ OEMCryptoResult ODK_ParseProvisioning( device_id_length) != 0) { return ODK_ERROR_CORE_MESSAGE; } - - const uint8_t zero[ODK_DEVICE_ID_LEN_MAX] = {0}; - /* check bytes beyond device_id_length are 0 */ - if (crypto_memcmp( - zero, provisioning_response.request.device_id + device_id_length, - ODK_DEVICE_ID_LEN_MAX - device_id_length) != 0) { - return ODK_ERROR_CORE_MESSAGE; - } } else { // v18 ODK_ProvisioningResponse provisioning_response = {0}; diff --git a/oemcrypto/odk/src/odk_assert.h b/oemcrypto/odk/src/odk_assert.h index 0517818b..e0de19df 100644 --- a/oemcrypto/odk/src/odk_assert.h +++ b/oemcrypto/odk/src/odk_assert.h @@ -9,7 +9,7 @@ extern "C" { #endif -#if (__STDC_VERSION__ >= 201112L) +#if defined(_MSC_VER) || (__STDC_VERSION__ >= 201112L) #include #define odk_static_assert static_assert #else diff --git a/oemcrypto/odk/src/odk_structs_priv.h b/oemcrypto/odk/src/odk_structs_priv.h index abfdc23b..5306a4b6 100644 --- a/oemcrypto/odk/src/odk_structs_priv.h +++ b/oemcrypto/odk/src/odk_structs_priv.h @@ -123,8 +123,10 @@ typedef struct { // ../test/odk_test.cpp for validations of each of the defined request sizes. #define ODK_CORE_MESSAGE_SIZE 20u #define ODK_LICENSE_REQUEST_SIZE 90u +#define ODK_LICENSE_REQUEST_SIZE_V17 20u #define ODK_RENEWAL_REQUEST_SIZE 28u #define ODK_PROVISIONING_REQUEST_SIZE 94u +#define ODK_PROVISIONING_REQUEST_SIZE_V17 88u #define ODK_PROVISIONING40_REQUEST_SIZE 862u #define ODK_RENEWED_PROVISIONING_REQUEST_SIZE 1694u #define ODK_MESSAGECOUNTERINFO_SIZE 70u diff --git a/oemcrypto/odk/src/odk_timer.c b/oemcrypto/odk/src/odk_timer.c index 1b09551e..0b503f5e 100644 --- a/oemcrypto/odk/src/odk_timer.c +++ b/oemcrypto/odk/src/odk_timer.c @@ -254,11 +254,6 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits, if (timer_limits == NULL || clock_values == NULL || nonce_values == NULL) { return OEMCrypto_ERROR_INVALID_CONTEXT; } - /* Check that the API version passed in from OEMCrypto matches the version of - * this ODK library. */ - if (api_major_version != ODK_MAJOR_VERSION) { - return OEMCrypto_ERROR_INVALID_CONTEXT; - } timer_limits->soft_enforce_rental_duration = false; timer_limits->soft_enforce_playback_duration = false; timer_limits->earliest_playback_start_seconds = 0; @@ -268,8 +263,23 @@ OEMCryptoResult ODK_InitializeSessionValues(ODK_TimerLimits* timer_limits, ODK_InitializeClockValues(clock_values, 0); - nonce_values->api_major_version = ODK_MAJOR_VERSION; - nonce_values->api_minor_version = ODK_MINOR_VERSION; + nonce_values->api_major_version = api_major_version; + // This needs to be updated with new version releases in the default features + // of core message features. + switch (nonce_values->api_major_version) { + case 16: + nonce_values->api_minor_version = 5; + break; + case 17: + nonce_values->api_minor_version = 2; + break; + case 18: + nonce_values->api_minor_version = 5; + break; + default: + nonce_values->api_minor_version = 0; + break; + } nonce_values->nonce = 0; nonce_values->session_id = session_id; @@ -300,6 +310,7 @@ OEMCryptoResult ODK_InitializeClockValues(ODK_ClockValues* clock_values, clock_values->time_of_license_request_signed = system_time_seconds; clock_values->time_of_first_decrypt = 0; clock_values->time_of_last_decrypt = 0; + clock_values->time_of_renewal_request = 0; clock_values->time_when_timer_expires = 0; clock_values->timer_status = ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED; clock_values->status = kUnused; diff --git a/oemcrypto/odk/test/odk_test.cpp b/oemcrypto/odk/test/odk_test.cpp index 31d0c3b4..c6937c3d 100644 --- a/oemcrypto/odk/test/odk_test.cpp +++ b/oemcrypto/odk/test/odk_test.cpp @@ -334,12 +334,21 @@ TEST(OdkTest, NullRequestTest) { &nonce_values, nullptr, 0uL, nullptr)); // Null device id in provisioning request is ok - uint8_t message[ODK_PROVISIONING_REQUEST_SIZE] = {0}; - core_message_length = ODK_PROVISIONING_REQUEST_SIZE; - EXPECT_EQ(OEMCrypto_SUCCESS, - ODK_PrepareCoreProvisioningRequest( - message, ODK_PROVISIONING_REQUEST_SIZE, &core_message_length, - &nonce_values, &counter_info)); + if (nonce_values.api_major_version > 17) { + uint8_t message[ODK_PROVISIONING_REQUEST_SIZE] = {0}; + core_message_length = ODK_PROVISIONING_REQUEST_SIZE; + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_PrepareCoreProvisioningRequest( + message, ODK_PROVISIONING_REQUEST_SIZE, &core_message_length, + &nonce_values, &counter_info)); + } else { + uint8_t message[ODK_PROVISIONING_REQUEST_SIZE_V17] = {0}; + core_message_length = ODK_PROVISIONING_REQUEST_SIZE_V17; + EXPECT_EQ(OEMCrypto_SUCCESS, + ODK_PrepareCoreProvisioningRequest( + message, ODK_PROVISIONING_REQUEST_SIZE_V17, + &core_message_length, &nonce_values, &counter_info)); + } // Null device info in provisioning 4.0 request is ok uint8_t message_prov4[ODK_PROVISIONING40_REQUEST_SIZE] = {0}; @@ -885,7 +894,10 @@ TEST(OdkTest, ParseRenewalErrorTimer) { uint32_t buf_size = 0; ODK_BuildMessageBuffer(&(params.core_message), params.extra_fields, &buf, &buf_size); - params.clock_values.time_of_renewal_request = 0; + // Set the time for the last renewal request, as seen in clock_values, to be + // after the time in the request. + // TODO: b/290249855 - This is reversed. It should be +5. + params.clock_values.time_of_renewal_request = params.playback_clock - 5; OEMCryptoResult err = ODK_ParseRenewal( buf, buf_size, buf_size, &(params.core_message.nonce_values), params.system_time, &(params.timer_limits), &(params.clock_values), @@ -1204,7 +1216,7 @@ std::vector TestCases() { // number. {16, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 16, 5}, {17, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 17, 2}, - {18, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 18, 2}, + {18, ODK_MAJOR_VERSION, ODK_MINOR_VERSION, 18, 5}, // Here are some known good versions. Make extra sure they work. {ODK_MAJOR_VERSION, 16, 3, 16, 3}, {ODK_MAJOR_VERSION, 16, 4, 16, 4}, @@ -1213,12 +1225,16 @@ std::vector TestCases() { {ODK_MAJOR_VERSION, 17, 2, 17, 2}, {ODK_MAJOR_VERSION, 18, 1, 18, 1}, {ODK_MAJOR_VERSION, 18, 2, 18, 2}, + {ODK_MAJOR_VERSION, 18, 3, 18, 3}, + {ODK_MAJOR_VERSION, 18, 4, 18, 4}, + {ODK_MAJOR_VERSION, 18, 5, 18, 5}, {0, 16, 3, 16, 3}, {0, 16, 4, 16, 4}, {0, 16, 5, 16, 5}, {0, 17, 1, 17, 1}, {0, 17, 2, 17, 2}, - {0, 18, 2, 18, 2}, // Change to 19 when the default version is updated. + {0, 18, 4, 18, 4}, + {0, 18, 5, 18, 5}, }; return test_cases; } @@ -1243,7 +1259,11 @@ TEST(OdkSizeTest, LicenseRequest) { &core_message_length, &nonce_values, &counter_info)); // the core_message_length should be appropriately set - EXPECT_EQ(ODK_LICENSE_REQUEST_SIZE, core_message_length); + if (nonce_values.api_major_version > 17) { + EXPECT_EQ(ODK_LICENSE_REQUEST_SIZE, core_message_length); + } else { + EXPECT_EQ(ODK_LICENSE_REQUEST_SIZE_V17, core_message_length); + } } TEST(OdkSizeTest, RenewalRequest) { @@ -1307,7 +1327,11 @@ TEST(OdkSizeTest, ProvisioningRequest) { &core_message_length, &nonce_values, &counter_info)); // the core_message_length should be appropriately set - EXPECT_EQ(ODK_PROVISIONING_REQUEST_SIZE, core_message_length); + if (nonce_values.api_major_version > 17) { + EXPECT_EQ(ODK_PROVISIONING_REQUEST_SIZE, core_message_length); + } else { + EXPECT_EQ(ODK_PROVISIONING_REQUEST_SIZE_V17, core_message_length); + } } // Verify the version string contains the right version numbers. diff --git a/oemcrypto/test/GEN_api_lock_file.c b/oemcrypto/test/GEN_api_lock_file.c index 2a29201b..a60a6cfb 100644 --- a/oemcrypto/test/GEN_api_lock_file.c +++ b/oemcrypto/test/GEN_api_lock_file.c @@ -366,3 +366,12 @@ OEMCryptoResult _oecc141(const uint8_t* challenge, size_t challenge_length, // OEMCrypto_EnterTestMode defined in v18.1 OEMCryptoResult _oecc140(void); + +// OEMCrypto_FactoryInstallBCCSignature defined in v18.3 +OEMCryptoResult _oecc142(const uint8_t* signature, size_t signature_length); + +// OEMCrypto_GetEmbeddedDrmCertificate defined in v18.5 +OEMCryptoResult _oecc143(uint8_t* public_cert, size_t* public_cert_length); + +// OEMCrypto_UseSecondaryKey defined in v18.5 +OEMCryptoResult _oecc144(OEMCrypto_SESSION session_id, bool dual_key); diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_entitled_key_session_fuzz.cc b/oemcrypto/test/fuzz_tests/oemcrypto_entitled_key_session_fuzz.cc new file mode 100644 index 00000000..78323dd9 --- /dev/null +++ b/oemcrypto/test/fuzz_tests/oemcrypto_entitled_key_session_fuzz.cc @@ -0,0 +1,136 @@ +// Copyright 2023 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine +// License Agreement. + +#include + +#include "FuzzedDataProvider.h" +#include "OEMCryptoCENC.h" +#include "oemcrypto_fuzz_helper.h" + +namespace { + +enum class ApiMethod { + kOpenSession, + kCloseSession, + kCreateEntitledKeySession, + kReassociateEntitledKeySession, + kRemoveEntitledKeySession, + kMaxValue = kRemoveEntitledKeySession, +}; + +struct Session { + OEMCrypto_SESSION value; + std::vector::const_iterator iterator; +}; + +Session PickSession(FuzzedDataProvider& fuzzed_data, + const std::vector& sessions) { + Session session; + + session.iterator = + sessions.cbegin() + + fuzzed_data.ConsumeIntegralInRange(0, sessions.size()); + + if (session.iterator != sessions.cend()) { + session.value = *session.iterator; + } else { + session.value = fuzzed_data.ConsumeIntegral(); + } + + return session; +} + +} // namespace + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + wvoec::RedirectStdoutToFile(); + + wvoec::SessionUtil session_util; + wvoec::InitializeFuzz(session_util); + + // Contains all open and some closed OEMCrypto sessions. + std::vector oec_sessions; + + // Contains all current and some removed key sessions. + std::vector key_sessions; + + FuzzedDataProvider fuzzed_data(data, size); + + while (fuzzed_data.remaining_bytes() > 0) { + switch (fuzzed_data.ConsumeEnum()) { + case ApiMethod::kOpenSession: { + OEMCrypto_SESSION session = 0; + const OEMCryptoResult result = OEMCrypto_OpenSession(&session); + + if (result == OEMCrypto_SUCCESS) { + oec_sessions.push_back(session); + } + + break; + } + + case ApiMethod::kCloseSession: { + const Session session = PickSession(fuzzed_data, oec_sessions); + + const OEMCryptoResult result = OEMCrypto_CloseSession(session.value); + + if (result == OEMCrypto_SUCCESS && + session.iterator != oec_sessions.cend() && + fuzzed_data.ConsumeBool()) { + oec_sessions.erase(session.iterator); + } + + break; + } + + case ApiMethod::kCreateEntitledKeySession: { + const OEMCrypto_SESSION oec_session = + PickSession(fuzzed_data, oec_sessions).value; + + OEMCrypto_SESSION key_session_data = 0; + OEMCrypto_SESSION* const key_session = + fuzzed_data.ConsumeBool() ? &key_session_data : nullptr; + + const OEMCryptoResult result = + OEMCrypto_CreateEntitledKeySession(oec_session, key_session); + + if (result == OEMCrypto_SUCCESS) { + key_sessions.push_back(*key_session); + } + + break; + } + + case ApiMethod::kReassociateEntitledKeySession: { + const OEMCrypto_SESSION key_session = + PickSession(fuzzed_data, key_sessions).value; + + const OEMCrypto_SESSION oec_session = + PickSession(fuzzed_data, oec_sessions).value; + + OEMCrypto_ReassociateEntitledKeySession(key_session, oec_session); + + break; + } + + case ApiMethod::kRemoveEntitledKeySession: { + const Session key_session = PickSession(fuzzed_data, key_sessions); + + const OEMCryptoResult result = + OEMCrypto_RemoveEntitledKeySession(key_session.value); + + if (result == OEMCrypto_SUCCESS && + key_session.iterator != key_sessions.cend() && + fuzzed_data.ConsumeBool()) { + key_sessions.erase(key_session.iterator); + } + + break; + } + } + } + + OEMCrypto_Terminate(); + return 0; +} diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.cc b/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.cc index 50929b1a..112f353b 100644 --- a/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.cc +++ b/oemcrypto/test/fuzz_tests/oemcrypto_fuzz_helper.cc @@ -46,7 +46,6 @@ void SessionFuzz::Terminate() { void OEMCryptoLicenseAPIFuzz::Initialize() { session_fuzz_.Initialize(); session_fuzz_.InstallTestDrmKey(); - session_fuzz_.session().GenerateNonce(); } void OEMCryptoLicenseAPIFuzz::Terminate() { diff --git a/oemcrypto/test/fuzz_tests/oemcrypto_opk_fuzztests.gyp b/oemcrypto/test/fuzz_tests/oemcrypto_opk_fuzztests.gyp index f5ac0112..fe60be90 100644 --- a/oemcrypto/test/fuzz_tests/oemcrypto_opk_fuzztests.gyp +++ b/oemcrypto/test/fuzz_tests/oemcrypto_opk_fuzztests.gyp @@ -21,12 +21,6 @@ 'oemcrypto_copy_buffer_fuzz.cc', ], }, - { - 'target_name': 'oemcrypto_opk_create_and_remove_entitled_key_session_fuzz', - 'sources': [ - 'oemcrypto_create_and_remove_entitled_key_session_fuzz.cc', - ], - }, { 'target_name': 'oemcrypto_opk_deactivate_usage_entry_fuzz', 'sources': [ @@ -66,6 +60,12 @@ '<(oemcrypto_dir)/opk/ports/trusty/serialization_adapter/shared_memory.c', ], }, + { + 'target_name': 'oemcrypto_opk_entitled_key_session_fuzz', + 'sources': [ + 'oemcrypto_entitled_key_session_fuzz.cc', + ], + }, { 'target_name': 'oemcrypto_opk_generate_certificate_key_pair_first_stage_fuzz', 'sources': [ diff --git a/oemcrypto/test/fuzz_tests/partner_oemcrypto_fuzztests.gyp b/oemcrypto/test/fuzz_tests/partner_oemcrypto_fuzztests.gyp index c970e505..4ebf7833 100644 --- a/oemcrypto/test/fuzz_tests/partner_oemcrypto_fuzztests.gyp +++ b/oemcrypto/test/fuzz_tests/partner_oemcrypto_fuzztests.gyp @@ -19,12 +19,6 @@ 'oemcrypto_copy_buffer_fuzz.cc', ], }, - { - 'target_name': 'oemcrypto_create_and_remove_entitled_key_session_fuzz', - 'sources': [ - 'oemcrypto_create_and_remove_entitled_key_session_fuzz.cc', - ], - }, { 'target_name': 'oemcrypto_deactivate_usage_entry_fuzz', 'sources': [ @@ -43,6 +37,12 @@ 'oemcrypto_decrypt_hash_fuzz.cc', ], }, + { + 'target_name': 'oemcrypto_entitled_key_session_fuzz', + 'sources': [ + 'oemcrypto_entitled_key_session_fuzz.cc', + ], + }, { 'target_name': 'oemcrypto_generate_certificate_key_pair_first_stage_fuzz', 'sources': [ diff --git a/oemcrypto/test/oec_device_features.cpp b/oemcrypto/test/oec_device_features.cpp index 22640539..77538c7f 100644 --- a/oemcrypto/test/oec_device_features.cpp +++ b/oemcrypto/test/oec_device_features.cpp @@ -148,14 +148,6 @@ void DeviceFeatures::Initialize() { std::string DeviceFeatures::RestrictFilter(const std::string& initial_filter) { std::string filter = initial_filter; // clang-format off - if (!uses_keybox) FilterOut(&filter, "*KeyboxTest*"); - // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for - // provisioning 4. Disabled here temporarily. - if (!loads_certificate || - provisioning_method == OEMCrypto_BootCertificateChain) - FilterOut(&filter, "OEMCryptoLoadsCert*"); - if (!generic_crypto) FilterOut(&filter, "*GenericCrypto*"); - if (derive_key_method == NO_METHOD) FilterOut(&filter, "*SessionTest*"); if (api_version < 17) FilterOut(&filter, "*API17*"); if (api_version < 18) FilterOut(&filter, "*API18*"); // clang-format on @@ -181,6 +173,7 @@ void DeviceFeatures::PickDerivedKey() { derive_key_method = TEST_PROVISION_30; return; case OEMCrypto_DrmCertificate: + case OEMCrypto_DrmReprovisioning: if (OEMCrypto_ERROR_NOT_IMPLEMENTED != OEMCrypto_LoadTestRSAKey()) { derive_key_method = LOAD_TEST_RSA_KEY; } @@ -267,6 +260,8 @@ const char* ProvisioningMethodName(OEMCrypto_ProvisioningMethod method) { return "OEMCrypto_OEMCertificate"; case OEMCrypto_BootCertificateChain: return "OEMCrypto_BootCertificateChain"; + case OEMCrypto_DrmReprovisioning: + return "OEMCrypto_DrmReprovisioning"; } // Not reachable return ""; diff --git a/oemcrypto/test/oec_session_util.cpp b/oemcrypto/test/oec_session_util.cpp index 9e442fb9..783caa1d 100644 --- a/oemcrypto/test/oec_session_util.cpp +++ b/oemcrypto/test/oec_session_util.cpp @@ -82,30 +82,6 @@ class FuzzedData { size_t source_size_; }; -// Encrypt a block of data using CTR mode. -void EncryptCTR(const vector& in_buffer, const uint8_t* key, - const uint8_t* starting_iv, vector* out_buffer) { - ASSERT_NE(nullptr, key); - ASSERT_NE(nullptr, starting_iv); - ASSERT_NE(nullptr, out_buffer); - AES_KEY aes_key; - AES_set_encrypt_key(key, AES_BLOCK_SIZE * 8, &aes_key); - out_buffer->resize(in_buffer.size()); - - uint8_t iv[AES_BLOCK_SIZE]; // Current iv. - - memcpy(iv, &starting_iv[0], AES_BLOCK_SIZE); - size_t l = 0; // byte index into encrypted subsample. - while (l < in_buffer.size()) { - uint8_t aes_output[AES_BLOCK_SIZE]; - AES_encrypt(iv, aes_output, &aes_key); - for (size_t n = 0; n < AES_BLOCK_SIZE && l < in_buffer.size(); n++, l++) { - (*out_buffer)[l] = aes_output[n] ^ in_buffer[l]; - } - ctr128_inc64(1, iv); - } -} - // Uses OEMCrypto to decrypt some random data in 'cenc' mode. This function // assumes that the correct key is already selected in the session. It requires // the plaintext of that key so that it can encrypt the test data. It resizes @@ -138,6 +114,30 @@ OEMCryptoResult DecryptCTR(const vector& key_handle, } // namespace +// Encrypt a block of data using CTR mode. +void EncryptCTR(const vector& in_buffer, const uint8_t* key, + const uint8_t* starting_iv, vector* out_buffer) { + ASSERT_NE(nullptr, key); + ASSERT_NE(nullptr, starting_iv); + ASSERT_NE(nullptr, out_buffer); + AES_KEY aes_key; + AES_set_encrypt_key(key, AES_BLOCK_SIZE * 8, &aes_key); + out_buffer->resize(in_buffer.size()); + + uint8_t iv[AES_BLOCK_SIZE]; // Current iv. + + memcpy(iv, &starting_iv[0], AES_BLOCK_SIZE); + size_t l = 0; // byte index into encrypted subsample. + while (l < in_buffer.size()) { + uint8_t aes_output[AES_BLOCK_SIZE]; + AES_encrypt(iv, aes_output, &aes_key); + for (size_t n = 0; n < AES_BLOCK_SIZE && l < in_buffer.size(); n++, l++) { + (*out_buffer)[l] = aes_output[n] ^ in_buffer[l]; + } + ctr128_inc64(1, iv); + } +} + int GetRandBytes(unsigned char* buf, size_t num) { // returns 1 on success, -1 if not supported, or 0 if other failure. return RAND_bytes(buf, static_cast(num)); @@ -792,9 +792,6 @@ void LicenseRoundTrip::FillAndVerifyCoreRequest( // for L3 release only. EXPECT_LE(3, core_request_.api_minor_version); EXPECT_GE(5, core_request_.api_minor_version); - } else if (global_features.api_version == ODK_MAJOR_VERSION) { - // We do not expect older tests to work with a newer OEMCrypto. - EXPECT_GE(ODK_MINOR_VERSION, core_request_.api_minor_version); } if (expect_request_has_correct_nonce_) { EXPECT_EQ(session()->nonce(), core_request_.nonce); @@ -1139,8 +1136,8 @@ OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session, response_signature_.size()); if (verify_keys && result == OEMCrypto_SUCCESS) { // Give the session object a copy of the license truth data so that it can - // call SelectKey, use key control information, and so that it has key data - // to verify decrypt operations. + // call GetKeyHandle, use key control information, and so that it has key + // data to verify decrypt operations. session->set_license(response_data_); // Also, if the license has new mac keys, then install them now. if (core_response_.enc_mac_keys.length > 0) { @@ -1234,6 +1231,12 @@ void EntitledMessage::MakeOneKey(size_t entitlement_key_index) { sizeof(key_data->content_key_data_iv))); offsets->content_key_data_iv = FindSubstring( key_data->content_key_data_iv, sizeof(key_data->content_key_data_iv)); + + EXPECT_EQ(1, + GetRandBytes(key_data->content_iv, sizeof(key_data->content_iv))); + key_data->content_iv_length = sizeof(key_data->content_iv); + offsets->content_iv = + FindSubstring(key_data->content_iv, key_data->content_iv_length); } OEMCrypto_EntitledContentKeyObject* EntitledMessage::entitled_key_array() { @@ -1367,8 +1370,8 @@ void EntitledMessage::LoadCasKeys(bool load_even, bool load_odd, // Convert the OEMCrypto_EntitledContentKeyObject to // OEMCrypto_EntitledCasKeyObject. Only the first two key object is used. - OEMCrypto_EntitledContentKeyObject even_key; - OEMCrypto_EntitledContentKeyObject odd_key; + OEMCrypto_EntitledContentKeyObject even_key = {}; + OEMCrypto_EntitledContentKeyObject odd_key = {}; bool has_even = load_even && num_keys_ >= 1; bool has_odd = load_odd && num_keys_ >= 2; if (has_even) { @@ -1376,14 +1379,16 @@ void EntitledMessage::LoadCasKeys(bool load_even, bool load_odd, even_key.content_key_id = entitled_key_array_[0].content_key_id; even_key.content_key_data_iv = entitled_key_array_[0].content_key_data_iv; even_key.content_key_data = entitled_key_array_[0].content_key_data; - even_key.content_iv.length = 0; + even_key.content_iv = entitled_key_array_[0].content_iv; + even_key.cipher_mode = OEMCrypto_CipherMode_CBC; } if (has_odd) { odd_key.entitlement_key_id = entitled_key_array_[1].entitlement_key_id; odd_key.content_key_id = entitled_key_array_[1].content_key_id; odd_key.content_key_data_iv = entitled_key_array_[1].content_key_data_iv; odd_key.content_key_data = entitled_key_array_[1].content_key_data; - odd_key.content_iv.length = 0; + odd_key.content_iv = entitled_key_array_[1].content_iv; + odd_key.cipher_mode = OEMCrypto_CipherMode_CBC; } OEMCryptoResult sts = OEMCrypto_LoadCasECMKeys( @@ -1464,6 +1469,7 @@ void EntitledMessage::VerifyDecrypt() { void RenewalRoundTrip::VerifyRequestSignature( const vector& data, const vector& generated_signature, size_t core_message_length) { + (void)core_message_length; ASSERT_EQ(HMAC_SHA256_SIGNATURE_SIZE, generated_signature.size()); std::vector expected_signature; session()->key_deriver().ClientSignBuffer(data, &expected_signature); @@ -1770,7 +1776,7 @@ void Session::TestDecryptEntitled(OEMCryptoResult expected_result, // We only have a few errors that we test are reported. ASSERT_NO_FATAL_FAILURE( TestDecryptResult(expected_result, getkeyhandle_result, decrypt_result)) - << "Either SelectKey or DecryptCENC should return " << expected_result + << "Either GetKeyHandle or DecryptCENC should return" << expected_result << ", but they returned " << getkeyhandle_result << " and " << decrypt_result << ", respectively."; } diff --git a/oemcrypto/test/oec_session_util.h b/oemcrypto/test/oec_session_util.h index e13bb519..ec11dcef 100644 --- a/oemcrypto/test/oec_session_util.h +++ b/oemcrypto/test/oec_session_util.h @@ -110,6 +110,8 @@ struct EntitledContentKeyData { uint8_t content_key_data_iv[KEY_IV_SIZE]; uint8_t content_key_data[KEY_SIZE]; uint8_t encrypted_content_key_data[KEY_SIZE]; + uint8_t content_iv[KEY_IV_SIZE]; + size_t content_iv_length; size_t key_index; // Index into the license's key array. Only for testing. }; @@ -121,6 +123,10 @@ void GenerateSimpleSampleDescription(const std::vector& in, OEMCrypto_SampleDescription* sample, OEMCrypto_SubSampleDescription* subsample); +// Encrypt a block of data using CTR mode. +void EncryptCTR(const vector& in_buffer, const uint8_t* key, + const uint8_t* starting_iv, vector* out_buffer); + // 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 @@ -334,6 +340,7 @@ class Provisioning40RoundTrip void CreateDefaultResponse() override{}; void EncryptAndSignResponse() override{}; OEMCryptoResult LoadResponse(Session* session) override { + (void)session; return OEMCrypto_ERROR_NOT_IMPLEMENTED; } @@ -597,7 +604,7 @@ class RenewalRoundTrip class EntitledMessage { public: EntitledMessage(LicenseRoundTrip* license_messages) - : license_messages_(license_messages), num_keys_() {} + : license_messages_(license_messages) {} void FillKeyArray(); void MakeOneKey(size_t entitlement_key_index); void SetEntitledKeySession(uint32_t key_session) { @@ -631,13 +638,13 @@ class EntitledMessage { void VerifyDecrypt(); LicenseRoundTrip* license_messages_; - uint32_t num_keys_; + uint32_t num_keys_ = 0; // Clear Entitlement key data. This is the backing data for // |entitled_key_array_|. - EntitledContentKeyData entitled_key_data_[kMaxNumKeys]; + EntitledContentKeyData entitled_key_data_[kMaxNumKeys] = {}; // Entitled key object. Pointers are backed by |entitled_key_data_|. - OEMCrypto_EntitledContentKeyObject entitled_key_array_[kMaxNumKeys]; - uint32_t entitled_key_session_; + OEMCrypto_EntitledContentKeyObject entitled_key_array_[kMaxNumKeys] = {}; + uint32_t entitled_key_session_ = 0; }; class Session { diff --git a/oemcrypto/test/oemcrypto_basic_test.cpp b/oemcrypto/test/oemcrypto_basic_test.cpp index 9747ea54..015a14bd 100644 --- a/oemcrypto/test/oemcrypto_basic_test.cpp +++ b/oemcrypto/test/oemcrypto_basic_test.cpp @@ -156,7 +156,7 @@ TEST_F(OEMCryptoClientTest, FreeUnallocatedSecureBufferNoFailure) { */ TEST_F(OEMCryptoClientTest, VersionNumber) { const std::string log_message = - "OEMCrypto unit tests for API 18.2. Tests last updated 2023-04-12"; + "OEMCrypto unit tests for API 18.5. Tests last updated 2024-03-21"; cout << " " << log_message << "\n"; cout << " " << "These tests are part of Android U." @@ -165,7 +165,7 @@ TEST_F(OEMCryptoClientTest, VersionNumber) { // If any of the following fail, then it is time to update the log message // above. EXPECT_EQ(ODK_MAJOR_VERSION, 18); - EXPECT_EQ(ODK_MINOR_VERSION, 2); + EXPECT_EQ(ODK_MINOR_VERSION, 5); EXPECT_EQ(kCurrentAPI, static_cast(ODK_MAJOR_VERSION)); OEMCrypto_Security_Level level = OEMCrypto_SecurityLevel(); EXPECT_GT(level, OEMCrypto_Level_Unknown); @@ -200,6 +200,13 @@ TEST_F(OEMCryptoClientTest, VersionNumber) { if (build_info.size() != buf_length) { build_info.resize(buf_length); } + const std::string comma = ","; + const std::string pretty_comma = ",\n "; + std::string::size_type pos = 0; + while ((pos = build_info.find(comma, pos)) != std::string::npos) { + build_info.replace(pos, comma.size(), pretty_comma); + pos += pretty_comma.size(); + } cout << " BuildInformation: " << build_info << endl; OEMCrypto_WatermarkingSupport support = OEMCrypto_GetWatermarkingSupport(); cout << " WatermarkingSupport: " << support << endl; diff --git a/oemcrypto/test/oemcrypto_cast_test.cpp b/oemcrypto/test/oemcrypto_cast_test.cpp index 6dc25527..91730253 100644 --- a/oemcrypto/test/oemcrypto_cast_test.cpp +++ b/oemcrypto/test/oemcrypto_cast_test.cpp @@ -11,21 +11,36 @@ using ::testing::Range; namespace wvoec { -// The alternate padding is only required for cast receivers, but all devices -// should forbid the alternate padding for regular certificates. -TEST_F(OEMCryptoLoadsCertificateAlternates, DisallowForbiddenPaddingAPI09) { - LoadWithAllowedSchemes(kSign_RSASSA_PSS, - true); // Use default padding scheme - DisallowForbiddenPadding(kSign_PKCS1_Block1, 50); -} - -// The alternate padding is only required for cast receivers, but if a device -// does load an alternate certificate, it should NOT use it for generating -// a license request signature. +/** If a device can load a private key with the alternate padding schemes, it + * should support signing with the alternate scheme. */ TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1) { // Try to load an RSA key with alternative padding schemes. This signing // scheme is used by cast receivers. - LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); + LoadCastCertificateKey(false); + // If the device is a cast receiver, then this scheme is required. + if (global_features.cast_receiver) { + ASSERT_TRUE(key_loaded_); + } + // If the key loaded with no error, then we will verify that it is not used + // for forbidden padding schemes. + if (key_loaded_) { + if (global_features.cast_receiver) { + // A signature with a valid size should succeed. + TestSignature(kSign_PKCS1_Block1, 83); + TestSignature(kSign_PKCS1_Block1, 50); + } + // A signature with padding that is too big should fail. + DisallowForbiddenPaddingDRMKey(kSign_PKCS1_Block1, 84); // too big. + } +} + +/** The alternate padding is only required for cast receivers, but if a device + * does load an alternate certificate, it should NOT be used as a DRM cert + * key. */ +TEST_F(OEMCryptoLoadsCertificateAlternates, ForbidUseAsDRMCert) { + // Try to load an RSA key with alternative padding schemes. This signing + // scheme is used by cast receivers. + LoadCastCertificateKey(false); // If the device is a cast receiver, then this scheme is required. if (global_features.cast_receiver) { ASSERT_TRUE(key_loaded_); @@ -34,15 +49,44 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, TestSignaturePKCS1) { // for forbidden padding schemes. if (key_loaded_) { // The other padding scheme should fail. - DisallowForbiddenPadding(kSign_RSASSA_PSS, 83); + DisallowForbiddenPaddingDRMKey(kSign_RSASSA_PSS, 83); DisallowDeriveKeys(); - if (global_features.cast_receiver) { - // A signature with a valid size should succeed. - TestSignature(kSign_PKCS1_Block1, 83); - TestSignature(kSign_PKCS1_Block1, 50); - } - // A signature with padding that is too big should fail. - DisallowForbiddenPadding(kSign_PKCS1_Block1, 84); // too big. + } +} + +/** A Cast receiver certificate private key cannot be used with the function + * PrepAndSignLicenseRequest. + */ +TEST_F(OEMCryptoLoadsCertificateAlternates, ForbidPrepAndSign) { + // Try to load an RSA key with alternative padding schemes. This signing + // scheme is used by cast receivers. + LoadCastCertificateKey(false); + // If the device is a cast receiver, then this scheme is required. + if (global_features.cast_receiver) { + ASSERT_TRUE(key_loaded_); + } + // If the key loaded with no error, then we will verify that it is not used + // for forbidden padding schemes. + if (key_loaded_) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_)); + s.GenerateNonce(); + + size_t core_message_length = 100; + std::vector message(128, 0); + std::vector signature(256, 0); + size_t signature_length = signature.size(); + + OEMCryptoResult result = OEMCrypto_PrepAndSignLicenseRequest( + s.session_id(), message.data(), message.size(), &core_message_length, + signature.data(), &signature_length); + // TODO: remove OEMCrypto_ERROR_INVALID_RSA_KEY once OEMCrypto v16 is not + // supported anymore. This error code has been deprecated since v17. + ASSERT_TRUE(result == OEMCrypto_ERROR_INVALID_KEY || + result == OEMCrypto_ERROR_INVALID_RSA_KEY); + const vector zero(signature.size(), 0); + ASSERT_EQ(signature, zero); // Signature should not have been computed. } } @@ -275,7 +319,7 @@ TEST_F(OEMCryptoCastReceiverTest, SupportsCertificatesAPI13) { // # PKCS#1 v1.5 Signature Example 15.1 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_1) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "f45d55f35551e975d6a8dc7ea9f48859" "3940cc75694a278f27e578a163d839b3" @@ -314,7 +358,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_1) { // # PKCS#1 v1.5 Signature Example 15.2 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_2) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "c14b4c6075b2f9aad661def4ecfd3cb9" "33c623f4e63bf53410d2f016d1ab98e2" @@ -349,7 +393,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_2) { // # PKCS#1 v1.5 Signature Example 15.3 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_3) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "d02371ad7ee48bbfdb2763de7a843b94" "08ce5eb5abf847ca3d735986df84e906" @@ -390,7 +434,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_3) { // # PKCS#1 v1.5 Signature Example 15.4 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_4) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "29035584ab7e0226a9ec4b02e8dcf127" "2dc9a41d73e2820007b0f6e21feccd5b" @@ -419,7 +463,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_4) { // # PKCS#1 v1.5 Signature Example 15.5 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_5) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex("bda3a1c79059eae598308d3df609"); vector signature = wvutil::a2b_hex( "a156176cb96777c7fb96105dbd913bc4" @@ -444,7 +488,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_5) { // # PKCS#1 v1.5 Signature Example 15.6 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_6) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "c187915e4e87da81c08ed4356a0cceac" "1c4fb5c046b45281b387ec28f1abfd56" @@ -476,7 +520,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_6) { // # PKCS#1 v1.5 Signature Example 15.7 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_7) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "abfa2ecb7d29bd5bcb9931ce2bad2f74" "383e95683cee11022f08e8e7d0b8fa05" @@ -509,7 +553,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_7) { // # PKCS#1 v1.5 Signature Example 15.8 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_8) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "df4044a89a83e9fcbf1262540ae3038b" "bc90f2b2628bf2a4467ac67722d8546b" @@ -548,7 +592,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_8) { // # PKCS#1 v1.5 Signature Example 15.9 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_9) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "ea941ff06f86c226927fcf0e3b11b087" "2676170c1bfc33bda8e265c77771f9d0" @@ -585,7 +629,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_9) { // # PKCS#1 v1.5 Signature Example 15.10 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_10) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "d8b81645c13cd7ecf5d00ed2c91b9acd" "46c15568e5303c4a9775ede76b48403d" @@ -615,7 +659,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_10) { // # PKCS#1 v1.5 Signature Example 15.11 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_11) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "e5739b6c14c92d510d95b826933337ff" "0d24ef721ac4ef64c2bad264be8b44ef" @@ -649,7 +693,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_11) { // # PKCS#1 v1.5 Signature Example 15.12 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_12) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "7af42835917a88d6b3c6716ba2f5b0d5" "b20bd4e2e6e574e06af1eef7c81131be" @@ -690,7 +734,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_12) { // # PKCS#1 v1.5 Signature Example 15.13 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_13) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "ebaef3f9f23bdfe5fa6b8af4c208c189" "f2251bf32f5f137b9de4406378686b3f" @@ -719,7 +763,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_13) { // # PKCS#1 v1.5 Signature Example 15.14 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_14) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "c5a2711278761dfcdd4f0c99e6f5619d" "6c48b5d4c1a80982faa6b4cf1cf7a60f" @@ -755,7 +799,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_14) { // # PKCS#1 v1.5 Signature Example 15.15 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_15) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "9bf8aa253b872ea77a7e23476be26b23" "29578cf6ac9ea2805b357f6fc3ad130d" @@ -794,7 +838,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_15) { // # PKCS#1 v1.5 Signature Example 15.16 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_16) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "32474830e2203754c8bf0681dc4f842a" "fe360930378616c108e833656e5640c8" @@ -835,7 +879,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_16) { // # PKCS#1 v1.5 Signature Example 15.17 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_17) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "008e59505eafb550aae5e845584cebb0" "0b6de1733e9f95d42c882a5bbeb5ce1c" @@ -864,7 +908,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_17) { // # PKCS#1 v1.5 Signature Example 15.18 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_18) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "6abc54cf8d1dff1f53b17d8160368878" "a8788cc6d22fa5c2258c88e660b09a89" @@ -894,7 +938,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_18) { // # PKCS#1 v1.5 Signature Example 15.19 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_19) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "af2d78152cf10efe01d274f217b177f6" "b01b5e749f1567715da324859cd3dd88" @@ -931,7 +975,7 @@ TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_19) { // # PKCS#1 v1.5 Signature Example 15.20 TEST_F(OEMCryptoCastReceiverTest, TestSignaturePKCS1_15_20) { BuildRSAKey(); - LoadWithAllowedSchemes(kSign_PKCS1_Block1, true); + LoadCastCertificateKey(true); vector message = wvutil::a2b_hex( "40ee992458d6f61486d25676a96dd2cb" "93a37f04b178482f2b186cf88215270d" @@ -974,4 +1018,4 @@ TEST_P(OEMCryptoSessionTestLoadCasKeysWithHDCP, CasOnlyLoadCasKeysAPI17) { } INSTANTIATE_TEST_SUITE_P(TestHDCP, OEMCryptoSessionTestLoadCasKeysWithHDCP, Range(1, 6)); -} // namespace wvoec \ No newline at end of file +} // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_cast_test.h b/oemcrypto/test/oemcrypto_cast_test.h index b6e2116c..283df69b 100644 --- a/oemcrypto/test/oemcrypto_cast_test.h +++ b/oemcrypto/test/oemcrypto_cast_test.h @@ -25,36 +25,6 @@ std::string MaybeHex(const std::vector& data); // This test attempts to use alternate algorithms for loaded device certs. class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { protected: - void DisallowForbiddenPadding(RSA_Padding_Scheme scheme, size_t size) { - OEMCryptoResult sts; - Session s; - ASSERT_NO_FATAL_FAILURE(s.open()); - ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_)); - - // Sign a Message - vector licenseRequest(size); - GetRandBytes(licenseRequest.data(), licenseRequest.size()); - size_t signature_length = 256; - vector signature(signature_length); - sts = OEMCrypto_GenerateRSASignature( - s.session_id(), licenseRequest.data(), licenseRequest.size(), - signature.data(), &signature_length, scheme); - // Allow OEMCrypto to request a full buffer. - if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { - ASSERT_NE(static_cast(0), signature_length); - signature.assign(signature_length, 0); - sts = OEMCrypto_GenerateRSASignature( - s.session_id(), licenseRequest.data(), licenseRequest.size(), - signature.data(), &signature_length, scheme); - } - - EXPECT_NE(OEMCrypto_SUCCESS, sts) - << "Signed with forbidden padding scheme=" << (int)scheme - << ", size=" << (int)size; - const vector zero(signature.size(), 0); - ASSERT_EQ(zero, signature); // signature should not be computed. - } - void TestSignature(RSA_Padding_Scheme scheme, size_t size) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -105,7 +75,12 @@ class OEMCryptoLoadsCertificateAlternates : public OEMCryptoLoadsCertificate { } // If force is true, we assert that the key loads successfully. - void LoadWithAllowedSchemes(uint32_t schemes, bool force) { + void LoadCastCertificateKey(bool force) { + if (!wvoec::global_features.cast_receiver) { + GTEST_SKIP() << "Cast not supported"; + } + // Padding scheme used to sign cast data. + constexpr uint32_t schemes = kSign_PKCS1_Block1; // prov 2 or prov 3 if (global_features.provisioning_method == OEMCrypto_Keybox || global_features.provisioning_method == OEMCrypto_OEMCertificate) { diff --git a/oemcrypto/test/oemcrypto_corpus_generator_helper.cpp b/oemcrypto/test/oemcrypto_corpus_generator_helper.cpp index 31b9bb52..6b603dcf 100644 --- a/oemcrypto/test/oemcrypto_corpus_generator_helper.cpp +++ b/oemcrypto/test/oemcrypto_corpus_generator_helper.cpp @@ -1,12 +1,14 @@ /* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */ /* source code may only be used and distributed under the Widevine */ /* License Agreement. */ + #include "oemcrypto_corpus_generator_helper.h" #include #include namespace wvoec { + bool g_generate_corpus; void AppendToFile(const std::string& file_name, const char* message, @@ -32,7 +34,7 @@ void AppendSeparator(const std::string& file_name) { std::string GetFileName(const char* directory) { std::string file_name(PATH_TO_CORPUS); file_name += directory; - file_name += "/"; + file_name += '/'; file_name += std::to_string(rand()); return file_name; } @@ -40,5 +42,7 @@ std::string GetFileName(const char* directory) { void SetGenerateCorpus(bool should_generate_corpus) { g_generate_corpus = should_generate_corpus; } + bool ShouldGenerateCorpus() { return g_generate_corpus; } + } // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_corpus_generator_helper.h b/oemcrypto/test/oemcrypto_corpus_generator_helper.h index e3a73b43..005dadef 100644 --- a/oemcrypto/test/oemcrypto_corpus_generator_helper.h +++ b/oemcrypto/test/oemcrypto_corpus_generator_helper.h @@ -1,16 +1,18 @@ /* Copyright 2020 Google LLC. All rights reserved. This file and proprietary */ /* source code may only be used and distributed under the Widevine */ /* License Agreement. */ + #ifndef CDM_OEMCRYPTO_CORPUS_GENERATOR_HELPER_H_ #define CDM_OEMCRYPTO_CORPUS_GENERATOR_HELPER_H_ #define PATH_TO_CORPUS "./oemcrypto/test/fuzz_tests/corpus/" -#include +#include #include #include namespace wvoec { + const uint8_t kFuzzDataSeparator[] = {'-', '_', '^', '_'}; void AppendToFile(const std::string& file_name, const char* message, @@ -22,9 +24,11 @@ void AppendSeparator(const std::string& file_name); std::string GetFileName(const char* directory); void SetGenerateCorpus(bool should_generate_corpus); + // Output of this function decides if binary data needs to be written // to corpus files or not. Controlled by --generate_corpus flag. bool ShouldGenerateCorpus(); + } // namespace wvoec #endif // CDM_OEMCRYPTO_CORPUS_GENERATOR_HELPER_H_ diff --git a/oemcrypto/test/oemcrypto_decrypt_test.cpp b/oemcrypto/test/oemcrypto_decrypt_test.cpp index 22c810c0..a61c8933 100644 --- a/oemcrypto/test/oemcrypto_decrypt_test.cpp +++ b/oemcrypto/test/oemcrypto_decrypt_test.cpp @@ -49,10 +49,9 @@ TEST_P(OEMCryptoLicenseTest, FailDecryptWithOldKeyHandle) { session_.TestDecryptCTR(false, OEMCrypto_ERROR_UNKNOWN_FAILURE)); } -// SelectKey should fail if we attempt to select a key that has not been loaded. -// Also, the error should be NO_CONTENT_KEY. -// This test should pass for v15 devices, except that the exact error code was -// not specified until v16. +// GetKeyHandle should fail if we attempt to select a key that has not been +// loaded. Also, the error should be NO_CONTENT_KEY. This test should pass for +// v15 devices, except that the exact error code was not specified until v16. TEST_P(OEMCryptoLicenseTest, SelectKeyNotThereAPI16) { ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); @@ -540,6 +539,17 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptMaxSubsample) { ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); } +TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptZeroSizeSubSample) { + ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ + {10, 10}, + {0, 0}, + })); + ASSERT_NO_FATAL_FAILURE(LoadLicense()); + ASSERT_NO_FATAL_FAILURE(MakeBuffers()); + ASSERT_NO_FATAL_FAILURE(EncryptData()); + ASSERT_NO_FATAL_FAILURE(TestDecryptCENC()); +} + // There are probably no frames this small, but we should handle them anyway. TEST_P(OEMCryptoSessionTestsDecryptTests, DecryptSmallBuffer) { ASSERT_NO_FATAL_FAILURE(SetSubsampleSizes({ @@ -683,4 +693,4 @@ TEST_P(OEMCryptoLicenseTest, KeyDuration) { INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoLicenseTest, Range(kCurrentAPI - 2, kCurrentAPI + 1)); -} // namespace wvoec \ No newline at end of file +} // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_decrypt_test.h b/oemcrypto/test/oemcrypto_decrypt_test.h index 9f9b5005..36a249c8 100644 --- a/oemcrypto/test/oemcrypto_decrypt_test.h +++ b/oemcrypto/test/oemcrypto_decrypt_test.h @@ -98,6 +98,10 @@ class OEMCryptoSessionTestsDecryptTests protected: void SetUp() override { OEMCryptoLicenseTestAPI16::SetUp(); + if (wvoec::global_features.derive_key_method == + wvoec::DeviceFeatures::NO_METHOD) { + GTEST_SKIP() << "Test for devices that can derive session keys only."; + } pattern_ = ::testing::get<0>(GetParam()); cipher_mode_ = ::testing::get<1>(GetParam()); decrypt_inplace_ = ::testing::get<2>(GetParam()).decrypt_inplace; diff --git a/oemcrypto/test/oemcrypto_license_test.cpp b/oemcrypto/test/oemcrypto_license_test.cpp index fedd8f9b..93065b22 100644 --- a/oemcrypto/test/oemcrypto_license_test.cpp +++ b/oemcrypto/test/oemcrypto_license_test.cpp @@ -84,10 +84,6 @@ void TestMaxKeys(SessionUtil* util, size_t num_keys_per_session) { } } -TEST_F(OEMCryptoSessionTestKeyboxTest, TestKeyboxIsValid) { - ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid()); -} - TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryPrepareLicenseRequestForHugeRequestMessageLength) { TestPrepareLicenseRequestForHugeBufferLengths( @@ -875,7 +871,7 @@ TEST_P(OEMCryptoRefreshTest, RefreshLargeBuffer) { } // 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 +// that happens, GetKeyHandle would be called before the first decrypt, and then // would not need to be called again, even if the license is refreshed. TEST_P(OEMCryptoRefreshTest, RefreshWithNoSelectKey) { LoadLicense(); @@ -967,4 +963,4 @@ INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoRefreshTestAPI16, Range(kCoreMessagesAPI, kCurrentAPI + 1)); /// @} -} // namespace wvoec \ No newline at end of file +} // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_license_test.h b/oemcrypto/test/oemcrypto_license_test.h index a2860503..8d7339b2 100644 --- a/oemcrypto/test/oemcrypto_license_test.h +++ b/oemcrypto/test/oemcrypto_license_test.h @@ -39,6 +39,10 @@ class OEMCryptoSessionTests : public OEMCryptoClientTest { void SetUp() override { OEMCryptoClientTest::SetUp(); + if (wvoec::global_features.derive_key_method == + wvoec::DeviceFeatures::NO_METHOD) { + GTEST_SKIP() << "Test for devices that can derive session keys only."; + } EnsureTestROT(); if (global_features.usage_table) { CreateUsageTableHeader(); @@ -92,8 +96,6 @@ class OEMCryptoSessionTests : public OEMCryptoClientTest { } }; -class OEMCryptoSessionTestKeyboxTest : public OEMCryptoSessionTests {}; - // This class is for testing a single license with the default API version // of 16. class OEMCryptoLicenseTestAPI16 : public OEMCryptoSessionTests { @@ -407,4 +409,4 @@ class OEMCryptoRefreshTestAPI16 : public OEMCryptoRefreshTest {}; } // namespace wvoec -#endif // CDM_OEMCRYPTO_LICENSE_TEST_ \ No newline at end of file +#endif // CDM_OEMCRYPTO_LICENSE_TEST_ diff --git a/oemcrypto/test/oemcrypto_provisioning_test.cpp b/oemcrypto/test/oemcrypto_provisioning_test.cpp index 5504e768..47d7228d 100644 --- a/oemcrypto/test/oemcrypto_provisioning_test.cpp +++ b/oemcrypto/test/oemcrypto_provisioning_test.cpp @@ -119,36 +119,24 @@ TEST_F(OEMCryptoProv30Test, OEMCertValid) { ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert(kVerify)); // Load and verify. } -// This verifies that the OEM Certificate cannot be used for other RSA padding -// schemes. Those schemes should only be used by cast receiver certificates. -TEST_F(OEMCryptoProv30Test, OEMCertForbiddenPaddingScheme) { +/** This verifies that the OEM Certificate cannot be used with + * GenerateRSASignature. + */ +TEST_F(OEMCryptoProv30Test, OEMCertForbidGenerateRSASignature1) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); - OEMCryptoResult sts; - // Sign a Message - vector data(500); - GetRandBytes(data.data(), data.size()); - size_t signature_length = 0; - // We need a size one vector to pass as a pointer. - vector signature(1, 0); - vector zero(1, 0); + DisallowForbiddenPadding(s.session_id(), kSign_PKCS1_Block1, 80); +} - sts = OEMCrypto_GenerateRSASignature(s.session_id(), data.data(), data.size(), - signature.data(), &signature_length, - kSign_PKCS1_Block1); - if (OEMCrypto_ERROR_SHORT_BUFFER == sts) { - // The OEMCrypto could complain about buffer length first, so let's - // resize and check if it's writing to the signature again. - signature.resize(signature_length, 0); - zero.resize(signature_length, 0); - sts = OEMCrypto_GenerateRSASignature(s.session_id(), data.data(), - data.size(), signature.data(), - &signature_length, kSign_PKCS1_Block1); - } - EXPECT_NE(OEMCrypto_SUCCESS, sts) - << "OEM Cert Signed with forbidden kSign_PKCS1_Block1."; - ASSERT_EQ(zero, signature); // signature should not be computed. +/** This verifies that the OEM Certificate cannot be used with + * GenerateRSASignature. + */ +TEST_F(OEMCryptoProv30Test, OEMCertForbidGenerateRSASignature2) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadOEMCert()); + DisallowForbiddenPadding(s.session_id(), kSign_RSASSA_PSS, 80); } // Calling OEMCrypto_GetOEMPublicCertificate should not change the session's @@ -186,6 +174,46 @@ TEST_F(OEMCryptoProv30Test, GetCertOnlyAPI16) { ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); } +/** This verifies that the OEM Certificate cannot be used with + * GenerateRSASignature. + */ +TEST_F(OEMCryptoProv40Test, OEMCertForbidGenerateRSASignature1) { + // Create an OEM Cert and save it for later. + Session s1; + ASSERT_NO_FATAL_FAILURE(s1.open()); + ASSERT_NO_FATAL_FAILURE(CreateProv4OEMKey(&s1)); + ASSERT_EQ(s1.IsPublicKeySet(), true); + s1.close(); + Session s2; + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_InstallOemPrivateKey( + s2.session_id(), oem_key_type_, + reinterpret_cast(wrapped_oem_key_.data()), + wrapped_oem_key_.size())); + DisallowForbiddenPadding(s2.session_id(), kSign_PKCS1_Block1, 80); +} + +/** This verifies that the OEM Certificate cannot be used with + * GenerateRSASignature. + */ +TEST_F(OEMCryptoProv40Test, OEMCertForbidGenerateRSASignature2) { + // Create an OEM Cert and save it for later. + Session s1; + ASSERT_NO_FATAL_FAILURE(s1.open()); + ASSERT_NO_FATAL_FAILURE(CreateProv4OEMKey(&s1)); + ASSERT_EQ(s1.IsPublicKeySet(), true); + s1.close(); + Session s2; + ASSERT_NO_FATAL_FAILURE(s2.open()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_InstallOemPrivateKey( + s2.session_id(), oem_key_type_, + reinterpret_cast(wrapped_oem_key_.data()), + wrapped_oem_key_.size())); + DisallowForbiddenPadding(s2.session_id(), kSign_RSASSA_PSS, 80); +} + // This verifies that the device really does claim to have BCC. // It should be filtered out for devices that have a keybox or factory OEM // cert. @@ -539,7 +567,7 @@ TEST_F(OEMCryptoProv40Test, InstallOemPrivateKeyCanBeUsed) { * cert. */ TEST_F(OEMCryptoProv40Test, OEMPrivateKeyCannotBeDRMKey) { - // Create an OEM Cert and save it for alter. + // Create an OEM Cert and save it for later. Session s1; ASSERT_NO_FATAL_FAILURE(s1.open()); ASSERT_NO_FATAL_FAILURE(CreateProv4OEMKey(&s1)); @@ -654,7 +682,22 @@ TEST_P(OEMCryptoProv40CastTest, ProvisionCastWorks) { INSTANTIATE_TEST_SUITE_P(Prov4CastProvisioningBasic, OEMCryptoProv40CastTest, testing::Values(true, false)); +// Verify that you cannot use GenerateRSASignature with a normal DRM Cert. +// that function needs a cast cert. +TEST_F(OEMCryptoLoadsCertificate, ForbidRSASignatureForDRMKey1) { + DisallowForbiddenPadding(session_.session_id(), kSign_RSASSA_PSS, 80); +} + +TEST_F(OEMCryptoLoadsCertificate, ForbidRSASignatureForDRMKey2) { + DisallowForbiddenPadding(session_.session_id(), kSign_PKCS1_Block1, 80); +} + TEST_F(OEMCryptoLoadsCertificate, PrepAndSignLicenseRequestCounterAPI18) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -675,6 +718,11 @@ TEST_F(OEMCryptoLoadsCertificate, PrepAndSignLicenseRequestCounterAPI18) { // This test verifies that we can create a wrapped RSA key, and then reload it. TEST_F(OEMCryptoLoadsCertificate, LoadRSASessionKey) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -682,6 +730,11 @@ TEST_F(OEMCryptoLoadsCertificate, LoadRSASessionKey) { } TEST_F(OEMCryptoLoadsCertificate, SignProvisioningRequest) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ASSERT_NO_FATAL_FAILURE(s.open()); if (global_features.provisioning_method == OEMCrypto_OEMCertificate) { @@ -696,6 +749,11 @@ TEST_F(OEMCryptoLoadsCertificate, SignProvisioningRequest) { // This tests a large message size. The size is larger than we required in v15. TEST_F(OEMCryptoLoadsCertificate, SignLargeProvisioningRequestAPI16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ASSERT_NO_FATAL_FAILURE(s.open()); if (global_features.provisioning_method == OEMCrypto_OEMCertificate) { @@ -714,6 +772,11 @@ TEST_F(OEMCryptoLoadsCertificate, SignLargeProvisioningRequestAPI16) { // unencrypted key is not found in the wrapped key. The wrapped key should be // encrypted. TEST_F(OEMCryptoLoadsCertificate, CertificateProvision) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -730,6 +793,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvision) { // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -747,6 +815,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange1_API16) { // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -764,6 +837,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange2_API16) { // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -783,6 +861,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange3_API16) { // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange4_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -802,6 +885,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange4_API16) { // Verify that RewrapDeviceRSAKey checks pointers are within the provisioning // message. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange5Prov30_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } if (global_features.provisioning_method != OEMCrypto_OEMCertificate) { GTEST_SKIP() << "Test for Prov 3.0 devices only."; } @@ -825,6 +913,14 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRange5Prov30_API16) { // TODO(b/144186970): This test should also run on Prov 3.0 devices. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadSignatureKeyboxTestAPI16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } + if (global_features.provisioning_method != OEMCrypto_Keybox) { + GTEST_SKIP() << "Test for Prov 2.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -839,6 +935,11 @@ TEST_F(OEMCryptoLoadsCertificate, // Test that RewrapDeviceRSAKey verifies the nonce is current. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonce_API16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -853,6 +954,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadNonce_API16) { // Test that RewrapDeviceRSAKey verifies the RSA key is valid. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKey) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -868,6 +974,14 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKey) { // TODO(b/144186970): This test should also run on Prov 3.0 devices. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionBadRSAKeyKeyboxTestAPI16) { + if (global_features.provisioning_method != OEMCrypto_Keybox) { + GTEST_SKIP() << "Test for Prov 2.0 devices only."; + } + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); provisioning_messages.PrepareSession(keybox_); @@ -887,6 +1001,11 @@ TEST_F(OEMCryptoLoadsCertificate, // Test that RewrapDeviceRSAKey accepts the maximum message size. TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBuffer) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); const size_t max_size = GetResourceValue(kLargeMessageSize); @@ -904,6 +1023,11 @@ TEST_F(OEMCryptoLoadsCertificate, CertificateProvisionLargeBuffer) { // Test that a wrapped RSA key can be loaded. TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -912,6 +1036,15 @@ TEST_F(OEMCryptoLoadsCertificate, LoadWrappedRSAKey) { class OEMCryptoLoadsCertVariousKeys : public OEMCryptoLoadsCertificate { public: + void SetUp() override { + OEMCryptoLoadsCertificate::SetUp(); + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } + } + void TestKey(const uint8_t* key, size_t key_length) { encoded_rsa_key_.assign(key, key + key_length); ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); @@ -987,6 +1120,11 @@ TEST_F(OEMCryptoLoadsCertVariousKeys, TestEulerZeroNormalDer) { // This tests that two sessions can use different RSA keys simultaneously. TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); Session s1; // Session s1 loads the default rsa key, but doesn't use it // until after s2 uses its key. @@ -1023,6 +1161,11 @@ TEST_F(OEMCryptoLoadsCertificate, TestMultipleRSAKeys) { // This tests the maximum number of DRM private keys that OEMCrypto can load TEST_F(OEMCryptoLoadsCertificate, TestMaxDRMKeys) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } const size_t max_total_keys = GetResourceValue(kMaxTotalDRMPrivateKeys); std::vector> sessions; std::vector> licenses; @@ -1090,6 +1233,11 @@ TEST_F(OEMCryptoLoadsCertificate, TestMaxDRMKeys) { // Devices that load certificates, should at least support RSA 2048 keys. TEST_F(OEMCryptoLoadsCertificate, SupportsCertificatesAPI13) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NE(0u, OEMCrypto_Supports_RSA_2048bit & OEMCrypto_SupportedCertificates()) << "Supported certificates is only " << OEMCrypto_SupportedCertificates(); @@ -1098,6 +1246,11 @@ TEST_F(OEMCryptoLoadsCertificate, SupportsCertificatesAPI13) { // This test is not run by default, because it takes a long time and // is used to measure RSA performance, not test functionality. TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } const std::chrono::milliseconds kTestDuration(5000); OEMCryptoResult sts; std::chrono::steady_clock clock; @@ -1199,7 +1352,9 @@ TEST_F(OEMCryptoLoadsCertificate, RSAPerformance) { delta_time / std::chrono::milliseconds(1) / count; OEMCrypto_Security_Level level = OEMCrypto_SecurityLevel(); - printf("PERF:head, security, provision (ms), lic req(ms), derive keys(ms)\n"); + printf( + "PERF:head, security, provision (ms), lic req(ms), derive " + "keys(ms)\n"); printf("PERF:stat, %u, %8.3f, %8.3f, %8.3f\n", static_cast(level), provision_time, license_request_time, derive_keys_time); @@ -1225,4 +1380,4 @@ TEST_F(OEMCryptoUsesCertificate, GenerateDerivedKeysLargeBuffer) { enc_context.data(), enc_context.size())); } -} // namespace wvoec \ No newline at end of file +} // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_provisioning_test.h b/oemcrypto/test/oemcrypto_provisioning_test.h index b44369c5..8877b175 100644 --- a/oemcrypto/test/oemcrypto_provisioning_test.h +++ b/oemcrypto/test/oemcrypto_provisioning_test.h @@ -17,62 +17,84 @@ namespace wvoec { -// Tests using this class are only used for devices with a keybox. They are not -// run for devices with an OEM Certificate. -class OEMCryptoKeyboxTest : public OEMCryptoClientTest { - void SetUp() override { - OEMCryptoClientTest::SetUp(); - OEMCryptoResult sts = OEMCrypto_IsKeyboxValid(); - // If the production keybox is valid, use it for these tests. Most of the - // other tests will use a test keybox anyway, but it's nice to check the - // device ID for the real keybox if we can. - if (sts == OEMCrypto_SUCCESS) return; - printf("Production keybox is NOT valid. All tests use test keybox.\n"); - ASSERT_EQ( - OEMCrypto_SUCCESS, - OEMCrypto_LoadTestKeybox(reinterpret_cast(&kTestKeybox), - sizeof(kTestKeybox))); - } -}; - -// This class is for tests that have an OEM Certificate instead of a keybox. -class OEMCryptoProv30Test : public OEMCryptoClientTest { - void SetUp() override { - OEMCryptoClientTest::SetUp(); - if (global_features.provisioning_method != OEMCrypto_OEMCertificate) { - GTEST_SKIP() << "Test for Prov 3.0 devices only."; - } - } -}; - -// This class is for tests that have boot certificate chain instead of a keybox. -class OEMCryptoProv40Test : public OEMCryptoClientTest { - void SetUp() override { - OEMCryptoClientTest::SetUp(); - if (global_features.provisioning_method != OEMCrypto_BootCertificateChain) { - GTEST_SKIP() << "Test for Prov 4.0 devices only."; - } - } -}; - -class OEMCryptoProv40CastTest : public OEMCryptoClientTest, - public testing::WithParamInterface { - void SetUp() override { - OEMCryptoClientTest::SetUp(); - if (!global_features.cast_receiver) { - GTEST_SKIP() << "Test for cast devices only."; - } - if (global_features.provisioning_method != OEMCrypto_BootCertificateChain) { - GTEST_SKIP() << "Test for Prov 4.0 devices only."; - } - } -}; - // // Certificate Root of Trust Tests // -class OEMCryptoLoadsCertificate : public OEMCryptoSessionTestKeyboxTest { +// These tests are run by all L1 devices that load and use certificates. It is +// also run by a few L3 devices that use a baked in certificate, but cannot load +// a certificate. +class OEMCryptoUsesCertificate : public OEMCryptoSessionTests { protected: + void SetUp() override { + OEMCryptoSessionTests::SetUp(); + ASSERT_NO_FATAL_FAILURE(session_.open()); + if (global_features.derive_key_method == + DeviceFeatures::LOAD_TEST_RSA_KEY) { + ASSERT_NO_FATAL_FAILURE(session_.SetRsaPublicKeyFromPrivateKeyInfo( + encoded_rsa_key_.data(), encoded_rsa_key_.size())); + } else { + InstallTestDrmKey(&session_); + } + } + + void TearDown() override { + ASSERT_NO_FATAL_FAILURE(session_.close()); + OEMCryptoSessionTests::TearDown(); + } + + Session session_; +}; + +/** These tests cover all systems that can load a DRM Certificate. That includes + * Provisioning 2, 3 and 4. */ +class OEMCryptoLoadsCertificate : public OEMCryptoUsesCertificate { + protected: + void SetUp() override { + OEMCryptoUsesCertificate::SetUp(); + if (!global_features.loads_certificate) { + GTEST_SKIP() << "Test for devices that load a DRM certificate only."; + } + } + + /** Verify that the specified padding scheme does not work with the DRM + * key and the function OEMCrypto_GenerateRSASignature. */ + void DisallowForbiddenPaddingDRMKey(RSA_Padding_Scheme scheme, size_t size) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(s.LoadWrappedRsaDrmKey(wrapped_drm_key_)); + DisallowForbiddenPadding(s.session_id(), scheme, size); + } + + /** Verify that the specified padding scheme does not work with whichever key + * is currently loaded into the specified session and the function + * OEMCrypto_GenerateRSASignature. */ + void DisallowForbiddenPadding(OEMCrypto_SESSION session, + RSA_Padding_Scheme scheme, size_t size) { + OEMCryptoResult sts; + // Sign a Message + vector message(size); + GetRandBytes(message.data(), message.size()); + size_t signature_length = 256; + vector signature(signature_length); + sts = OEMCrypto_GenerateRSASignature(session, message.data(), + message.size(), signature.data(), + &signature_length, scheme); + // Allow OEMCrypto to request a full buffer. + if (sts == OEMCrypto_ERROR_SHORT_BUFFER) { + ASSERT_NE(static_cast(0), signature_length); + signature.assign(signature_length, 0); + sts = OEMCrypto_GenerateRSASignature(session, message.data(), + message.size(), signature.data(), + &signature_length, scheme); + } + + EXPECT_NE(OEMCrypto_SUCCESS, sts) + << "Signed with forbidden padding scheme=" << (int)scheme + << ", size=" << (int)size; + const vector zero(signature.size(), 0); + ASSERT_EQ(zero, signature); // signature should not be computed. + } + void TestPrepareProvisioningRequestForHugeBufferLengths( const std::function f, bool check_status) { @@ -139,31 +161,63 @@ class OEMCryptoLoadsCertificate : public OEMCryptoSessionTestKeyboxTest { } }; -// These tests are run by all L1 devices that load and use certificates. It is -// also run by a few L3 devices that use a baked in certificate, but cannot load -// a certificate. -class OEMCryptoUsesCertificate : public OEMCryptoLoadsCertificate { +// Tests using this class are only used for devices with a keybox. They are not +// run for devices with an OEM Certificate. +class OEMCryptoKeyboxTest : public OEMCryptoLoadsCertificate { protected: void SetUp() override { OEMCryptoLoadsCertificate::SetUp(); - ASSERT_NO_FATAL_FAILURE(session_.open()); - if (global_features.derive_key_method == - DeviceFeatures::LOAD_TEST_RSA_KEY) { - ASSERT_NO_FATAL_FAILURE(session_.SetRsaPublicKeyFromPrivateKeyInfo( - encoded_rsa_key_.data(), encoded_rsa_key_.size())); - } else { - InstallTestDrmKey(&session_); + if (global_features.provisioning_method != OEMCrypto_Keybox) { + GTEST_SKIP() << "Test for Prov 2.0 devices only."; + } + OEMCryptoResult sts = OEMCrypto_IsKeyboxValid(); + // If the production keybox is valid, use it for these tests. Most of the + // other tests will use a test keybox anyway, but it's nice to check the + // device ID for the real keybox if we can. + if (sts == OEMCrypto_SUCCESS) return; + printf("Production keybox is NOT valid. All tests use test keybox.\n"); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_LoadTestKeybox(reinterpret_cast(&kTestKeybox), + sizeof(kTestKeybox))); + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid()) + << "After loading Test keybox, the keybox was still not valid."; + } +}; + +// This class is for tests that have an OEM Certificate instead of a keybox. +class OEMCryptoProv30Test : public OEMCryptoLoadsCertificate { + protected: + void SetUp() override { + OEMCryptoLoadsCertificate::SetUp(); + if (global_features.provisioning_method != OEMCrypto_OEMCertificate) { + GTEST_SKIP() << "Test for Prov 3.0 devices only."; } } +}; - void TearDown() override { - ASSERT_NO_FATAL_FAILURE(session_.close()); - OEMCryptoLoadsCertificate::TearDown(); +// This class is for tests that have boot certificate chain instead of a keybox. +class OEMCryptoProv40Test : public OEMCryptoLoadsCertificate { + protected: + void SetUp() override { + OEMCryptoLoadsCertificate::SetUp(); + if (global_features.provisioning_method != OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for Prov 4.0 devices only."; + } } +}; - Session session_; +class OEMCryptoProv40CastTest : public OEMCryptoProv40Test, + public testing::WithParamInterface { + protected: + void SetUp() override { + OEMCryptoProv40Test::SetUp(); + if (!global_features.cast_receiver) { + GTEST_SKIP() << "Test for cast devices only."; + } + } }; } // namespace wvoec -#endif // CDM_OEMCRYPTO_PROVISIONING_TEST_ \ No newline at end of file +#endif // CDM_OEMCRYPTO_PROVISIONING_TEST_ diff --git a/oemcrypto/test/oemcrypto_security_test.cpp b/oemcrypto/test/oemcrypto_security_test.cpp index af9b0462..72390eef 100644 --- a/oemcrypto/test/oemcrypto_security_test.cpp +++ b/oemcrypto/test/oemcrypto_security_test.cpp @@ -39,6 +39,8 @@ namespace wvoec { /// @addtogroup security /// @{ +/** Test that OEMCrypto_FreeSecureBuffer fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoClientTest, OEMCryptoMemoryAllocateSecureBufferForHugeBufferSize) { Session s; @@ -57,6 +59,8 @@ TEST_F(OEMCryptoClientTest, s.close(); } +/** Test that OEMCrypto_WrapKeyboxOrOEMCert fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoClientTest, OEMCryptoMemoryWrapKeyboxOrOEMCertForHugeKeyboxLength) { auto oemcrypto_function = [](size_t keybox_length) { @@ -74,6 +78,8 @@ TEST_F(OEMCryptoClientTest, kHugeInputBufferLength, kCheckStatus); } +/** Test that OEMCrypto_WrapKeyboxOrOEMCert fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoClientTest, OEMCryptoMemoryWrapKeyboxOrOEMCertForHugeWrappedKeyboxLength) { auto oemcrypto_function = [](size_t buffer_length) { @@ -91,6 +97,8 @@ TEST_F(OEMCryptoClientTest, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } +/** Test that OEMCrypto_WrapKeyboxOrOEMCert fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoClientTest, OEMCryptoMemoryWrapKeyboxOrOEMCertForHugeTransportKey) { auto oemcrypto_function = [](size_t transport_key_length) { @@ -105,6 +113,8 @@ TEST_F(OEMCryptoClientTest, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that OEMCrypto_WrapKeyboxOrOEMCert fails gracefully on a huge buffer. + */ TEST_F( OEMCryptoClientTest, OEMCryptoMemoryWrapKeyboxOrOEMCertForHugeKeyboxLengthStartingFromLength1) { @@ -124,7 +134,8 @@ TEST_F( TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } -// Test that set sandbox doesn't crash for a large sandbox id leangth. +/** Test that OEMCrypto_SetSandbox fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoClientTest, OEMCryptoMemorySetSandboxForHugeSandboxIdLength) { auto oemcrypto_function = [](size_t buffer_length) { vector buffer(buffer_length); @@ -133,6 +144,8 @@ TEST_F(OEMCryptoClientTest, OEMCryptoMemorySetSandboxForHugeSandboxIdLength) { TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } +/** Test that OEMCrypto_CopyBuffer fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoClientTest, OEMCryptoMemoryCopyBufferForHugeBufferLengths) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -163,6 +176,9 @@ TEST_F(OEMCryptoClientTest, OEMCryptoMemoryCopyBufferForHugeBufferLengths) { TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** @ingroup security + * Test that OEMCrypto_CopyBuffer fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoClientTest, OEMCryptoMemoryCopyBufferDirectForHugeBufferLengths) { Session s; @@ -184,6 +200,9 @@ TEST_F(OEMCryptoClientTest, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** @ingroup security + * Test that OEMCrypto_CopyBuffer fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoClientTest, OEMCryptoMemoryCopyBufferForOutOfRangeOffset) { Session s; ASSERT_NO_FATAL_FAILURE(s.open()); @@ -245,6 +264,9 @@ TEST_F(OEMCryptoKeyboxTest, } #endif +/** @ingroup security + * Test that OEMCrypto_LoadTestKeybox fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryLoadTestKeyBoxForHugeKeyboxBuffer) { auto f = [](size_t keybox_length) { vector keybox(keybox_length); @@ -257,6 +279,9 @@ TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryLoadTestKeyBoxForHugeKeyboxBuffer) { kCheckStatus); } +/** @ingroup security + * Test that OEMCrypto_LoadTestKeybox fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryLoadTestKeyBoxForHugeKeyboxBufferStartingFromLength1) { auto f = [](size_t keybox_length) { @@ -268,6 +293,8 @@ TEST_F(OEMCryptoKeyboxTest, TestHugeLengthDoesNotCrashAPI(f, !kCheckStatus); } +/** Test that OEMCrypto_GetDeviceID fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryGetDeviceIdForHugeIdLength) { auto oemcrypto_function = [](size_t input_length) { size_t device_id_length = input_length; @@ -277,6 +304,8 @@ TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryGetDeviceIdForHugeIdLength) { TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that OEMCrypto_GetKeyData fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryGetKeyIdForHugeIdLength) { auto oemcrypto_function = [](size_t input_length) { size_t key_data_length = input_length; @@ -286,6 +315,8 @@ TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryGetKeyIdForHugeIdLength) { TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } +/** Test that OEMCrypto_GenerateDerivedKeys fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryGenerateDerivedKeysForHugeMacContextLength) { Session s; @@ -304,6 +335,8 @@ TEST_F(OEMCryptoKeyboxTest, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that OEMCrypto_GenerateDerivedKeys fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoKeyboxTest, OEMCryptoMemoryGenerateDerivedKeysForHugeEncContextLength) { Session s; @@ -322,6 +355,9 @@ TEST_F(OEMCryptoKeyboxTest, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that OEMCrypto_GetOEMPublicCertificate fails gracefully on a huge + * buffer. + */ TEST_F(OEMCryptoProv30Test, OEMCryptoMemoryGetOEMPublicCertForHugeCertLength) { if (wrapped_rsa_key_.size() == 0) { // If we don't have a wrapped key yet, create one. @@ -343,6 +379,9 @@ TEST_F(OEMCryptoProv30Test, OEMCryptoMemoryGetOEMPublicCertForHugeCertLength) { TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that OEMCrypto_CreateUsageTableHeader fails gracefully on a huge + * buffer. + */ TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryCreateUsageTableHeaderForHugeHeaderBufferLength) { auto oemcrypto_function = [](size_t buffer_length) { @@ -354,6 +393,9 @@ TEST_F(OEMCryptoSessionTests, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that OEMCrypto_PrepAndSignRenewalRequest fails gracefully on a huge + * buffer. + */ TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryPrepareRenewalRequestForHugeBufferLength) { RenewalRoundTrip renewal_messages(&license_messages_); @@ -364,6 +406,9 @@ TEST_F(OEMCryptoMemoryLicenseTest, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that OEMCrypto_PrepAndSignRenewalRequest fails gracefully on a huge + * buffer. + */ TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryPrepareRenewalRequestForHugeSignatureLength) { RenewalRoundTrip renewal_messages(&license_messages_); @@ -374,6 +419,9 @@ TEST_F(OEMCryptoMemoryLicenseTest, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that OEMCrypto_PrepAndSignRenewalRequest fails gracefully on a huge + * buffer. + */ TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryPrepareRenewalRequestForHugeCoreMessageLength) { RenewalRoundTrip renewal_messages(&license_messages_); @@ -384,8 +432,9 @@ TEST_F(OEMCryptoMemoryLicenseTest, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } -// This verifies that entitled content keys API does not crash for unreasonable -// input message buffer lengths. +/** Test that loading entitled content keys fails gracefully on a huge + * buffer. + */ TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForHugeBufferLength) { auto oemcrypto_function = [&](size_t buffer_length) { @@ -402,6 +451,8 @@ TEST_F(OEMCryptoMemoryLicenseTest, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } +/** Test that OEMCrypto_LoadLicense fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryLoadLicenseForHugeSignatureLength) { auto oemcrypto_function = [&](size_t signature_size) { @@ -424,6 +475,8 @@ TEST_F(OEMCryptoSessionTests, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } +/** Test that OEMCrypto_LoadRenewal fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryLoadRenewalForHugeResponseLength) { auto oemcrypto_function = [&](size_t message_size) { Session s; @@ -443,6 +496,8 @@ TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryLoadRenewalForHugeResponseLength) { TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that OEMCrypto_LoadRenewal fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryLoadRenewalForHugeSignatureLength) { auto oemcrypto_function = [&](size_t signature_size) { @@ -467,6 +522,8 @@ TEST_F(OEMCryptoSessionTests, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } +/** Test that OEMCrypto_QueryKeyControl fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryLoadRenewalForHugeCoreMessageLength) { auto oemcrypto_function = [&](size_t core_message_size) { @@ -487,7 +544,8 @@ TEST_F(OEMCryptoSessionTests, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } -// Test OEMCrypto_QueryKeyControl doesn't crash for huge key_id_length. +/** Test that OEMCrypto_QueryKeyControl fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryQueryKeyControlForHugeKeyIdLength) { Session session; @@ -512,8 +570,9 @@ TEST_F(OEMCryptoSessionTests, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } -// Test OEMCrypto_QueryKeyControl doesn't crash for huge key_control_block -// length. +/** Test OEMCrypto_QueryKeyControl doesn't crash for huge key_control_block + * length. + */ TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryQueryKeyControlForHugeKeyControlBlockLength) { Session session; @@ -534,8 +593,9 @@ TEST_F(OEMCryptoSessionTests, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } -// This test verifies that OEMCrypto_SetDecryptHash doesn't crash for a very -// large hash buffer. +/** This test verifies that OEMCrypto_SetDecryptHash doesn't crash for a very + large hash buffer. +*/ TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryDecryptHashForHugeHashBuffer) { uint32_t session_id = session_.session_id(); @@ -548,6 +608,7 @@ TEST_F(OEMCryptoMemoryLicenseTest, TestHugeLengthDoesNotCrashAPI(f, kCheckStatus); } +/** Test Decrypt fails gracefully for huge input. */ TEST_P(OEMCryptoSessionTestsDecryptTests, OEMCryptoMemoryDecryptCENCForHugeNumberOfSubSamples) { auto oemcrypto_function = [&](size_t number_of_subsamples) { @@ -575,6 +636,7 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, MakeBuffers(); } +/** Test Decrypt fails gracefully for huge input. */ TEST_P(OEMCryptoSessionTestsDecryptTests, OEMCryptoMemoryDecryptCENCForHugeNumberOfSamples) { auto oemcrypto_function = [&](size_t number_of_samples) { @@ -604,8 +666,16 @@ TEST_P(OEMCryptoSessionTestsDecryptTests, MakeBuffers(); } +/** Test that OEMCrypto_LoadProvisioning fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeSignatureLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } auto oemcrypto_function = [&](size_t signature_size) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); @@ -636,8 +706,16 @@ TEST_F(OEMCryptoLoadsCertificate, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } +/** Test that OEMCrypto_LoadProvisioning fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeWrappedRsaKeyLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } auto oemcrypto_function = [&](size_t buffer_length) { Session s; ProvisioningRoundTrip provisioning_messages(&s, encoded_rsa_key_); @@ -661,8 +739,16 @@ TEST_F(OEMCryptoLoadsCertificate, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that OEMCrypto_LoadDRMPrivateKey fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadDrmPrivateKeyForHugeWrappedRsaKeyLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); auto oemcrypto_function = [&](size_t wrapped_rsa_key_length) { Session s; @@ -682,9 +768,17 @@ TEST_F(OEMCryptoLoadsCertificate, kHugeInputBufferLength, !kCheckStatus); } +/** Test that OEMCrypto_LoadDRMPrivateKey fails gracefully on a huge buffer. + */ TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadDrmPrivateKeyForHugeWrappedRsaKeyLengthStartingFromLength1) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } ASSERT_NO_FATAL_FAILURE(CreateWrappedDRMKey()); auto oemcrypto_function = [&](size_t wrapped_rsa_key_length) { Session s; @@ -702,6 +796,8 @@ TEST_F( TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } +/** Test that OEMCrypto_LoadDRMPrivateKey fails gracefully on a huge buffer. + */ TEST_F(OEMCryptoUsesCertificate, OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeMacContext) { vector session_key; @@ -722,6 +818,8 @@ TEST_F(OEMCryptoUsesCertificate, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that OEMCrypto_DeriveKeysFromSessionKey fails gracefully on a huge + * buffer. */ TEST_F(OEMCryptoUsesCertificate, OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeEncContext) { vector session_key; @@ -742,6 +840,8 @@ TEST_F(OEMCryptoUsesCertificate, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that OEMCrypto_DeriveKeysFromSessionKey fails gracefully on a huge + * buffer. */ TEST_F(OEMCryptoUsesCertificate, OEMCryptoMemoryDeriveKeysFromSessionKeyForHugeEncSessionKey) { vector session_key; @@ -763,10 +863,18 @@ TEST_F(OEMCryptoUsesCertificate, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } +/** Test that OEMCrypto_GenerateRSASignature fails gracefully on a huge + * buffer. */ TEST_F(OEMCryptoLoadsCertificateAlternates, OEMCryptoMemoryGenerateRSASignatureForHugeBuffer) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } OEMCryptoResult sts; - LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); + LoadCastCertificateKey(false); // If the device is a cast receiver, then this scheme is required. if (global_features.cast_receiver) { ASSERT_TRUE(key_loaded_); @@ -782,6 +890,7 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, sts = OEMCrypto_GenerateRSASignature(s.session_id(), message_buffer.data(), message_buffer.size(), nullptr, &signature_length, kSign_PKCS1_Block1); + if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) return; ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); ASSERT_NE(static_cast(0), signature_length); vector signature(signature_length); @@ -797,9 +906,17 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, } } +/** Test that OEMCrypto_GenerateRSASignature fails gracefully on a huge + * buffer. */ TEST_F(OEMCryptoLoadsCertificateAlternates, OEMCryptoMemoryGenerateRSASignatureForHugeSignatureLength) { - LoadWithAllowedSchemes(kSign_PKCS1_Block1, false); + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } + LoadCastCertificateKey(false); // If the device is a cast receiver, then this scheme is required. if (global_features.cast_receiver) { ASSERT_TRUE(key_loaded_); @@ -823,6 +940,7 @@ TEST_F(OEMCryptoLoadsCertificateAlternates, } } +/** Test that GetKeyHandle fails gracefully on a huge buffer. */ TEST_P(OEMCryptoGenericCryptoTest, OEMCryptoMemorySelectKeyForHugeKeyIdLength) { EncryptAndLoadKeys(); OEMCrypto_SESSION session_id = session_.session_id(); @@ -835,6 +953,7 @@ TEST_P(OEMCryptoGenericCryptoTest, OEMCryptoMemorySelectKeyForHugeKeyIdLength) { TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } +/** Test that API fails gracefully on a huge buffer. */ TEST_P(OEMCryptoGenericCryptoTest, OEMCryptoMemoryGenericKeyEncryptForHugeBuffer) { EncryptAndLoadKeys(); @@ -859,6 +978,7 @@ TEST_P(OEMCryptoGenericCryptoTest, kCheckStatus); } +/** Test that API fails gracefully on a huge buffer. */ TEST_P(OEMCryptoGenericCryptoTest, OEMCryptoMemoryGenericKeyDecryptForHugeBuffer) { EncryptAndLoadKeys(); @@ -884,6 +1004,7 @@ TEST_P(OEMCryptoGenericCryptoTest, kCheckStatus); } +/** Test that API fails gracefully on a huge buffer. */ TEST_P(OEMCryptoGenericCryptoTest, OEMCryptoMemoryGenericKeySignForHugeBuffer) { EncryptAndLoadKeys(); unsigned int key_index = 2; @@ -908,6 +1029,7 @@ TEST_P(OEMCryptoGenericCryptoTest, OEMCryptoMemoryGenericKeySignForHugeBuffer) { TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that API fails gracefully on a huge buffer. */ TEST_P(OEMCryptoGenericCryptoTest, OEMCryptoMemoryGenericKeySignForHugeSignatureLength) { EncryptAndLoadKeys(); @@ -934,6 +1056,7 @@ TEST_P(OEMCryptoGenericCryptoTest, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that API fails gracefully on a huge buffer. */ TEST_P(OEMCryptoGenericCryptoTest, OEMCryptoMemoryGenericKeyVerifyForHugeBuffer) { EncryptAndLoadKeys(); @@ -956,6 +1079,7 @@ TEST_P(OEMCryptoGenericCryptoTest, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that API fails gracefully on a huge buffer. */ TEST_P(OEMCryptoGenericCryptoTest, OEMCryptoMemoryGenericKeyVerifyForHugeSignatureLength) { EncryptAndLoadKeys(); @@ -982,6 +1106,7 @@ TEST_P(OEMCryptoGenericCryptoTest, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that API fails gracefully on a huge buffer. */ TEST_P(OEMCryptoUsageTableTest, OEMCryptoMemoryUpdateUsageEntryForHugeHeaderBuffer) { if (!wvoec::global_features.usage_table) { @@ -1012,6 +1137,7 @@ TEST_P(OEMCryptoUsageTableTest, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that API fails gracefully on a huge buffer. */ TEST_P(OEMCryptoUsageTableTest, OEMCryptoMemoryUpdateUsageEntryForHugeUsageEntryBuffer) { if (!wvoec::global_features.usage_table) { @@ -1039,6 +1165,7 @@ TEST_P(OEMCryptoUsageTableTest, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that API fails gracefully on a huge buffer. */ TEST_P(OEMCryptoUsageTableTest, OEMCryptoMemoryDeactivateUsageEntryForHugePstBuffer) { if (!wvoec::global_features.usage_table) { @@ -1061,6 +1188,7 @@ TEST_P(OEMCryptoUsageTableTest, kCheckStatus); } +/** Test that API fails gracefully on a huge buffer. */ TEST_P(OEMCryptoUsageTableTest, OEMCryptoMemoryLoadUsageTableHeaderForHugeHeader) { if (!wvoec::global_features.usage_table) { @@ -1087,6 +1215,7 @@ TEST_P(OEMCryptoUsageTableTest, kHugeInputBufferLength, !kCheckStatus); } +/** Test that API fails gracefully on a huge buffer. */ TEST_P( OEMCryptoUsageTableTest, OEMCryptoMemoryLoadUsageTableHeaderForHugeHeaderStartingHeaderLengthFrom1) { @@ -1107,6 +1236,7 @@ TEST_P( TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } +/** Test that API fails gracefully on a huge buffer. */ TEST_P(OEMCryptoUsageTableTest, OEMCryptoMemoryLoadUsageEntryForHugeUsageEntryBuffer) { if (!wvoec::global_features.usage_table) { @@ -1138,6 +1268,7 @@ TEST_P(OEMCryptoUsageTableTest, TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } +/** Test that API fails gracefully on a huge buffer. */ TEST_P(OEMCryptoUsageTableTest, OEMCryptoMemoryReportUsageForHugeReportBuffer) { if (!wvoec::global_features.usage_table) { GTEST_SKIP() << "Usage tables are not supported."; @@ -1167,6 +1298,7 @@ TEST_P(OEMCryptoUsageTableTest, OEMCryptoMemoryReportUsageForHugeReportBuffer) { TestHugeLengthDoesNotCrashAPI(oemcrypto_function, kCheckStatus); } +/** Test that API fails gracefully on a huge buffer. */ TEST_P(OEMCryptoUsageTableTest, OEMCryptoMemoryReportUsageForHugePstBuffer) { if (!wvoec::global_features.usage_table) { GTEST_SKIP() << "Usage tables are not supported."; @@ -1189,6 +1321,7 @@ TEST_P(OEMCryptoUsageTableTest, OEMCryptoMemoryReportUsageForHugePstBuffer) { TestHugeLengthDoesNotCrashAPI(oemcrypto_function, !kCheckStatus); } +/** Test that API fails gracefully on a huge buffer. */ TEST_P(OEMCryptoUsageTableTest, OEMCryptoMemoryShrinkUsageTableHeaderForHugeHeaderBufferLength) { if (!wvoec::global_features.usage_table) { diff --git a/oemcrypto/test/oemcrypto_test.cpp b/oemcrypto/test/oemcrypto_test.cpp index 4452e939..1b6d5b48 100644 --- a/oemcrypto/test/oemcrypto_test.cpp +++ b/oemcrypto/test/oemcrypto_test.cpp @@ -43,6 +43,11 @@ * * @defgroup security Security Tests * Buffer overflow tests, off-by-one tests, and other security tests. + * + * The way the huge buffer tests work is to create a large buffer and then call + * the API. The test then loops and doubles the buffer until the API returns an + * error. An error is considered a passing test. We expect OEMCrypto to fail + * gracefully on a huge buffer rather than crashing. */ #include @@ -443,6 +448,9 @@ TEST_P(OEMCryptoEntitlementLicenseTest, */ TEST_P(OEMCryptoEntitlementLicenseTest, LoadEntitlementKeysOemcryptoSessionAPI17) { + if (!global_features.supports_cas) { + GTEST_SKIP() << "OEMCrypto does not support CAS"; + } LoadEntitlementLicense(); uint32_t key_session_id = 0; ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( @@ -485,6 +493,7 @@ INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoEntitlementLicenseTest, /// @addtogroup security /// @{ +/** Test that LoadEntitledContentKeys fails gracefully on huge buffer. */ TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForHugeSubstringContentKeyIdLength) { TestLoadEntitledKeysForHugeBufferLengths( @@ -495,6 +504,7 @@ TEST_F(OEMCryptoMemoryLicenseTest, !kCheckStatus); } +/** Test that LoadEntitledContentKeys fails gracefully on huge buffer. */ TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForHugeSubstringContentKeyIdOffset) { TestLoadEntitledKeysForHugeBufferLengths( @@ -505,6 +515,7 @@ TEST_F(OEMCryptoMemoryLicenseTest, !kCheckStatus); } +/** Test that LoadEntitledContentKeys fails gracefully on huge buffer. */ TEST_F( OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringContentKeyIdLength) { @@ -515,6 +526,7 @@ TEST_F( ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys()); } +/** Test LoadEntitledContentKeys rejects out of range substring. */ TEST_F( OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringContentKeyIdOffset) { @@ -525,6 +537,7 @@ TEST_F( ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys()); } +/** Test that LoadEntitledContentKeys fails gracefully on huge substring. */ TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForHugeSubstringEntitlementKeyIdLength) { TestLoadEntitledKeysForHugeBufferLengths( @@ -535,6 +548,7 @@ TEST_F(OEMCryptoMemoryLicenseTest, !kCheckStatus); } +/** Test LoadEntitledContentKeys rejects out of range substring. */ TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForHugeSubstringEntitlementKeyIdOffset) { TestLoadEntitledKeysForHugeBufferLengths( @@ -545,6 +559,7 @@ TEST_F(OEMCryptoMemoryLicenseTest, !kCheckStatus); } +/** Test LoadEntitledContentKeys rejects out of range substring. */ TEST_F( OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringEntitlementKeyIdLength) { @@ -555,6 +570,7 @@ TEST_F( ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys()); } +/** Test LoadEntitledContentKeys rejects out of range substring. */ TEST_F( OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringEntitlementKeyIdOffset) { @@ -565,6 +581,7 @@ TEST_F( ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys()); } +/** Test LoadEntitledContentKeys rejects out of range substring. */ TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForHugeSubstringContentKeyDataIvLength) { TestLoadEntitledKeysForHugeBufferLengths( @@ -575,6 +592,7 @@ TEST_F(OEMCryptoMemoryLicenseTest, !kCheckStatus); } +/** Test LoadEntitledContentKeys rejects out of range substring. */ TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForHugeSubstringContentKeyDataIvOffset) { TestLoadEntitledKeysForHugeBufferLengths( @@ -585,6 +603,7 @@ TEST_F(OEMCryptoMemoryLicenseTest, !kCheckStatus); } +/** Test LoadEntitledContentKeys rejects out of range substring. */ TEST_F( OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringContentKeyDataIvLength) { @@ -595,6 +614,7 @@ TEST_F( ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys()); } +/** Test LoadEntitledContentKeys rejects out of range substring. */ TEST_F( OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringContentKeyDataIvOffset) { @@ -605,6 +625,7 @@ TEST_F( ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys()); } +/** Test LoadEntitledContentKeys rejects out of range substring. */ TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForHugeSubstringContentKeyDataLength) { TestLoadEntitledKeysForHugeBufferLengths( @@ -615,6 +636,7 @@ TEST_F(OEMCryptoMemoryLicenseTest, !kCheckStatus); } +/** Test LoadEntitledContentKeys rejects out of range substring. */ TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForHugeSubstringContentKeyDataOffset) { TestLoadEntitledKeysForHugeBufferLengths( @@ -625,6 +647,7 @@ TEST_F(OEMCryptoMemoryLicenseTest, !kCheckStatus); } +/** Test LoadEntitledContentKeys rejects out of range substring. */ TEST_F( OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringContentKeyDataLength) { @@ -635,6 +658,7 @@ TEST_F( ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys()); } +/** Test LoadEntitledContentKeys rejects out of range substring. */ TEST_F( OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForOutOfRangeSubstringContentKeyDataOffset) { @@ -645,6 +669,7 @@ TEST_F( ASSERT_NE(OEMCrypto_SUCCESS, entitled_message_.LoadKeys()); } +/** Test LoadEntitledContentKeys rejects out of range substring. */ TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForHugeEntitlementKeyIdLength) { TestLoadEntitledKeysForHugeBufferLengths( @@ -655,6 +680,7 @@ TEST_F(OEMCryptoMemoryLicenseTest, !kCheckStatus); } +/** Test LoadEntitledContentKeys rejects out of range substring. */ TEST_F(OEMCryptoMemoryLicenseTest, OEMCryptoMemoryLoadEntitledKeysForHugeContentKeyIdLength) { TestLoadEntitledKeysForHugeBufferLengths( @@ -724,6 +750,9 @@ TEST_P(OEMCryptoLicenseTest, SelectKeyEntitledKeyNotThereAPI17) { * id. */ TEST_P(OEMCryptoLicenseTest, SelectKeyEntitlementKeyAPI17) { + if (!global_features.supports_cas) { + GTEST_SKIP() << "OEMCrypto does not support CAS"; + } license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); @@ -799,6 +828,9 @@ TEST_P(OEMCryptoLicenseTest, // This verifies that multiple entitled key sessions can be created. They can // load and select keys independently. TEST_P(OEMCryptoLicenseTest, EntitledKeySessionMultipleKeySessionsAPI17) { + if (!global_features.supports_cas) { + GTEST_SKIP() << "OEMCrypto does not support CAS"; + } license_messages_.set_license_type(OEMCrypto_EntitlementLicense); ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); @@ -826,9 +858,7 @@ TEST_P(OEMCryptoLicenseTest, EntitledKeySessionMultipleKeySessionsAPI17) { session_.session_id(), &key_session_id_2); // For DRM, but not for CAS, we allow there to be only a single entitled // session. - if (!global_features.supports_cas && - (key_session_id_2 == key_session_id_1 || - status == OEMCrypto_ERROR_TOO_MANY_SESSIONS)) { + if (status == OEMCrypto_ERROR_TOO_MANY_SESSIONS) { GTEST_SKIP() << "Skipping test because multiple entitled sessions not supported."; } @@ -1018,6 +1048,7 @@ TEST_P(OEMCryptoEntitlementLicenseTest, ReassociateEntitledKeySessionAPI17) { /// @addtogroup security /// @{ +/** Test that LoadLicense fails gracefully on huge buffer. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyIdLength) { TestLoadLicenseForHugeBufferLengths( @@ -1028,6 +1059,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyIdOffset) { TestLoadLicenseForHugeBufferLengths( @@ -1037,6 +1069,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyIdLength) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( @@ -1046,6 +1079,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyIdOffset) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( @@ -1055,6 +1089,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyDataIvLength) { TestLoadLicenseForHugeBufferLengths( @@ -1065,6 +1100,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyDataIvOffset) { TestLoadLicenseForHugeBufferLengths( @@ -1075,6 +1111,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyDataIvLength) { @@ -1086,6 +1123,7 @@ TEST_P( }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyDataIvOffset) { @@ -1097,6 +1135,7 @@ TEST_P( }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyDataLength) { TestLoadLicenseForHugeBufferLengths( @@ -1107,6 +1146,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyDataOffset) { TestLoadLicenseForHugeBufferLengths( @@ -1116,6 +1156,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyDataLength) { @@ -1127,6 +1168,7 @@ TEST_P( }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyDataOffset) { @@ -1138,6 +1180,7 @@ TEST_P( }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyControlIvLength) { @@ -1149,6 +1192,7 @@ TEST_P( !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyControlIvOffset) { @@ -1160,6 +1204,7 @@ TEST_P( !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyControlIvLengthAPI16) { @@ -1172,6 +1217,7 @@ TEST_P( }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyControlIvOffset) { @@ -1184,6 +1230,7 @@ TEST_P( }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyControlLength) { TestLoadLicenseForHugeBufferLengths( @@ -1194,6 +1241,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringKeyControlOffset) { TestLoadLicenseForHugeBufferLengths( @@ -1204,6 +1252,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyControlLengthAPI16) { @@ -1215,6 +1264,7 @@ TEST_P( }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringKeyControlOffset) { @@ -1226,6 +1276,7 @@ TEST_P( }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringEncMacKeyIvLength) { TestLoadLicenseForHugeBufferLengths( @@ -1235,6 +1286,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringEncMacKeyIvOffset) { TestLoadLicenseForHugeBufferLengths( @@ -1244,6 +1296,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringEncMacKeyIvLength) { @@ -1256,6 +1309,7 @@ TEST_P( }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringEncMacKeyIvOffset) { @@ -1268,6 +1322,7 @@ TEST_P( }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringEncMacKeyLength) { TestLoadLicenseForHugeBufferLengths( @@ -1277,6 +1332,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringEncMacKeyOffset) { TestLoadLicenseForHugeBufferLengths( @@ -1286,6 +1342,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringEncMacKeyLength) { @@ -1296,6 +1353,7 @@ TEST_P( }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringEncMacKeyOffset) { @@ -1306,6 +1364,7 @@ TEST_P( }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringPstLength) { TestLoadLicenseForHugeBufferLengths( @@ -1315,6 +1374,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringPstOffset) { TestLoadLicenseForHugeBufferLengths( @@ -1324,6 +1384,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringPstLength) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( @@ -1333,6 +1394,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringPstOffset) { TestLoadLicenseForOutOfRangeSubStringOffSetAndLengths( @@ -1343,6 +1405,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringSrmRestrictionDataLength) { @@ -1353,6 +1416,7 @@ TEST_P( !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageSubstringSrmRestrictionDataOffset) { @@ -1363,6 +1427,7 @@ TEST_P( !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringSrmRestrictionDataLength) { @@ -1375,6 +1440,7 @@ TEST_P( }); } +/** Test that LoadLicense fails gracefully on out of range substring. */ TEST_P( OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForOutOfRangeCoreMessageSubstringSrmRestrictionDataOffset) { @@ -1387,6 +1453,7 @@ TEST_P( }); } +/** Test that LoadLicense fails gracefully on huge buffer. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeResponseLength) { TestLoadLicenseForHugeBufferLengths( @@ -1396,6 +1463,7 @@ TEST_P(OEMCryptoLicenseOverflowTest, !kCheckStatus, !kUpdateCoreMessageSubstringValues); } +/** Test that LoadLicense fails gracefully on huge buffer. */ TEST_P(OEMCryptoLicenseOverflowTest, OEMCryptoMemoryLoadLicenseForHugeCoreMessageLength) { TestLoadLicenseForHugeBufferLengths( @@ -1418,8 +1486,15 @@ INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoLicenseOverflowTest, /// @addtogroup security /// @{ +/** Test that LoadProvisioning fails gracefully on huge buffer. */ TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeResponseLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t message_size, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->set_message_size(message_size); @@ -1427,8 +1502,15 @@ TEST_F(OEMCryptoLoadsCertificate, !kCheckStatus, !kUpdateCoreMessageSubstringValues); } +/** Test that LoadProvisioning fails gracefully on huge buffer. */ TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t message_size, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->set_core_message_size(message_size); @@ -1436,8 +1518,15 @@ TEST_F(OEMCryptoLoadsCertificate, !kCheckStatus, !kUpdateCoreMessageSubstringValues); } +/** Test that LoadProvisioning fails gracefully on huge buffer. */ TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t length, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().enc_private_key.length = length; @@ -1445,8 +1534,15 @@ TEST_F(OEMCryptoLoadsCertificate, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadProvisioning fails gracefully on huge buffer. */ TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyOffset) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t offset, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().enc_private_key.offset = offset; @@ -1454,9 +1550,16 @@ TEST_F(OEMCryptoLoadsCertificate, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadProvisioning fails gracefully on out of range substring. */ TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths( [](size_t response_message_length, ProvisioningRoundTrip* provisioning_messages) { @@ -1467,9 +1570,16 @@ TEST_F( }); } +/** Test that LoadProvisioning fails gracefully on out of range substring. */ TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyOffset) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths( [](size_t response_message_length, ProvisioningRoundTrip* provisioning_messages) { @@ -1480,8 +1590,15 @@ TEST_F( }); } +/** Test that LoadProvisioning fails gracefully on out of range substring. */ TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyIvLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t length, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().enc_private_key_iv.length = @@ -1490,8 +1607,15 @@ TEST_F(OEMCryptoLoadsCertificate, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadProvisioning fails gracefully on out of range substring. */ TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncPrivateKeyIvOffset) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t offset, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().enc_private_key_iv.offset = @@ -1500,9 +1624,16 @@ TEST_F(OEMCryptoLoadsCertificate, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadProvisioning fails gracefully on out of range substring. */ TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyIvLengthAPI16) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths( [](size_t response_message_length, ProvisioningRoundTrip* provisioning_messages) { @@ -1513,9 +1644,16 @@ TEST_F( }); } +/** Test that LoadProvisioning fails gracefully on out of range substring. */ TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncPrivateKeyIvOffset) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForOutOfRangeSubstringOffsetAndLengths( [](size_t response_message_length, ProvisioningRoundTrip* provisioning_messages) { @@ -1526,8 +1664,15 @@ TEST_F( }); } +/** Test that LoadProvisioning fails gracefully on out of range substring. */ TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncMessageKeyLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t length, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().encrypted_message_key.length = @@ -1536,8 +1681,15 @@ TEST_F(OEMCryptoLoadsCertificate, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadProvisioning fails gracefully on out of range substring. */ TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForHugeCoreMessageEncMessageKeyOffset) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestLoadProvisioningForHugeBufferLengths( [](size_t offset, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->core_response().encrypted_message_key.offset = @@ -1546,9 +1698,16 @@ TEST_F(OEMCryptoLoadsCertificate, !kCheckStatus, kUpdateCoreMessageSubstringValues); } +/** Test that LoadProvisioning fails gracefully on out of range substring. */ TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncMessageKeyLengthProv30) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } if (global_features.provisioning_method != OEMCrypto_OEMCertificate) { GTEST_SKIP() << "Test for Prov 3.0 devices only."; } @@ -1562,9 +1721,16 @@ TEST_F( }); } +/** Test that LoadProvisioning fails gracefully on out of range substring. */ TEST_F( OEMCryptoLoadsCertificate, OEMCryptoMemoryLoadProvisioningForOutOfRangeCoreMessageEncMessageKeyOffsetProv30) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } if (global_features.provisioning_method != OEMCrypto_OEMCertificate) { GTEST_SKIP() << "Test for Prov 3.0 devices only."; } @@ -1583,8 +1749,17 @@ TEST_F( /// @addtogroup security /// @{ +/** Test that OEMCrypto_PrepAndSignProvisioningRequest fails gracefully on a + * huge buffer. + */ TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryPrepareProvisioningRequestForHugeRequestMessageLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestPrepareProvisioningRequestForHugeBufferLengths( [](size_t message_size, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->set_message_size(message_size); @@ -1592,8 +1767,17 @@ TEST_F(OEMCryptoLoadsCertificate, kCheckStatus); } +/** Test that OEMCrypto_PrepAndSignProvisioningRequest fails gracefully on a + * huge buffer. + */ TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryPrepareProvisioningRequestForHugeSignatureLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestPrepareProvisioningRequestForHugeBufferLengths( [](size_t message_size, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->set_request_signature_size(message_size); @@ -1601,8 +1785,17 @@ TEST_F(OEMCryptoLoadsCertificate, !kCheckStatus); } +/** Test that OEMCrypto_PrepAndSignProvisioningRequest fails gracefully on a + * huge buffer. + */ TEST_F(OEMCryptoLoadsCertificate, OEMCryptoMemoryPrepareProvisioningRequestForHugeCoreMessageLength) { + // TODO(b/197141970): Need to revisit OEMCryptoLoadsCert* tests for + // provisioning 4. Disabled here temporarily. + if (!global_features.loads_certificate || + global_features.provisioning_method == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } TestPrepareProvisioningRequestForHugeBufferLengths( [](size_t message_size, ProvisioningRoundTrip* provisioning_messages) { provisioning_messages->set_core_message_size(message_size); @@ -1616,4 +1809,90 @@ TEST_F(OEMCryptoLoadsCertificate, /// @{ /// @} + +#ifdef CAS_TEST + +# include "tuner_hal.h" + +class OEMCryptoCasDemoTest : public OEMCryptoEntitlementLicenseTest {}; + +TEST_P(OEMCryptoCasDemoTest, BasicFlow) { + // License contains entitlement keys, function reused from + // OEMCryptoEntitlementLicenseTest + LoadEntitlementLicense(); + uint32_t key_session_id = 0; + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_CreateEntitledKeySession( + session_.session_id(), &key_session_id)); + + EntitledMessage entitled_message(&license_messages_); + + // Randomly generate entitled content keys + entitled_message.FillKeyArray(); + if (session_.session_id() == key_session_id) { + GTEST_SKIP() + << "Skipping test because entitled and entitlement sessions are both " + << key_session_id << "."; + } + entitled_message.SetEntitledKeySession(key_session_id); + + // Encrypt and load 0th key (even key) into OEMCrypto + ASSERT_NO_FATAL_FAILURE(entitled_message.LoadCasKeys( + /*load_even=*/true, /*load_odd=*/false, OEMCrypto_SUCCESS)); + + // + // Perform DecryptCTR() but for CAS + // + vector unencrypted_data(256, 0); + vector encrypted_data(256, 0); + vector output_buffer(256, 0); + unencrypted_data.resize(encrypted_data.size()); + output_buffer.resize(encrypted_data.size()); + + OEMCrypto_SampleDescription sample_description; + OEMCrypto_SubSampleDescription subsample_description; + GenerateSimpleSampleDescription(encrypted_data, output_buffer, + &sample_description, &subsample_description); + + // Use 0th entitled content key and IV to encrypt test data + EncryptCTR(unencrypted_data, + entitled_message.entitled_key_data()->content_key_data, + entitled_message.entitled_key_data()->content_iv, &encrypted_data); + + // Assume 0,0 pattern for CTR example + OEMCrypto_CENCEncryptPatternDesc pattern = {0, 0}; + + // Demo only -- copy IV into sample description so we can use + // WTPI_DecryptSample() in the Tuner decrypt impl. A real implementation would + // use the IV from the entitled content key, but the demo relies on the + // existing decrypt which uses SampleDescription IV. + memcpy(sample_description.iv, + entitled_message.entitled_key_data()->content_iv, 16); + + // Get key token to send to Tuner for decrypt + std::vector key_token; + size_t key_token_length = key_token.size(); + OEMCryptoResult res = OEMCrypto_GetOEMKeyToken( + key_session_id, key_token.data(), &key_token_length); + if (res == OEMCrypto_ERROR_SHORT_BUFFER) { + key_token.resize(key_token_length); + res = OEMCrypto_GetOEMKeyToken(key_session_id, key_token.data(), + &key_token_length); + } + ASSERT_EQ(OEMCrypto_SUCCESS, res); + + // Decrypt the data + ASSERT_EQ(TUNER_HAL_SUCCESS, + TunerHal_Decrypt(key_token.data(), key_token_length, + TunerHal_KeyParityType_EvenKey, + &sample_description, // an array of samples. + 1, // the number of samples. + &pattern)); + + ASSERT_EQ(unencrypted_data, output_buffer); +} + +INSTANTIATE_TEST_SUITE_P(TestAll, OEMCryptoCasDemoTest, + Range(kCoreMessagesAPI, kCurrentAPI + 1)); + +#endif } // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_test_android.cpp b/oemcrypto/test/oemcrypto_test_android.cpp index 4e3ab9b9..4337f756 100644 --- a/oemcrypto/test/oemcrypto_test_android.cpp +++ b/oemcrypto/test/oemcrypto_test_android.cpp @@ -27,6 +27,9 @@ class OEMCryptoAndroidLMPTest : public ::testing::Test { void SetUp() override { OEMCrypto_SetSandbox(kTestSandbox, sizeof(kTestSandbox)); ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_Initialize()); + if (OEMCrypto_GetProvisioningMethod() == OEMCrypto_BootCertificateChain) { + GTEST_SKIP() << "Test for non Prov 4.0 devices only."; + } OEMCrypto_SetMaxAPIVersion(kCurrentAPI); OEMCrypto_EnterTestMode(); } @@ -36,6 +39,10 @@ class OEMCryptoAndroidLMPTest : public ::testing::Test { // Android devices must have a keybox, or use provisioning 3.0. TEST_F(OEMCryptoAndroidLMPTest, GetKeyDataImplemented) { + if (global_features.provisioning_method != OEMCrypto_Keybox && + global_features.provisioning_method != OEMCrypto_OEMCertificate) { + GTEST_SKIP() << "Test for Prov 2.0 and 3.0 devices only."; + } uint8_t key_data[256]; size_t key_data_len = sizeof(key_data); if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) { @@ -59,6 +66,9 @@ TEST_F(OEMCryptoAndroidLMPTest, MinVersionNumber9) { } TEST_F(OEMCryptoAndroidLMPTest, ValidKeyboxTest) { + if (global_features.provisioning_method != OEMCrypto_Keybox) { + GTEST_SKIP() << "Test for Prov 2.0 devices only."; + } ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_IsKeyboxValid()); } @@ -112,6 +122,9 @@ TEST_F(OEMCryptoAndroidMNCTest, MinVersionNumber10) { // Android devices using Provisioning 2.0 must be able to load a test keybox. // If they are not using Provisioning 2.0, then they must use Provisioning 3.0. TEST_F(OEMCryptoAndroidMNCTest, LoadsTestKeyboxImplemented) { + if (global_features.provisioning_method != OEMCrypto_Keybox) { + GTEST_SKIP() << "Test for Prov 2.0 devices only."; + } if (OEMCrypto_Keybox == OEMCrypto_GetProvisioningMethod()) { ASSERT_EQ( OEMCrypto_SUCCESS, diff --git a/oemcrypto/test/oemcrypto_usage_table_test.cpp b/oemcrypto/test/oemcrypto_usage_table_test.cpp index d2ac143f..40491b42 100644 --- a/oemcrypto/test/oemcrypto_usage_table_test.cpp +++ b/oemcrypto/test/oemcrypto_usage_table_test.cpp @@ -175,9 +175,15 @@ TEST_F(OEMCryptoSessionTests, MasterGeneration_IncrementCounterAPI18) { ASSERT_TRUE(prov_count2 == prov_count1); ASSERT_TRUE(lic_count2 > lic_count1); - ASSERT_TRUE(decrypt_count2 > decrypt_count1); ASSERT_TRUE(master_generation_number2 > master_generation_number1); + + // Log if decrypt counter hasn't gone up. Not a hard requirement, so don't + // assert for it. + if (decrypt_count2 <= decrypt_count1) { + LOGE("Decrypt count did not increase."); + } } + TEST_P(OEMCryptoUsageTableTest, OEMCryptoMemoryLoadUsageEntryForHugeInvalidUsageEntryNumber) { LicenseWithUsageEntry entry; @@ -440,6 +446,9 @@ TEST_P(OEMCryptoUsageTableTest, LoadEntryInMultipleSessions) { // Test generic encrypt when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoEncrypt) { + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); @@ -479,6 +488,9 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoEncrypt) { // Test generic decrypt when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoDecrypt) { + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); @@ -516,6 +528,9 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoDecrypt) { // Test generic sign when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoSign) { + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); @@ -565,6 +580,9 @@ TEST_P(OEMCryptoUsageTableTest, GenericCryptoSign) { // Test generic verify when the license uses a PST. TEST_P(OEMCryptoUsageTableTest, GenericCryptoVerify) { + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } LicenseWithUsageEntry entry; entry.license_messages().set_api_version(license_api_version_); entry.set_generic_crypto(true); @@ -983,6 +1001,39 @@ TEST_P(OEMCryptoUsageTableDefragTest, MoveUsageEntries) { FailReloadLicense(&entries[3], OEMCrypto_ERROR_UNKNOWN_FAILURE)); } +TEST_P(OEMCryptoUsageTableDefragTest, MakeAndMoveEntry) { + // 1. Make an entry then close. + LicenseWithUsageEntry entry; + ASSERT_NO_FATAL_FAILURE(entry.set_pst("pst 0")); + ASSERT_NO_FATAL_FAILURE(entry.MakeOfflineAndClose(this)); + ASSERT_NO_FATAL_FAILURE(entry.OpenAndReload(this)); + ASSERT_NO_FATAL_FAILURE(entry.session().close()); + + // 2. Make an entry then immediately move it into the previous slot. + // Not using helper functions because they shoehorn the session state into + // a limited set of possibilities. We want to create the specific case of + // immediately moving a newly created entry. + + // Like LicenseWithUsageEntry::MakeAndLoad() but stop after creating the new + // usage entry. + Session session; + ASSERT_NO_FATAL_FAILURE(session.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestDrmKey(&session)); + LicenseRoundTrip license_messages_(&session); + license_messages_.set_control(wvoec::kControlNonceOrEntry); + ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); + ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); + OEMCryptoResult result; + ASSERT_NO_FATAL_FAILURE(session.CreateNewUsageEntry(&result)); + + // Not the same as Session::MoveUsageEntry, which opens and closes a session + // around the move operation. We just want to call MoveEntry on the current + // state. + ASSERT_EQ(OEMCrypto_SUCCESS, OEMCrypto_MoveEntry(session.session_id(), 0)); + ASSERT_NO_FATAL_FAILURE(session.close()); +} + // A usage table entry cannot be moved into an entry where an open session is // currently using the entry. TEST_P(OEMCryptoUsageTableDefragTest, MoveUsageEntriesToOpenSession) { @@ -1700,4 +1751,4 @@ INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoUsageTableDefragTest, INSTANTIATE_TEST_SUITE_P(TestAPI16, OEMCryptoUsageTableTestWallClock, Values(kCurrentAPI)); -} // namespace wvoec \ No newline at end of file +} // namespace wvoec diff --git a/oemcrypto/test/oemcrypto_usage_table_test.h b/oemcrypto/test/oemcrypto_usage_table_test.h index ce5d7ab9..b36404a5 100644 --- a/oemcrypto/test/oemcrypto_usage_table_test.h +++ b/oemcrypto/test/oemcrypto_usage_table_test.h @@ -28,6 +28,9 @@ class OEMCryptoGenericCryptoTest : public OEMCryptoRefreshTest { void SetUp() override { OEMCryptoRefreshTest::SetUp(); + if (!wvoec::global_features.generic_crypto) { + GTEST_SKIP() << "Test for devices with generic crypto API only"; + } ASSERT_NO_FATAL_FAILURE(license_messages_.SignAndVerifyRequest()); ASSERT_NO_FATAL_FAILURE( license_messages_.CreateResponseWithGenericCryptoKeys()); diff --git a/third_party/boringssl/boringssl.gyp b/third_party/boringssl/boringssl.gyp index 4268292a..8f04b507 100644 --- a/third_party/boringssl/boringssl.gyp +++ b/third_party/boringssl/boringssl.gyp @@ -155,35 +155,26 @@ }, ], }], - ['OS=="mac"', { + ['OS in {"ios","mac"}', { 'conditions': [ ['asm_target_arch=="x86"', { 'sources': [ - '<@(boringssl_mac_x86_files)', + '<@(boringssl_apple_x86_files)', ], }], ['asm_target_arch in {"x86-64", "x64"}', { 'sources': [ - '<@(boringssl_mac_x86_64_files)', - ], - }], - ], - }], - ['OS=="ios"', { - 'conditions': [ - ['asm_target_arch in {"x86-64", "x64"}', { - 'sources': [ - '<@(boringssl_mac_x86_64_files)', + '<@(boringssl_apple_x86_64_files)', ], }], ['asm_target_arch=="arm"', { 'sources': [ - '<@(boringssl_ios_arm_files)', + '<@(boringssl_apple_arm_files)', ], }], ['asm_target_arch=="arm64"', { 'sources': [ - '<@(boringssl_ios_aarch64_files)', + '<@(boringssl_apple_aarch64_files)', ], }], ], diff --git a/third_party/generate_proto_cc.gyp b/third_party/generate_proto_cc.gyp new file mode 100644 index 00000000..32d20550 --- /dev/null +++ b/third_party/generate_proto_cc.gyp @@ -0,0 +1,43 @@ +{ + 'includes': [ + '../cdm/platform_properties.gypi', + ], + 'targets': [ + { + 'toolsets' : [ 'target' ], + 'target_name': 'generate_license_protocol', + 'type': 'none', + 'standalone_static_library': 0, + 'hard_dependency': 1, + 'includes': [ 'protoc.gypi' ], + 'sources': [ '<(DEPTH)/core/src/license_protocol.proto' ], + 'variables': { + 'proto_in_dir': '<(DEPTH)/core/src', + }, + }, + { + 'toolsets' : [ 'target' ], + 'target_name': 'generate_device_files', + 'type': 'none', + 'standalone_static_library': 0, + 'hard_dependency': 1, + 'includes': [ 'protoc.gypi' ], + 'sources': [ '<(DEPTH)/core/src/device_files.proto' ], + 'variables': { + 'proto_in_dir': '<(DEPTH)/core/src', + }, + }, + { + 'toolsets' : [ 'target' ], + 'target_name': 'generate_metrics_proto', + 'type': 'none', + 'standalone_static_library': 0, + 'hard_dependency': 1, + 'includes': [ 'protoc.gypi' ], + 'sources': [ '<(DEPTH)/metrics/src/wv_metrics.proto' ], + 'variables': { + 'proto_in_dir': '<(DEPTH)/metrics/src', + }, + }, + ] +} diff --git a/third_party/protobuf.gyp b/third_party/protobuf.gyp index 1c14e0cb..386e1961 100644 --- a/third_party/protobuf.gyp +++ b/third_party/protobuf.gyp @@ -278,6 +278,8 @@ ], 'xcode_settings': { 'OTHER_CFLAGS': ['-w'], + 'PRODUCT_BUNDLE_IDENTIFIER': 'com.google.protoc', + 'PROVISIONING_PROFILE_SPECIFIER': '', 'USE_HEADERMAP': 'NO', }, 'msvs_settings': {