diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index 1a5dc4a7..1f8c36c0 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -24,6 +24,7 @@ #include "log.h" #include "profiled_scope.h" #include "properties.h" +#include "wv_cdm_constants.h" using namespace wvoec3; using wvcdm::kLevelDefault; @@ -31,6 +32,8 @@ using wvcdm::kLevel3; namespace { +static const size_t kMaxGenericEncryptChunkSize = 100*1024; + typedef struct { const uint8_t* key_id; size_t key_id_length; @@ -1107,8 +1110,23 @@ extern "C" OEMCryptoResult OEMCrypto_Generic_Encrypt( if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; LevelSession pair = kAdapter->get(session); if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; - return pair.fcn->Generic_Encrypt(pair.session, in_buffer, buffer_length, iv, - algorithm, out_buffer); + OEMCryptoResult status = OEMCrypto_SUCCESS; + std::vector current_iv(iv, iv + wvcdm::KEY_IV_SIZE); + while (buffer_length > 0 && status == OEMCrypto_SUCCESS) { + const size_t chunk_size = std::min(buffer_length, + kMaxGenericEncryptChunkSize); + status = pair.fcn->Generic_Encrypt(pair.session, in_buffer, chunk_size, + ¤t_iv[0], + algorithm, out_buffer); + buffer_length -= chunk_size; + in_buffer += chunk_size; + out_buffer += chunk_size; + if (buffer_length > 0) { + current_iv.assign(out_buffer - wvcdm::KEY_IV_SIZE, + out_buffer); + } + } + return status; } extern "C" OEMCryptoResult OEMCrypto_Generic_Decrypt( @@ -1121,8 +1139,23 @@ extern "C" OEMCryptoResult OEMCrypto_Generic_Decrypt( if (!kAdapter) return OEMCrypto_ERROR_UNKNOWN_FAILURE; LevelSession pair = kAdapter->get(session); if (!pair.fcn) return OEMCrypto_ERROR_INVALID_SESSION; - return pair.fcn->Generic_Decrypt(pair.session, in_buffer, buffer_length, iv, - algorithm, out_buffer); + OEMCryptoResult status = OEMCrypto_SUCCESS; + std::vector current_iv(iv, iv + wvcdm::KEY_IV_SIZE); + while (buffer_length > 0 && status == OEMCrypto_SUCCESS) { + const size_t chunk_size = std::min(buffer_length, + kMaxGenericEncryptChunkSize); + status = pair.fcn->Generic_Decrypt(pair.session, in_buffer, chunk_size, + ¤t_iv[0], + algorithm, out_buffer); + buffer_length -= chunk_size; + in_buffer += chunk_size; + out_buffer += chunk_size; + if (buffer_length > 0) { + current_iv.assign(in_buffer - wvcdm::KEY_IV_SIZE, + in_buffer); + } + } + return status; } extern "C" OEMCryptoResult OEMCrypto_Generic_Sign(OEMCrypto_SESSION session, diff --git a/libwvdrmengine/docs/WidevineModularDRMSecurityIntegrationGuideforCENC.pdf b/libwvdrmengine/docs/WidevineModularDRMSecurityIntegrationGuideforCENC.pdf index 05dc4576..c024d6aa 100644 Binary files a/libwvdrmengine/docs/WidevineModularDRMSecurityIntegrationGuideforCENC.pdf and b/libwvdrmengine/docs/WidevineModularDRMSecurityIntegrationGuideforCENC.pdf differ diff --git a/libwvdrmengine/docs/Widevine_Modular_DRM_Version_11_Delta.pdf b/libwvdrmengine/docs/Widevine_Modular_DRM_Version_11_Delta.pdf index 7a7a8191..0827756f 100644 Binary files a/libwvdrmengine/docs/Widevine_Modular_DRM_Version_11_Delta.pdf and b/libwvdrmengine/docs/Widevine_Modular_DRM_Version_11_Delta.pdf differ diff --git a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp index ddab2c8a..63e1126b 100644 --- a/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp +++ b/libwvdrmengine/oemcrypto/test/oemcrypto_test.cpp @@ -2464,7 +2464,7 @@ class OEMCryptoSessionTestsDecryptTests pattern_.offset; // block index into encrypted/skip block pattern. size_t buffer_index = 0; // byte index into in and out. size_t block_offset = 0; // byte index into current block. - for (int i = 0; i < subsample_size_.size(); i++) { + for (size_t i = 0; i < subsample_size_.size(); i++) { // Copy clear content. if (subsample_size_[i].clear_size > 0) { memcpy(&(*out_buffer)[buffer_index], &in_buffer[buffer_index], @@ -4431,7 +4431,7 @@ class GenericCryptoTest : public OEMCryptoSessionTests { session_.license().keys[2].key_data_length = wvcdm::MAC_KEY_SIZE; session_.license().keys[3].key_data_length = wvcdm::MAC_KEY_SIZE; - clear_buffer_.assign(kBufferSize, 0); + clear_buffer_.assign(buffer_size_, 0); for (size_t i = 0; i < clear_buffer_.size(); i++) { clear_buffer_[i] = 1 + i % 250; } @@ -4542,7 +4542,8 @@ class GenericCryptoTest : public OEMCryptoSessionTests { EXPECT_NE(OEMCrypto_SUCCESS, sts); } - static const size_t kBufferSize = 160; // multiple of encryption block size. + // This must be a multiple of encryption block size. + size_t buffer_size_ = 160; vector clear_buffer_; vector encrypted_buffer_; uint8_t iv_[wvcdm::KEY_IV_SIZE]; @@ -4571,11 +4572,12 @@ TEST_F(GenericCryptoTest, GenericKeyEncrypt) { TEST_F(GenericCryptoTest, GenericKeyBadEncrypt) { EncryptAndLoadKeys(); - BadEncrypt(0, OEMCrypto_HMAC_SHA256, kBufferSize); - BadEncrypt(0, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize - 10); - BadEncrypt(1, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize); - BadEncrypt(2, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize); - BadEncrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize); + BadEncrypt(0, OEMCrypto_HMAC_SHA256, buffer_size_); + // The buffer size must be a multiple of 16, so subtracting 10 is bad. + BadEncrypt(0, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_ - 10); + BadEncrypt(1, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); + BadEncrypt(2, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); + BadEncrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); } TEST_F(GenericCryptoTest, GenericKeyDecrypt) { @@ -4618,11 +4620,12 @@ TEST_F(GenericCryptoTest, GenericSecureToClear) { TEST_F(GenericCryptoTest, GenericKeyBadDecrypt) { EncryptAndLoadKeys(); - BadDecrypt(1, OEMCrypto_HMAC_SHA256, kBufferSize); - BadDecrypt(1, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize - 10); - BadDecrypt(0, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize); - BadDecrypt(2, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize); - BadDecrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, kBufferSize); + BadDecrypt(1, OEMCrypto_HMAC_SHA256, buffer_size_); + // The buffer size must be a multiple of 16, so subtracting 10 is bad. + BadDecrypt(1, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_ - 10); + BadDecrypt(0, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); + BadDecrypt(2, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); + BadDecrypt(3, OEMCrypto_AES_CBC_128_NO_PADDING, buffer_size_); } TEST_F(GenericCryptoTest, GenericKeySign) { @@ -4686,6 +4689,91 @@ TEST_F(GenericCryptoTest, GenericKeyBadVerify) { BadVerify(3, OEMCrypto_AES_CBC_128_NO_PADDING, SHA256_DIGEST_LENGTH, false); } +TEST_F(GenericCryptoTest, GenericKeyEncryptLargeBufferAPI11) { + // Some applications are known to pass in a block that is almost 400k, but + // the layer above oemcrypto can break it into 100k chunks. + buffer_size_ = 100 * 1024; + EncryptAndLoadKeys(); + unsigned int key_index = 0; + vector expected_encrypted; + EncryptBuffer(key_index, clear_buffer_, &expected_encrypted); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length)); + vector encrypted(clear_buffer_.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Encrypt( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), + iv_, OEMCrypto_AES_CBC_128_NO_PADDING, &encrypted[0])); + ASSERT_EQ(expected_encrypted, encrypted); +} + +TEST_F(GenericCryptoTest, GenericKeyDecryptLargeBufferAPI11) { + // Some applications are known to pass in a block that is almost 400k. + buffer_size_ = 400 * 1024; + EncryptAndLoadKeys(); + unsigned int key_index = 1; + vector encrypted; + EncryptBuffer(key_index, clear_buffer_, &encrypted); + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length)); + vector resultant(encrypted.size()); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Decrypt( + session_.session_id(), &encrypted[0], encrypted.size(), iv_, + OEMCrypto_AES_CBC_128_NO_PADDING, &resultant[0])); + ASSERT_EQ(clear_buffer_, resultant); +} + +TEST_F(GenericCryptoTest, GenericKeySignLargeBufferAPI11) { + buffer_size_ = 100 * 1024; + EncryptAndLoadKeys(); + unsigned int key_index = 2; + vector expected_signature; + SignBuffer(key_index, clear_buffer_, &expected_signature); + + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length)); + size_t gen_signature_length = 0; + ASSERT_EQ(OEMCrypto_ERROR_SHORT_BUFFER, + OEMCrypto_Generic_Sign(session_.session_id(), &clear_buffer_[0], + clear_buffer_.size(), OEMCrypto_HMAC_SHA256, + NULL, &gen_signature_length)); + ASSERT_EQ(static_cast(SHA256_DIGEST_LENGTH), gen_signature_length); + vector signature(SHA256_DIGEST_LENGTH); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Sign(session_.session_id(), &clear_buffer_[0], + clear_buffer_.size(), OEMCrypto_HMAC_SHA256, + &signature[0], &gen_signature_length)); + ASSERT_EQ(expected_signature, signature); +} + +TEST_F(GenericCryptoTest, GenericKeyVerifyLargeBufferAPI11) { + buffer_size_ = 100 * 1024; + EncryptAndLoadKeys(); + unsigned int key_index = 3; + vector signature; + SignBuffer(key_index, clear_buffer_, &signature); + + ASSERT_EQ( + OEMCrypto_SUCCESS, + OEMCrypto_SelectKey(session_.session_id(), + session_.license().keys[key_index].key_id, + session_.license().keys[key_index].key_id_length)); + ASSERT_EQ(OEMCrypto_SUCCESS, + OEMCrypto_Generic_Verify( + session_.session_id(), &clear_buffer_[0], clear_buffer_.size(), + OEMCrypto_HMAC_SHA256, &signature[0], signature.size())); +} + TEST_F(GenericCryptoTest, KeyDurationEncrypt) { EncryptAndLoadKeys(); vector expected_encrypted;