From 088288cb762245c082b1f54777fc37167982fec5 Mon Sep 17 00:00:00 2001 From: "John \"Juce\" Bruce" Date: Mon, 22 Apr 2013 14:00:46 -0700 Subject: [PATCH] Increment IV in WVCryptoPlugin WVCryptoPlugin was not properly implementing part of its responsibilities to AES-CTR in ISO-CENC. Specifically, it was not incrementing the IV after each block. Also, I have greatly expanded the unit tests for decrypt() to catch more edge cases. This change fixes the two failing test vectors in the Java integration tests. Copied from https://widevine-internal-review.googlesource.com/#/c/5123/2 Bug: 8656421 Change-Id: If935edbf01068f5b0d5254b4e657057ef57d8fcf --- .../mediacrypto/include/WVCryptoPlugin.h | 1 + .../mediacrypto/src/WVCryptoPlugin.cpp | 20 +++- .../mediacrypto/test/WVCryptoPlugin_test.cpp | 109 ++++++++++++------ 3 files changed, 91 insertions(+), 39 deletions(-) diff --git a/libwvdrmengine/mediacrypto/include/WVCryptoPlugin.h b/libwvdrmengine/mediacrypto/include/WVCryptoPlugin.h index 411e4001..172935bc 100644 --- a/libwvdrmengine/mediacrypto/include/WVCryptoPlugin.h +++ b/libwvdrmengine/mediacrypto/include/WVCryptoPlugin.h @@ -36,6 +36,7 @@ class WVCryptoPlugin : public android::CryptoPlugin { const wvcdm::CdmSessionId mSessionId; wvcdm::CdmSessionId configureTestMode(const void* data, size_t size); + static void incrementIV(uint64_t increaseBy, std::vector* ivPtr); }; } // namespace wvdrm diff --git a/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp b/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp index 8a8f3ba7..b4b8071a 100644 --- a/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp +++ b/libwvdrmengine/mediacrypto/src/WVCryptoPlugin.cpp @@ -8,6 +8,7 @@ #include "WVCryptoPlugin.h" +#include #include #include #include @@ -78,13 +79,14 @@ ssize_t WVCryptoPlugin::decrypt(bool secure, const uint8_t key[KEY_ID_SIZE], // Convert parameters to the form the CDM wishes to consume them in. const KeyId keyId(reinterpret_cast(key), KEY_ID_SIZE); - const vector ivVector(iv, iv + KEY_IV_SIZE); + vector ivVector(iv, iv + KEY_IV_SIZE); const uint8_t* const source = static_cast(srcPtr); uint8_t* const dest = static_cast(dstPtr); // Iterate through subsamples, sending them to the CDM serially. size_t offset = 0; - size_t encrypted_offset = 0; + static const size_t kAESBlockSize = 16; + size_t blockOffset = 0; for (size_t i = 0; i < numSubSamples; ++i) { const SubSample &subSample = subSamples[i]; @@ -119,8 +121,7 @@ ssize_t WVCryptoPlugin::decrypt(bool secure, const uint8_t key[KEY_ID_SIZE], CdmResponseType res = mCDM->Decrypt(mSessionId, true, keyId, source + offset, subSample.mNumBytesOfEncryptedData, - ivVector, encrypted_offset % 16, dest, - offset); + ivVector, blockOffset, dest, offset); if (!isCdmResponseTypeSuccess(res)) { ALOGE("Decrypt error result in session %s during encrypted block: %d", @@ -130,7 +131,10 @@ ssize_t WVCryptoPlugin::decrypt(bool secure, const uint8_t key[KEY_ID_SIZE], } offset += subSample.mNumBytesOfEncryptedData; - encrypted_offset += subSample.mNumBytesOfEncryptedData; + + blockOffset += subSample.mNumBytesOfEncryptedData; + incrementIV(blockOffset / kAESBlockSize, &ivVector); + blockOffset %= kAESBlockSize; } } @@ -158,4 +162,10 @@ ssize_t WVCryptoPlugin::decrypt(bool secure, const uint8_t key[KEY_ID_SIZE], return static_cast(offset); } +void WVCryptoPlugin::incrementIV(uint64_t increaseBy, vector* ivPtr) { + vector& iv = *ivPtr; + uint64_t* counterBuffer = reinterpret_cast(&iv[8]); + (*counterBuffer) = htonq(ntohq(*counterBuffer) + increaseBy); +} + } // namespace wvdrm diff --git a/libwvdrmengine/mediacrypto/test/WVCryptoPlugin_test.cpp b/libwvdrmengine/mediacrypto/test/WVCryptoPlugin_test.cpp index 184eab2a..ed515cd5 100644 --- a/libwvdrmengine/mediacrypto/test/WVCryptoPlugin_test.cpp +++ b/libwvdrmengine/mediacrypto/test/WVCryptoPlugin_test.cpp @@ -36,32 +36,11 @@ class WVCryptoPluginTest : public Test { static const uint32_t kSessionIdSize = 16; uint8_t sessionId[kSessionIdSize]; - uint8_t keyId[KEY_ID_SIZE]; - uint8_t iv[KEY_IV_SIZE]; - - static const uint32_t kDataSize = 64; - uint8_t in[kDataSize]; - uint8_t out[kDataSize]; - - static const uint32_t kSubSampleCount = 3; - CryptoPlugin::SubSample subSamples[kSubSampleCount]; - virtual void SetUp() { FILE* fp = fopen("/dev/urandom", "r"); fread(sessionId, sizeof(uint8_t), kSessionIdSize, fp); - 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); - memset(out, 0, sizeof(out)); - - memset(subSamples, 0, sizeof(subSamples)); - subSamples[0].mNumBytesOfEncryptedData = 16; - subSamples[1].mNumBytesOfClearData = 16; - subSamples[1].mNumBytesOfEncryptedData = 24; - subSamples[2].mNumBytesOfEncryptedData = 8; - // Set default CdmResponseType value for gMock DefaultValue::Set(wvcdm::NO_ERROR); } @@ -95,34 +74,96 @@ TEST_F(WVCryptoPluginTest, AttemptsToDecrypt) { MockCDM cdm; WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm); - // Specify the expected calls to Decrypt + uint8_t keyId[KEY_ID_SIZE]; + uint8_t baseIv[KEY_IV_SIZE]; + + static const size_t kDataSize = 185; + uint8_t in[kDataSize]; + uint8_t out[kDataSize]; + + FILE* fp = fopen("/dev/urandom", "r"); + fread(keyId, sizeof(uint8_t), KEY_ID_SIZE, fp); + fread(baseIv, sizeof(uint8_t), KEY_IV_SIZE, fp); + fread(in, sizeof(uint8_t), kDataSize, fp); + fclose(fp); + + 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 + + uint8_t iv[5][KEY_IV_SIZE]; + memcpy(iv[0], baseIv, sizeof(baseIv)); + iv[0][15] = 0; + memcpy(iv[1], baseIv, sizeof(baseIv)); + iv[1][15] = 1; + memcpy(iv[2], baseIv, sizeof(baseIv)); + iv[2][15] = 2; + memcpy(iv[3], baseIv, sizeof(baseIv)); + iv[3][15] = 4; + memcpy(iv[4], baseIv, sizeof(baseIv)); + iv[4][15] = 7; + { InSequence calls; + // SubSample 0 EXPECT_CALL(cdm, Decrypt(ElementsAreArray(sessionId, kSessionIdSize), true, ElementsAreArray(keyId, KEY_ID_SIZE), in, 16, - ElementsAreArray(iv, KEY_IV_SIZE), 0, out, 0)) - .WillOnce(Return(wvcdm::NO_ERROR)); + 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, KEY_IV_SIZE), 0, out, 16)) - .WillOnce(Return(wvcdm::NO_ERROR)); + 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, 24, - ElementsAreArray(iv, KEY_IV_SIZE), 0, out, 32)) - .WillOnce(Return(wvcdm::NO_ERROR)); + 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)) + .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)) + .Times(1); EXPECT_CALL(cdm, Decrypt(ElementsAreArray(sessionId, kSessionIdSize), true, - ElementsAreArray(keyId, KEY_ID_SIZE), in + 56, 8, - ElementsAreArray(iv, KEY_IV_SIZE), 8, out, 56)) - .WillOnce(Return(wvcdm::NO_ERROR)); + 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)) + .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)) + .Times(1); } AString errorDetailMessage; - ssize_t res = plugin.decrypt(false, keyId, iv, CryptoPlugin::kMode_AES_CTR, + ssize_t res = plugin.decrypt(false, keyId, iv[0], CryptoPlugin::kMode_AES_CTR, in, subSamples, kSubSampleCount, out, &errorDetailMessage); @@ -130,4 +171,4 @@ TEST_F(WVCryptoPluginTest, AttemptsToDecrypt) { "WVCryptoPlugin decrypted the wrong number of bytes"; EXPECT_EQ(0u, errorDetailMessage.size()) << "WVCryptoPlugin reported a detailed error message."; -} +} \ No newline at end of file