Implement AesPatternCryptor for pattern encryption/decryption
Issue #78 Change-Id: If0fadf6f83ef67dd39af29080bab6ed71fb35290
This commit is contained in:
parent
2adaf1712d
commit
1d74988159
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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',
|
||||||
|
|
Loading…
Reference in New Issue