From 8c4c2383242e977493708be26d7176c35d933f13 Mon Sep 17 00:00:00 2001 From: Cong Lin Date: Fri, 17 Jun 2022 14:30:19 -0700 Subject: [PATCH] Add unit test for clear KCB in LS SDK 16.4 response This is a merge from: https://widevine-internal-review.googlesource.com/c/cdm/+/152897 and http://go/wvgerrit/153709 Adding a new OEMCrypto unit test will allow partners to correct a problem earlier in their integration. Verifies current oemcrypto implementation handles clear KCB in a mocked 16.4 license response. Unit test release date updated to 2022-06-17. Test: run_x86_64_tests; opk_ta Bug: 235870170 Bug: 234645065 Change-Id: I59fef2c25f5c007624447d4f46147d96adeddad9 --- .../oemcrypto/test/oec_session_util.cpp | 35 ++++++++++++++----- .../oemcrypto/test/oec_session_util.h | 13 +++++++ .../oemcrypto/test/oemcrypto_test.cpp | 35 ++++++++++++++++++- 3 files changed, 74 insertions(+), 9 deletions(-) diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp index 5e6f7ec7..7ffd2cea 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp @@ -28,7 +28,6 @@ #include "OEMCryptoCENC.h" #include "clock.h" #include "core_message_deserialize.h" -#include "core_message_features.h" #include "core_message_serialize.h" #include "disallow_copy_and_assign.h" #include "log.h" @@ -770,7 +769,7 @@ void LicenseRoundTrip::FillCoreResponseSubstrings() { } } -void LicenseRoundTrip::EncryptAndSignResponse() { +void LicenseRoundTrip::EncryptResponse(bool force_clear_kcb) { ASSERT_NO_FATAL_FAILURE(session_->GenerateDerivedKeysFromSessionKey()); encrypted_response_data_ = response_data_; uint8_t iv_buffer[KEY_IV_SIZE]; @@ -786,7 +785,8 @@ void LicenseRoundTrip::EncryptAndSignResponse() { // Fuzzing skip encryption: key_data_length being a random value will // encrypt data which is not expected to, there by leading to inefficient // fuzzing. - if (response_data_.keys[i].key_data_length <= + if (!force_clear_kcb && + response_data_.keys[i].key_data_length <= sizeof(response_data_.keys[i].key_data) && response_data_.keys[i].key_data_length % 16 == 0) { memcpy(iv_buffer, &response_data_.keys[i].control_iv[0], KEY_IV_SIZE); @@ -811,6 +811,10 @@ void LicenseRoundTrip::EncryptAndSignResponse() { response_data_.keys[i].key_iv); } } +} + +void LicenseRoundTrip::CreateCoreLicenseResponseWithFeatures( + const CoreMessageFeatures& features) { if (api_version_ < kCoreMessagesAPI) { serialized_core_message_.resize(0); } else { @@ -823,11 +827,6 @@ void LicenseRoundTrip::EncryptAndSignResponse() { } std::string request_hash_string( reinterpret_cast(request_hash_), sizeof(request_hash_)); - // We might try to test a future api_version_, but we can only make a core - // message with at most the current ODK version. This is only done to verify - // that OEMCrypto does not attempt to load a future version. - CoreMessageFeatures features = CoreMessageFeatures::DefaultFeatures( - std::min(api_version_, static_cast(ODK_MAJOR_VERSION))); ASSERT_TRUE(oemcrypto_core_message::serialize::CreateCoreLicenseResponse( features, core_response_, core_request_, request_hash_string, &serialized_core_message_)); @@ -836,7 +835,9 @@ void LicenseRoundTrip::EncryptAndSignResponse() { serialized_core_message_.resize( std::max(required_core_message_size_, serialized_core_message_.size())); } +} +void LicenseRoundTrip::SignEncryptedResponse() { // Make the message buffer a just big enough, or the // required size, whichever is larger. const size_t message_size = @@ -860,6 +861,24 @@ void LicenseRoundTrip::EncryptAndSignResponse() { &response_signature_); } +void LicenseRoundTrip::EncryptAndSignResponse() { + EncryptResponse(); + // We might try to test a future api_version_, but we can only make a core + // message with at most the current ODK version. This is only done to verify + // that OEMCrypto does not attempt to load a future version. + CoreMessageFeatures features = CoreMessageFeatures::DefaultFeatures( + std::min(api_version_, static_cast(ODK_MAJOR_VERSION))); + CreateCoreLicenseResponseWithFeatures(features); + SignEncryptedResponse(); +} + +void LicenseRoundTrip::EncryptAndSignResponseWithCoreMessageFeatures( + const CoreMessageFeatures& features, bool force_clear_kcb) { + EncryptResponse(force_clear_kcb); + CreateCoreLicenseResponseWithFeatures(features); + SignEncryptedResponse(); +} + OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session) { return LoadResponse(session, true); } diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.h b/libwvdrmengine/oemcrypto/test/oec_session_util.h index d8ffe67a..a0fb1b85 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.h +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.h @@ -16,6 +16,7 @@ #include #include "core_message_deserialize.h" +#include "core_message_features.h" #include "core_message_serialize.h" #include "odk.h" #include "oec_device_features.h" @@ -342,6 +343,18 @@ class LicenseRoundTrip // Fill the |core_response| substrings. virtual void FillCoreResponseSubstrings(); void EncryptAndSignResponse() override; + // Encrypt and sign license response created from a specific odk version. + void EncryptAndSignResponseWithCoreMessageFeatures( + const oemcrypto_core_message::features::CoreMessageFeatures& features, + bool force_clear_kcb); + // Encrypt license response. This is used in EncryptAndSignResponse(). + void EncryptResponse(bool force_clear_kcb = false); + // Create core license response with a specific ODK version. This is used in + // EncryptAndSignResponse(). + void CreateCoreLicenseResponseWithFeatures( + const oemcrypto_core_message::features::CoreMessageFeatures& features); + // Sign license response. This is used in EncryptAndSignResponse(). + void SignEncryptedResponse(); OEMCryptoResult LoadResponse() override { return LoadResponse(session_); } OEMCryptoResult LoadResponse(Session* session) override; OEMCryptoResult LoadResponse(Session* session, bool verify_keys); diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 0c01a801..a0d77917 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -266,7 +266,7 @@ TEST_F(OEMCryptoClientTest, FreeUnallocatedSecureBufferNoFailure) { */ TEST_F(OEMCryptoClientTest, VersionNumber) { const std::string log_message = - "OEMCrypto unit tests for API 17.0. Tests last updated 2022-02-18"; + "OEMCrypto unit tests for API 17.1. Tests last updated 2022-06-17"; cout << " " << log_message << "\n"; cout << " " << "These tests are part of Android T." @@ -3445,6 +3445,39 @@ TEST_P(OEMCryptoLicenseTest, QueryKeyControl) { strlen(key_id), reinterpret_cast(&block), &size)); } +// This case tests against the issue where certain 16.4.x SDK versions return a +// clear key control block (KCB) in the license response. An OEMCrypto v17.1+ +// implementation should be able to handle the clear KCB in the 16.4.x response +// and load the license correctly. +TEST_F(OEMCryptoSessionTests, ClearKcbAPI16_4) { + Session s; + ASSERT_NO_FATAL_FAILURE(s.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&s)); + LicenseRoundTrip license_messages(&s); + ASSERT_NO_FATAL_FAILURE(license_messages.SignAndVerifyRequest()); + ASSERT_NO_FATAL_FAILURE(license_messages.CreateDefaultResponse()); + // Set odk version in the license response to be 16.4 + oemcrypto_core_message::features::CoreMessageFeatures features = {}; + features.maximum_major_version = 16; + features.maximum_minor_version = 4; + constexpr bool kForceClearKcb = true; + ASSERT_NO_FATAL_FAILURE( + license_messages.EncryptAndSignResponseWithCoreMessageFeatures( + features, kForceClearKcb)); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages.LoadResponse()); + + KeyControlBlock block; + size_t size = sizeof(block); + OEMCryptoResult sts = OEMCrypto_QueryKeyControl( + s.session_id(), license_messages.response_data().keys[0].key_id, + license_messages.response_data().keys[0].key_id_length, + reinterpret_cast(&block), &size); + if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) { + return; + } + ASSERT_EQ(OEMCrypto_SUCCESS, sts); +} + TEST_F(OEMCryptoSessionTests, OEMCryptoMemoryLoadLicenseForHugeSignatureLength) { auto oemcrypto_function = [&](size_t signature_size) {