diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index 280ac36d..9c847cdb 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -754,6 +754,10 @@ class Adapter { LOOKUP_ALL(8, Initialize, OEMCrypto_Initialize); LOOKUP_ALL(8, APIVersion, OEMCrypto_APIVersion); LOOKUP_ALL(8, Terminate, OEMCrypto_Terminate); + if (level1_.Initialize == nullptr || level1_.APIVersion == nullptr || + level1_.Terminate == nullptr) { + level1_valid_ = false; + } if (!level1_valid_) { metrics->OemCryptoDynamicAdapterMetrics::SetInitializationMode( wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_INVALID_L1); diff --git a/libwvdrmengine/oemcrypto/odk/README b/libwvdrmengine/oemcrypto/odk/README index 44dac83f..ba8c8c73 100644 --- a/libwvdrmengine/oemcrypto/odk/README +++ b/libwvdrmengine/oemcrypto/odk/README @@ -1,4 +1,4 @@ -The ODK Library is used to generate and parse core OEMCrypto messages for +This ODK Library is used to generate and parse core OEMCrypto messages for OEMCrypto v16 and above. This library is used by both OEMCrypto on a device, and by Widevine license and diff --git a/libwvdrmengine/oemcrypto/odk/include/odk_structs.h b/libwvdrmengine/oemcrypto/odk/include/odk_structs.h index b24f7d9d..d2cb635d 100644 --- a/libwvdrmengine/oemcrypto/odk/include/odk_structs.h +++ b/libwvdrmengine/oemcrypto/odk/include/odk_structs.h @@ -12,7 +12,13 @@ /* The version of this library. */ #define ODK_MAJOR_VERSION 16 -#define ODK_MINOR_VERSION 2 +#define ODK_MINOR_VERSION 3 + +/* ODK Version string. Date changed automatically on each release. */ +#define ODK_RELEASE_DATE "ODK v16.2 2020-06-02" + +/* The lowest version number for an ODK message. */ +#define ODK_FIRST_VERSION 16 /* Some useful constants. */ #define ODK_DEVICE_ID_LEN_MAX 64 diff --git a/libwvdrmengine/oemcrypto/odk/src/core_message_deserialize.cpp b/libwvdrmengine/oemcrypto/odk/src/core_message_deserialize.cpp index f2e909b5..1cef5ee7 100644 --- a/libwvdrmengine/oemcrypto/odk/src/core_message_deserialize.cpp +++ b/libwvdrmengine/oemcrypto/odk/src/core_message_deserialize.cpp @@ -19,9 +19,6 @@ namespace oemcrypto_core_message { namespace deserialize { namespace { -constexpr int EARLIEST_OEMCRYPTO_VERSION_WITH_ODK = 16; -constexpr int LATEST_OEMCRYPTO_VERSION = 16; - /** * Template for parsing requests * @@ -59,21 +56,31 @@ bool ParseRequest(uint32_t message_type, core_request->session_id = core_message.nonce_values.session_id; // Verify that the minor version matches the released version for the given // major version. - if ((core_request->api_major_version < EARLIEST_OEMCRYPTO_VERSION_WITH_ODK) || - (core_request->api_major_version > LATEST_OEMCRYPTO_VERSION)) { - // Non existing and future versions are not supported. + if (core_request->api_major_version < ODK_FIRST_VERSION) { + // Non existing versions are not supported. return false; } else if (core_request->api_major_version == 16) { // For version 16, we demand a minor version of at least 2. + // We accept 16.2, 16.3, or higher. if (core_request->api_major_version < 2) return false; } else { // Other versions do not (yet) have a restriction on minor number. + // In particular, future versions are accepted for forward compatibility. } - return core_message.message_type == message_type && - core_message.message_length == GetOffset(msg) && - core_request->api_major_version >= - EARLIEST_OEMCRYPTO_VERSION_WITH_ODK && - core_request->api_major_version <= LATEST_OEMCRYPTO_VERSION; + // For v16, a release and a renewal use the same message structure. + // However, for future API versions, the release might be a separate + // message. Otherwise, we expect an exact match of message types. + if (core_message.message_type != message_type && + !(message_type == ODK_Renewal_Request_Type && + core_message.message_type == ODK_Release_Request_Type)) { + return false; + } + // Verify that the amount of buffer we read, which is GetOffset, is not more + // than the total message size. We allow the total message size to be larger + // for forward compatibility because future messages might have extra fields + // that we can ignore. + if (core_message.message_length < GetOffset(msg)) return false; + return true; } } // namespace diff --git a/libwvdrmengine/oemcrypto/odk/src/core_message_serialize.cpp b/libwvdrmengine/oemcrypto/odk/src/core_message_serialize.cpp index cdd97447..45464607 100644 --- a/libwvdrmengine/oemcrypto/odk/src/core_message_serialize.cpp +++ b/libwvdrmengine/oemcrypto/odk/src/core_message_serialize.cpp @@ -41,6 +41,12 @@ bool CreateResponse(uint32_t message_type, const S& core_request, header->nonce_values.api_minor_version = core_request.api_minor_version; header->nonce_values.nonce = core_request.nonce; header->nonce_values.session_id = core_request.session_id; + // The message API version for the response is the minimum of our version and + // the request's version. + if (core_request.api_major_version > ODK_MAJOR_VERSION) { + header->nonce_values.api_major_version = ODK_MAJOR_VERSION; + header->nonce_values.api_minor_version = ODK_MINOR_VERSION; + } static constexpr size_t BUF_CAPACITY = 2048; std::vector buf(BUF_CAPACITY, 0); diff --git a/libwvdrmengine/oemcrypto/odk/src/core_message_serialize_proto.cpp b/libwvdrmengine/oemcrypto/odk/src/core_message_serialize_proto.cpp index 90b5fdd9..cacba429 100644 --- a/libwvdrmengine/oemcrypto/odk/src/core_message_serialize_proto.cpp +++ b/libwvdrmengine/oemcrypto/odk/src/core_message_serialize_proto.cpp @@ -87,8 +87,7 @@ bool CreateCoreLicenseResponseFromProto(const std::string& serialized_license, } parsed_lic.enc_mac_keys_iv = GetOecSubstring(serialized_license, k.iv()); - std::string mac_keys(k.key(), k.key().size()); - parsed_lic.enc_mac_keys = GetOecSubstring(serialized_license, mac_keys); + parsed_lic.enc_mac_keys = GetOecSubstring(serialized_license, k.key()); break; } case video_widevine::License_KeyContainer::CONTENT: { diff --git a/libwvdrmengine/oemcrypto/odk/src/odk.c b/libwvdrmengine/oemcrypto/odk/src/odk.c index be34f879..42e82d5e 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk.c @@ -190,9 +190,9 @@ OEMCryptoResult ODK_PrepareCoreRenewalRequest(uint8_t* message, * renewal. All releases use v15. */ if (clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_NOT_LOADED || clock_values->timer_status == ODK_CLOCK_TIMER_STATUS_LICENSE_INACTIVE) { - nonce_values->api_major_version = 15; + nonce_values->api_major_version = ODK_FIRST_VERSION - 1; } - if (nonce_values->api_major_version < 16) { + if (nonce_values->api_major_version < ODK_FIRST_VERSION) { *core_message_size = 0; return OEMCrypto_SUCCESS; } @@ -270,12 +270,28 @@ OEMCryptoResult ODK_ParseLicense( return err; } - /* This function should not be used for legacy licenses. */ - if (license_response.request.core_message.nonce_values.api_major_version != - ODK_MAJOR_VERSION) { + /* We do not support future API version. Also, this function should not be + * used for legacy licenses. */ + if (license_response.request.core_message.nonce_values.api_major_version > + ODK_MAJOR_VERSION || + license_response.request.core_message.nonce_values.api_major_version < + ODK_FIRST_VERSION) { return ODK_UNSUPPORTED_API; } + /* If the server sent us an older format, record the license's API version. */ + if (nonce_values->api_major_version > + license_response.request.core_message.nonce_values.api_major_version) { + nonce_values->api_major_version = + license_response.request.core_message.nonce_values.api_major_version; + nonce_values->api_minor_version = + license_response.request.core_message.nonce_values.api_minor_version; + } else if (nonce_values->api_minor_version > + license_response.request.core_message.nonce_values + .api_minor_version) { + nonce_values->api_minor_version = + license_response.request.core_message.nonce_values.api_minor_version; + } /* If the license has a provider session token (pst), then OEMCrypto should * have a usage entry loaded. The opposite is also an error. */ if ((usage_entry_present && parsed_license->pst.length == 0) || @@ -302,7 +318,7 @@ OEMCryptoResult ODK_ParseLicense( * OEMCrypto stores a hash of the core license request and only signs the * message body. Here, when we process the license response, we verify that * the server has the same hash of the core request. */ - if (initial_license_load && + if (initial_license_load && parsed_license->nonce_required && crypto_memcmp(request_hash, license_response.request_hash, ODK_SHA256_HASH_SIZE)) { return ODK_ERROR_CORE_MESSAGE; diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_assert.h b/libwvdrmengine/oemcrypto/odk/src/odk_assert.h index 2057c40a..6fda98b1 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_assert.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_assert.h @@ -10,11 +10,11 @@ extern "C" { #endif #if (__STDC_VERSION__ >= 201112L) -# include -# define odk_static_assert static_assert +#include +#define odk_static_assert static_assert #else -# define odk_static_assert(msg, e) \ - enum { odk_static_assert = 1 / (!!((msg) && (e))) }; +#define odk_static_assert(msg, e) \ + enum { odk_static_assert = 1 / (!!((msg) && (e))) }; #endif #ifdef __cplusplus diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_endian.h b/libwvdrmengine/oemcrypto/odk/src/odk_endian.h index bfbb9bf0..2a6f143e 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_endian.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_endian.h @@ -10,11 +10,11 @@ extern "C" { #endif #if defined(__linux__) || defined(__ANDROID__) -# include -# define oemcrypto_htobe32 htobe32 -# define oemcrypto_be32toh be32toh -# define oemcrypto_htobe64 htobe64 -# define oemcrypto_be64toh be64toh +#include +#define oemcrypto_htobe32 htobe32 +#define oemcrypto_be32toh be32toh +#define oemcrypto_htobe64 htobe64 +#define oemcrypto_be64toh be64toh #else /* defined(__linux__) || defined(__ANDROID__) */ uint32_t oemcrypto_htobe32(uint32_t u32); uint32_t oemcrypto_be32toh(uint32_t u32); diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h b/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h index 991f59d0..3c5a502b 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h +++ b/libwvdrmengine/oemcrypto/odk/src/odk_structs_priv.h @@ -21,6 +21,10 @@ typedef enum { ODK_Renewal_Response_Type = 4, ODK_Provisioning_Request_Type = 5, ODK_Provisioning_Response_Type = 6, + + /* Reserve future message types to support forward compatibility. */ + ODK_Release_Request_Type = 7, + ODK_Release_Response_Type = 8, } ODK_MessageType; typedef struct { diff --git a/libwvdrmengine/oemcrypto/odk/src/odk_timer.c b/libwvdrmengine/oemcrypto/odk/src/odk_timer.c index 10a0f7dc..0fbcf18e 100644 --- a/libwvdrmengine/oemcrypto/odk/src/odk_timer.c +++ b/libwvdrmengine/oemcrypto/odk/src/odk_timer.c @@ -4,6 +4,7 @@ #include #include + #include "odk.h" #include "odk_overflow.h" #include "odk_structs_priv.h" diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp index 01596307..6c18aea9 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.cpp +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.cpp @@ -630,7 +630,7 @@ OEMCryptoResult LicenseRoundTrip::LoadResponse(Session* session) { // Note: we verify content licenses here. For entitlement license, we verify // the key control blocks after loading entitled content keys. - if (license_type_ == OEMCrypto_ContentLicense) VerifyTestKeys(); + if (license_type_ == OEMCrypto_ContentLicense) VerifyTestKeys(session); } return result; } @@ -644,12 +644,12 @@ OEMCryptoResult LicenseRoundTrip::ReloadResponse(Session* session) { // with the truth key control block. Failures in this function probably // indicate the OEMCrypto_LoadLicense/LoadKeys did not correctly process the key // control block. -void LicenseRoundTrip::VerifyTestKeys() { +void LicenseRoundTrip::VerifyTestKeys(Session* session) { for (unsigned int i = 0; i < num_keys_; i++) { KeyControlBlock block; size_t size = sizeof(block); OEMCryptoResult sts = OEMCrypto_QueryKeyControl( - session_->session_id(), response_data_.keys[i].key_id, + session->session_id(), response_data_.keys[i].key_id, response_data_.keys[i].key_id_length, reinterpret_cast(&block), &size); if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) { diff --git a/libwvdrmengine/oemcrypto/test/oec_session_util.h b/libwvdrmengine/oemcrypto/test/oec_session_util.h index 36848d52..54ee3187 100644 --- a/libwvdrmengine/oemcrypto/test/oec_session_util.h +++ b/libwvdrmengine/oemcrypto/test/oec_session_util.h @@ -298,7 +298,7 @@ class LicenseRoundTrip // Reload an offline license into a different session. This derives new mac // keys and then calls LoadResponse. OEMCryptoResult ReloadResponse(Session* session); - void VerifyTestKeys(); + void VerifyTestKeys(Session* session); // Set the default key control block for all keys. This is used in // CreateDefaultResponse. The key control block determines the restrictions // that OEMCrypto should place on a key's use. For example, it specifies the diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index e584e20f..b04eba28 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -154,13 +154,13 @@ class OEMCryptoClientTest : public ::testing::Test, public SessionUtil { // tests are failing when the device has the wrong keybox installed. TEST_F(OEMCryptoClientTest, VersionNumber) { const std::string log_message = - "OEMCrypto unit tests for API 16.2. Tests last updated 2020-03-27"; + "OEMCrypto unit tests for API 16.3. Tests last updated 2020-06-01"; cout << " " << log_message << "\n"; LOGI("%s", log_message.c_str()); // If any of the following fail, then it is time to update the log message // above. EXPECT_EQ(ODK_MAJOR_VERSION, 16); - EXPECT_EQ(ODK_MINOR_VERSION, 2); + EXPECT_EQ(ODK_MINOR_VERSION, 3); EXPECT_EQ(kCurrentAPI, 16u); const char* level = OEMCrypto_SecurityLevel(); ASSERT_NE(nullptr, level); @@ -854,7 +854,13 @@ TEST_P(OEMCryptoLicenseTest, LoadKeyWithNoRequest) { license_messages_.core_request().api_minor_version = ODK_MINOR_VERSION; ASSERT_NO_FATAL_FAILURE(license_messages_.CreateDefaultResponse()); ASSERT_NO_FATAL_FAILURE(license_messages_.EncryptAndSignResponse()); - ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse()); + + // Load license in a different session, which did not create the request. + Session session2; + ASSERT_NO_FATAL_FAILURE(session2.open()); + ASSERT_NO_FATAL_FAILURE(InstallTestRSAKey(&session2)); + ASSERT_NO_FATAL_FAILURE(session2.GenerateDerivedKeysFromSessionKey()); + ASSERT_EQ(OEMCrypto_SUCCESS, license_messages_.LoadResponse(&session2)); } // Verify that a license may be loaded with a nonce. @@ -2197,10 +2203,11 @@ class OEMCryptoSessionTestsDecryptTests void TestDecryptCENC() { OEMCryptoResult sts; + // OEMCrypto only supports providing a decrypt hash for one sample. + if (samples_.size() > 1) verify_crc_ = false; + // If supported, check the decrypt hashes. if (verify_crc_) { - // OEMCrypto only supports providing a decrypt hash for the first sample - // in the sample array. const TestSample& sample = samples_[0]; uint32_t hash = @@ -2255,7 +2262,7 @@ class OEMCryptoSessionTestsDecryptTests } } } - if (global_features.supports_crc) { + if (verify_crc_) { uint32_t frame; ASSERT_EQ(OEMCrypto_GetHashErrorCode(session_.session_id(), &frame), OEMCrypto_SUCCESS);