// // Copyright 2013 Google Inc. All Rights Reserved. // #include #include #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "media/stagefright/foundation/ABase.h" #include "media/stagefright/foundation/AString.h" #include "wv_cdm_constants.h" #include "wv_cdm_types.h" #include "wv_content_decryption_module.h" #include "WVCryptoPlugin.h" using namespace android; using namespace std; using namespace testing; using namespace wvcdm; using namespace wvdrm; class MockCDM : public WvContentDecryptionModule { public: 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*)); }; class WVCryptoPluginTest : public Test { protected: static const uint32_t kSessionIdSize = 16; uint8_t sessionId[kSessionIdSize]; virtual void SetUp() { FILE* fp = fopen("/dev/urandom", "r"); fread(sessionId, sizeof(uint8_t), kSessionIdSize, fp); fclose(fp); // Set default CdmResponseType value for gMock DefaultValue::Set(wvcdm::NO_ERROR); } }; TEST_F(WVCryptoPluginTest, CorrectlyReportsSecureBuffers) { MockCDM cdm; WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm); CdmQueryMap l1Map; l1Map[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L1; CdmQueryMap l3Map; l3Map[QUERY_KEY_SECURITY_LEVEL] = QUERY_VALUE_SECURITY_LEVEL_L3; EXPECT_CALL(cdm, QueryStatus(_)) .WillOnce(DoAll(SetArgPointee<0>(l1Map), Return(wvcdm::NO_ERROR))) .WillOnce(DoAll(SetArgPointee<0>(l3Map), Return(wvcdm::NO_ERROR))); EXPECT_TRUE(plugin.requiresSecureDecoderComponent("video/mp4")) << "WVCryptoPlugin incorrectly allows an insecure video decoder on L1"; EXPECT_FALSE(plugin.requiresSecureDecoderComponent("video/mp4")) << "WVCryptoPlugin incorrectly expects a secure video decoder on L3"; EXPECT_FALSE(plugin.requiresSecureDecoderComponent("audio/aac")) << "WVCryptoPlugin incorrectly expects a secure audio decoder"; } TEST_F(WVCryptoPluginTest, AttemptsToDecrypt) { MockCDM cdm; WVCryptoPlugin plugin(sessionId, kSessionIdSize, &cdm); 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; 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)); 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, 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, 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, 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, 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, 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, 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, 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, false, 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[0], CryptoPlugin::kMode_AES_CTR, in, subSamples, kSubSampleCount, out, &errorDetailMessage); EXPECT_EQ(static_cast(kDataSize), res) << "WVCryptoPlugin decrypted the wrong number of bytes"; 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."; }