From 3a28eeeb684c5eee84f2627d10325a58df5c9ae8 Mon Sep 17 00:00:00 2001 From: Jeff Tinker Date: Mon, 22 Apr 2013 17:50:38 -0700 Subject: [PATCH] Part of Qualcomm L1 OEMCrypto integration on mako bug: 8621521 This fixes a problem where insecure audio buffers were being passed incorrectly as secure buffers to the trusted environment's OEMCrypto_DecryptCTR. This is a merge of the following changes from the widevine git repository to android git repository: https://widevine-internal-review.googlesource.com/#/c/5163/2 Allow selection of secure/non-secure buffers https://widevine-internal-review.googlesource.com/#/c/5164/ Pass Secure Buffer Request to CDM Change-Id: Iec1192a216305c6cf92c359b15b148eccc6ce6ce --- libwvdrmengine/cdm/core/include/cdm_engine.h | 1 + libwvdrmengine/cdm/core/include/cdm_session.h | 1 + .../cdm/core/include/crypto_session.h | 1 + libwvdrmengine/cdm/core/src/cdm_engine.cpp | 3 +- libwvdrmengine/cdm/core/src/cdm_session.cpp | 8 +- .../cdm/core/src/crypto_session.cpp | 4 +- .../include/wv_content_decryption_module.h | 1 + .../cdm/src/wv_content_decryption_module.cpp | 3 +- .../cdm/test/request_license_test.cpp | 16 +++ .../mediacrypto/src/WVCryptoPlugin.cpp | 4 +- .../mediacrypto/test/WVCryptoPlugin_test.cpp | 116 +++++++++++++----- 11 files changed, 122 insertions(+), 36 deletions(-) diff --git a/libwvdrmengine/cdm/core/include/cdm_engine.h b/libwvdrmengine/cdm/core/include/cdm_engine.h index ae6099c9..adbc2114 100644 --- a/libwvdrmengine/cdm/core/include/cdm_engine.h +++ b/libwvdrmengine/cdm/core/include/cdm_engine.h @@ -85,6 +85,7 @@ class CdmEngine : public TimerHandler { // Accept encrypted buffer and return decrypted data. CdmResponseType Decrypt(const CdmSessionId& session_id, bool is_encrypted, + bool is_secure, const KeyId& key_id, const uint8_t* encrypt_buffer, size_t encrypt_length, diff --git a/libwvdrmengine/cdm/core/include/cdm_session.h b/libwvdrmengine/cdm/core/include/cdm_session.h index 9d01a852..92d3ed11 100644 --- a/libwvdrmengine/cdm/core/include/cdm_session.h +++ b/libwvdrmengine/cdm/core/include/cdm_session.h @@ -52,6 +52,7 @@ class CdmSession { // Decrypt() - Accept encrypted buffer and return decrypted data. CdmResponseType Decrypt(bool is_encrypted, + bool is_secure, const KeyId& key_id, const uint8_t* encrypt_buffer, size_t encrypt_length, diff --git a/libwvdrmengine/cdm/core/include/crypto_session.h b/libwvdrmengine/cdm/core/include/crypto_session.h index 8e68b266..0e031f42 100644 --- a/libwvdrmengine/cdm/core/include/crypto_session.h +++ b/libwvdrmengine/cdm/core/include/crypto_session.h @@ -66,6 +66,7 @@ class CryptoSession { // Media data path bool SelectKey(const std::string& key_id); CdmResponseType Decrypt(bool is_encrypted, + bool is_secure, const uint8_t* encrypt_buffer, size_t encrypt_length, const std::vector& iv, diff --git a/libwvdrmengine/cdm/core/src/cdm_engine.cpp b/libwvdrmengine/cdm/core/src/cdm_engine.cpp index 66e2c56d..3a1fda6e 100644 --- a/libwvdrmengine/cdm/core/src/cdm_engine.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_engine.cpp @@ -670,6 +670,7 @@ CdmResponseType CdmEngine::ReleaseSecureStops( CdmResponseType CdmEngine::Decrypt( const CdmSessionId& session_id, bool is_encrypted, + bool is_secure, const KeyId& key_id, const uint8_t* encrypt_buffer, size_t encrypt_length, @@ -684,7 +685,7 @@ CdmResponseType CdmEngine::Decrypt( return KEY_ERROR; } - return iter->second->Decrypt(is_encrypted, key_id, encrypt_buffer, + return iter->second->Decrypt(is_encrypted, is_secure, key_id, encrypt_buffer, encrypt_length, iv, block_offset, decrypt_buffer, decrypt_buffer_offset, is_video); diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index bf19575e..75dfcdc9 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -145,6 +145,7 @@ CdmResponseType CdmSession::CancelKeyRequest() { // Decrypt() - Accept encrypted buffer and return decrypted data. CdmResponseType CdmSession::Decrypt(bool is_encrypted, + bool is_secure, const KeyId& key_id, const uint8_t* encrypt_buffer, size_t encrypt_length, @@ -168,9 +169,10 @@ CdmResponseType CdmSession::Decrypt(bool is_encrypted, } } - return crypto_session_->Decrypt(is_encrypted, encrypt_buffer, encrypt_length, - iv, block_offset, decrypt_buffer, - decrypt_buffer_offset, is_video); + return crypto_session_->Decrypt(is_encrypted, is_secure, encrypt_buffer, + encrypt_length, iv, block_offset, + decrypt_buffer, decrypt_buffer_offset, + is_video); } // License renewal diff --git a/libwvdrmengine/cdm/core/src/crypto_session.cpp b/libwvdrmengine/cdm/core/src/crypto_session.cpp index 724e9031..18c4e2b9 100755 --- a/libwvdrmengine/cdm/core/src/crypto_session.cpp +++ b/libwvdrmengine/cdm/core/src/crypto_session.cpp @@ -387,6 +387,7 @@ bool CryptoSession::GenerateSignature(const std::string& message, } CdmResponseType CryptoSession::Decrypt(bool is_encrypted, + bool is_secure, const uint8_t* encrypt_buffer, size_t encrypt_length, const std::vector& iv, @@ -400,7 +401,8 @@ CdmResponseType CryptoSession::Decrypt(bool is_encrypted, } OEMCrypto_DestBufferDesc buffer_descriptor; - buffer_descriptor.type = destination_buffer_type_; + buffer_descriptor.type = + is_secure ? destination_buffer_type_: OEMCrypto_BufferType_Clear; switch (buffer_descriptor.type) { case OEMCrypto_BufferType_Clear: diff --git a/libwvdrmengine/cdm/include/wv_content_decryption_module.h b/libwvdrmengine/cdm/include/wv_content_decryption_module.h index 7343fdb0..9da0b01e 100644 --- a/libwvdrmengine/cdm/include/wv_content_decryption_module.h +++ b/libwvdrmengine/cdm/include/wv_content_decryption_module.h @@ -63,6 +63,7 @@ class WvContentDecryptionModule { // Accept encrypted buffer and return decrypted data. virtual CdmResponseType Decrypt(const CdmSessionId& session_id, bool is_encrypted, + bool is_secure, const KeyId& key_id, const uint8_t* encrypt_buffer, size_t encrypt_length, diff --git a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp index 8ce82340..8f30578b 100644 --- a/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp +++ b/libwvdrmengine/cdm/src/wv_content_decryption_module.cpp @@ -97,6 +97,7 @@ CdmResponseType WvContentDecryptionModule::ReleaseSecureStops( CdmResponseType WvContentDecryptionModule::Decrypt( const CdmSessionId& session_id, bool is_encrypted, + bool is_secure, const KeyId& key_id, const uint8_t* encrypt_buffer, size_t encrypt_length, @@ -104,7 +105,7 @@ CdmResponseType WvContentDecryptionModule::Decrypt( size_t block_offset, void* decrypt_buffer, size_t decrypt_buffer_offset) { - return cdm_engine_->Decrypt(session_id, is_encrypted, key_id, + return cdm_engine_->Decrypt(session_id, is_encrypted, is_secure, key_id, encrypt_buffer, encrypt_length, iv, block_offset, decrypt_buffer, decrypt_buffer_offset, true); diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 4ae1c606..5b4f5b6b 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -29,6 +29,7 @@ namespace wvcdm { typedef struct DecryptionData { bool is_encrypted; + bool is_secure; wvcdm::KeyId key_id; std::vector encrypt_data; std::vector iv; @@ -252,6 +253,7 @@ TEST_F(WvCdmRequestLicenseTest, ClearDecryptionTest) { // key 1, clear, 256b DecryptionData data; data.is_encrypted = false; + data.is_secure = false; data.key_id = wvcdm::a2bs_hex("E02562E04CD55351B14B3D748D36ED8E"); data.encrypt_data = wvcdm::a2b_hex( "9da401105ab8da443e93e6fe089dfc69e00a9a51690d406872f338c5fa7dd3d5" @@ -280,6 +282,7 @@ TEST_F(WvCdmRequestLicenseTest, ClearDecryptionTest) { EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, data.is_encrypted, + data.is_secure, data.key_id, &data.encrypt_data.front(), encrypt_length, @@ -299,6 +302,7 @@ TEST_F(WvCdmRequestLicenseTest, ClearDecryptionNoKeyTest) { // key 1, clear, 256b DecryptionData data; data.is_encrypted = false; + data.is_secure = false; data.key_id = wvcdm::a2bs_hex("77777777777777777777777777777777"); data.encrypt_data = wvcdm::a2b_hex( "9da401105ab8da443e93e6fe089dfc69e00a9a51690d406872f338c5fa7dd3d5" @@ -327,6 +331,7 @@ TEST_F(WvCdmRequestLicenseTest, ClearDecryptionNoKeyTest) { EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, data.is_encrypted, + data.is_secure, data.key_id, &data.encrypt_data.front(), encrypt_length, @@ -348,6 +353,7 @@ TEST_F(WvCdmRequestLicenseTest, DecryptionTest) { // key 1, encrypted, 256b DecryptionData data; data.is_encrypted = true; + data.is_secure = false; data.key_id = wvcdm::a2bs_hex("E02562E04CD55351B14B3D748D36ED8E"); data.encrypt_data = wvcdm::a2b_hex( "3b2cbde084973539329bd5656da22d20396249bf4a18a51c38c4743360cc9fea" @@ -376,6 +382,7 @@ TEST_F(WvCdmRequestLicenseTest, DecryptionTest) { EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, data.is_encrypted, + data.is_secure, data.key_id, &data.encrypt_data.front(), encrypt_length, @@ -399,6 +406,7 @@ TEST_F(WvCdmRequestLicenseTest, SwitchKeyDecryptionTest) { // block 0, key 1, encrypted, 256b data[0].is_encrypted = true; + data[0].is_secure = false; data[0].key_id = wvcdm::a2bs_hex("E02562E04CD55351B14B3D748D36ED8E"); data[0].encrypt_data = wvcdm::a2b_hex( "3b2cbde084973539329bd5656da22d20396249bf4a18a51c38c4743360cc9fea" @@ -423,6 +431,7 @@ TEST_F(WvCdmRequestLicenseTest, SwitchKeyDecryptionTest) { // block 1, key 3, encrypted, 256b data[1].is_encrypted = true; + data[1].is_secure = false; data[1].key_id = wvcdm::a2bs_hex("0065901A64A25899A5193664ABF9AF62"); data[1].encrypt_data = wvcdm::a2b_hex( "337f294addb4c16d1015fd839e80314472432eda503bd0529422318bec7d2b34" @@ -452,6 +461,7 @@ TEST_F(WvCdmRequestLicenseTest, SwitchKeyDecryptionTest) { EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, data[i].is_encrypted, + data[i].is_secure, data[i].key_id, &data[i].encrypt_data.front(), encrypt_length, @@ -475,6 +485,7 @@ TEST_F(WvCdmRequestLicenseTest, PartialBlockDecryptionTest) { // key 3, encrypted, 125b, offset 0 DecryptionData data; data.is_encrypted = true; + data.is_secure = false; data.key_id = wvcdm::a2bs_hex("0065901A64A25899A5193664ABF9AF62"); data.encrypt_data = wvcdm::a2b_hex( "337f294addb4c16d1015fd839e80314472432eda503bd0529422318bec7d2b34" @@ -495,6 +506,7 @@ TEST_F(WvCdmRequestLicenseTest, PartialBlockDecryptionTest) { EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, data.is_encrypted, + data.is_secure, data.key_id, &data.encrypt_data.front(), encrypt_length, @@ -516,6 +528,7 @@ TEST_F(WvCdmRequestLicenseTest, PartialBlockWithOffsetDecryptionTest) { // key 3, encrypted, 123b, offset 5 DecryptionData data; data.is_encrypted = true; + data.is_secure = false; data.key_id = wvcdm::a2bs_hex("0065901A64A25899A5193664ABF9AF62"); data.encrypt_data = wvcdm::a2b_hex( "97f39b919ba56f3c3a51ecdcd7318bc130f054320c74db3990f925" @@ -536,6 +549,7 @@ TEST_F(WvCdmRequestLicenseTest, PartialBlockWithOffsetDecryptionTest) { EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, data.is_encrypted, + data.is_secure, data.key_id, &data.encrypt_data.front(), encrypt_length, @@ -561,6 +575,7 @@ TEST_F(WvCdmRequestLicenseTest, KeyControlBlockDecryptionTest) { // block 4, key 2, encrypted data.is_encrypted = true; + data.is_secure = false; data.key_id = wvcdm::a2bs_hex("0915007CAA9B5931B76A3A85F046523E"); data.encrypt_data = wvcdm::a2b_hex( "6758ac1c6ccf5d08479e3bfc62bbc0fd154aff4415aa7ed53d89e3983248d117" @@ -589,6 +604,7 @@ TEST_F(WvCdmRequestLicenseTest, KeyControlBlockDecryptionTest) { EXPECT_EQ(NO_ERROR, decryptor_.Decrypt(session_id_, data.is_encrypted, + data.is_secure, data.key_id, &data.encrypt_data.front(), encrypt_length, diff --git a/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp b/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp index b4b8071a..6bcc682a 100644 --- a/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp +++ b/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp @@ -100,7 +100,7 @@ ssize_t WVCryptoPlugin::decrypt(bool secure, const uint8_t key[KEY_ID_SIZE], // "Decrypt" any unencrypted data. Per the ISO-CENC standard, clear data // comes before encrypted data. if (subSample.mNumBytesOfClearData != 0) { - CdmResponseType res = mCDM->Decrypt(mSessionId, false, keyId, + CdmResponseType res = mCDM->Decrypt(mSessionId, false, secure, keyId, source + offset, subSample.mNumBytesOfClearData, ivVector, 0, dest, offset); @@ -118,7 +118,7 @@ ssize_t WVCryptoPlugin::decrypt(bool secure, const uint8_t key[KEY_ID_SIZE], // Decrypt any encrypted data. Per the ISO-CENC standard, encrypted data // comes after clear data. if (subSample.mNumBytesOfEncryptedData != 0) { - CdmResponseType res = mCDM->Decrypt(mSessionId, true, keyId, + CdmResponseType res = mCDM->Decrypt(mSessionId, true, secure, keyId, source + offset, subSample.mNumBytesOfEncryptedData, ivVector, blockOffset, dest, offset); diff --git a/libwvdrmengine/mediacrypto/test/WVCryptoPlugin_test.cpp b/libwvdrmengine/mediacrypto/test/WVCryptoPlugin_test.cpp index 15a3fb21..e4f6c94d 100644 --- a/libwvdrmengine/mediacrypto/test/WVCryptoPlugin_test.cpp +++ b/libwvdrmengine/mediacrypto/test/WVCryptoPlugin_test.cpp @@ -23,10 +23,10 @@ using namespace wvdrm; class MockCDM : public WvContentDecryptionModule { public: - MOCK_METHOD9(Decrypt, CdmResponseType(const CdmSessionId&, bool, const KeyId&, - const uint8_t*, size_t, - const std::vector&, size_t, - void*, size_t)); + MOCK_METHOD10(Decrypt, CdmResponseType(const CdmSessionId&, bool, bool, + const KeyId&, const uint8_t*, size_t, + const std::vector&, size_t, + void*, size_t)); MOCK_METHOD1(QueryStatus, CdmResponseType(CdmQueryMap*)); }; @@ -90,14 +90,14 @@ TEST_F(WVCryptoPluginTest, AttemptsToDecrypt) { static const size_t kSubSampleCount = 6; CryptoPlugin::SubSample subSamples[kSubSampleCount]; memset(subSamples, 0, sizeof(subSamples)); - subSamples[0].mNumBytesOfEncryptedData = 16; // 0 - subSamples[1].mNumBytesOfClearData = 16; // 1 - subSamples[1].mNumBytesOfEncryptedData = 16; // 1 - subSamples[2].mNumBytesOfEncryptedData = 8; // 2 - subSamples[3].mNumBytesOfClearData = 29; // 2 - subSamples[3].mNumBytesOfEncryptedData = 24; // 2 - subSamples[4].mNumBytesOfEncryptedData = 60; // 3 - subSamples[5].mNumBytesOfEncryptedData = 16; // 4 + subSamples[0].mNumBytesOfEncryptedData = 16; + subSamples[1].mNumBytesOfClearData = 16; + subSamples[1].mNumBytesOfEncryptedData = 16; + subSamples[2].mNumBytesOfEncryptedData = 8; + subSamples[3].mNumBytesOfClearData = 29; + subSamples[3].mNumBytesOfEncryptedData = 24; + subSamples[4].mNumBytesOfEncryptedData = 60; + subSamples[5].mNumBytesOfEncryptedData = 16; uint8_t iv[5][KEY_IV_SIZE]; memcpy(iv[0], baseIv, sizeof(baseIv)); @@ -116,48 +116,56 @@ TEST_F(WVCryptoPluginTest, AttemptsToDecrypt) { // SubSample 0 EXPECT_CALL(cdm, Decrypt(ElementsAreArray(sessionId, kSessionIdSize), true, - ElementsAreArray(keyId, KEY_ID_SIZE), in, 16, - ElementsAreArray(iv[0], KEY_IV_SIZE), 0, out, 0)) + false, ElementsAreArray(keyId, KEY_ID_SIZE), + in, 16, ElementsAreArray(iv[0], KEY_IV_SIZE), + 0, out, 0)) .Times(1); // SubSample 1 EXPECT_CALL(cdm, Decrypt(ElementsAreArray(sessionId, kSessionIdSize), false, - ElementsAreArray(keyId, KEY_ID_SIZE), in + 16, 16, - ElementsAreArray(iv[1], KEY_IV_SIZE), 0, out, 16)) + false, ElementsAreArray(keyId, KEY_ID_SIZE), + in + 16, 16, ElementsAreArray(iv[1], KEY_IV_SIZE), + 0, out, 16)) .Times(1); EXPECT_CALL(cdm, Decrypt(ElementsAreArray(sessionId, kSessionIdSize), true, - ElementsAreArray(keyId, KEY_ID_SIZE), in + 32, 16, - ElementsAreArray(iv[1], KEY_IV_SIZE), 0, out, 32)) + false, ElementsAreArray(keyId, KEY_ID_SIZE), + in + 32, 16, ElementsAreArray(iv[1], KEY_IV_SIZE), + 0, out, 32)) .Times(1); // SubSample 2 EXPECT_CALL(cdm, Decrypt(ElementsAreArray(sessionId, kSessionIdSize), true, - ElementsAreArray(keyId, KEY_ID_SIZE), in + 48, 8, - ElementsAreArray(iv[2], KEY_IV_SIZE), 0, out, 48)) + false, ElementsAreArray(keyId, KEY_ID_SIZE), + in + 48, 8, ElementsAreArray(iv[2], KEY_IV_SIZE), + 0, out, 48)) .Times(1); // SubSample 3 EXPECT_CALL(cdm, Decrypt(ElementsAreArray(sessionId, kSessionIdSize), false, - ElementsAreArray(keyId, KEY_ID_SIZE), in + 56, 29, - ElementsAreArray(iv[2], KEY_IV_SIZE), 0, out, 56)) + false, ElementsAreArray(keyId, KEY_ID_SIZE), + in + 56, 29, ElementsAreArray(iv[2], KEY_IV_SIZE), + 0, out, 56)) .Times(1); EXPECT_CALL(cdm, Decrypt(ElementsAreArray(sessionId, kSessionIdSize), true, - ElementsAreArray(keyId, KEY_ID_SIZE), in + 85, 24, - ElementsAreArray(iv[2], KEY_IV_SIZE), 8, out, 85)) + false, ElementsAreArray(keyId, KEY_ID_SIZE), + in + 85, 24, ElementsAreArray(iv[2], KEY_IV_SIZE), + 8, out, 85)) .Times(1); // SubSample 4 EXPECT_CALL(cdm, Decrypt(ElementsAreArray(sessionId, kSessionIdSize), true, - ElementsAreArray(keyId, KEY_ID_SIZE), in + 109, 60, - ElementsAreArray(iv[3], KEY_IV_SIZE), 0, out, 109)) + false, ElementsAreArray(keyId, KEY_ID_SIZE), + in + 109, 60, ElementsAreArray(iv[3], KEY_IV_SIZE), + 0, out, 109)) .Times(1); // SubSample 5 EXPECT_CALL(cdm, Decrypt(ElementsAreArray(sessionId, kSessionIdSize), true, - ElementsAreArray(keyId, KEY_ID_SIZE), in + 169, 16, - ElementsAreArray(iv[4], KEY_IV_SIZE), 12, out, 169)) + false, ElementsAreArray(keyId, KEY_ID_SIZE), + in + 169, 16, ElementsAreArray(iv[4], KEY_IV_SIZE), + 12, out, 169)) .Times(1); } @@ -172,3 +180,55 @@ TEST_F(WVCryptoPluginTest, AttemptsToDecrypt) { EXPECT_EQ(0u, errorDetailMessage.size()) << "WVCryptoPlugin reported a detailed error message."; } + +TEST_F(WVCryptoPluginTest, CommunicatesSecureBufferRequest) { + MockCDM cdm; + WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm); + + uint8_t keyId[KEY_ID_SIZE]; + uint8_t iv[KEY_IV_SIZE]; + + static const size_t kDataSize = 32; + uint8_t in[kDataSize]; + uint8_t out[kDataSize]; + + FILE* fp = fopen("/dev/urandom", "r"); + fread(keyId, sizeof(uint8_t), KEY_ID_SIZE, fp); + fread(iv, sizeof(uint8_t), KEY_IV_SIZE, fp); + fread(in, sizeof(uint8_t), kDataSize, fp); + fclose(fp); + + static const uint32_t kSubSampleCount = 1; + CryptoPlugin::SubSample subSamples[kSubSampleCount]; + memset(subSamples, 0, sizeof(subSamples)); + subSamples[0].mNumBytesOfClearData = 16; + subSamples[0].mNumBytesOfEncryptedData = 16; + + // Specify the expected calls to Decrypt + { + InSequence calls; + + EXPECT_CALL(cdm, Decrypt(_, _, false, _, _, _, _, _, _, _)) + .Times(2); + + EXPECT_CALL(cdm, Decrypt(_, _, true, _, _, _, _, _, _, _)) + .Times(2); + } + + AString errorDetailMessage; + + ssize_t res = plugin.decrypt(false, keyId, iv, CryptoPlugin::kMode_AES_CTR, + in, subSamples, kSubSampleCount, out, + &errorDetailMessage); + ASSERT_GE(res, 0) << + "WVCryptoPlugin returned an error"; + EXPECT_EQ(0u, errorDetailMessage.size()) << + "WVCryptoPlugin reported a detailed error message."; + + res = plugin.decrypt(true, keyId, iv, CryptoPlugin::kMode_AES_CTR, in, + subSamples, kSubSampleCount, out, &errorDetailMessage); + ASSERT_GE(res, 0) << + "WVCryptoPlugin returned an error"; + EXPECT_EQ(0u, errorDetailMessage.size()) << + "WVCryptoPlugin reported a detailed error message."; +}