More cleanups in AesEncryptor and AesDecryptor

- Create AesCryptor class as the common base
- AesCryptor::Crypt function will serve as an Encrypt function for
  encryptor and a Decrypt function for Decryptor.

Change-Id: Ie91fb14964b5091786705bf510656f40d73af160
This commit is contained in:
KongQun Yang 2016-04-05 17:19:16 -07:00
parent e92556658e
commit 5fc09763ce
14 changed files with 307 additions and 331 deletions

View File

@ -0,0 +1,59 @@
// 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 <openssl/aes.h>
#include "packager/base/logging.h"
#include "packager/base/stl_util.h"
namespace edash_packager {
namespace media {
AesCryptor::AesCryptor() : aes_key_(new AES_KEY) {}
AesCryptor::~AesCryptor() {}
bool AesCryptor::Crypt(const std::vector<uint8_t>& text,
std::vector<uint8_t>* crypt_text) {
// Save text size to make it work for in-place conversion, since the
// next statement will update the text size.
const size_t text_size = text.size();
crypt_text->resize(text_size + NumPaddingBytes(text_size));
size_t crypt_text_size = crypt_text->size();
if (!CryptInternal(text.data(), text_size, crypt_text->data(),
&crypt_text_size)) {
return false;
}
DCHECK_LE(crypt_text_size, crypt_text->size());
crypt_text->resize(crypt_text_size);
return true;
}
bool AesCryptor::Crypt(const std::string& text, std::string* crypt_text) {
// Save text size to make it work for in-place conversion, since the
// next statement will update the text size.
const size_t text_size = text.size();
crypt_text->resize(text_size + NumPaddingBytes(text_size));
size_t crypt_text_size = crypt_text->size();
if (!CryptInternal(reinterpret_cast<const uint8_t*>(text.data()), text_size,
reinterpret_cast<uint8_t*>(string_as_array(crypt_text)),
&crypt_text_size))
return false;
DCHECK_LE(crypt_text_size, crypt_text->size());
crypt_text->resize(crypt_text_size);
return true;
}
size_t AesCryptor::NumPaddingBytes(size_t size) const {
// No padding by default.
return 0;
}
} // namespace media
} // namespace edash_packager

View File

@ -0,0 +1,100 @@
// 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
#ifndef PACKAGER_MEDIA_BASE_AES_CRYPTOR_H_
#define PACKAGER_MEDIA_BASE_AES_CRYPTOR_H_
#include <string>
#include <vector>
#include "packager/base/macros.h"
#include "packager/base/memory/scoped_ptr.h"
struct aes_key_st;
typedef struct aes_key_st AES_KEY;
namespace edash_packager {
namespace media {
// AES cryptor interface. Inherited by various AES encryptor and decryptor
// implementations.
class AesCryptor {
public:
AesCryptor();
virtual ~AesCryptor();
/// Initialize the cryptor with specified key and IV.
/// @return true on successful initialization, false otherwise.
virtual bool InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv) = 0;
/// @name Various forms of crypt (Encrypt/Decrypt) calls.
/// It is an Encrypt function for encryptor and a Decrypt function for
/// decryptor. The text and crypt_text pointers can be the same address for
/// in place encryption/decryption.
/// @{
bool Crypt(const std::vector<uint8_t>& text,
std::vector<uint8_t>* crypt_text);
bool Crypt(const std::string& text, std::string* crypt_text);
/// @param crypt_text should have at least @a text_size bytes.
bool Crypt(const uint8_t* text, size_t text_size, uint8_t* crypt_text) {
size_t crypt_text_size = text_size;
return CryptInternal(text, text_size, crypt_text, &crypt_text_size);
}
/// @}
/// Set IV.
/// @return true if successful, false if the input is invalid.
virtual bool SetIv(const std::vector<uint8_t>& iv) = 0;
/// Update IV for next sample. As recommended in ISO/IEC 23001-7: IV need to
/// be updated per sample for CENC.
/// This is used by encryptors only.
virtual void UpdateIv() = 0;
/// @return The current iv.
const std::vector<uint8_t>& iv() const { return iv_; }
protected:
void set_iv(const std::vector<uint8_t>& iv) { iv_ = iv; }
const AES_KEY* aes_key() const { return aes_key_.get(); }
AES_KEY* mutable_aes_key() { return aes_key_.get(); }
private:
// Internal implementation of crypt function.
// |text| points to the input text.
// |text_size| is the size of input text.
// |crypt_text| points to the output encrypted or decrypted text, depends on
// whether it is an encryption or decryption. |text| and |crypt_text| can
// point to the same address for in place encryption/decryption.
// |crypt_text_size| contains the size of |crypt_text| and it will be updated
// to contain the actual encrypted/decrypted size for |crypt_text| on success.
// Return false if the input |crypt_text_size| is not large enough to hold the
// output |crypt_text| or if there is any error in encryption/decryption.
virtual bool CryptInternal(const uint8_t* text,
size_t text_size,
uint8_t* crypt_text,
size_t* crypt_text_size) = 0;
// |size| specifies the input text size.
// Return the number of padding bytes needed.
// Note: No paddings should be needed except for pkcs5-cbc encryptor.
virtual size_t NumPaddingBytes(size_t size) const;
// Initialization vector, with size 8 or 16.
std::vector<uint8_t> iv_;
// Openssl AES_KEY.
scoped_ptr<AES_KEY> aes_key_;
DISALLOW_COPY_AND_ASSIGN(AesCryptor);
};
} // namespace media
} // namespace edash_packager
#endif // PACKAGER_MEDIA_BASE_AES_CRYPTOR_H_

View File

@ -159,22 +159,22 @@ class AesCtrEncryptorTest : public testing::Test {
TEST_F(AesCtrEncryptorTest, NistTestCase) {
std::vector<uint8_t> encrypted;
EXPECT_TRUE(encryptor_.Encrypt(plaintext_, &encrypted));
ASSERT_TRUE(encryptor_.Crypt(plaintext_, &encrypted));
EXPECT_EQ(ciphertext_, encrypted);
EXPECT_TRUE(decryptor_.SetIv(iv_));
ASSERT_TRUE(decryptor_.SetIv(iv_));
std::vector<uint8_t> decrypted;
EXPECT_TRUE(decryptor_.Decrypt(encrypted, &decrypted));
ASSERT_TRUE(decryptor_.Crypt(encrypted, &decrypted));
EXPECT_EQ(plaintext_, decrypted);
}
TEST_F(AesCtrEncryptorTest, NistTestCaseInplaceEncryptionDecryption) {
std::vector<uint8_t> buffer = plaintext_;
EXPECT_TRUE(encryptor_.Encrypt(&buffer[0], buffer.size(), &buffer[0]));
ASSERT_TRUE(encryptor_.Crypt(&buffer[0], buffer.size(), &buffer[0]));
EXPECT_EQ(ciphertext_, buffer);
EXPECT_TRUE(decryptor_.SetIv(iv_));
EXPECT_TRUE(decryptor_.Decrypt(&buffer[0], buffer.size(), &buffer[0]));
ASSERT_TRUE(decryptor_.SetIv(iv_));
ASSERT_TRUE(decryptor_.Crypt(&buffer[0], buffer.size(), &buffer[0]));
EXPECT_EQ(plaintext_, buffer);
}
@ -184,13 +184,13 @@ TEST_F(AesCtrEncryptorTest, EncryptDecryptString) {
"82E3AD1EF90C5CC09EB37F1B9EFBD99016441A1C15123F0777CD57BB993E14DA02";
std::string ciphertext;
EXPECT_TRUE(encryptor_.Encrypt(kPlaintext, &ciphertext));
ASSERT_TRUE(encryptor_.Crypt(kPlaintext, &ciphertext));
EXPECT_EQ(kExpectedCiphertextInHex,
base::HexEncode(ciphertext.data(), ciphertext.size()));
std::string decrypted;
EXPECT_TRUE(decryptor_.SetIv(iv_));
EXPECT_TRUE(decryptor_.Decrypt(ciphertext, &decrypted));
ASSERT_TRUE(decryptor_.SetIv(iv_));
ASSERT_TRUE(decryptor_.Crypt(ciphertext, &decrypted));
EXPECT_EQ(kPlaintext, decrypted);
}
@ -202,7 +202,7 @@ TEST_F(AesCtrEncryptorTest, 128BitIVBoundaryCaseEncryption) {
kIv128Max64 + arraysize(kIv128Max64));
ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_max64));
std::vector<uint8_t> encrypted;
EXPECT_TRUE(encryptor_.Encrypt(plaintext_, &encrypted));
ASSERT_TRUE(encryptor_.Crypt(plaintext_, &encrypted));
std::vector<uint8_t> iv_one_and_three(
kIv128OneAndThree, kIv128OneAndThree + arraysize(kIv128OneAndThree));
@ -211,12 +211,12 @@ TEST_F(AesCtrEncryptorTest, 128BitIVBoundaryCaseEncryption) {
ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_max64));
std::vector<uint8_t> encrypted_verify(plaintext_.size(), 0);
EXPECT_TRUE(
encryptor_.Encrypt(&plaintext_[0], kAesBlockSize, &encrypted_verify[0]));
ASSERT_TRUE(
encryptor_.Crypt(&plaintext_[0], kAesBlockSize, &encrypted_verify[0]));
std::vector<uint8_t> iv_zero(kIv128Zero, kIv128Zero + arraysize(kIv128Zero));
ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_zero));
EXPECT_TRUE(encryptor_.Encrypt(&plaintext_[kAesBlockSize], kAesBlockSize * 3,
&encrypted_verify[kAesBlockSize]));
ASSERT_TRUE(encryptor_.Crypt(&plaintext_[kAesBlockSize], kAesBlockSize * 3,
&encrypted_verify[kAesBlockSize]));
EXPECT_EQ(encrypted, encrypted_verify);
}
@ -224,8 +224,8 @@ TEST_F(AesCtrEncryptorTest, InitWithRandomIv) {
const uint8_t kIvSize = 8;
ASSERT_TRUE(encryptor_.InitializeWithRandomIv(key_, kIvSize));
ASSERT_EQ(kIvSize, encryptor_.iv().size());
LOG(INFO) << "Random IV: " << base::HexEncode(&encryptor_.iv()[0],
encryptor_.iv().size());
LOG(INFO) << "Random IV: "
<< base::HexEncode(&encryptor_.iv()[0], encryptor_.iv().size());
}
TEST_F(AesCtrEncryptorTest, UnsupportedKeySize) {
@ -252,19 +252,17 @@ TEST_P(AesCtrEncryptorSubsampleTest, NistTestCaseSubsamples) {
std::vector<uint8_t> encrypted(plaintext_.size(), 0);
for (uint32_t i = 0, offset = 0; i < test_case->subsample_count; ++i) {
uint32_t len = test_case->subsample_sizes[i];
EXPECT_TRUE(
encryptor_.Encrypt(&plaintext_[offset], len, &encrypted[offset]));
ASSERT_TRUE(encryptor_.Crypt(&plaintext_[offset], len, &encrypted[offset]));
offset += len;
EXPECT_EQ(offset % kAesBlockSize, encryptor_.block_offset());
}
EXPECT_EQ(ciphertext_, encrypted);
EXPECT_TRUE(decryptor_.SetIv(iv_));
ASSERT_TRUE(decryptor_.SetIv(iv_));
std::vector<uint8_t> decrypted(encrypted.size(), 0);
for (uint32_t i = 0, offset = 0; i < test_case->subsample_count; ++i) {
uint32_t len = test_case->subsample_sizes[i];
EXPECT_TRUE(
decryptor_.Decrypt(&encrypted[offset], len, &decrypted[offset]));
ASSERT_TRUE(decryptor_.Crypt(&encrypted[offset], len, &decrypted[offset]));
offset += len;
EXPECT_EQ(offset % kAesBlockSize, decryptor_.block_offset());
}
@ -291,7 +289,7 @@ TEST_P(AesCtrEncryptorIvTest, IvTest) {
ASSERT_TRUE(encryptor.InitializeWithIv(key, iv_test));
std::vector<uint8_t> encrypted;
EXPECT_TRUE(encryptor.Encrypt(plaintext, &encrypted));
ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted));
encryptor.UpdateIv();
EXPECT_EQ(iv_expected, encryptor.iv());
}
@ -300,9 +298,9 @@ INSTANTIATE_TEST_CASE_P(IvTestCases,
AesCtrEncryptorIvTest,
::testing::ValuesIn(kIvTestCases));
class AesCbcEncryptDecryptTest {
class AesCbcTest : public ::testing::Test {
public:
AesCbcEncryptDecryptTest()
AesCbcTest()
: encryptor_(new AesCbcEncryptor(kPkcs5Padding, !kChainAcrossCalls)),
decryptor_(new AesCbcDecryptor(kPkcs5Padding, !kChainAcrossCalls)),
key_(kAesKey, kAesKey + arraysize(kAesKey)),
@ -330,11 +328,11 @@ class AesCbcEncryptDecryptTest {
ASSERT_TRUE(decryptor_->InitializeWithIv(key_, iv_));
T encrypted;
EXPECT_TRUE(encryptor_->Encrypt(plaintext, &encrypted));
ASSERT_TRUE(encryptor_->Crypt(plaintext, &encrypted));
EXPECT_EQ(expected_ciphertext, encrypted);
T decrypted;
EXPECT_TRUE(decryptor_->Decrypt(encrypted, &decrypted));
ASSERT_TRUE(decryptor_->Crypt(encrypted, &decrypted));
EXPECT_EQ(plaintext, decrypted);
}
@ -345,9 +343,9 @@ class AesCbcEncryptDecryptTest {
ASSERT_TRUE(decryptor_->InitializeWithIv(key_, iv_));
T buffer(plaintext);
EXPECT_TRUE(encryptor_->Encrypt(buffer, &buffer));
ASSERT_TRUE(encryptor_->Crypt(buffer, &buffer));
EXPECT_EQ(expected_ciphertext, buffer);
EXPECT_TRUE(decryptor_->Decrypt(buffer, &buffer));
ASSERT_TRUE(decryptor_->Crypt(buffer, &buffer));
EXPECT_EQ(plaintext, buffer);
}
@ -357,8 +355,6 @@ class AesCbcEncryptDecryptTest {
std::vector<uint8_t> iv_;
};
class AesCbcTest : public AesCbcEncryptDecryptTest, public testing::Test {};
TEST_F(AesCbcTest, Aes256CbcPkcs5) {
// NIST SP 800-38A test vector F.2.5 CBC-AES256.Encrypt.
static const uint8_t kAesCbcKey[] = {
@ -462,22 +458,22 @@ TEST_F(AesCbcTest, NoPaddingNoChainAcrossCalls) {
ASSERT_TRUE(encryptor.InitializeWithIv(key_, iv_));
std::vector<uint8_t> encrypted;
ASSERT_TRUE(encryptor.Encrypt(plaintext, &encrypted));
ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted));
EXPECT_EQ(ciphertext, encrypted);
// Iv should not have been updated.
EXPECT_EQ(iv_, encryptor.iv());
ASSERT_TRUE(encryptor.Encrypt(plaintext, &encrypted));
ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted));
EXPECT_EQ(ciphertext, encrypted);
AesCbcDecryptor decryptor(kNoPadding, !kChainAcrossCalls);
ASSERT_TRUE(decryptor.InitializeWithIv(key_, iv_));
std::vector<uint8_t> decrypted;
ASSERT_TRUE(decryptor.Decrypt(ciphertext, &decrypted));
ASSERT_TRUE(decryptor.Crypt(ciphertext, &decrypted));
EXPECT_EQ(plaintext, decrypted);
// Iv should not have been updated.
EXPECT_EQ(iv_, encryptor.iv());
ASSERT_TRUE(decryptor.Decrypt(ciphertext, &decrypted));
ASSERT_TRUE(decryptor.Crypt(ciphertext, &decrypted));
EXPECT_EQ(plaintext, decrypted);
}
@ -506,24 +502,24 @@ TEST_F(AesCbcTest, NoPaddingChainAcrossCalls) {
ASSERT_TRUE(encryptor.InitializeWithIv(key_, iv_));
std::vector<uint8_t> encrypted;
ASSERT_TRUE(encryptor.Encrypt(plaintext, &encrypted));
ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted));
EXPECT_EQ(ciphertext, encrypted);
// Iv should have been updated.
EXPECT_NE(iv_, encryptor.iv());
// If run encrypt again, the result will be different.
ASSERT_TRUE(encryptor.Encrypt(plaintext, &encrypted));
ASSERT_TRUE(encryptor.Crypt(plaintext, &encrypted));
EXPECT_EQ(ciphertext2, encrypted);
AesCbcDecryptor decryptor(kNoPadding, kChainAcrossCalls);
ASSERT_TRUE(decryptor.InitializeWithIv(key_, iv_));
std::vector<uint8_t> decrypted;
ASSERT_TRUE(decryptor.Decrypt(ciphertext, &decrypted));
ASSERT_TRUE(decryptor.Crypt(ciphertext, &decrypted));
EXPECT_EQ(plaintext, decrypted);
// Iv should have been updated.
EXPECT_NE(iv_, encryptor.iv());
// If run decrypt on ciphertext2 now, it will return the original plaintext.
ASSERT_TRUE(decryptor.Decrypt(ciphertext2, &decrypted));
ASSERT_TRUE(decryptor.Crypt(ciphertext2, &decrypted));
EXPECT_EQ(plaintext, decrypted);
}
@ -540,13 +536,13 @@ TEST_F(AesCbcTest, UnsupportedIvSize) {
TEST_F(AesCbcTest, Pkcs5CipherTextNotMultipleOfBlockSize) {
std::string plaintext;
ASSERT_TRUE(decryptor_->InitializeWithIv(key_, iv_));
EXPECT_FALSE(decryptor_->Decrypt("1", &plaintext));
EXPECT_FALSE(decryptor_->Crypt("1", &plaintext));
}
TEST_F(AesCbcTest, Pkcs5CipherTextEmpty) {
std::string plaintext;
ASSERT_TRUE(decryptor_->InitializeWithIv(key_, iv_));
EXPECT_FALSE(decryptor_->Decrypt("", &plaintext));
EXPECT_FALSE(decryptor_->Crypt("", &plaintext));
}
struct CbcTestCase {
@ -585,8 +581,8 @@ const CbcTestCase kCbcTestCases[] = {
};
class AesCbcCryptorVerificationTest
: public AesCbcEncryptDecryptTest,
public ::testing::TestWithParam<CbcTestCase> {};
: public AesCbcTest,
public ::testing::WithParamInterface<CbcTestCase> {};
TEST_P(AesCbcCryptorVerificationTest, EncryptDecryptTest) {
encryptor_.reset(

View File

@ -7,8 +7,6 @@
#include "packager/media/base/aes_decryptor.h"
#include <openssl/aes.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include "packager/base/logging.h"
@ -24,60 +22,6 @@ bool IsKeySizeValidForAes(size_t key_size) {
namespace edash_packager {
namespace media {
AesDecryptor::AesDecryptor() {}
AesDecryptor::~AesDecryptor() {}
bool AesDecryptor::Decrypt(const std::vector<uint8_t>& ciphertext,
std::vector<uint8_t>* plaintext) {
DCHECK(plaintext);
plaintext->resize(ciphertext.size());
size_t plaintext_size;
if (!DecryptInternal(ciphertext.data(), ciphertext.size(), plaintext->data(),
&plaintext_size))
return false;
plaintext->resize(plaintext_size);
return true;
}
bool AesDecryptor::Decrypt(const std::string& ciphertext,
std::string* plaintext) {
DCHECK(plaintext);
plaintext->resize(ciphertext.size());
size_t plaintext_size;
if (!DecryptInternal(reinterpret_cast<const uint8_t*>(ciphertext.data()),
ciphertext.size(),
reinterpret_cast<uint8_t*>(string_as_array(plaintext)),
&plaintext_size))
return false;
plaintext->resize(plaintext_size);
return true;
}
AesCtrDecryptor::AesCtrDecryptor() {}
AesCtrDecryptor::~AesCtrDecryptor() {}
bool AesCtrDecryptor::InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv) {
encryptor_.reset(new AesCtrEncryptor);
return encryptor_->InitializeWithIv(key, iv);
}
bool AesCtrDecryptor::SetIv(const std::vector<uint8_t>& iv) {
DCHECK(encryptor_);
return encryptor_->SetIv(iv);
}
bool AesCtrDecryptor::DecryptInternal(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext,
size_t* plaintext_size) {
DCHECK(encryptor_);
*plaintext_size = ciphertext_size;
// For AES CTR, encryption and decryption are identical.
return encryptor_->Encrypt(ciphertext, ciphertext_size, plaintext);
}
AesCbcDecryptor::AesCbcDecryptor(CbcPaddingScheme padding_scheme,
bool chain_across_calls)
: padding_scheme_(padding_scheme),
@ -87,6 +31,7 @@ AesCbcDecryptor::AesCbcDecryptor(CbcPaddingScheme padding_scheme,
"sense if the padding_scheme is kNoPadding.";
}
}
AesCbcDecryptor::~AesCbcDecryptor() {}
bool AesCbcDecryptor::InitializeWithIv(const std::vector<uint8_t>& key,
@ -96,9 +41,8 @@ bool AesCbcDecryptor::InitializeWithIv(const std::vector<uint8_t>& key,
return false;
}
aes_key_.reset(new AES_KEY());
CHECK_EQ(AES_set_decrypt_key(key.data(), key.size() * 8, aes_key_.get()), 0);
CHECK_EQ(AES_set_decrypt_key(key.data(), key.size() * 8, mutable_aes_key()),
0);
return SetIv(iv);
}
@ -108,36 +52,43 @@ bool AesCbcDecryptor::SetIv(const std::vector<uint8_t>& iv) {
return false;
}
iv_ = iv;
set_iv(iv);
return true;
}
bool AesCbcDecryptor::DecryptInternal(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext,
size_t* plaintext_size) {
bool AesCbcDecryptor::CryptInternal(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext,
size_t* plaintext_size) {
DCHECK(plaintext_size);
DCHECK(aes_key_);
DCHECK(aes_key());
// Plaintext size is the same as ciphertext size except for pkcs5 padding.
// Will update later if using pkcs5 padding.
// Will update later if using pkcs5 padding. For pkcs5 padding, we still
// need at least |ciphertext_size| bytes for intermediate operation.
if (*plaintext_size < ciphertext_size) {
LOG(ERROR) << "Expecting output size of at least " << ciphertext_size
<< " bytes.";
return false;
}
*plaintext_size = ciphertext_size;
if (ciphertext_size == 0) {
if (padding_scheme_ == kPkcs5Padding) {
LOG(ERROR) << "Expected ciphertext to be at least " << AES_BLOCK_SIZE
<< " bytes with Pkcs5 padding";
<< " bytes with Pkcs5 padding.";
return false;
}
return true;
}
DCHECK(plaintext);
std::vector<uint8_t> local_iv(iv_);
std::vector<uint8_t> local_iv(iv());
const size_t residual_block_size = ciphertext_size % AES_BLOCK_SIZE;
if (residual_block_size == 0) {
AES_cbc_encrypt(ciphertext, plaintext, ciphertext_size, aes_key_.get(),
AES_cbc_encrypt(ciphertext, plaintext, ciphertext_size, aes_key(),
local_iv.data(), AES_DECRYPT);
if (chain_across_calls_)
iv_ = local_iv;
set_iv(local_iv);
if (padding_scheme_ != kPkcs5Padding)
return true;
@ -167,8 +118,8 @@ bool AesCbcDecryptor::DecryptInternal(const uint8_t* ciphertext,
// AES-CBC decrypt everything up to the next-to-last full block.
const size_t cbc_size = ciphertext_size - residual_block_size;
if (cbc_size > AES_BLOCK_SIZE) {
AES_cbc_encrypt(ciphertext, plaintext, cbc_size - AES_BLOCK_SIZE,
aes_key_.get(), local_iv.data(), AES_DECRYPT);
AES_cbc_encrypt(ciphertext, plaintext, cbc_size - AES_BLOCK_SIZE, aes_key(),
local_iv.data(), AES_DECRYPT);
}
const uint8_t* next_to_last_ciphertext_block =
@ -186,7 +137,7 @@ bool AesCbcDecryptor::DecryptInternal(const uint8_t* ciphertext,
// Decrypt the next-to-last block using the IV determined above. This decrypts
// the residual block bits.
AES_cbc_encrypt(next_to_last_ciphertext_block, next_to_last_plaintext_block,
AES_BLOCK_SIZE, aes_key_.get(), last_iv.data(), AES_DECRYPT);
AES_BLOCK_SIZE, aes_key(), last_iv.data(), AES_DECRYPT);
// Swap back the residual block bits and the next-to-last block.
if (plaintext == ciphertext) {
@ -202,7 +153,7 @@ bool AesCbcDecryptor::DecryptInternal(const uint8_t* ciphertext,
// Decrypt the next-to-last full block.
AES_cbc_encrypt(next_to_last_plaintext_block, next_to_last_plaintext_block,
AES_BLOCK_SIZE, aes_key_.get(), local_iv.data(), AES_DECRYPT);
AES_BLOCK_SIZE, aes_key(), local_iv.data(), AES_DECRYPT);
return true;
}

View File

@ -6,98 +6,23 @@
//
// AES Decryptor implementation using openssl.
#ifndef MEDIA_BASE_AES_DECRYPTOR_H_
#define MEDIA_BASE_AES_DECRYPTOR_H_
#ifndef PACKAGER_MEDIA_BASE_AES_DECRYPTOR_H_
#define PACKAGER_MEDIA_BASE_AES_DECRYPTOR_H_
#include <string>
#include <vector>
#include "packager/base/memory/scoped_ptr.h"
#include "packager/base/stl_util.h"
#include "packager/base/macros.h"
#include "packager/media/base/aes_cryptor.h"
#include "packager/media/base/aes_encryptor.h"
struct aes_key_st;
typedef struct aes_key_st AES_KEY;
namespace edash_packager {
namespace media {
class AesDecryptor {
public:
AesDecryptor();
virtual ~AesDecryptor();
/// For AES-CTR, encryption and decryption are identical.
using AesCtrDecryptor = AesCtrEncryptor;
/// Initialize the decryptor with specified key and IV.
/// @return true on successful initialization, false otherwise.
virtual bool InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv) = 0;
/// @name Various forms of decrypt calls.
/// The plaintext and ciphertext pointers can be the same address.
/// @{
bool Decrypt(const std::vector<uint8_t>& ciphertext,
std::vector<uint8_t>* plaintext);
bool Decrypt(const std::string& ciphertext, std::string* plaintext);
bool Decrypt(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext) {
size_t plaintext_size;
return DecryptInternal(ciphertext, ciphertext_size, plaintext,
&plaintext_size);
}
/// @}
/// Set IV.
/// @return true if successful, false if the input is invalid.
virtual bool SetIv(const std::vector<uint8_t>& iv) = 0;
protected:
/// Internal implementation of decrypt function.
/// @param ciphertext points to the input ciphertext.
/// @param ciphertext_size is the input ciphertext size.
/// @param[out] plaintext points to the output plaintext. @a plaintext and
/// @a ciphertext can point to the same address.
/// @param[out] plaintext_size contains the size of plaintext on success.
/// It should never be larger than @a ciphertext_size.
virtual bool DecryptInternal(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext,
size_t* plaintext_size) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(AesDecryptor);
};
// Class which implements AES-CTR counter-mode decryption.
class AesCtrDecryptor : public AesDecryptor {
public:
AesCtrDecryptor();
~AesCtrDecryptor() override;
/// @name AesDecryptor 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;
/// @}
uint32_t block_offset() const { return encryptor_->block_offset(); }
protected:
bool DecryptInternal(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext,
size_t* plaintext_size) override;
private:
scoped_ptr<AesCtrEncryptor> encryptor_;
DISALLOW_COPY_AND_ASSIGN(AesCtrDecryptor);
};
// Class which implements AES-CBC (Cipher block chaining) decryption.
class AesCbcDecryptor : public AesDecryptor {
/// Class which implements AES-CBC (Cipher block chaining) decryption.
class AesCbcDecryptor : public AesCryptor {
public:
/// @param padding_scheme indicates the padding scheme used. Currently
/// supported schemes: kNoPadding, kPkcs5Padding, kCtsPadding.
@ -107,25 +32,22 @@ class AesCbcDecryptor : public AesDecryptor {
AesCbcDecryptor(CbcPaddingScheme padding_scheme, bool chain_across_calls);
~AesCbcDecryptor() override;
/// @name AesDecryptor implementation overrides.
/// @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 {
// Nop for decryptor.
}
/// @}
protected:
bool DecryptInternal(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext,
size_t* plaintext_size) override;
private:
// Openssl AES_KEY.
scoped_ptr<AES_KEY> aes_key_;
// Initialization vector, must be 16 for CBC.
std::vector<uint8_t> iv_;
bool CryptInternal(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext,
size_t* plaintext_size) override;
const CbcPaddingScheme padding_scheme_;
const bool chain_across_calls_;
@ -135,4 +57,4 @@ class AesCbcDecryptor : public AesDecryptor {
} // namespace media
} // namespace edash_packager
#endif // MEDIA_BASE_AES_DECRYPTOR_H_
#endif // PACKAGER_MEDIA_BASE_AES_DECRYPTOR_H_

View File

@ -40,9 +40,8 @@ namespace media {
AesEncryptor::AesEncryptor() {}
AesEncryptor::~AesEncryptor() {}
bool AesEncryptor::InitializeWithRandomIv(
const std::vector<uint8_t>& key,
uint8_t iv_size) {
bool AesEncryptor::InitializeWithRandomIv(const std::vector<uint8_t>& key,
uint8_t iv_size) {
std::vector<uint8_t> iv(iv_size, 0);
if (RAND_bytes(iv.data(), iv_size) != 1) {
LOG(ERROR) << "RAND_bytes failed with error: "
@ -59,31 +58,11 @@ bool AesEncryptor::InitializeWithIv(const std::vector<uint8_t>& key,
return false;
}
aes_key_.reset(new AES_KEY());
CHECK_EQ(AES_set_encrypt_key(key.data(), key.size() * 8, aes_key_.get()), 0);
CHECK_EQ(AES_set_encrypt_key(key.data(), key.size() * 8, mutable_aes_key()),
0);
return SetIv(iv);
}
bool AesEncryptor::Encrypt(const std::vector<uint8_t>& plaintext,
std::vector<uint8_t>* ciphertext) {
// Save plaintext size to make it work for in-place conversion, since the
// next statement will update the plaintext size.
const size_t plaintext_size = plaintext.size();
ciphertext->resize(plaintext_size + NumPaddingBytes(plaintext.size()));
return EncryptInternal(plaintext.data(), plaintext_size, ciphertext->data());
}
bool AesEncryptor::Encrypt(const std::string& plaintext,
std::string* ciphertext) {
// Save plaintext size to make it work for in-place conversion, since the
// next statement will update the plaintext size.
const size_t plaintext_size = plaintext.size();
ciphertext->resize(plaintext_size + NumPaddingBytes(plaintext.size()));
return EncryptInternal(
reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext_size,
reinterpret_cast<uint8_t*>(string_as_array(ciphertext)));
}
AesCtrEncryptor::AesCtrEncryptor()
: block_offset_(0),
encrypted_counter_(AES_BLOCK_SIZE, 0),
@ -134,13 +113,22 @@ bool AesCtrEncryptor::SetIv(const std::vector<uint8_t>& iv) {
return true;
}
bool AesCtrEncryptor::EncryptInternal(const uint8_t* plaintext,
size_t plaintext_size,
uint8_t* ciphertext) {
bool AesCtrEncryptor::CryptInternal(const uint8_t* plaintext,
size_t plaintext_size,
uint8_t* ciphertext,
size_t* ciphertext_size) {
DCHECK(plaintext);
DCHECK(ciphertext);
DCHECK(aes_key());
// |ciphertext_size| is always the same as |plaintext_size| for counter mode.
if (*ciphertext_size < plaintext_size) {
LOG(ERROR) << "Expecting output size of at least " << plaintext_size
<< " bytes.";
return false;
}
*ciphertext_size = plaintext_size;
for (size_t i = 0; i < plaintext_size; ++i) {
if (block_offset_ == 0) {
AES_encrypt(&counter_[0], &encrypted_counter_[0], aes_key());
@ -158,11 +146,6 @@ bool AesCtrEncryptor::EncryptInternal(const uint8_t* plaintext,
return true;
}
size_t AesCtrEncryptor::NumPaddingBytes(size_t size) const {
// No padding needed for CTR.
return 0;
}
AesCbcEncryptor::AesCbcEncryptor(CbcPaddingScheme padding_scheme,
bool chain_across_calls)
: padding_scheme_(padding_scheme),
@ -194,9 +177,10 @@ bool AesCbcEncryptor::SetIv(const std::vector<uint8_t>& iv) {
return true;
}
bool AesCbcEncryptor::EncryptInternal(const uint8_t* plaintext,
size_t plaintext_size,
uint8_t* ciphertext) {
bool AesCbcEncryptor::CryptInternal(const uint8_t* plaintext,
size_t plaintext_size,
uint8_t* ciphertext,
size_t* ciphertext_size) {
DCHECK(aes_key());
const size_t residual_block_size = plaintext_size % AES_BLOCK_SIZE;
@ -206,6 +190,15 @@ bool AesCbcEncryptor::EncryptInternal(const uint8_t* plaintext,
return false;
}
const size_t num_padding_bytes = NumPaddingBytes(plaintext_size);
const size_t required_ciphertext_size = plaintext_size + num_padding_bytes;
if (*ciphertext_size < required_ciphertext_size) {
LOG(ERROR) << "Expecting output size of at least "
<< required_ciphertext_size << " bytes.";
return false;
}
*ciphertext_size = required_ciphertext_size;
// Encrypt everything but the residual block using CBC.
const size_t cbc_size = plaintext_size - residual_block_size;
std::vector<uint8_t> local_iv(iv());
@ -231,13 +224,14 @@ bool AesCbcEncryptor::EncryptInternal(const uint8_t* plaintext,
uint8_t* residual_ciphertext_block = ciphertext + cbc_size;
if (padding_scheme_ == kPkcs5Padding) {
const size_t num_padding_bytes = AES_BLOCK_SIZE - residual_block_size;
DCHECK_EQ(num_padding_bytes, NumPaddingBytes(plaintext_size));
DCHECK_EQ(num_padding_bytes, AES_BLOCK_SIZE - residual_block_size);
// Pad residue block with PKCS5 padding.
residual_block.resize(AES_BLOCK_SIZE, static_cast<char>(num_padding_bytes));
AES_cbc_encrypt(residual_block.data(), residual_ciphertext_block,
AES_BLOCK_SIZE, aes_key(), local_iv.data(), AES_ENCRYPT);
} else {
DCHECK_EQ(num_padding_bytes, 0u);
DCHECK_EQ(padding_scheme_, kCtsPadding);
// Zero-pad the residual block and encrypt using CBC.

View File

@ -12,77 +12,29 @@
#include <string>
#include <vector>
#include "packager/base/macros.h"
#include "packager/base/memory/scoped_ptr.h"
#include "packager/base/stl_util.h"
struct aes_key_st;
typedef struct aes_key_st AES_KEY;
#include "packager/media/base/aes_cryptor.h"
namespace edash_packager {
namespace media {
class AesEncryptor {
class AesEncryptor : public AesCryptor {
public:
AesEncryptor();
virtual ~AesEncryptor();
~AesEncryptor() override;
/// Initialize the encryptor with specified key and a random generated IV
/// of the specified size.
/// @return true on successful initialization, false otherwise.
bool InitializeWithRandomIv(const std::vector<uint8_t>& key,
uint8_t iv_size);
bool InitializeWithRandomIv(const std::vector<uint8_t>& key, uint8_t iv_size);
/// Initialize the encryptor with specified key and IV.
/// @return true on successful initialization, false otherwise.
bool InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv);
/// @name Various forms of encrypt calls.
/// The plaintext and ciphertext pointers can be the same address.
bool Encrypt(const std::vector<uint8_t>& plaintext,
std::vector<uint8_t>* ciphertext);
bool Encrypt(const std::string& plaintext, std::string* ciphertext);
bool Encrypt(const uint8_t* plaintext,
size_t plaintext_size,
uint8_t* ciphertext) {
return EncryptInternal(plaintext, plaintext_size, ciphertext);
}
/// @}
/// Update IV for next sample.
/// As recommended in ISO/IEC FDIS 23001-7:
/// IV need to be updated per sample for CENC.
virtual void UpdateIv() = 0;
/// Set IV.
/// @return true if successful, false if the input is invalid.
virtual bool SetIv(const std::vector<uint8_t>& iv) = 0;
/// @return The current iv.
const std::vector<uint8_t>& iv() const { return iv_; }
protected:
/// Internal implementation of encrypt function.
/// @param plaintext points to the input plaintext.
/// @param plaintext_size is the size of input plaintext.
/// @param[out] ciphertext points to the output ciphertext. @a plaintext and
/// @a ciphertext can point to the same address.
virtual bool EncryptInternal(const uint8_t* plaintext,
size_t plaintext_size,
uint8_t* ciphertext) = 0;
/// @param size specifies the input plaintext size.
/// @returns The number of padding bytes needed for output ciphertext.
virtual size_t NumPaddingBytes(size_t size) const = 0;
void set_iv(const std::vector<uint8_t>& iv) { iv_ = iv; }
AES_KEY* aes_key() const { return aes_key_.get(); }
const std::vector<uint8_t>& iv) override;
private:
// Initialization vector, with size 8 or 16.
std::vector<uint8_t> iv_;
// Openssl AES_KEY.
scoped_ptr<AES_KEY> aes_key_;
DISALLOW_COPY_AND_ASSIGN(AesEncryptor);
};
@ -92,7 +44,7 @@ class AesCtrEncryptor : public AesEncryptor {
AesCtrEncryptor();
~AesCtrEncryptor() override;
/// @name AesEncryptor implementation overrides.
/// @name AesCryptor implementation overrides.
/// @{
/// Update IV for next sample. @a block_offset_ is reset to 0.
/// As recommended in ISO/IEC FDIS 23001-7: CENC spec,
@ -105,13 +57,12 @@ class AesCtrEncryptor : public AesEncryptor {
uint32_t block_offset() const { return block_offset_; }
protected:
bool EncryptInternal(const uint8_t* plaintext,
size_t plaintext_size,
uint8_t* ciphertext) override;
size_t NumPaddingBytes(size_t size) const override;
private:
bool CryptInternal(const uint8_t* plaintext,
size_t plaintext_size,
uint8_t* ciphertext,
size_t* ciphertext_size) override;
// Current block offset.
uint32_t block_offset_;
// Current AES-CTR counter.
@ -143,20 +94,20 @@ class AesCbcEncryptor : public AesEncryptor {
AesCbcEncryptor(CbcPaddingScheme padding_scheme, bool chain_across_calls);
~AesCbcEncryptor() override;
/// @name AesEncryptor implementation overrides.
/// @name AesCryptor implementation overrides.
/// @{
void UpdateIv() override;
bool SetIv(const std::vector<uint8_t>& iv) override;
/// @}
protected:
bool EncryptInternal(const uint8_t* plaintext,
size_t plaintext_size,
uint8_t* ciphertext) override;
private:
bool CryptInternal(const uint8_t* plaintext,
size_t plaintext_size,
uint8_t* ciphertext,
size_t* ciphertext_size) override;
size_t NumPaddingBytes(size_t size) const override;
private:
const CbcPaddingScheme padding_scheme_;
const bool chain_across_calls_;

View File

@ -27,7 +27,7 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config,
DCHECK(buffer);
// Get the decryptor object.
AesDecryptor* decryptor;
AesCryptor* decryptor;
auto found = decryptor_map_.find(decrypt_config->key_id());
if (found == decryptor_map_.end()) {
// Create new AesDecryptor based on decryption mode.
@ -38,7 +38,7 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config,
return false;
}
scoped_ptr<AesDecryptor> aes_decryptor;
scoped_ptr<AesCryptor> aes_decryptor;
switch (decrypt_config->decryption_mode()) {
case kEncryptionModeAesCtr:
aes_decryptor.reset(new AesCtrDecryptor);
@ -68,7 +68,7 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config,
if (decrypt_config->subsamples().empty()) {
// Sample not encrypted using subsample encryption. Decrypt whole.
if (!decryptor->Decrypt(buffer, buffer_size, buffer)) {
if (!decryptor->Crypt(buffer, buffer_size, buffer)) {
LOG(ERROR) << "Error during bulk sample decryption.";
return false;
}
@ -86,7 +86,7 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config,
return false;
}
current_ptr += subsample.clear_bytes;
if (!decryptor->Decrypt(current_ptr, subsample.cipher_bytes, current_ptr)) {
if (!decryptor->Crypt(current_ptr, subsample.cipher_bytes, current_ptr)) {
LOG(ERROR) << "Error decrypting subsample buffer.";
return false;
}

View File

@ -29,7 +29,7 @@ class DecryptorSource {
private:
KeySource* key_source_;
std::map<std::vector<uint8_t>, AesDecryptor*> decryptor_map_;
std::map<std::vector<uint8_t>, AesCryptor*> decryptor_map_;
DISALLOW_COPY_AND_ASSIGN(DecryptorSource);
};

View File

@ -13,6 +13,8 @@
'target_name': 'media_base',
'type': '<(component)',
'sources': [
'aes_cryptor.cc',
'aes_cryptor.h',
'aes_decryptor.cc',
'aes_decryptor.h',
'aes_encryptor.cc',
@ -113,7 +115,7 @@
'target_name': 'media_base_unittest',
'type': '<(gtest_target_type)',
'sources': [
'aes_encryptor_unittest.cc',
'aes_cryptor_unittest.cc',
'audio_timestamp_helper_unittest.cc',
'bit_reader_unittest.cc',
'buffer_writer_unittest.cc',

View File

@ -6,6 +6,7 @@
#include "packager/media/base/request_signer.h"
#include "packager/base/logging.h"
#include "packager/base/sha1.h"
#include "packager/base/strings/string_number_conversions.h"
#include "packager/media/base/aes_encryptor.h"
@ -48,7 +49,7 @@ AesRequestSigner* AesRequestSigner::CreateSigner(const std::string& signer_name,
bool AesRequestSigner::GenerateSignature(const std::string& message,
std::string* signature) {
aes_cbc_encryptor_->Encrypt(base::SHA1HashString(message), signature);
aes_cbc_encryptor_->Crypt(base::SHA1HashString(message), signature);
return true;
}

View File

@ -199,7 +199,7 @@ Status EncryptingFragmenter::CreateEncryptor() {
void EncryptingFragmenter::EncryptBytes(uint8_t* data, uint32_t size) {
DCHECK(encryptor_);
CHECK(encryptor_->Encrypt(data, size, data));
CHECK(encryptor_->Crypt(data, size, data));
}
Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {

View File

@ -78,7 +78,7 @@ Status Encryptor::EncryptFrame(scoped_refptr<MediaSample> sample,
uint8_t* sample_data = sample->writable_data();
// Encrypt the data in-place.
if (!encryptor_->Encrypt(sample_data, sample_size, sample_data)) {
if (!encryptor_->Crypt(sample_data, sample_size, sample_data)) {
return Status(error::MUXER_FAILURE, "Failed to encrypt the frame.");
}

View File

@ -779,9 +779,9 @@ bool WvmMediaParser::DemuxNextPes(bool is_program_end) {
if (!content_decryptor_) {
output_encrypted_sample = true;
} else {
content_decryptor_->Decrypt(&sample_data_[crypto_unit_start_pos_],
sample_data_.size() - crypto_unit_start_pos_,
&sample_data_[crypto_unit_start_pos_]);
content_decryptor_->Crypt(&sample_data_[crypto_unit_start_pos_],
sample_data_.size() - crypto_unit_start_pos_,
&sample_data_[crypto_unit_start_pos_]);
}
}
// Demux media sample if we are at program end or if we are not at a
@ -1124,8 +1124,8 @@ bool WvmMediaParser::ProcessEcm() {
kEcmFlagsSizeBytes + kEcmContentKeySizeBytes +
kEcmPaddingSizeBytes; // flags + contentKey + padding.
std::vector<uint8_t> content_key_buffer(content_key_buffer_size);
CHECK(asset_decryptor.Decrypt(ecm_data, content_key_buffer_size,
content_key_buffer.data()));
CHECK(asset_decryptor.Crypt(ecm_data, content_key_buffer_size,
content_key_buffer.data()));
std::vector<uint8_t> decrypted_content_key_vec(
content_key_buffer.begin() + 4,