diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index 1c14e22e..91b9a555 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -321,6 +321,7 @@ class Adapter { if (level1_.version == 9) { LOOKUP(GetHDCPCapability_V9, OEMCrypto_GetHDCPCapability_V9); } else { + LOOKUP(QueryKeyControl, OEMCrypto_QueryKeyControl); LOOKUP(GetHDCPCapability, OEMCrypto_GetHDCPCapability); LOOKUP(IsAntiRollbackHwPresent, OEMCrypto_IsAntiRollbackHwPresent); LOOKUP(GetNumberOfOpenSessions, OEMCrypto_GetNumberOfOpenSessions); @@ -677,6 +678,17 @@ extern "C" OEMCryptoResult OEMCrypto_RefreshKeys( signature_length, num_keys, key_array); } +extern "C" OEMCryptoResult OEMCrypto_QueryKeyControl( + OEMCrypto_SESSION session, const uint8_t* key_id, size_t key_id_length, + uint8_t* key_control_block, size_t* key_control_block_length) { + if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; + LevelSession pair = kAdapter->get(session); + if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; + if (pair.fcn->version < 10) return OEMCrypto_ERROR_NOT_IMPLEMENTED; + return pair.fcn->QueryKeyControl(pair.session, key_id, key_id_length, + key_control_block, key_control_block_length); +} + extern "C" OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session, const uint8_t* key_id, size_t key_id_length) { diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp index 3f9a7208..bd815392 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.cpp @@ -941,6 +941,25 @@ bool SessionContext::UpdateMacKeys(const std::vector& enc_mac_keys, return true; } +bool SessionContext::QueryKeyControlBlock(const KeyId& key_id, uint32_t* data) { + const Key* content_key = session_keys_.Find(key_id); + if (LogCategoryEnabled(kLoggingTraceDecryption)){ + LOGI(( "Select Key: key_id = " + + wvcdm::b2a_hex(key_id) ).c_str()); + LOGI(( "Select Key: key = " + + wvcdm::b2a_hex(content_key->value()) ).c_str()); + } + if (NULL == content_key) { + LOGE("[QueryKeyControlBlock(): No key matches key id]"); + return false; + } + data[0] = 0; // verification optional. + data[1] = htonl(content_key->control().duration()); + data[2] = 0; // nonce optional. + data[3] = htonl(content_key->control().control_bits()); + return true; +} + bool SessionContext::SelectContentKey(const KeyId& key_id) { const Key* content_key = session_keys_.Find(key_id); diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h index 86f824ba..6f9aca04 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_engine_mock.h @@ -167,6 +167,7 @@ class SessionContext { const std::vector& key_control_iv); bool UpdateMacKeys(const std::vector& mac_keys, const std::vector& iv); + bool QueryKeyControlBlock(const KeyId& key_id, uint32_t* data); bool SelectContentKey(const KeyId& key_id); const Key* current_content_key(void) {return current_content_key_;} void set_mac_key_server(const std::vector& mac_key_server) { diff --git a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp index 4c3486d8..cb1b2c62 100644 --- a/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp +++ b/libwvdrmengine/oemcrypto/mock/src/oemcrypto_mock.cpp @@ -451,6 +451,43 @@ OEMCryptoResult OEMCrypto_RefreshKeys( return OEMCrypto_SUCCESS; } +extern "C" OEMCryptoResult OEMCrypto_QueryKeyControl( + OEMCrypto_SESSION session, const uint8_t* key_id, size_t key_id_length, + uint8_t* key_control_block, size_t* key_control_block_length) { + if (LogCategoryEnabled(kLoggingTraceOEMCryptoCalls)) { + LOGI("-- OEMCryptoResult OEMCrypto_QueryKeyControl" + "(const OEMCrypto_SESSION session)\n"); + if (wvcdm::g_cutoff >= wvcdm::LOG_VERBOSE) { + dump_hex("key_id", key_id, key_id_length); + } + } + uint32_t* block = reinterpret_cast(key_control_block); + if ((key_control_block_length == NULL) + || (*key_control_block_length < wvcdm::KEY_CONTROL_SIZE)) { + LOGE("[OEMCrypto_QueryKeyControl(): OEMCrypto_ERROR_SHORT_BUFFER]"); + return OEMCrypto_ERROR_SHORT_BUFFER; + } + *key_control_block_length = wvcdm::KEY_CONTROL_SIZE; + if (key_id == NULL) { + LOGE("[OEMCrypto_QueryKeyControl(): key_id null. " + "OEMCrypto_ERROR_UNKNOWN_FAILURE]"); + return OEMCrypto_ERROR_UNKNOWN_FAILURE; + } + + SessionContext* session_ctx = crypto_engine->FindSession(session); + if (!session_ctx || !session_ctx->isValid()) { + LOGE("[OEMCrypto_QueryKeyControl(): ERROR_INVALID_SESSION]"); + return OEMCrypto_ERROR_INVALID_SESSION; + } + const std::vector key_id_str = + std::vector(key_id, key_id + key_id_length); + if (!session_ctx->QueryKeyControlBlock(key_id_str, block)) { + LOGE("[OEMCrypto_QueryKeyControl(): FAIL]"); + return OEMCrypto_ERROR_NO_CONTENT_KEY; + } + return OEMCrypto_SUCCESS; +} + extern "C" OEMCryptoResult OEMCrypto_SelectKey(const OEMCrypto_SESSION session, const uint8_t* key_id, diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index 783f4c6d..6754324c 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -1012,6 +1012,26 @@ class Session { &signature_[0], signature_.size(), NULL, NULL, kNumKeys, key_array_, pst_ptr, pst.length())); } + VerifyTestKeys(); + } + + void VerifyTestKeys() { + for (unsigned int i = 0; i < kNumKeys; i++) { + KeyControlBlock block; + size_t size = sizeof(block); + OEMCryptoResult sts = OEMCrypto_QueryKeyControl( + session_id(), license_.keys[i].key_id, + sizeof(license_.keys[i].key_id), + reinterpret_cast(&block), &size); + if (sts != OEMCrypto_ERROR_NOT_IMPLEMENTED) { + ASSERT_EQ(OEMCrypto_SUCCESS, sts); + ASSERT_EQ(sizeof(block), size); + ASSERT_EQ(license_.keys[i].control.duration, + block.duration) << "For key " << i; + ASSERT_EQ(license_.keys[i].control.control_bits, + block.control_bits) << "For key " << i; + } + } } void RefreshTestKeys(const size_t key_count, uint32_t control_bits, @@ -2331,6 +2351,32 @@ TEST_F(DISABLED_TestKeybox, LoadKeysWithNoDerivedKeys) { ASSERT_NE(OEMCrypto_SUCCESS, sts); } +TEST_F(DISABLED_TestKeybox, QueryKeyControl) { + Session s; + s.open(); + s.GenerateDerivedKeys(); + s.FillSimpleMessage(0, wvoec_mock::kControlNonceEnabled, s.get_nonce()); + s.EncryptAndSign(); + s.LoadTestKeys(); + // Note: successful cases are tested in VerifyTestKeys. + KeyControlBlock block; + size_t size = sizeof(block) - 1; + OEMCryptoResult sts = OEMCrypto_QueryKeyControl( + s.session_id(), s.license().keys[0].key_id, + sizeof(s.license().keys[0].key_id), reinterpret_cast(&block), + &size); + if (sts == OEMCrypto_ERROR_NOT_IMPLEMENTED) { + return; + } + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, sts); + const char *key_id = "no_key"; + size = sizeof(block); + ASSERT_EQ(OEMCrypto_ERROR_NO_CONTENT_KEY, + OEMCrypto_QueryKeyControl( + s.session_id(), reinterpret_cast(key_id), + strlen(key_id), reinterpret_cast(&block), &size)); +} + TEST_F(DISABLED_TestKeybox, AntiRollbackHardwareRequired) { Session s; s.open();