Implement AesPatternCryptor for pattern encryption/decryption

Issue #78

Change-Id: If0fadf6f83ef67dd39af29080bab6ed71fb35290
This commit is contained in:
KongQun Yang 2016-04-05 17:26:20 -07:00
parent 2adaf1712d
commit 1d74988159
4 changed files with 331 additions and 0 deletions

View File

@ -0,0 +1,84 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/base/aes_pattern_cryptor.h"
#include <openssl/aes.h>
#include <algorithm>
#include "packager/base/logging.h"
namespace edash_packager {
namespace media {
AesPatternCryptor::AesPatternCryptor(uint8_t crypt_byte_block,
uint8_t skip_byte_block,
ConstantIvFlag constant_iv_flag,
scoped_ptr<AesCryptor> cryptor)
: crypt_byte_block_(crypt_byte_block),
skip_byte_block_(skip_byte_block),
constant_iv_flag_(constant_iv_flag),
cryptor_(cryptor.Pass()) {
DCHECK(cryptor_);
}
AesPatternCryptor::~AesPatternCryptor() {}
bool AesPatternCryptor::InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv) {
iv_ = iv;
return cryptor_->InitializeWithIv(key, iv);
}
bool AesPatternCryptor::SetIv(const std::vector<uint8_t>& iv) {
iv_ = iv;
return cryptor_->SetIv(iv);
}
void AesPatternCryptor::UpdateIv() {
cryptor_->UpdateIv();
}
bool AesPatternCryptor::CryptInternal(const uint8_t* text,
size_t text_size,
uint8_t* crypt_text,
size_t* crypt_text_size) {
if (constant_iv_flag_ == AesPatternCryptor::kUseConstantIv)
CHECK(SetIv(iv_));
// |crypt_text_size| is always the same as |text_size| for pattern encryption.
if (*crypt_text_size < text_size) {
LOG(ERROR) << "Expecting output size of at least " << text_size
<< " bytes.";
return false;
}
*crypt_text_size = text_size;
while (text_size > 0) {
const size_t crypt_byte_size = crypt_byte_block_ * AES_BLOCK_SIZE;
if (text_size >= crypt_byte_size) {
if (!cryptor_->Crypt(text, crypt_byte_size, crypt_text))
return false;
} else {
// If there is not enough data, just keep it in clear.
memcpy(crypt_text, text, text_size);
return true;
}
text += crypt_byte_size;
text_size -= crypt_byte_size;
crypt_text += crypt_byte_size;
const size_t skip_byte_size = std::min(
static_cast<size_t>(skip_byte_block_ * AES_BLOCK_SIZE), text_size);
memcpy(crypt_text, text, skip_byte_size);
text += skip_byte_size;
text_size -= skip_byte_size;
crypt_text += skip_byte_size;
}
return true;
}
} // namespace media
} // namespace edash_packager

View File

@ -0,0 +1,66 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/media/base/aes_cryptor.h"
#include "packager/base/macros.h"
#include "packager/base/memory/scoped_ptr.h"
namespace edash_packager {
namespace media {
/// Implements pattern-based encryption/decryption.
class AesPatternCryptor : public AesCryptor {
public:
enum ConstantIvFlag {
kUseConstantIv,
kDontUseConstantIv,
};
/// @param crypt_byte_block indicates number of encrypted blocks (16-byte) in
/// pattern based encryption.
/// @param skip_byte_block indicates number of unencrypted blocks (16-byte)
/// in pattern based encryption.
/// @param constant_iv_flag indicates whether a constant iv is used,
/// kUseConstantIv means that the same iv is used for all Crypt calls
/// until iv is changed via SetIv; otherwise, iv can be incremented
/// (for counter mode) or chained (for cipher block chaining mode)
/// internally inside Crypt call, i.e. iv will be updated across Crypt
/// calls.
/// @param cryptor points to an AesCryptor instance which performs the actual
/// encryption/decryption.
AesPatternCryptor(uint8_t crypt_byte_block,
uint8_t skip_byte_block,
ConstantIvFlag constant_iv_flag,
scoped_ptr<AesCryptor> cryptor);
~AesPatternCryptor() override;
/// @name AesCryptor implementation overrides.
/// @{
bool InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv) override;
bool SetIv(const std::vector<uint8_t>& iv) override;
void UpdateIv() override;
/// @}
protected:
bool CryptInternal(const uint8_t* text,
size_t text_size,
uint8_t* crypt_text,
size_t* crypt_text_size) override;
private:
const uint8_t crypt_byte_block_;
const uint8_t skip_byte_block_;
const ConstantIvFlag constant_iv_flag_;
scoped_ptr<AesCryptor> cryptor_;
std::vector<uint8_t> iv_;
DISALLOW_COPY_AND_ASSIGN(AesPatternCryptor);
};
} // namespace media
} // namespace edash_packager

View File

@ -0,0 +1,178 @@
// Copyright 2016 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "packager/base/strings/string_number_conversions.h"
#include "packager/media/base/aes_pattern_cryptor.h"
using ::testing::_;
using ::testing::Invoke;
using ::testing::Return;
namespace {
const uint8_t kCryptByteBlock = 2u;
const uint8_t kSkipByteBlock = 1u;
} // namespace
namespace edash_packager {
namespace media {
class MockAesCryptor : public AesCryptor {
public:
MOCK_METHOD2(InitializeWithIv,
bool(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv));
MOCK_METHOD1(SetIv, bool(const std::vector<uint8_t>& iv));
MOCK_METHOD0(UpdateIv, void());
MOCK_METHOD4(CryptInternal,
bool(const uint8_t* text,
size_t text_size,
uint8_t* crypt_text,
size_t* crypt_text_size));
};
class AesPatternCryptorTest : public ::testing::Test {
public:
AesPatternCryptorTest()
: mock_cryptor_(new MockAesCryptor),
pattern_cryptor_(kCryptByteBlock,
kSkipByteBlock,
AesPatternCryptor::kDontUseConstantIv,
scoped_ptr<MockAesCryptor>(mock_cryptor_)) {}
protected:
MockAesCryptor* mock_cryptor_;
AesPatternCryptor pattern_cryptor_;
};
TEST_F(AesPatternCryptorTest, InitializeWithIv) {
std::vector<uint8_t> key(16, 'k');
std::vector<uint8_t> iv(8, 'i');
EXPECT_CALL(*mock_cryptor_, InitializeWithIv(key, iv)).WillOnce(Return(true));
EXPECT_TRUE(pattern_cryptor_.InitializeWithIv(key, iv));
}
TEST_F(AesPatternCryptorTest, UpdateIv) {
EXPECT_CALL(*mock_cryptor_, UpdateIv());
pattern_cryptor_.UpdateIv();
}
namespace {
struct PatternTestCase {
const char* text_hex;
const char* expected_crypt_text_hex;
};
const PatternTestCase kPatternTestCases[] = {
// Empty.
{"", ""},
// One partial block (not encrypted).
{"010203", "010203"},
// One block (not encrypted).
{"01020304050607080910111213141516", "01020304050607080910111213141516"},
// One block + partial block (not encrypted).
{"010203040506070809101112131415161718",
"010203040506070809101112131415161718"},
// Two blocks (encrypted) - the mock encryptor adds every byte by 0x10.
{"0102030405060708091011121314151617181920212223242526272829303132",
"1112131415161718192021222324252627282930313233343536373839404142"},
// Two blocks + partial block (only the first two blocks are encrypted).
{"0102030405060708091011121314151617181920212223242526272829303132"
"333435363738",
"1112131415161718192021222324252627282930313233343536373839404142"
"333435363738"},
// Seven blocks.
{// kCryptByteBlock (2) blocks encrypted.
"0102030405060708091011121314151617181920212223242526272829303132"
// kSkipByteBlock (1) block not encrypted.
"33343536373839404142434445464748"
// kCryptByteBlock (2) blocks encrypted.
"4950515253545556575859606162636465666768697071727374757677787980"
// kSkipByteBlock (1) block not encrypted.
"81828384858687888990919293949596"
// Less than kCryptByteBlock blocks remaining, so not encrypted.
"97989900010203040506070809101112",
"1112131415161718192021222324252627282930313233343536373839404142"
"33343536373839404142434445464748"
"5960616263646566676869707172737475767778798081828384858687888990"
"81828384858687888990919293949596"
"97989900010203040506070809101112"},
};
} // namespace
class AesPatternCryptorVerificationTest
: public AesPatternCryptorTest,
public ::testing::WithParamInterface<PatternTestCase> {};
TEST_P(AesPatternCryptorVerificationTest, PatternTest) {
std::vector<uint8_t> text;
std::string text_hex(GetParam().text_hex);
if (!text_hex.empty())
ASSERT_TRUE(base::HexStringToBytes(text_hex, &text));
std::vector<uint8_t> expected_crypt_text;
std::string expected_crypt_text_hex(GetParam().expected_crypt_text_hex);
if (!expected_crypt_text_hex.empty()) {
ASSERT_TRUE(
base::HexStringToBytes(expected_crypt_text_hex, &expected_crypt_text));
}
ON_CALL(*mock_cryptor_, CryptInternal(_, _, _, _))
.WillByDefault(Invoke([](const uint8_t* text, size_t text_size,
uint8_t* crypt_text, size_t* crypt_text_size) {
*crypt_text_size = text_size;
for (size_t i = 0; i < text_size; ++i) {
*crypt_text++ = *text++ + 0x10;
}
return true;
}));
std::vector<uint8_t> crypt_text;
ASSERT_TRUE(pattern_cryptor_.Crypt(text, &crypt_text));
EXPECT_EQ(expected_crypt_text, crypt_text);
}
INSTANTIATE_TEST_CASE_P(PatternTestCases,
AesPatternCryptorVerificationTest,
::testing::ValuesIn(kPatternTestCases));
TEST(AesPatternCryptorConstIvTest, UseConstantIv) {
MockAesCryptor* mock_cryptor = new MockAesCryptor;
AesPatternCryptor pattern_cryptor(kCryptByteBlock, kSkipByteBlock,
AesPatternCryptor::kUseConstantIv,
scoped_ptr<MockAesCryptor>(mock_cryptor));
std::vector<uint8_t> iv(8, 'i');
// SetIv will be called twice:
// once by AesPatternCryptor::SetIv,
// once by AesPatternCryptor::Crypt, to make sure the same iv is used.
EXPECT_CALL(*mock_cryptor, SetIv(iv)).Times(2).WillRepeatedly(Return(true));
EXPECT_TRUE(pattern_cryptor.SetIv(iv));
std::string crypt_text;
ASSERT_TRUE(pattern_cryptor.Crypt("010203", &crypt_text));
}
TEST(AesPatternCryptorConstIvTest, DontUseConstantIv) {
MockAesCryptor* mock_cryptor = new MockAesCryptor;
AesPatternCryptor pattern_cryptor(kCryptByteBlock, kSkipByteBlock,
AesPatternCryptor::kDontUseConstantIv,
scoped_ptr<MockAesCryptor>(mock_cryptor));
std::vector<uint8_t> iv(8, 'i');
// SetIv will be called only once by AesPatternCryptor::SetIv.
EXPECT_CALL(*mock_cryptor, SetIv(iv)).WillOnce(Return(true));
EXPECT_TRUE(pattern_cryptor.SetIv(iv));
std::string crypt_text;
ASSERT_TRUE(pattern_cryptor.Crypt("010203", &crypt_text));
}
} // namespace media
} // namespace edash_packager

View File

@ -19,6 +19,8 @@
'aes_decryptor.h', 'aes_decryptor.h',
'aes_encryptor.cc', 'aes_encryptor.cc',
'aes_encryptor.h', 'aes_encryptor.h',
'aes_pattern_cryptor.cc',
'aes_pattern_cryptor.h',
'audio_stream_info.cc', 'audio_stream_info.cc',
'audio_stream_info.h', 'audio_stream_info.h',
'audio_timestamp_helper.cc', 'audio_timestamp_helper.cc',
@ -116,6 +118,7 @@
'type': '<(gtest_target_type)', 'type': '<(gtest_target_type)',
'sources': [ 'sources': [
'aes_cryptor_unittest.cc', 'aes_cryptor_unittest.cc',
'aes_pattern_cryptor_unittest.cc',
'audio_timestamp_helper_unittest.cc', 'audio_timestamp_helper_unittest.cc',
'bit_reader_unittest.cc', 'bit_reader_unittest.cc',
'buffer_writer_unittest.cc', 'buffer_writer_unittest.cc',