From 8471679a9f30b269bdf91376aeed84c35c330082 Mon Sep 17 00:00:00 2001 From: Hua Wu Date: Wed, 24 Apr 2024 10:03:33 -0700 Subject: [PATCH] Update partner repo This change includes: - update zlib - add helper to initialize whitebox on another thread - provider key on security level SW_SECURE_CRYPTO above --- whitebox/WORKSPACE | 6 +- whitebox/api/BUILD | 1 + whitebox/api/license_whitebox_decrypt_test.cc | 8 +- whitebox/api/license_whitebox_main.cc | 30 +++++ whitebox/api/test_license_builder.cc | 22 +++- whitebox/crypto_utils/whitebox_init_helper.h | 105 ++++++++++++++++++ whitebox/reference/impl/license_parser.cc | 10 +- whitebox/reference/impl/license_parser.h | 20 ++-- whitebox/reference/impl/odk_license_parser.cc | 2 +- .../reference/impl/protobuf_license_parser.cc | 2 +- 10 files changed, 184 insertions(+), 22 deletions(-) create mode 100644 whitebox/crypto_utils/whitebox_init_helper.h diff --git a/whitebox/WORKSPACE b/whitebox/WORKSPACE index eb69323..934029e 100644 --- a/whitebox/WORKSPACE +++ b/whitebox/WORKSPACE @@ -56,9 +56,9 @@ git_repository( http_archive( name = "zlib", build_file = "@com_google_protobuf//:third_party/zlib.BUILD", - sha256 = "ff0ba4c292013dbc27530b3a81e1f9a813cd39de01ca5e0f8bf355702efa593e", - strip_prefix = "zlib-1.3", - urls = ["https://zlib.net/zlib-1.3.tar.gz"], + sha256 = "9a93b2b7dfdac77ceba5a558a580e74667dd6fede4585b91eefb60f03b72df23", + strip_prefix = "zlib-1.3.1", + urls = ["https://zlib.net/zlib-1.3.1.tar.gz"], ) # ODK diff --git a/whitebox/api/BUILD b/whitebox/api/BUILD index b709ee8..bb8f724 100644 --- a/whitebox/api/BUILD +++ b/whitebox/api/BUILD @@ -23,6 +23,7 @@ cc_library( # protocol v2.2 enabled (b/177271059). # TODO(kqyang): Remove the flag after deprecating v16.x server SDKs. "WORKAROUND_STRIP_PADDING_BUG", + "PROVIDER_KEY_SW_SECURE_CRYPTO_ABOVE", ], }) + select({ "//:is_chromeos": ["WV_ENABLE_HW_VERIFICATION=1"], diff --git a/whitebox/api/license_whitebox_decrypt_test.cc b/whitebox/api/license_whitebox_decrypt_test.cc index d3187c0..6ecc55e 100644 --- a/whitebox/api/license_whitebox_decrypt_test.cc +++ b/whitebox/api/license_whitebox_decrypt_test.cc @@ -613,7 +613,6 @@ TEST_P(LicenseWhiteboxDecryptTest, MismatchProviderKeyId) { if (!LoadLicense(settings, other_provider_key_id)) GTEST_SKIP(); - // Decryption should succeed, but the plaintext should be incorrect. ASSERT_EQ(WB_License_Decrypt( whitebox_, WB_CIPHER_MODE_CBC, golden_data_.CBCContent().software_crypto_key.id.data(), @@ -625,7 +624,14 @@ TEST_P(LicenseWhiteboxDecryptTest, MismatchProviderKeyId) { &plaintext_size_), WB_RESULT_OK); plaintext_.resize(plaintext_size_); + // If the software_crypto_key doesn't use provider key, the mismatched + // provider does not affect content decryption. Otherwise, the decyrption + // would be in correct. +#ifdef PROVIDER_KEY_SW_SECURE_CRYPTO_ABOVE + ASSERT_EQ(plaintext_, golden_data_.CBCContent().plaintext); +#else ASSERT_NE(plaintext_, golden_data_.CBCContent().plaintext); +#endif } INSTANTIATE_TEST_SUITE_P( diff --git a/whitebox/api/license_whitebox_main.cc b/whitebox/api/license_whitebox_main.cc index 593f8dd..a444c2f 100644 --- a/whitebox/api/license_whitebox_main.cc +++ b/whitebox/api/license_whitebox_main.cc @@ -63,6 +63,31 @@ class LicenseWhitebox { return true; } + bool SignRequest() { + if (!whitebox_) + return false; + + size_t license_request_signature_size = 256; + std::vector license_request_signature( + license_request_signature_size); + std::vector invalid_license_request = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; + auto const result = WB_License_SignLicenseRequest( + whitebox_, invalid_license_request.data(), + invalid_license_request.size(), license_request_signature.data(), + &license_request_signature_size); + if (result != WB_RESULT_OK) { + LOG(ERROR) << "WB_License_SignLicenseRequest failed: " << result; + return false; + } + return true; + } + WB_License_Whitebox* whitebox() { return whitebox_; } private: @@ -77,6 +102,11 @@ int main(int argc, char** argv) { widevine::LicenseWhitebox whitebox; + if (!whitebox.SignRequest()) { + LOG(ERROR) << "Failed to sign license."; + return 1; + } + widevine::GoldenData golden_data; if (!whitebox.LoadLicense(golden_data.CTRContent().software_crypto_key)) { LOG(ERROR) << "Failed to load license."; diff --git a/whitebox/api/test_license_builder.cc b/whitebox/api/test_license_builder.cc index 35091f4..00a47a5 100644 --- a/whitebox/api/test_license_builder.cc +++ b/whitebox/api/test_license_builder.cc @@ -281,14 +281,23 @@ std::vector GetPadding(TestLicenseBuilder::Padding padding) { // Wrap the content key |unwrapped_content_key| using |provider_key_id| and // |key_encryption_key|, as appropriate. -std::string WrapContentKey(const std::vector& unwrapped_content_key, - size_t provider_key_id, - const std::string& key_encryption_key, - const std::string& key_encryption_key_iv) { +std::string WrapContentKey( + const std::vector& unwrapped_content_key, + size_t provider_key_id, + const std::string& key_encryption_key, + const std::string& key_encryption_key_iv, + video_widevine::License_KeyContainer_SecurityLevel security_level) { auto provider_keys = GetProviderKeys(); - const bool provider_key_id_valid = + bool provider_key_id_valid = (provider_key_id >= 1 && provider_key_id <= provider_keys.size()); +#ifdef PROVIDER_KEY_SW_SECURE_CRYPTO_ABOVE + provider_key_id_valid = + provider_key_id_valid && + security_level != + video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO; +#endif + // If |provider_key_id| is used, encrypt the unwrapped key using the // appropriate provider key. Otherwise, use |unwrapped_content_key| for the // next step. @@ -353,7 +362,8 @@ void AddContentKeyToContainer(const ContentKeyData& key_data, key.insert(key.end(), padding.begin(), padding.end()); auto wrapped_content_key = - WrapContentKey(key, settings.provider_key_id, container_key, key_iv); + WrapContentKey(key, settings.provider_key_id, container_key, key_iv, + container->level()); container->set_key(wrapped_content_key); if (settings.content_key_key_size_override) { diff --git a/whitebox/crypto_utils/whitebox_init_helper.h b/whitebox/crypto_utils/whitebox_init_helper.h new file mode 100644 index 0000000..6e95db5 --- /dev/null +++ b/whitebox/crypto_utils/whitebox_init_helper.h @@ -0,0 +1,105 @@ +// Copyright 2024 Google LLC. All Rights Reserved. This file and proprietary +// source code may only be used and distributed under the Widevine +// License Agreement. + +#ifndef WHITEBOX_INIT_HELPER_H_ +#define WHITEBOX_INIT_HELPER_H_ + +#include +#include +#include +#include +#include +#include + +#include + +/** + * Defines a helper class to initialize the Zimperium whitebox. This spawns + * a background thread to initialize in the background. + */ +template ::value, + std::condition_variable, + std::condition_variable_any>::type> +class WhiteboxInitHelper final { + public: + enum class State { + None, + SignReady, + ProcessReady, + Error, + }; + + WhiteboxInitHelper() {} + ~WhiteboxInitHelper() { + if (thread_) thread_->join(); + } + + State state() const { + std::unique_lock lock(mutex_); + return state_; + } + + WB_Result result() const { + std::unique_lock lock(mutex_); + return result_; + } + + /** Spawns the background thread and starts initializing. */ + void StartThread() { + if (!thread_) { + thread_.reset( + new Thread(std::bind(&WhiteboxInitHelper::ThreadMain, this))); + } + } + + /** Blocks the current thread until the sign initialization has finished. */ + WB_Result EnsureSignReady() { + std::unique_lock lock(mutex_); + while (state_ < State::SignReady) { + cond_.wait(lock); + } + return result_; + } + + /** + * Blocks the current thread until the process initialization has finished. + */ + WB_Result EnsureProcessReady() { + std::unique_lock lock(mutex_); + while (state_ < State::ProcessReady) { + cond_.wait(lock); + } + return result_; + } + + private: + void ThreadMain() { + auto result = WB_License_SignLicenseRequest_Init(); + { + std::unique_lock lock(mutex_); + state_ = result == WB_RESULT_OK ? State::SignReady : State::Error; + result_ = result; + cond_.notify_all(); + if (result != WB_RESULT_OK) return; + } + + result = WB_License_ProcessLicenseResponse_Init(); + + std::unique_lock lock(mutex_); + state_ = result == WB_RESULT_OK ? State::ProcessReady : State::Error; + result_ = result; + cond_.notify_all(); + } + + std::unique_ptr thread_; + ConditionVariable cond_; + Mutex mutex_; + State state_ = State::None; + WB_Result result_ = WB_RESULT_OK; +}; + +#endif // WHITEBOX_INIT_HELPER_H_ diff --git a/whitebox/reference/impl/license_parser.cc b/whitebox/reference/impl/license_parser.cc index ab1772d..c08e657 100644 --- a/whitebox/reference/impl/license_parser.cc +++ b/whitebox/reference/impl/license_parser.cc @@ -37,12 +37,20 @@ bool LicenseParser::UnwrapKey( size_t provider_key_id, const std::string& key_decryption_key, const std::string& key_decryption_key_iv, + video_widevine::License_KeyContainer_SecurityLevel security_level, std::string* unwrapped_key) { // provider keys are only applied on content keys. - const bool provider_key_id_valid = + bool provider_key_id_valid = (provider_key_id >= 1 && provider_key_id <= provider_keys.size() && key_type == KeyType::kContentKey); +#ifdef PROVIDER_KEY_SW_SECURE_CRYPTO_ABOVE + provider_key_id_valid = + provider_key_id_valid && + security_level != + video_widevine::License_KeyContainer_SecurityLevel_SW_SECURE_CRYPTO; +#endif + // If |provider_key_id| is used and valid, then start by unmasking it. std::string key = wrapped_key; if (provider_key_id_valid) { diff --git a/whitebox/reference/impl/license_parser.h b/whitebox/reference/impl/license_parser.h index 4dd6065..48f4194 100644 --- a/whitebox/reference/impl/license_parser.h +++ b/whitebox/reference/impl/license_parser.h @@ -50,16 +50,18 @@ class LicenseParser { const std::string& encrypted, std::string* decrypted); - // Unwrap key |wrapped_key| using |key_type|, |provider_key_id| and - // |key_decryption_key|, as necessary. Returns true and + // Unwrap key |wrapped_key| using |key_type|, |provider_key_id|, + // |key_decryption_key| and |security_level|, as necessary. Returns true and // |unwrapped_content_key| is updated on success, false otherwise. - static bool UnwrapKey(KeyType key_type, - const std::string& wrapped_key, - const std::vector& provider_keys, - size_t provider_key_id, - const std::string& key_decryption_key, - const std::string& key_decryption_key_iv, - std::string* unwrapped_key); + static bool UnwrapKey( + KeyType key_type, + const std::string& wrapped_key, + const std::vector& provider_keys, + size_t provider_key_id, + const std::string& key_decryption_key, + const std::string& key_decryption_key_iv, + video_widevine::License_KeyContainer_SecurityLevel security_level, + std::string* unwrapped_key); // Creates and returns a InternalKey based on the values provided. // |level| determines whether decrypt or masked_decrypt is allowed. diff --git a/whitebox/reference/impl/odk_license_parser.cc b/whitebox/reference/impl/odk_license_parser.cc index 2e503e8..0718aba 100644 --- a/whitebox/reference/impl/odk_license_parser.cc +++ b/whitebox/reference/impl/odk_license_parser.cc @@ -258,7 +258,7 @@ InternalKey OdkLicenseParser::ParseInternalKey( std::string unwrapped_key; if (!UnwrapKey(key_type, wrapped_key, provider_keys, provider_key_id, - decryption_key, iv, &unwrapped_key)) { + decryption_key, iv, security_level, &unwrapped_key)) { VLOG(3) << "Failed to decrypt key."; return InternalKey(); } diff --git a/whitebox/reference/impl/protobuf_license_parser.cc b/whitebox/reference/impl/protobuf_license_parser.cc index e30168c..15b02ee 100644 --- a/whitebox/reference/impl/protobuf_license_parser.cc +++ b/whitebox/reference/impl/protobuf_license_parser.cc @@ -226,7 +226,7 @@ InternalKey ProtobufLicenseParser::ParseInternalKey( std::string wrapped_key = key.key().substr(0, key_size_without_padding); std::string unwrapped_key; if (!UnwrapKey(key_type, wrapped_key, provider_keys, provider_key_id, - decryption_key, key.iv(), &unwrapped_key)) { + decryption_key, key.iv(), key.level(), &unwrapped_key)) { VLOG(3) << "Failed to decrypt content key."; return InternalKey(); }