blob: bc0a0de45e3553b4ff5b6592da527bf546cb07eb [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "webkit/media/crypto/proxy_decryptor.h"
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop.h"
#include "media/base/decoder_buffer.h"
#include "media/base/decrypt_config.h"
#include "media/base/mock_filters.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::AtLeast;
using ::testing::IsNull;
using ::testing::NotNull;
using media::DecoderBuffer;
using media::DecryptConfig;
using media::Decryptor;
namespace webkit_media {
static const uint8 kFakeKeyId[] = { 0x4b, 0x65, 0x79, 0x20, 0x49, 0x44 };
static const uint8 kFakeIv[DecryptConfig::kDecryptionKeySize] = { 0 };
static const char kFakeKeySystem[] = "system.key.fake";
static const char kFakeSessionId[] = "FakeSessionId";
static const uint8 kFakeKey[] = { 0x4b, 0x65, 0x79 };
static const uint8 kEncryptedData[] = { 0x65, 0x6E, 0x63, 0x72, 0x79 };
static const uint8 kDecryptedData[] = { 0x64, 0x65, 0x63, 0x72, 0x79 };
// Creates a fake non-empty encrypted buffer.
static scoped_refptr<DecoderBuffer> CreateFakeEncryptedBuffer() {
const int encrypted_frame_offset = 1; // This should be non-zero.
scoped_refptr<DecoderBuffer> encrypted_buffer =
DecoderBuffer::CopyFrom(kEncryptedData, arraysize(kEncryptedData));
encrypted_buffer->SetDecryptConfig(scoped_ptr<DecryptConfig>(
new DecryptConfig(
std::string(reinterpret_cast<const char*>(kFakeKeyId),
arraysize(kFakeKeyId)),
std::string(reinterpret_cast<const char*>(kFakeIv),
DecryptConfig::kDecryptionKeySize),
encrypted_frame_offset,
std::vector<media::SubsampleEntry>())));
return encrypted_buffer;
}
ACTION_P2(RunDecryptCB, status, buffer) {
arg1.Run(status, buffer);
}
ACTION_P(ScheduleMessageLoopToStop, message_loop) {
message_loop->PostTask(FROM_HERE, MessageLoop::QuitClosure());
}
// Tests the interaction between external Decryptor calls and concrete Decryptor
// implementations. This test is not interested in any specific Decryptor
// implementation. A MockDecryptor is used here to serve this purpose.
class ProxyDecryptorTest : public testing::Test {
public:
ProxyDecryptorTest()
: proxy_decryptor_(&client_, NULL, NULL),
real_decryptor_(new media::MockDecryptor()),
encrypted_buffer_(CreateFakeEncryptedBuffer()),
decrypted_buffer_(DecoderBuffer::CopyFrom(kDecryptedData,
arraysize(kDecryptedData))),
null_buffer_(scoped_refptr<DecoderBuffer>()),
decrypt_cb_(base::Bind(&ProxyDecryptorTest::BufferDecrypted,
base::Unretained(this))) {
}
// Instead of calling ProxyDecryptor::GenerateKeyRequest() here to create a
// real Decryptor, inject a MockDecryptor for testing purpose.
void GenerateKeyRequest() {
proxy_decryptor_.set_decryptor_for_testing(
scoped_ptr<Decryptor>(real_decryptor_));
}
// Since we are using the MockDecryptor, we can simulate any decryption
// behavior we want. Therefore, we do not care which key is really added,
// hence always use fake key IDs and keys.
void AddKey() {
EXPECT_CALL(*real_decryptor_, AddKey(kFakeKeySystem,
kFakeKeyId, arraysize(kFakeKeyId),
kFakeKey, arraysize(kFakeKey),
kFakeSessionId));
proxy_decryptor_.AddKey(kFakeKeySystem,
kFakeKeyId, arraysize(kFakeKeyId),
kFakeKey, arraysize(kFakeKey),
kFakeSessionId);
}
MOCK_METHOD2(BufferDecrypted, void(Decryptor::Status,
const scoped_refptr<DecoderBuffer>&));
protected:
MessageLoop message_loop_;
media::MockDecryptorClient client_;
ProxyDecryptor proxy_decryptor_;
media::MockDecryptor* real_decryptor_;
scoped_refptr<DecoderBuffer> encrypted_buffer_;
scoped_refptr<DecoderBuffer> decrypted_buffer_;
scoped_refptr<DecoderBuffer> null_buffer_;
Decryptor::DecryptCB decrypt_cb_;
private:
DISALLOW_COPY_AND_ASSIGN(ProxyDecryptorTest);
};
// Tests a typical use case: GKR(), AddKey() and Decrypt() succeeds.
TEST_F(ProxyDecryptorTest, NormalDecryption_Success) {
GenerateKeyRequest();
AddKey();
EXPECT_CALL(*real_decryptor_, Decrypt(encrypted_buffer_, _))
.WillOnce(RunDecryptCB(Decryptor::kSuccess, decrypted_buffer_));
EXPECT_CALL(*this, BufferDecrypted(Decryptor::kSuccess, decrypted_buffer_));
proxy_decryptor_.Decrypt(encrypted_buffer_, decrypt_cb_);
}
// Tests the case where Decrypt() fails.
TEST_F(ProxyDecryptorTest, NormalDecryption_Error) {
GenerateKeyRequest();
AddKey();
EXPECT_CALL(*real_decryptor_, Decrypt(encrypted_buffer_, _))
.WillOnce(RunDecryptCB(Decryptor::kError, null_buffer_));
EXPECT_CALL(*this, BufferDecrypted(Decryptor::kError, IsNull()));
proxy_decryptor_.Decrypt(encrypted_buffer_, decrypt_cb_);
}
// Tests the case where no key is available for decryption.
TEST_F(ProxyDecryptorTest, NormalDecryption_NoKey) {
GenerateKeyRequest();
EXPECT_CALL(*real_decryptor_, Decrypt(encrypted_buffer_, _))
.WillOnce(RunDecryptCB(Decryptor::kNoKey, null_buffer_));
EXPECT_CALL(client_, NeedKeyMock("", "", NotNull(), arraysize(kFakeKeyId)));
proxy_decryptor_.Decrypt(encrypted_buffer_, decrypt_cb_);
}
// Tests the case where Decrypt() is called before GKR() is called and the right
// key is added.
TEST_F(ProxyDecryptorTest, DecryptBeforeGenerateKeyRequest) {
EXPECT_CALL(client_, NeedKeyMock("", "", NotNull(), arraysize(kFakeKeyId)));
proxy_decryptor_.Decrypt(encrypted_buffer_, decrypt_cb_);
EXPECT_CALL(*real_decryptor_, Decrypt(encrypted_buffer_, _))
.WillOnce(RunDecryptCB(Decryptor::kSuccess, decrypted_buffer_));
EXPECT_CALL(*this, BufferDecrypted(Decryptor::kSuccess, decrypted_buffer_))
.WillOnce(ScheduleMessageLoopToStop(&message_loop_));
GenerateKeyRequest();
AddKey();
message_loop_.Run();
}
// Tests the case where multiple AddKey() is called to add some irrelevant keys
// before the real key that can decrypt |encrypted_buffer_| is added.
TEST_F(ProxyDecryptorTest, MultipleAddKeys) {
EXPECT_CALL(client_, NeedKeyMock("", "", NotNull(), arraysize(kFakeKeyId)))
.Times(AtLeast(1));
proxy_decryptor_.Decrypt(encrypted_buffer_, decrypt_cb_);
EXPECT_CALL(*real_decryptor_, Decrypt(encrypted_buffer_, _))
.WillRepeatedly(RunDecryptCB(Decryptor::kNoKey, null_buffer_));
GenerateKeyRequest();
const int number_of_irrelevant_addkey = 5;
for (int i = 0; i < number_of_irrelevant_addkey; ++i)
AddKey(); // Some irrelevant keys are added.
EXPECT_CALL(*real_decryptor_, Decrypt(encrypted_buffer_, _))
.WillOnce(RunDecryptCB(Decryptor::kSuccess, decrypted_buffer_));
EXPECT_CALL(*this, BufferDecrypted(Decryptor::kSuccess, decrypted_buffer_))
.WillOnce(ScheduleMessageLoopToStop(&message_loop_));
AddKey(); // Correct key added.
message_loop_.Run();
}
// Tests the case where Decrypt() is called multiple times (e.g. from multiple
// stream) before the right key is added via AddKey().
TEST_F(ProxyDecryptorTest, MultiplePendingDecryptions) {
EXPECT_CALL(client_, NeedKeyMock("", "", NotNull(), arraysize(kFakeKeyId)))
.Times(AtLeast(1));
EXPECT_CALL(*real_decryptor_, Decrypt(encrypted_buffer_, _))
.WillRepeatedly(RunDecryptCB(Decryptor::kNoKey, null_buffer_));
proxy_decryptor_.Decrypt(encrypted_buffer_, decrypt_cb_);
GenerateKeyRequest();
proxy_decryptor_.Decrypt(encrypted_buffer_, decrypt_cb_);
AddKey(); // An irrelevant key is added.
proxy_decryptor_.Decrypt(encrypted_buffer_, decrypt_cb_);
EXPECT_CALL(*real_decryptor_, Decrypt(encrypted_buffer_, _))
.WillRepeatedly(RunDecryptCB(Decryptor::kSuccess, decrypted_buffer_));
EXPECT_CALL(*this, BufferDecrypted(Decryptor::kSuccess, decrypted_buffer_))
.Times(3);
AddKey(); // Correct key added.
message_loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure());
message_loop_.Run();
}
TEST_F(ProxyDecryptorTest, StopWhenDecryptionsPending) {
EXPECT_CALL(client_, NeedKeyMock("", "", NotNull(), arraysize(kFakeKeyId)))
.Times(AtLeast(1));
EXPECT_CALL(*real_decryptor_, Decrypt(encrypted_buffer_, _))
.WillRepeatedly(RunDecryptCB(Decryptor::kNoKey, null_buffer_));
proxy_decryptor_.Decrypt(encrypted_buffer_, decrypt_cb_);
GenerateKeyRequest();
proxy_decryptor_.Decrypt(encrypted_buffer_, decrypt_cb_);
AddKey(); // An irrelevant key is added.
proxy_decryptor_.Decrypt(encrypted_buffer_, decrypt_cb_);
EXPECT_CALL(*real_decryptor_, CancelDecrypt());
EXPECT_CALL(*this, BufferDecrypted(Decryptor::kError, null_buffer_))
.Times(3);
proxy_decryptor_.CancelDecrypt();
message_loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure());
message_loop_.Run();
}
} // namespace webkit_media