Add support for "AES 128-bit Cipher Block Chaining (CBC-128) Encryption"

- Part 1.

- Add packager command line argument "protection_scheme" to specify
  protection scheme. Plumb through packager code to enable CBC
  encryption/decryption.
- Add scheme type "cbc1" to sinf.
- Refactor AES encryptor and decryptor.
- Need more work in the subsample handling.

Issue #77

Change-Id: I3a9304d89adf5efbfb226b6e805a3077b6cb8c68
This commit is contained in:
Bei Li 2016-03-17 10:03:19 -07:00
parent ef81be5f7b
commit e39c3572af
32 changed files with 801 additions and 476 deletions

View File

@ -25,6 +25,7 @@
#include "packager/base/time/clock.h" #include "packager/base/time/clock.h"
#include "packager/media/base/container_names.h" #include "packager/media/base/container_names.h"
#include "packager/media/base/demuxer.h" #include "packager/media/base/demuxer.h"
#include "packager/media/base/encryption_modes.h"
#include "packager/media/base/key_source.h" #include "packager/media/base/key_source.h"
#include "packager/media/base/muxer_options.h" #include "packager/media/base/muxer_options.h"
#include "packager/media/base/muxer_util.h" #include "packager/media/base/muxer_util.h"
@ -104,6 +105,18 @@ std::string DetermineTextFileFormat(const std::string& file) {
return ""; return "";
} }
edash_packager::media::EncryptionMode GetEncryptionMode(
const std::string& protection_scheme) {
if (protection_scheme == "cenc") {
return edash_packager::media::kEncryptionModeAesCtr;
} else if (protection_scheme == "cbc1") {
return edash_packager::media::kEncryptionModeAesCbc;
} else {
LOG(ERROR) << "Protection scheme is unknown.";
return edash_packager::media::kEncryptionModeUnknown;
}
}
} // namespace } // namespace
namespace edash_packager { namespace edash_packager {
@ -296,7 +309,8 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors,
muxer->SetKeySource(key_source, muxer->SetKeySource(key_source,
FLAGS_max_sd_pixels, FLAGS_max_sd_pixels,
FLAGS_clear_lead, FLAGS_clear_lead,
FLAGS_crypto_period_duration); FLAGS_crypto_period_duration,
GetEncryptionMode(FLAGS_protection_scheme));
} }
scoped_ptr<MuxerListener> muxer_listener; scoped_ptr<MuxerListener> muxer_listener;
@ -360,6 +374,10 @@ Status RunRemuxJobs(const std::vector<RemuxJob*>& remux_jobs) {
} }
bool RunPackager(const StreamDescriptorList& stream_descriptors) { bool RunPackager(const StreamDescriptorList& stream_descriptors) {
EncryptionMode encryption_mode = GetEncryptionMode(FLAGS_protection_scheme);
if (encryption_mode == kEncryptionModeUnknown)
return false;
if (!AssignFlagsFromProfile()) if (!AssignFlagsFromProfile())
return false; return false;

View File

@ -50,6 +50,10 @@ DEFINE_int32(crypto_period_duration,
0, 0,
"Crypto period duration in seconds. If it is non-zero, key " "Crypto period duration in seconds. If it is non-zero, key "
"rotation is enabled."); "rotation is enabled.");
DEFINE_string(protection_scheme,
"cenc",
"Choose protection scheme. Currently support cenc and cbc1. "
"Default is cenc.");
namespace edash_packager { namespace edash_packager {

View File

@ -22,6 +22,7 @@ DECLARE_string(aes_signing_key);
DECLARE_string(aes_signing_iv); DECLARE_string(aes_signing_iv);
DECLARE_string(rsa_signing_key_path); DECLARE_string(rsa_signing_key_path);
DECLARE_int32(crypto_period_duration); DECLARE_int32(crypto_period_duration);
DECLARE_string(protection_scheme);
namespace edash_packager { namespace edash_packager {

View File

@ -0,0 +1,268 @@
// 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_decryptor.h"
#include <openssl/aes.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include "packager/base/logging.h"
namespace {
// AES defines three key sizes: 128, 192 and 256 bits.
bool IsKeySizeValidForAes(size_t key_size) {
return key_size == 16 || key_size == 24 || key_size == 32;
}
} // namespace
namespace edash_packager {
namespace media {
AesDecryptor::AesDecryptor() {}
AesDecryptor::~AesDecryptor() {}
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);
}
// For AES CTR, encryption and decryption are identical.
bool AesCtrDecryptor::Decrypt(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext) {
DCHECK(encryptor_);
return encryptor_->EncryptData(ciphertext, ciphertext_size, plaintext);
}
bool AesCtrDecryptor::Decrypt(const std::vector<uint8_t>& ciphertext,
std::vector<uint8_t>* plaintext) {
DCHECK(encryptor_);
return encryptor_->Encrypt(ciphertext, plaintext);
}
bool AesCtrDecryptor::Decrypt(const std::string& ciphertext,
std::string* plaintext) {
DCHECK(encryptor_);
return encryptor_->Encrypt(ciphertext, plaintext);
}
bool AesCtrDecryptor::SetIv(const std::vector<uint8_t>& iv) {
DCHECK(encryptor_);
return encryptor_->SetIv(iv);
}
AesCbcPkcs5Decryptor::AesCbcPkcs5Decryptor() {}
AesCbcPkcs5Decryptor::~AesCbcPkcs5Decryptor() {}
bool AesCbcPkcs5Decryptor::InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv) {
if (!IsKeySizeValidForAes(key.size())) {
LOG(ERROR) << "Invalid AES key size: " << key.size();
return false;
}
if (iv.size() != AES_BLOCK_SIZE) {
LOG(ERROR) << "Invalid IV size: " << iv.size();
return false;
}
aes_key_.reset(new AES_KEY());
CHECK_EQ(AES_set_decrypt_key(&key[0], key.size() * 8, aes_key_.get()), 0);
iv_ = iv;
return true;
}
bool AesCbcPkcs5Decryptor::Decrypt(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext) {
NOTIMPLEMENTED();
return false;
}
bool AesCbcPkcs5Decryptor::Decrypt(const std::vector<uint8_t>& ciphertext,
std::vector<uint8_t>* plaintext) {
NOTIMPLEMENTED();
return false;
}
bool AesCbcPkcs5Decryptor::Decrypt(const std::string& ciphertext,
std::string* plaintext) {
if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) {
LOG(ERROR) << "Expecting cipher text size to be multiple of "
<< AES_BLOCK_SIZE << ", got " << ciphertext.size();
return false;
}
DCHECK(plaintext);
DCHECK(aes_key_);
plaintext->resize(ciphertext.size());
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(ciphertext.data()),
reinterpret_cast<uint8_t*>(string_as_array(plaintext)),
ciphertext.size(),
aes_key_.get(),
&iv_[0],
AES_DECRYPT);
// Strip off PKCS5 padding bytes.
const uint8_t num_padding_bytes = (*plaintext)[plaintext->size() - 1];
if (num_padding_bytes > AES_BLOCK_SIZE) {
LOG(ERROR) << "Padding length is too large : "
<< static_cast<int>(num_padding_bytes);
return false;
}
plaintext->resize(plaintext->size() - num_padding_bytes);
return true;
}
bool AesCbcPkcs5Decryptor::SetIv(const std::vector<uint8_t>& iv) {
if (iv.size() != AES_BLOCK_SIZE) {
LOG(ERROR) << "Invalid IV size: " << iv.size();
return false;
}
iv_ = iv;
return true;
}
AesCbcCtsDecryptor::AesCbcCtsDecryptor() {}
AesCbcCtsDecryptor::~AesCbcCtsDecryptor() {}
bool AesCbcCtsDecryptor::InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv) {
if (!IsKeySizeValidForAes(key.size())) {
LOG(ERROR) << "Invalid AES key size: " << key.size();
return false;
}
if (iv.size() != AES_BLOCK_SIZE) {
LOG(ERROR) << "Invalid IV size: " << iv.size();
return false;
}
aes_key_.reset(new AES_KEY());
CHECK_EQ(AES_set_decrypt_key(&key[0], key.size() * 8, aes_key_.get()), 0);
iv_ = iv;
return true;
}
bool AesCbcCtsDecryptor::Decrypt(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext) {
DCHECK(ciphertext);
DCHECK(plaintext);
if (ciphertext_size < AES_BLOCK_SIZE) {
// Don't have a full block, leave unencrypted.
memcpy(plaintext, ciphertext, ciphertext_size);
return true;
}
std::vector<uint8_t> iv(iv_);
size_t residual_block_size = ciphertext_size % AES_BLOCK_SIZE;
if (residual_block_size == 0) {
// No residual block. No need to do ciphertext stealing.
AES_cbc_encrypt(ciphertext,
plaintext,
ciphertext_size,
aes_key_.get(),
&iv[0],
AES_DECRYPT);
return true;
}
// AES-CBC decrypt everything up to the next-to-last full block.
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(),
&iv[0],
AES_DECRYPT);
}
// Determine what the last IV should be so that we can "skip ahead" in the
// CBC decryption.
std::vector<uint8_t> last_iv(
ciphertext + ciphertext_size - residual_block_size,
ciphertext + ciphertext_size);
last_iv.resize(AES_BLOCK_SIZE, 0);
// Decrypt the next-to-last block using the IV determined above. This decrypts
// the residual block bits.
AES_cbc_encrypt(
ciphertext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE,
plaintext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE,
AES_BLOCK_SIZE, aes_key_.get(), &last_iv[0], AES_DECRYPT);
// Swap back the residual block bits and the next-to-last full block.
if (plaintext == ciphertext) {
uint8_t* ptr1 = plaintext + ciphertext_size - residual_block_size;
uint8_t* ptr2 = plaintext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE;
for (size_t i = 0; i < residual_block_size; ++i) {
uint8_t temp = *ptr1;
*ptr1 = *ptr2;
*ptr2 = temp;
++ptr1;
++ptr2;
}
} else {
uint8_t* residual_plaintext_block =
plaintext + ciphertext_size - residual_block_size;
memcpy(residual_plaintext_block, residual_plaintext_block - AES_BLOCK_SIZE,
residual_block_size);
memcpy(residual_plaintext_block - AES_BLOCK_SIZE,
ciphertext + ciphertext_size - residual_block_size,
residual_block_size);
}
// Decrypt the last full block.
AES_cbc_encrypt(
plaintext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE,
plaintext + ciphertext_size - residual_block_size - AES_BLOCK_SIZE,
AES_BLOCK_SIZE, aes_key_.get(), &iv[0], AES_DECRYPT);
return true;
}
bool AesCbcCtsDecryptor::Decrypt(const std::vector<uint8_t>& ciphertext,
std::vector<uint8_t>* plaintext) {
DCHECK(plaintext);
plaintext->resize(ciphertext.size(), 0);
if (ciphertext.empty())
return true;
return Decrypt(ciphertext.data(), ciphertext.size(), &(*plaintext)[0]);
}
bool AesCbcCtsDecryptor::Decrypt(const std::string& ciphertext,
std::string* plaintext) {
NOTIMPLEMENTED();
return false;
}
bool AesCbcCtsDecryptor::SetIv(const std::vector<uint8_t>& iv) {
if (iv.size() != AES_BLOCK_SIZE) {
LOG(ERROR) << "Invalid IV size: " << iv.size();
return false;
}
iv_ = iv;
return true;
}
} // namespace media
} // namespace edash_packager

View File

@ -0,0 +1,153 @@
// 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
//
// AES Decryptor implementation using openssl.
#ifndef MEDIA_BASE_AES_DECRYPTOR_H_
#define MEDIA_BASE_AES_DECRYPTOR_H_
#include <string>
#include <vector>
#include "packager/base/memory/scoped_ptr.h"
#include "packager/base/stl_util.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();
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.
/// @{
virtual bool Decrypt(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext) = 0;
virtual bool Decrypt(const std::vector<uint8_t>& ciphertext,
std::vector<uint8_t>* plaintext) = 0;
virtual bool Decrypt(const std::string& ciphertext,
std::string* plaintext) = 0;
/// @}
/// Set IV. @a block_offset_ is reset to 0 on success.
/// @return true if successful, false if the input is invalid.
virtual bool SetIv(const std::vector<uint8_t>& iv) = 0;
const std::vector<uint8_t>& iv() const { return iv_; }
protected:
// Initialization vector, with size 8 or 16.
std::vector<uint8_t> iv_;
// Openssl AES_KEY.
scoped_ptr<AES_KEY> aes_key_;
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 Decrypt(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext) override;
bool Decrypt(const std::vector<uint8_t>& ciphertext,
std::vector<uint8_t>* plaintext) override;
bool Decrypt(const std::string& ciphertext, std::string* plaintext) override;
bool SetIv(const std::vector<uint8_t>& iv) override;
/// @}
uint32_t block_offset() const { return encryptor_->block_offset(); }
private:
scoped_ptr<AesCtrEncryptor> encryptor_;
DISALLOW_COPY_AND_ASSIGN(AesCtrDecryptor);
};
// Class which implements AES-CBC (Cipher block chaining) decryption with
// PKCS#5 padding.
class AesCbcPkcs5Decryptor : public AesDecryptor {
public:
AesCbcPkcs5Decryptor();
~AesCbcPkcs5Decryptor() override;
/// @name AesDecryptor implementation overrides.
/// @{
bool InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv) override;
bool Decrypt(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext) override;
bool Decrypt(const std::vector<uint8_t>& ciphertext,
std::vector<uint8_t>* plaintext) override;
bool Decrypt(const std::string& ciphertext, std::string* plaintext) override;
bool SetIv(const std::vector<uint8_t>& iv) override;
/// @}
private:
DISALLOW_COPY_AND_ASSIGN(AesCbcPkcs5Decryptor);
};
// Class which implements AES-CBC (Cipher block chaining) decryption with
// Ciphertext stealing.
class AesCbcCtsDecryptor : public AesDecryptor {
public:
AesCbcCtsDecryptor();
~AesCbcCtsDecryptor() override;
/// @name AesDecryptor implementation overrides.
/// @{
bool InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv) override;
bool Decrypt(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext) override;
bool Decrypt(const std::vector<uint8_t>& ciphertext,
std::vector<uint8_t>* plaintext) override;
bool Decrypt(const std::string& ciphertext, std::string* plaintext) override;
bool SetIv(const std::vector<uint8_t>& iv) override;
/// @}
private:
DISALLOW_COPY_AND_ASSIGN(AesCbcCtsDecryptor);
};
} // namespace media
} // namespace edash_packager
#endif // MEDIA_BASE_AES_DECRYPTOR_H_

View File

@ -40,17 +40,11 @@ const uint32_t kCencKeySize = 16;
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
AesCtrEncryptor::AesCtrEncryptor() AesEncryptor::AesEncryptor() {}
: block_offset_(0), AesEncryptor::~AesEncryptor() {}
encrypted_counter_(AES_BLOCK_SIZE, 0),
counter_overflow_(false) {
COMPILE_ASSERT(AES_BLOCK_SIZE == kCencKeySize,
cenc_key_size_should_be_the_same_as_aes_block_size);
}
AesCtrEncryptor::~AesCtrEncryptor() {} bool AesEncryptor::InitializeWithRandomIv(
const std::vector<uint8_t>& key,
bool AesCtrEncryptor::InitializeWithRandomIv(const std::vector<uint8_t>& key,
uint8_t iv_size) { uint8_t iv_size) {
std::vector<uint8_t> iv(iv_size, 0); std::vector<uint8_t> iv(iv_size, 0);
if (RAND_bytes(&iv[0], iv_size) != 1) { if (RAND_bytes(&iv[0], iv_size) != 1) {
@ -61,6 +55,32 @@ bool AesCtrEncryptor::InitializeWithRandomIv(const std::vector<uint8_t>& key,
return InitializeWithIv(key, iv); return InitializeWithIv(key, iv);
} }
bool AesEncryptor::Encrypt(const std::vector<uint8_t>& plaintext,
std::vector<uint8_t>* ciphertext) {
if (plaintext.empty())
return true;
ciphertext->resize(plaintext.size() + NumPaddingBytes(plaintext.size()));
return EncryptData(plaintext.data(), plaintext.size(), ciphertext->data());
}
bool AesEncryptor::Encrypt(const std::string& plaintext,
std::string* ciphertext) {
ciphertext->resize(plaintext.size() + NumPaddingBytes(plaintext.size()));
return EncryptData(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),
counter_overflow_(false) {
COMPILE_ASSERT(AES_BLOCK_SIZE == kCencKeySize,
cenc_key_size_should_be_the_same_as_aes_block_size);
}
AesCtrEncryptor::~AesCtrEncryptor() {}
bool AesCtrEncryptor::InitializeWithIv(const std::vector<uint8_t>& key, bool AesCtrEncryptor::InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv) { const std::vector<uint8_t>& iv) {
if (key.size() != kCencKeySize) { if (key.size() != kCencKeySize) {
@ -77,7 +97,11 @@ bool AesCtrEncryptor::InitializeWithIv(const std::vector<uint8_t>& key,
return SetIv(iv); return SetIv(iv);
} }
bool AesCtrEncryptor::Encrypt(const uint8_t* plaintext, size_t AesCtrEncryptor::NumPaddingBytes(size_t size) {
return 0;
}
bool AesCtrEncryptor::EncryptData(const uint8_t* plaintext,
size_t plaintext_size, size_t plaintext_size,
uint8_t* ciphertext) { uint8_t* ciphertext) {
DCHECK(plaintext); DCHECK(plaintext);
@ -156,34 +180,40 @@ bool AesCbcPkcs5Encryptor::InitializeWithIv(const std::vector<uint8_t>& key,
return false; return false;
} }
encrypt_key_.reset(new AES_KEY()); aes_key_.reset(new AES_KEY());
CHECK_EQ(AES_set_encrypt_key(&key[0], key.size() * 8, encrypt_key_.get()), 0); CHECK_EQ(AES_set_encrypt_key(&key[0], key.size() * 8, aes_key_.get()), 0);
iv_ = iv; iv_ = iv;
return true; return true;
} }
void AesCbcPkcs5Encryptor::Encrypt(const std::string& plaintext, size_t AesCbcPkcs5Encryptor::NumPaddingBytes(size_t size) {
std::string* ciphertext) { return AES_BLOCK_SIZE - (size % AES_BLOCK_SIZE);
}
bool AesCbcPkcs5Encryptor::EncryptData(const uint8_t* plaintext,
size_t plaintext_size,
uint8_t* ciphertext) {
DCHECK(ciphertext); DCHECK(ciphertext);
DCHECK(encrypt_key_); DCHECK(aes_key_);
// Pad the input with PKCS5 padding. // Pad the input with PKCS5 padding.
const size_t num_padding_bytes = // TODO(kqyang): Consider more efficient implementation.
AES_BLOCK_SIZE - (plaintext.size() % AES_BLOCK_SIZE); memcpy(ciphertext, plaintext, plaintext_size);
std::string padded_text = plaintext; for (size_t i = plaintext_size;
padded_text.append(num_padding_bytes, static_cast<char>(num_padding_bytes)); i < plaintext_size + NumPaddingBytes(plaintext_size); ++i) {
ciphertext[i] = NumPaddingBytes(plaintext_size);
}
ciphertext->resize(padded_text.size());
std::vector<uint8_t> iv(iv_); std::vector<uint8_t> iv(iv_);
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(padded_text.data()), AES_cbc_encrypt(ciphertext, ciphertext,
reinterpret_cast<uint8_t*>(string_as_array(ciphertext)), plaintext_size + NumPaddingBytes(plaintext_size),
padded_text.size(), aes_key_.get(), &iv[0], AES_ENCRYPT);
encrypt_key_.get(), return true;
&iv[0],
AES_ENCRYPT);
} }
void AesCbcPkcs5Encryptor::UpdateIv() {}
bool AesCbcPkcs5Encryptor::SetIv(const std::vector<uint8_t>& iv) { bool AesCbcPkcs5Encryptor::SetIv(const std::vector<uint8_t>& iv) {
if (iv.size() != AES_BLOCK_SIZE) { if (iv.size() != AES_BLOCK_SIZE) {
LOG(ERROR) << "Invalid IV size: " << iv.size(); LOG(ERROR) << "Invalid IV size: " << iv.size();
@ -194,67 +224,6 @@ bool AesCbcPkcs5Encryptor::SetIv(const std::vector<uint8_t>& iv) {
return true; return true;
} }
AesCbcPkcs5Decryptor::AesCbcPkcs5Decryptor() {}
AesCbcPkcs5Decryptor::~AesCbcPkcs5Decryptor() {}
bool AesCbcPkcs5Decryptor::InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv) {
if (!IsKeySizeValidForAes(key.size())) {
LOG(ERROR) << "Invalid AES key size: " << key.size();
return false;
}
if (iv.size() != AES_BLOCK_SIZE) {
LOG(ERROR) << "Invalid IV size: " << iv.size();
return false;
}
decrypt_key_.reset(new AES_KEY());
CHECK_EQ(AES_set_decrypt_key(&key[0], key.size() * 8, decrypt_key_.get()), 0);
iv_ = iv;
return true;
}
bool AesCbcPkcs5Decryptor::Decrypt(const std::string& ciphertext,
std::string* plaintext) {
if ((ciphertext.size() % AES_BLOCK_SIZE) != 0) {
LOG(ERROR) << "Expecting cipher text size to be multiple of "
<< AES_BLOCK_SIZE << ", got " << ciphertext.size();
return false;
}
DCHECK(plaintext);
DCHECK(decrypt_key_);
plaintext->resize(ciphertext.size());
AES_cbc_encrypt(reinterpret_cast<const uint8_t*>(ciphertext.data()),
reinterpret_cast<uint8_t*>(string_as_array(plaintext)),
ciphertext.size(),
decrypt_key_.get(),
&iv_[0],
AES_DECRYPT);
// Strip off PKCS5 padding bytes.
const uint8_t num_padding_bytes = (*plaintext)[plaintext->size() - 1];
if (num_padding_bytes > AES_BLOCK_SIZE) {
LOG(ERROR) << "Padding length is too large : "
<< static_cast<int>(num_padding_bytes);
return false;
}
plaintext->resize(plaintext->size() - num_padding_bytes);
return true;
}
bool AesCbcPkcs5Decryptor::SetIv(const std::vector<uint8_t>& iv) {
if (iv.size() != AES_BLOCK_SIZE) {
LOG(ERROR) << "Invalid IV size: " << iv.size();
return false;
}
iv_ = iv;
return true;
}
AesCbcCtsEncryptor::AesCbcCtsEncryptor() {} AesCbcCtsEncryptor::AesCbcCtsEncryptor() {}
AesCbcCtsEncryptor::~AesCbcCtsEncryptor() {} AesCbcCtsEncryptor::~AesCbcCtsEncryptor() {}
@ -269,14 +238,18 @@ bool AesCbcCtsEncryptor::InitializeWithIv(const std::vector<uint8_t>& key,
return false; return false;
} }
encrypt_key_.reset(new AES_KEY()); aes_key_.reset(new AES_KEY());
CHECK_EQ(AES_set_encrypt_key(&key[0], key.size() * 8, encrypt_key_.get()), 0); CHECK_EQ(AES_set_encrypt_key(&key[0], key.size() * 8, aes_key_.get()), 0);
iv_ = iv; iv_ = iv;
return true; return true;
} }
void AesCbcCtsEncryptor::Encrypt(const uint8_t* plaintext, size_t AesCbcCtsEncryptor::NumPaddingBytes(size_t size) {
return 0;
}
bool AesCbcCtsEncryptor::EncryptData(const uint8_t* plaintext,
size_t size, size_t size,
uint8_t* ciphertext) { uint8_t* ciphertext) {
DCHECK(plaintext); DCHECK(plaintext);
@ -285,7 +258,7 @@ void AesCbcCtsEncryptor::Encrypt(const uint8_t* plaintext,
if (size < AES_BLOCK_SIZE) { if (size < AES_BLOCK_SIZE) {
// Don't have a full block, leave unencrypted. // Don't have a full block, leave unencrypted.
memcpy(ciphertext, plaintext, size); memcpy(ciphertext, plaintext, size);
return; return true;
} }
std::vector<uint8_t> iv(iv_); std::vector<uint8_t> iv(iv_);
@ -296,12 +269,12 @@ void AesCbcCtsEncryptor::Encrypt(const uint8_t* plaintext,
AES_cbc_encrypt(plaintext, AES_cbc_encrypt(plaintext,
ciphertext, ciphertext,
cbc_size, cbc_size,
encrypt_key_.get(), aes_key_.get(),
&iv[0], &iv[0],
AES_ENCRYPT); AES_ENCRYPT);
if (residual_block_size == 0) { if (residual_block_size == 0) {
// No residual block. No need to do ciphertext stealing. // No residual block. No need to do ciphertext stealing.
return; return true;
} }
// Zero-pad the residual block and encrypt using CBC. // Zero-pad the residual block and encrypt using CBC.
@ -311,7 +284,7 @@ void AesCbcCtsEncryptor::Encrypt(const uint8_t* plaintext,
AES_cbc_encrypt(&residual_block[0], AES_cbc_encrypt(&residual_block[0],
&residual_block[0], &residual_block[0],
AES_BLOCK_SIZE, AES_BLOCK_SIZE,
encrypt_key_.get(), aes_key_.get(),
&iv[0], &iv[0],
AES_ENCRYPT); AES_ENCRYPT);
@ -327,18 +300,10 @@ void AesCbcCtsEncryptor::Encrypt(const uint8_t* plaintext,
memcpy(residual_ciphertext_block - AES_BLOCK_SIZE, memcpy(residual_ciphertext_block - AES_BLOCK_SIZE,
residual_block.data(), residual_block.data(),
AES_BLOCK_SIZE); AES_BLOCK_SIZE);
return true;
} }
void AesCbcCtsEncryptor::Encrypt(const std::vector<uint8_t>& plaintext, void AesCbcCtsEncryptor::UpdateIv() {}
std::vector<uint8_t>* ciphertext) {
DCHECK(ciphertext);
ciphertext->resize(plaintext.size(), 0);
if (plaintext.empty())
return;
Encrypt(plaintext.data(), plaintext.size(), &(*ciphertext)[0]);
}
bool AesCbcCtsEncryptor::SetIv(const std::vector<uint8_t>& iv) { bool AesCbcCtsEncryptor::SetIv(const std::vector<uint8_t>& iv) {
if (iv.size() != AES_BLOCK_SIZE) { if (iv.size() != AES_BLOCK_SIZE) {
@ -350,129 +315,5 @@ bool AesCbcCtsEncryptor::SetIv(const std::vector<uint8_t>& iv) {
return true; return true;
} }
AesCbcCtsDecryptor::AesCbcCtsDecryptor() {}
AesCbcCtsDecryptor::~AesCbcCtsDecryptor() {}
bool AesCbcCtsDecryptor::InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv) {
if (!IsKeySizeValidForAes(key.size())) {
LOG(ERROR) << "Invalid AES key size: " << key.size();
return false;
}
if (iv.size() != AES_BLOCK_SIZE) {
LOG(ERROR) << "Invalid IV size: " << iv.size();
return false;
}
decrypt_key_.reset(new AES_KEY());
CHECK_EQ(AES_set_decrypt_key(&key[0], key.size() * 8, decrypt_key_.get()), 0);
iv_ = iv;
return true;
}
void AesCbcCtsDecryptor::Decrypt(const uint8_t* ciphertext,
size_t size,
uint8_t* plaintext) {
DCHECK(ciphertext);
DCHECK(plaintext);
if (size < AES_BLOCK_SIZE) {
// Don't have a full block, leave unencrypted.
memcpy(plaintext, ciphertext, size);
return;
}
std::vector<uint8_t> iv(iv_);
size_t residual_block_size = size % AES_BLOCK_SIZE;
if (residual_block_size == 0) {
// No residual block. No need to do ciphertext stealing.
AES_cbc_encrypt(ciphertext,
plaintext,
size,
decrypt_key_.get(),
&iv[0],
AES_DECRYPT);
return;
}
// AES-CBC decrypt everything up to the next-to-last full block.
size_t cbc_size = size - residual_block_size;
if (cbc_size > AES_BLOCK_SIZE) {
AES_cbc_encrypt(ciphertext,
plaintext,
cbc_size - AES_BLOCK_SIZE,
decrypt_key_.get(),
&iv[0],
AES_DECRYPT);
}
// Determine what the last IV should be so that we can "skip ahead" in the
// CBC decryption.
std::vector<uint8_t> last_iv(ciphertext + size - residual_block_size,
ciphertext + size);
last_iv.resize(AES_BLOCK_SIZE, 0);
// Decrypt the next-to-last block using the IV determined above. This decrypts
// the residual block bits.
AES_cbc_encrypt(ciphertext + size - residual_block_size - AES_BLOCK_SIZE,
plaintext + size - residual_block_size - AES_BLOCK_SIZE,
AES_BLOCK_SIZE,
decrypt_key_.get(),
&last_iv[0],
AES_DECRYPT);
// Swap back the residual block bits and the next-to-last full block.
if (plaintext == ciphertext) {
uint8_t* ptr1 = plaintext + size - residual_block_size;
uint8_t* ptr2 = plaintext + size - residual_block_size - AES_BLOCK_SIZE;
for (size_t i = 0; i < residual_block_size; ++i) {
uint8_t temp = *ptr1;
*ptr1 = *ptr2;
*ptr2 = temp;
++ptr1;
++ptr2;
}
} else {
uint8_t* residual_plaintext_block = plaintext + size - residual_block_size;
memcpy(residual_plaintext_block,
residual_plaintext_block - AES_BLOCK_SIZE,
residual_block_size);
memcpy(residual_plaintext_block - AES_BLOCK_SIZE,
ciphertext + size - residual_block_size,
residual_block_size);
}
// Decrypt the last full block.
AES_cbc_encrypt(plaintext + size - residual_block_size - AES_BLOCK_SIZE,
plaintext + size - residual_block_size - AES_BLOCK_SIZE,
AES_BLOCK_SIZE,
decrypt_key_.get(),
&iv[0],
AES_DECRYPT);
}
void AesCbcCtsDecryptor::Decrypt(const std::vector<uint8_t>& ciphertext,
std::vector<uint8_t>* plaintext) {
DCHECK(plaintext);
plaintext->resize(ciphertext.size(), 0);
if (ciphertext.empty())
return;
Decrypt(ciphertext.data(), ciphertext.size(), &(*plaintext)[0]);
}
bool AesCbcCtsDecryptor::SetIv(const std::vector<uint8_t>& iv) {
if (iv.size() != AES_BLOCK_SIZE) {
LOG(ERROR) << "Invalid IV size: " << iv.size();
return false;
}
iv_ = iv;
return true;
}
} // namespace media } // namespace media
} // namespace edash_packager } // namespace edash_packager

View File

@ -21,86 +21,93 @@ typedef struct aes_key_st AES_KEY;
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
// Class which implements AES-CTR counter-mode encryption/decryption. class AesEncryptor {
class AesCtrEncryptor {
public: public:
AesCtrEncryptor(); AesEncryptor();
~AesCtrEncryptor(); virtual ~AesEncryptor();
/// Initialize the encryptor with specified key and a random generated IV /// Initialize the encryptor with specified key and a random generated IV
/// of the specified size. block_offset() is reset to 0 on success. /// of the specified size.
/// @return true on successful initialization, false otherwise.
virtual 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.
virtual bool InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv) = 0;
virtual size_t NumPaddingBytes(size_t size) = 0;
/// @name Various forms of encrypt and decrypt calls.
/// The plaintext and ciphertext pointers can be the same address.
/// @{
virtual bool EncryptData(const uint8_t* plaintext,
size_t plaintext_size,
uint8_t* ciphertext) = 0;
bool Encrypt(const std::vector<uint8_t>& plaintext,
std::vector<uint8_t>* ciphertext);
bool Encrypt(const std::string& plaintext, std::string* ciphertext);
/// @}
/// Update IV for next sample.
/// As recommended in ISO/IEC FDIS 23001-7:
/// IV need to be updated per sample for CENC.
/// IV need not be unique per sample for CBC mode.
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;
const std::vector<uint8_t>& iv() const { return iv_; }
protected:
// Initialization vector, with size 8 or 16.
std::vector<uint8_t> iv_;
// Openssl AES_KEY.
scoped_ptr<AES_KEY> aes_key_;
private:
DISALLOW_COPY_AND_ASSIGN(AesEncryptor);
};
// Class which implements AES-CTR counter-mode encryption/decryption.
class AesCtrEncryptor : public AesEncryptor {
public:
AesCtrEncryptor();
~AesCtrEncryptor() override;
/// @name AesEncryptor implementation overrides.
/// @{
/// @param key should be 16 bytes in size as specified in CENC spec. /// @param key should be 16 bytes in size as specified in CENC spec.
/// @param iv_size should be either 8 or 16 as specified in CENC spec. /// @param iv_size should be either 8 or 16 as specified in CENC spec.
/// @return true on successful initialization, false otherwise. /// @return true on successful initialization, false otherwise.
bool InitializeWithRandomIv(const std::vector<uint8_t>& key, uint8_t iv_size);
/// Initialize the encryptor with specified key and IV. block_offset() is
/// reset to 0 on success.
/// @param key should be 16 bytes in size as specified in CENC spec.
/// @param iv should be 8 bytes or 16 bytes in size as specified in CENC spec.
/// @return true on successful initialization, false otherwise.
bool InitializeWithIv(const std::vector<uint8_t>& key, bool InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv); const std::vector<uint8_t>& iv) override;
/// @name Various forms of encrypt calls. size_t NumPaddingBytes(size_t size) override;
/// block_offset() will be updated according to input plaintext size.
/// The plaintext and ciphertext pointers can be the same address. bool EncryptData(const uint8_t* plaintext,
/// @{
bool Encrypt(const uint8_t* plaintext,
size_t plaintext_size, size_t plaintext_size,
uint8_t* ciphertext); uint8_t* ciphertext) override;
bool Encrypt(const std::vector<uint8_t>& plaintext,
std::vector<uint8_t>* ciphertext) {
ciphertext->resize(plaintext.size());
return Encrypt(&plaintext[0], plaintext.size(), &(*ciphertext)[0]);
}
bool Encrypt(const std::string& plaintext, std::string* ciphertext) {
ciphertext->resize(plaintext.size());
return Encrypt(reinterpret_cast<const uint8_t*>(plaintext.data()),
plaintext.size(),
reinterpret_cast<uint8_t*>(string_as_array(ciphertext)));
}
/// @}
// For AES CTR, encryption and decryption are identical.
bool Decrypt(const uint8_t* ciphertext,
size_t ciphertext_size,
uint8_t* plaintext) {
return Encrypt(ciphertext, ciphertext_size, plaintext);
}
bool Decrypt(const std::vector<uint8_t>& ciphertext,
std::vector<uint8_t>* plaintext) {
return Encrypt(ciphertext, plaintext);
}
bool Decrypt(const std::string& ciphertext, std::string* plaintext) {
return Encrypt(ciphertext, plaintext);
}
/// Update IV for next sample. @a block_offset_ is reset to 0. /// Update IV for next sample. @a block_offset_ is reset to 0.
/// As recommended in ISO/IEC FDIS 23001-7: CENC spec, /// As recommended in ISO/IEC FDIS 23001-7: CENC spec,
/// For 64-bit IV size, new_iv = old_iv + 1; /// For 64-bit IV size, new_iv = old_iv + 1;
/// For 128-bit IV size, new_iv = old_iv + previous_sample_block_count. /// For 128-bit IV size, new_iv = old_iv + previous_sample_block_count.
void UpdateIv(); void UpdateIv() override;
/// Set IV. @a block_offset_ is reset to 0 on success. bool SetIv(const std::vector<uint8_t>& iv) override;
/// @return true if successful, false if the input is invalid. /// @}
bool SetIv(const std::vector<uint8_t>& iv);
const std::vector<uint8_t>& iv() const { return iv_; }
uint32_t block_offset() const { return block_offset_; } uint32_t block_offset() const { return block_offset_; }
private: private:
// Initialization vector, with size 8 or 16.
std::vector<uint8_t> iv_;
// Current block offset. // Current block offset.
uint32_t block_offset_; uint32_t block_offset_;
// Openssl AES_KEY.
scoped_ptr<AES_KEY> aes_key_;
// Current AES-CTR counter. // Current AES-CTR counter.
std::vector<uint8_t> counter_; std::vector<uint8_t> counter_;
// Encrypted counter. // Encrypted counter.
@ -113,148 +120,58 @@ class AesCtrEncryptor {
// Class which implements AES-CBC (Cipher block chaining) encryption with // Class which implements AES-CBC (Cipher block chaining) encryption with
// PKCS#5 padding. // PKCS#5 padding.
class AesCbcPkcs5Encryptor { class AesCbcPkcs5Encryptor : public AesEncryptor {
public: public:
AesCbcPkcs5Encryptor(); AesCbcPkcs5Encryptor();
~AesCbcPkcs5Encryptor(); ~AesCbcPkcs5Encryptor() override;
/// Initialize the encryptor with specified key and IV. /// @name AesEncryptor implementation overrides.
/// @param key should be 128 bits or 192 bits or 256 bits in size as defined /// @{
/// in AES spec.
/// @param iv should be 16 bytes in size.
/// @return true on successful initialization, false otherwise.
bool InitializeWithIv(const std::vector<uint8_t>& key, bool InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv); const std::vector<uint8_t>& iv) override;
/// @param plaintext will be PKCS5 padded before being encrypted. size_t NumPaddingBytes(size_t size) override;
/// @param ciphertext should not be NULL.
void Encrypt(const std::string& plaintext, std::string* ciphertext);
/// @return true if successful, false if the input is invalid. bool EncryptData(const uint8_t* plaintext,
bool SetIv(const std::vector<uint8_t>& iv); size_t plaintext_size,
uint8_t* ciphertext) override;
const std::vector<uint8_t>& iv() const { return iv_; } void UpdateIv() override;
bool SetIv(const std::vector<uint8_t>& iv) override;
/// @}
private: private:
std::vector<uint8_t> iv_;
scoped_ptr<AES_KEY> encrypt_key_;
DISALLOW_COPY_AND_ASSIGN(AesCbcPkcs5Encryptor); DISALLOW_COPY_AND_ASSIGN(AesCbcPkcs5Encryptor);
}; };
// Class which implements AES-CBC (Cipher block chaining) decryption with
// PKCS#5 padding.
class AesCbcPkcs5Decryptor {
public:
AesCbcPkcs5Decryptor();
~AesCbcPkcs5Decryptor();
/// Initialize the decryptor with specified key and IV.
/// @param key should be 128 bits or 192 bits or 256 bits in size as defined
/// in AES spec.
/// @param iv should be 16 bytes in size.
/// @return true on successful initialization, false otherwise.
bool InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv);
/// @param ciphertext is expected to be padded with PKCS5 padding.
/// @param plaintext should not be NULL.
/// @return true on success, false otherwise.
bool Decrypt(const std::string& ciphertext, std::string* plaintext);
/// @return true if successful, false if the input is invalid.
bool SetIv(const std::vector<uint8_t>& iv);
const std::vector<uint8_t>& iv() const { return iv_; }
private:
std::vector<uint8_t> iv_;
scoped_ptr<AES_KEY> decrypt_key_;
DISALLOW_COPY_AND_ASSIGN(AesCbcPkcs5Decryptor);
};
// Class which implements AES-CBC (Cipher block chaining) encryption with // Class which implements AES-CBC (Cipher block chaining) encryption with
// Ciphertext stealing. // Ciphertext stealing.
class AesCbcCtsEncryptor { class AesCbcCtsEncryptor : public AesEncryptor {
public: public:
AesCbcCtsEncryptor(); AesCbcCtsEncryptor();
~AesCbcCtsEncryptor(); ~AesCbcCtsEncryptor() override;
/// Initialize the encryptor with specified key and IV. /// @name AesEncryptor implementation overrides.
/// @param key should be 128 bits or 192 bits or 256 bits in size as defined /// @{
/// in AES spec.
/// @param iv should be 16 bytes in size.
/// @return true on successful initialization, false otherwise.
bool InitializeWithIv(const std::vector<uint8_t>& key, bool InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv); const std::vector<uint8_t>& iv) override;
/// @param plaintext points to the data to be encrypted. size_t NumPaddingBytes(size_t size) override;
/// @param size is the number of bytes to be encrypted. If less than 16
/// bytes, it will be copied in the clear.
/// @param ciphertext should not be NULL. The buffer should be at least
/// @a size bytes in length.
void Encrypt(const uint8_t* plaintext, size_t size, uint8_t* ciphertext);
/// @param plaintext contains the data to be encrypted. If less than 16 bool EncryptData(const uint8_t* plaintext,
/// bytes in size, it will be copied in the clear. size_t plaintext_size,
/// @param ciphertext should not be NULL. Caller retains ownership. uint8_t* ciphertext) override;
void Encrypt(const std::vector<uint8_t>& plaintext,
std::vector<uint8_t>* ciphertext);
/// @param iv is the initialization vector. Should be 16 bytes in size. void UpdateIv() override;
/// @return true if successful, false if the input is invalid.
bool SetIv(const std::vector<uint8_t>& iv);
const std::vector<uint8_t>& iv() const { return iv_; } bool SetIv(const std::vector<uint8_t>& iv) override;
/// @}
private: private:
std::vector<uint8_t> iv_;
scoped_ptr<AES_KEY> encrypt_key_;
DISALLOW_COPY_AND_ASSIGN(AesCbcCtsEncryptor); DISALLOW_COPY_AND_ASSIGN(AesCbcCtsEncryptor);
}; };
// Class which implements AES-CBC (Cipher block chaining) decryption with
// Ciphertext stealing.
class AesCbcCtsDecryptor {
public:
AesCbcCtsDecryptor();
~AesCbcCtsDecryptor();
/// Initialize the decryptor with specified key and IV.
/// @param key should be 128 bits or 192 bits or 256 bits in size as defined
/// in AES spec.
/// @param iv should be 16 bytes in size.
/// @return true on successful initialization, false otherwise.
bool InitializeWithIv(const std::vector<uint8_t>& key,
const std::vector<uint8_t>& iv);
/// @param ciphertext points to the data to be decrypted.
/// @param size is the number of bytes to be decrypted. If less than 16
/// bytes, it will be copied in the clear.
/// @param plaintext should not be NULL. The buffer should be at least
/// @a size bytes in length.
void Decrypt(const uint8_t* ciphertext, size_t size, uint8_t* plaintext);
/// @param ciphertext contains the data to be decrypted. If less than 16
/// bytes in size, it will be copied in the clear.
/// @param plaintext should not be NULL. Caller retains ownership.
void Decrypt(const std::vector<uint8_t>& ciphertext,
std::vector<uint8_t>* plaintext);
/// @return true if successful, false if the input is invalid.
bool SetIv(const std::vector<uint8_t>& iv);
const std::vector<uint8_t>& iv() const { return iv_; }
private:
std::vector<uint8_t> iv_;
scoped_ptr<AES_KEY> decrypt_key_;
DISALLOW_COPY_AND_ASSIGN(AesCbcCtsDecryptor);
};
} // namespace media } // namespace media
} // namespace edash_packager } // namespace edash_packager

View File

@ -10,6 +10,7 @@
#include "packager/base/memory/scoped_ptr.h" #include "packager/base/memory/scoped_ptr.h"
#include "packager/base/strings/string_number_conversions.h" #include "packager/base/strings/string_number_conversions.h"
#include "packager/media/base/aes_encryptor.h" #include "packager/media/base/aes_encryptor.h"
#include "packager/media/base/aes_decryptor.h"
namespace { namespace {
@ -144,6 +145,7 @@ class AesCtrEncryptorTest : public testing::Test {
kAesCtrCiphertext + arraysize(kAesCtrCiphertext)); kAesCtrCiphertext + arraysize(kAesCtrCiphertext));
ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_)); ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_));
ASSERT_TRUE(decryptor_.InitializeWithIv(key_, iv_));
} }
protected: protected:
@ -152,6 +154,7 @@ class AesCtrEncryptorTest : public testing::Test {
std::vector<uint8_t> plaintext_; std::vector<uint8_t> plaintext_;
std::vector<uint8_t> ciphertext_; std::vector<uint8_t> ciphertext_;
AesCtrEncryptor encryptor_; AesCtrEncryptor encryptor_;
AesCtrDecryptor decryptor_;
}; };
TEST_F(AesCtrEncryptorTest, NistTestCase) { TEST_F(AesCtrEncryptorTest, NistTestCase) {
@ -159,19 +162,19 @@ TEST_F(AesCtrEncryptorTest, NistTestCase) {
EXPECT_TRUE(encryptor_.Encrypt(plaintext_, &encrypted)); EXPECT_TRUE(encryptor_.Encrypt(plaintext_, &encrypted));
EXPECT_EQ(ciphertext_, encrypted); EXPECT_EQ(ciphertext_, encrypted);
EXPECT_TRUE(encryptor_.SetIv(iv_)); EXPECT_TRUE(decryptor_.SetIv(iv_));
std::vector<uint8_t> decrypted; std::vector<uint8_t> decrypted;
EXPECT_TRUE(encryptor_.Decrypt(encrypted, &decrypted)); EXPECT_TRUE(decryptor_.Decrypt(encrypted, &decrypted));
EXPECT_EQ(plaintext_, decrypted); EXPECT_EQ(plaintext_, decrypted);
} }
TEST_F(AesCtrEncryptorTest, NistTestCaseInplaceEncryptionDecryption) { TEST_F(AesCtrEncryptorTest, NistTestCaseInplaceEncryptionDecryption) {
std::vector<uint8_t> buffer = plaintext_; std::vector<uint8_t> buffer = plaintext_;
EXPECT_TRUE(encryptor_.Encrypt(&buffer[0], buffer.size(), &buffer[0])); EXPECT_TRUE(encryptor_.EncryptData(&buffer[0], buffer.size(), &buffer[0]));
EXPECT_EQ(ciphertext_, buffer); EXPECT_EQ(ciphertext_, buffer);
EXPECT_TRUE(encryptor_.SetIv(iv_)); EXPECT_TRUE(decryptor_.SetIv(iv_));
EXPECT_TRUE(encryptor_.Decrypt(&buffer[0], buffer.size(), &buffer[0])); EXPECT_TRUE(decryptor_.Decrypt(&buffer[0], buffer.size(), &buffer[0]));
EXPECT_EQ(plaintext_, buffer); EXPECT_EQ(plaintext_, buffer);
} }
@ -186,8 +189,8 @@ TEST_F(AesCtrEncryptorTest, EncryptDecryptString) {
base::HexEncode(ciphertext.data(), ciphertext.size())); base::HexEncode(ciphertext.data(), ciphertext.size()));
std::string decrypted; std::string decrypted;
EXPECT_TRUE(encryptor_.SetIv(iv_)); EXPECT_TRUE(decryptor_.SetIv(iv_));
EXPECT_TRUE(encryptor_.Decrypt(ciphertext, &decrypted)); EXPECT_TRUE(decryptor_.Decrypt(ciphertext, &decrypted));
EXPECT_EQ(kPlaintext, decrypted); EXPECT_EQ(kPlaintext, decrypted);
} }
@ -208,11 +211,11 @@ TEST_F(AesCtrEncryptorTest, 128BitIVBoundaryCaseEncryption) {
ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_max64)); ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_max64));
std::vector<uint8_t> encrypted_verify(plaintext_.size(), 0); std::vector<uint8_t> encrypted_verify(plaintext_.size(), 0);
EXPECT_TRUE( EXPECT_TRUE(encryptor_.EncryptData(&plaintext_[0], kAesBlockSize,
encryptor_.Encrypt(&plaintext_[0], kAesBlockSize, &encrypted_verify[0])); &encrypted_verify[0]));
std::vector<uint8_t> iv_zero(kIv128Zero, kIv128Zero + arraysize(kIv128Zero)); std::vector<uint8_t> iv_zero(kIv128Zero, kIv128Zero + arraysize(kIv128Zero));
ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_zero)); ASSERT_TRUE(encryptor_.InitializeWithIv(key_, iv_zero));
EXPECT_TRUE(encryptor_.Encrypt(&plaintext_[kAesBlockSize], EXPECT_TRUE(encryptor_.EncryptData(&plaintext_[kAesBlockSize],
kAesBlockSize * 3, kAesBlockSize * 3,
&encrypted_verify[kAesBlockSize])); &encrypted_verify[kAesBlockSize]));
EXPECT_EQ(encrypted, encrypted_verify); EXPECT_EQ(encrypted, encrypted_verify);
@ -251,20 +254,20 @@ TEST_P(AesCtrEncryptorSubsampleTest, NistTestCaseSubsamples) {
for (uint32_t i = 0, offset = 0; i < test_case->subsample_count; ++i) { for (uint32_t i = 0, offset = 0; i < test_case->subsample_count; ++i) {
uint32_t len = test_case->subsample_sizes[i]; uint32_t len = test_case->subsample_sizes[i];
EXPECT_TRUE( EXPECT_TRUE(
encryptor_.Encrypt(&plaintext_[offset], len, &encrypted[offset])); encryptor_.EncryptData(&plaintext_[offset], len, &encrypted[offset]));
offset += len; offset += len;
EXPECT_EQ(offset % kAesBlockSize, encryptor_.block_offset()); EXPECT_EQ(offset % kAesBlockSize, encryptor_.block_offset());
} }
EXPECT_EQ(ciphertext_, encrypted); EXPECT_EQ(ciphertext_, encrypted);
EXPECT_TRUE(encryptor_.SetIv(iv_)); EXPECT_TRUE(decryptor_.SetIv(iv_));
std::vector<uint8_t> decrypted(encrypted.size(), 0); std::vector<uint8_t> decrypted(encrypted.size(), 0);
for (uint32_t i = 0, offset = 0; i < test_case->subsample_count; ++i) { for (uint32_t i = 0, offset = 0; i < test_case->subsample_count; ++i) {
uint32_t len = test_case->subsample_sizes[i]; uint32_t len = test_case->subsample_sizes[i];
EXPECT_TRUE( EXPECT_TRUE(
encryptor_.Decrypt(&encrypted[offset], len, &decrypted[offset])); decryptor_.Decrypt(&encrypted[offset], len, &decrypted[offset]));
offset += len; offset += len;
EXPECT_EQ(offset % kAesBlockSize, encryptor_.block_offset()); EXPECT_EQ(offset % kAesBlockSize, decryptor_.block_offset());
} }
EXPECT_EQ(plaintext_, decrypted); EXPECT_EQ(plaintext_, decrypted);
} }

View File

@ -11,8 +11,12 @@ namespace media {
DecryptConfig::DecryptConfig(const std::vector<uint8_t>& key_id, DecryptConfig::DecryptConfig(const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& iv, const std::vector<uint8_t>& iv,
const std::vector<SubsampleEntry>& subsamples) const std::vector<SubsampleEntry>& subsamples,
: key_id_(key_id), iv_(iv), subsamples_(subsamples) { EncryptionMode decryption_mode)
: key_id_(key_id),
iv_(iv),
subsamples_(subsamples),
decryption_mode_(decryption_mode) {
CHECK_GT(key_id.size(), 0u); CHECK_GT(key_id.size(), 0u);
} }

View File

@ -11,6 +11,7 @@
#include <vector> #include <vector>
#include "packager/base/memory/scoped_ptr.h" #include "packager/base/memory/scoped_ptr.h"
#include "packager/media/base/encryption_modes.h"
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
@ -46,14 +47,18 @@ class DecryptConfig {
/// @param subsamples defines the clear and encrypted portions of the sample /// @param subsamples defines the clear and encrypted portions of the sample
/// as described in SubsampleEntry. A decrypted buffer will be equal /// as described in SubsampleEntry. A decrypted buffer will be equal
/// in size to the sum of the subsample sizes. /// in size to the sum of the subsample sizes.
/// @param decryption_mode decryption_mode is to determine which decryptor to
/// use.
DecryptConfig(const std::vector<uint8_t>& key_id, DecryptConfig(const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& iv, const std::vector<uint8_t>& iv,
const std::vector<SubsampleEntry>& subsamples); const std::vector<SubsampleEntry>& subsamples,
EncryptionMode decryption_mode);
~DecryptConfig(); ~DecryptConfig();
const std::vector<uint8_t>& key_id() const { return key_id_; } const std::vector<uint8_t>& key_id() const { return key_id_; }
const std::vector<uint8_t>& iv() const { return iv_; } const std::vector<uint8_t>& iv() const { return iv_; }
const std::vector<SubsampleEntry>& subsamples() const { return subsamples_; } const std::vector<SubsampleEntry>& subsamples() const { return subsamples_; }
EncryptionMode decryption_mode() const { return decryption_mode_; }
private: private:
const std::vector<uint8_t> key_id_; const std::vector<uint8_t> key_id_;
@ -65,6 +70,8 @@ class DecryptConfig {
// (less data ignored by data_offset_) is encrypted. // (less data ignored by data_offset_) is encrypted.
const std::vector<SubsampleEntry> subsamples_; const std::vector<SubsampleEntry> subsamples_;
EncryptionMode decryption_mode_;
DISALLOW_COPY_AND_ASSIGN(DecryptConfig); DISALLOW_COPY_AND_ASSIGN(DecryptConfig);
}; };

View File

@ -27,22 +27,36 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config,
DCHECK(buffer); DCHECK(buffer);
// Get the decryptor object. // Get the decryptor object.
AesCtrEncryptor* decryptor; AesDecryptor* decryptor;
auto found = decryptor_map_.find(decrypt_config->key_id()); auto found = decryptor_map_.find(decrypt_config->key_id());
if (found == decryptor_map_.end()) { if (found == decryptor_map_.end()) {
// Create new AesCtrEncryptor // Create new AesDecryptor based on decryption mode.
EncryptionKey key; EncryptionKey key;
Status status(key_source_->GetKey(decrypt_config->key_id(), &key)); Status status(key_source_->GetKey(decrypt_config->key_id(), &key));
if (!status.ok()) { if (!status.ok()) {
LOG(ERROR) << "Error retrieving decryption key: " << status; LOG(ERROR) << "Error retrieving decryption key: " << status;
return false; return false;
} }
scoped_ptr<AesCtrEncryptor> aes_ctr_encryptor(new AesCtrEncryptor);
if (!aes_ctr_encryptor->InitializeWithIv(key.key, decrypt_config->iv())) { scoped_ptr<AesDecryptor> aes_decryptor;
LOG(ERROR) << "Failed to initialize AesCtrEncryptor for decryption."; switch (decrypt_config->decryption_mode()) {
case kEncryptionModeAesCtr:
aes_decryptor.reset(new AesCtrDecryptor);
break;
case kEncryptionModeAesCbc:
aes_decryptor.reset(new AesCbcPkcs5Decryptor);
break;
default:
LOG(ERROR) << "Unsupported Decryption Mode: "
<< decrypt_config->decryption_mode();
return false; return false;
} }
decryptor = aes_ctr_encryptor.release();
if (!aes_decryptor->InitializeWithIv(key.key, decrypt_config->iv())) {
LOG(ERROR) << "Failed to initialize AesDecryptor for decryption.";
return false;
}
decryptor = aes_decryptor.release();
decryptor_map_[decrypt_config->key_id()] = decryptor; decryptor_map_[decrypt_config->key_id()] = decryptor;
} else { } else {
decryptor = found->second; decryptor = found->second;

View File

@ -10,7 +10,7 @@
#include <map> #include <map>
#include <vector> #include <vector>
#include "packager/media/base/aes_encryptor.h" #include "packager/media/base/aes_decryptor.h"
#include "packager/media/base/decrypt_config.h" #include "packager/media/base/decrypt_config.h"
#include "packager/media/base/key_source.h" #include "packager/media/base/key_source.h"
@ -29,7 +29,7 @@ class DecryptorSource {
private: private:
KeySource* key_source_; KeySource* key_source_;
std::map<std::vector<uint8_t>, AesCtrEncryptor*> decryptor_map_; std::map<std::vector<uint8_t>, AesDecryptor*> decryptor_map_;
DISALLOW_COPY_AND_ASSIGN(DecryptorSource); DISALLOW_COPY_AND_ASSIGN(DecryptorSource);
}; };

View File

@ -80,7 +80,8 @@ TEST_F(DecryptorSourceTest, FullSampleDecryption) {
DecryptConfig decrypt_config(key_id_, DecryptConfig decrypt_config(key_id_,
std::vector<uint8_t>(kIv, kIv + arraysize(kIv)), std::vector<uint8_t>(kIv, kIv + arraysize(kIv)),
std::vector<SubsampleEntry>()); std::vector<SubsampleEntry>(),
kEncryptionModeAesCtr);
ASSERT_TRUE(decryptor_source_.DecryptSampleBuffer(&decrypt_config, &buffer_[0], ASSERT_TRUE(decryptor_source_.DecryptSampleBuffer(&decrypt_config, &buffer_[0],
buffer_.size())); buffer_.size()));
EXPECT_EQ(std::vector<uint8_t>( EXPECT_EQ(std::vector<uint8_t>(
@ -93,7 +94,7 @@ TEST_F(DecryptorSourceTest, FullSampleDecryption) {
buffer_.assign(kBuffer2, kBuffer2 + arraysize(kBuffer2)); buffer_.assign(kBuffer2, kBuffer2 + arraysize(kBuffer2));
DecryptConfig decrypt_config2( DecryptConfig decrypt_config2(
key_id_, std::vector<uint8_t>(kIv2, kIv2 + arraysize(kIv2)), key_id_, std::vector<uint8_t>(kIv2, kIv2 + arraysize(kIv2)),
std::vector<SubsampleEntry>()); std::vector<SubsampleEntry>(), kEncryptionModeAesCtr);
ASSERT_TRUE(decryptor_source_.DecryptSampleBuffer( ASSERT_TRUE(decryptor_source_.DecryptSampleBuffer(
&decrypt_config2, &buffer_[0], buffer_.size())); &decrypt_config2, &buffer_[0], buffer_.size()));
EXPECT_EQ(std::vector<uint8_t>(kExpectedDecryptedBuffer2, EXPECT_EQ(std::vector<uint8_t>(kExpectedDecryptedBuffer2,
@ -128,7 +129,8 @@ TEST_F(DecryptorSourceTest, SubsampleDecryption) {
DecryptConfig decrypt_config( DecryptConfig decrypt_config(
key_id_, std::vector<uint8_t>(kIv, kIv + arraysize(kIv)), key_id_, std::vector<uint8_t>(kIv, kIv + arraysize(kIv)),
std::vector<SubsampleEntry>(kSubsamples, std::vector<SubsampleEntry>(kSubsamples,
kSubsamples + arraysize(kSubsamples))); kSubsamples + arraysize(kSubsamples)),
kEncryptionModeAesCtr);
ASSERT_TRUE(decryptor_source_.DecryptSampleBuffer( ASSERT_TRUE(decryptor_source_.DecryptSampleBuffer(
&decrypt_config, &buffer_[0], buffer_.size())); &decrypt_config, &buffer_[0], buffer_.size()));
EXPECT_EQ(std::vector<uint8_t>( EXPECT_EQ(std::vector<uint8_t>(
@ -152,7 +154,8 @@ TEST_F(DecryptorSourceTest, SubsampleDecryptionSizeValidation) {
DecryptConfig decrypt_config( DecryptConfig decrypt_config(
key_id_, std::vector<uint8_t>(kIv, kIv + arraysize(kIv)), key_id_, std::vector<uint8_t>(kIv, kIv + arraysize(kIv)),
std::vector<SubsampleEntry>(kSubsamples, std::vector<SubsampleEntry>(kSubsamples,
kSubsamples + arraysize(kSubsamples))); kSubsamples + arraysize(kSubsamples)),
kEncryptionModeAesCtr);
ASSERT_FALSE(decryptor_source_.DecryptSampleBuffer( ASSERT_FALSE(decryptor_source_.DecryptSampleBuffer(
&decrypt_config, &buffer_[0], buffer_.size())); &decrypt_config, &buffer_[0], buffer_.size()));
} }
@ -163,7 +166,8 @@ TEST_F(DecryptorSourceTest, DecryptFailedIfGetKeyFailed) {
DecryptConfig decrypt_config(key_id_, DecryptConfig decrypt_config(key_id_,
std::vector<uint8_t>(kIv, kIv + arraysize(kIv)), std::vector<uint8_t>(kIv, kIv + arraysize(kIv)),
std::vector<SubsampleEntry>()); std::vector<SubsampleEntry>(),
kEncryptionModeAesCtr);
ASSERT_FALSE(decryptor_source_.DecryptSampleBuffer( ASSERT_FALSE(decryptor_source_.DecryptSampleBuffer(
&decrypt_config, &buffer_[0], buffer_.size())); &decrypt_config, &buffer_[0], buffer_.size()));
} }

View File

@ -0,0 +1,23 @@
// 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 MEDIA_BASE_ENCRYPTION_MODE_H_
#define MEDIA_BASE_ENCRYPTION_MODE_H_
namespace edash_packager {
namespace media {
/// Supported encryption mode.
enum EncryptionMode {
kEncryptionModeUnknown,
kEncryptionModeAesCtr,
kEncryptionModeAesCbc
};
} // namespace media
} // namespace edash_packager
#endif // MEDIA_BASE_ENCRYPTION_MODE_H_

View File

@ -13,6 +13,8 @@
'target_name': 'media_base', 'target_name': 'media_base',
'type': '<(component)', 'type': '<(component)',
'sources': [ 'sources': [
'aes_decryptor.cc',
'aes_decryptor.h',
'aes_encryptor.cc', 'aes_encryptor.cc',
'aes_encryptor.h', 'aes_encryptor.h',
'audio_stream_info.cc', 'audio_stream_info.cc',
@ -37,6 +39,7 @@
'decrypt_config.h', 'decrypt_config.h',
'decryptor_source.cc', 'decryptor_source.cc',
'decryptor_source.h', 'decryptor_source.h',
'encryption_modes.h',
'http_key_fetcher.cc', 'http_key_fetcher.cc',
'http_key_fetcher.h', 'http_key_fetcher.h',
'key_fetcher.cc', 'key_fetcher.cc',

View File

@ -19,6 +19,7 @@ Muxer::Muxer(const MuxerOptions& options)
max_sd_pixels_(0), max_sd_pixels_(0),
clear_lead_in_seconds_(0), clear_lead_in_seconds_(0),
crypto_period_duration_in_seconds_(0), crypto_period_duration_in_seconds_(0),
encryption_mode_(kEncryptionModeUnknown),
cancelled_(false), cancelled_(false),
clock_(NULL) {} clock_(NULL) {}
@ -27,12 +28,14 @@ Muxer::~Muxer() {}
void Muxer::SetKeySource(KeySource* encryption_key_source, void Muxer::SetKeySource(KeySource* encryption_key_source,
uint32_t max_sd_pixels, uint32_t max_sd_pixels,
double clear_lead_in_seconds, double clear_lead_in_seconds,
double crypto_period_duration_in_seconds) { double crypto_period_duration_in_seconds,
EncryptionMode encryption_mode) {
DCHECK(encryption_key_source); DCHECK(encryption_key_source);
encryption_key_source_ = encryption_key_source; encryption_key_source_ = encryption_key_source;
max_sd_pixels_ = max_sd_pixels; max_sd_pixels_ = max_sd_pixels;
clear_lead_in_seconds_ = clear_lead_in_seconds; clear_lead_in_seconds_ = clear_lead_in_seconds;
crypto_period_duration_in_seconds_ = crypto_period_duration_in_seconds; crypto_period_duration_in_seconds_ = crypto_period_duration_in_seconds;
encryption_mode_ = encryption_mode;
} }
void Muxer::AddStream(MediaStream* stream) { void Muxer::AddStream(MediaStream* stream) {

View File

@ -14,6 +14,7 @@
#include "packager/base/memory/ref_counted.h" #include "packager/base/memory/ref_counted.h"
#include "packager/base/memory/scoped_ptr.h" #include "packager/base/memory/scoped_ptr.h"
#include "packager/base/time/clock.h" #include "packager/base/time/clock.h"
#include "packager/media/base/encryption_modes.h"
#include "packager/media/base/muxer_options.h" #include "packager/media/base/muxer_options.h"
#include "packager/media/base/status.h" #include "packager/media/base/status.h"
#include "packager/media/event/muxer_listener.h" #include "packager/media/event/muxer_listener.h"
@ -47,7 +48,8 @@ class Muxer {
void SetKeySource(KeySource* encryption_key_source, void SetKeySource(KeySource* encryption_key_source,
uint32_t max_sd_pixels, uint32_t max_sd_pixels,
double clear_lead_in_seconds, double clear_lead_in_seconds,
double crypto_period_duration_in_seconds); double crypto_period_duration_in_seconds,
EncryptionMode encryption_mode);
/// Add video/audio stream. /// Add video/audio stream.
void AddStream(MediaStream* stream); void AddStream(MediaStream* stream);
@ -92,6 +94,7 @@ class Muxer {
MuxerListener* muxer_listener() { return muxer_listener_.get(); } MuxerListener* muxer_listener() { return muxer_listener_.get(); }
ProgressListener* progress_listener() { return progress_listener_.get(); } ProgressListener* progress_listener() { return progress_listener_.get(); }
base::Clock* clock() { return clock_; } base::Clock* clock() { return clock_; }
EncryptionMode encryption_mode() const { return encryption_mode_; }
private: private:
friend class MediaStream; // Needed to access AddSample. friend class MediaStream; // Needed to access AddSample.
@ -117,6 +120,7 @@ class Muxer {
uint32_t max_sd_pixels_; uint32_t max_sd_pixels_;
double clear_lead_in_seconds_; double clear_lead_in_seconds_;
double crypto_period_duration_in_seconds_; double crypto_period_duration_in_seconds_;
EncryptionMode encryption_mode_;
bool cancelled_; bool cancelled_;
scoped_ptr<MuxerListener> muxer_listener_; scoped_ptr<MuxerListener> muxer_listener_;

View File

@ -450,7 +450,7 @@ bool ProtectionSchemeInfo::ReadWriteInternal(BoxBuffer* buffer) {
buffer->PrepareChildren() && buffer->PrepareChildren() &&
buffer->ReadWriteChild(&format) && buffer->ReadWriteChild(&format) &&
buffer->ReadWriteChild(&type)); buffer->ReadWriteChild(&type));
if (type.type == FOURCC_CENC) if (type.type == FOURCC_CENC || type.type == FOURCC_CBC1)
RCHECK(buffer->ReadWriteChild(&info)); RCHECK(buffer->ReadWriteChild(&info));
// Other protection schemes are silently ignored. Since the protection scheme // Other protection schemes are silently ignored. Since the protection scheme
// type can't be determined until this box is opened, we return 'true' for // type can't be determined until this box is opened, we return 'true' for
@ -1300,7 +1300,7 @@ bool VideoSampleEntry::ReadWriteInternal(BoxBuffer* buffer) {
if (buffer->Reading()) { if (buffer->Reading()) {
// Continue scanning until a recognized protection scheme is found, // Continue scanning until a recognized protection scheme is found,
// or until we run out of protection schemes. // or until we run out of protection schemes.
while (sinf.type.type != FOURCC_CENC) { while (sinf.type.type != FOURCC_CENC && sinf.type.type != FOURCC_CBC1) {
if (!buffer->ReadWriteChild(&sinf)) if (!buffer->ReadWriteChild(&sinf))
return false; return false;
} }
@ -1485,7 +1485,7 @@ bool AudioSampleEntry::ReadWriteInternal(BoxBuffer* buffer) {
if (buffer->Reading()) { if (buffer->Reading()) {
// Continue scanning until a recognized protection scheme is found, // Continue scanning until a recognized protection scheme is found,
// or until we run out of protection schemes. // or until we run out of protection schemes.
while (sinf.type.type != FOURCC_CENC) { while (sinf.type.type != FOURCC_CENC && sinf.type.type != FOURCC_CBC1) {
if (!buffer->ReadWriteChild(&sinf)) if (!buffer->ReadWriteChild(&sinf))
return false; return false;
} }

View File

@ -64,12 +64,14 @@ EncryptingFragmenter::EncryptingFragmenter(
scoped_refptr<StreamInfo> info, scoped_refptr<StreamInfo> info,
TrackFragment* traf, TrackFragment* traf,
scoped_ptr<EncryptionKey> encryption_key, scoped_ptr<EncryptionKey> encryption_key,
int64_t clear_time) int64_t clear_time,
EncryptionMode encryption_mode)
: Fragmenter(traf), : Fragmenter(traf),
info_(info), info_(info),
encryption_key_(encryption_key.Pass()), encryption_key_(encryption_key.Pass()),
nalu_length_size_(GetNaluLengthSize(*info)), nalu_length_size_(GetNaluLengthSize(*info)),
clear_time_(clear_time) { clear_time_(clear_time),
encryption_mode_(encryption_mode) {
DCHECK(encryption_key_); DCHECK(encryption_key_);
VideoCodec video_codec = GetVideoCodec(*info); VideoCodec video_codec = GetVideoCodec(*info);
if (video_codec == kCodecVP8) { if (video_codec == kCodecVP8) {
@ -168,8 +170,14 @@ void EncryptingFragmenter::FinalizeFragmentForEncryption() {
Status EncryptingFragmenter::CreateEncryptor() { Status EncryptingFragmenter::CreateEncryptor() {
DCHECK(encryption_key_); DCHECK(encryption_key_);
scoped_ptr<AesEncryptor> encryptor;
scoped_ptr<AesCtrEncryptor> encryptor(new AesCtrEncryptor()); if (encryption_mode_ == kEncryptionModeAesCtr) {
encryptor.reset(new AesCtrEncryptor);
} else if (encryption_mode_ == kEncryptionModeAesCbc) {
encryptor.reset(new AesCbcPkcs5Encryptor);
} else {
return Status(error::MUXER_FAILURE, "Unsupported encryption mode.");
}
const bool initialized = encryption_key_->iv.empty() const bool initialized = encryption_key_->iv.empty()
? encryptor->InitializeWithRandomIv( ? encryptor->InitializeWithRandomIv(
encryption_key_->key, kDefaultIvSize) encryption_key_->key, kDefaultIvSize)
@ -183,7 +191,7 @@ Status EncryptingFragmenter::CreateEncryptor() {
void EncryptingFragmenter::EncryptBytes(uint8_t* data, uint32_t size) { void EncryptingFragmenter::EncryptBytes(uint8_t* data, uint32_t size) {
DCHECK(encryptor_); DCHECK(encryptor_);
CHECK(encryptor_->Encrypt(data, size, data)); CHECK(encryptor_->EncryptData(data, size, data));
} }
Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) { Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {

View File

@ -9,6 +9,7 @@
#include "packager/base/memory/ref_counted.h" #include "packager/base/memory/ref_counted.h"
#include "packager/base/memory/scoped_ptr.h" #include "packager/base/memory/scoped_ptr.h"
#include "packager/media/base/encryption_modes.h"
#include "packager/media/filters/vpx_parser.h" #include "packager/media/filters/vpx_parser.h"
#include "packager/media/formats/mp4/fragmenter.h" #include "packager/media/formats/mp4/fragmenter.h"
#include "packager/media/formats/mp4/video_slice_header_parser.h" #include "packager/media/formats/mp4/video_slice_header_parser.h"
@ -16,7 +17,7 @@
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
class AesCtrEncryptor; class AesEncryptor;
class StreamInfo; class StreamInfo;
struct EncryptionKey; struct EncryptionKey;
@ -32,7 +33,8 @@ class EncryptingFragmenter : public Fragmenter {
EncryptingFragmenter(scoped_refptr<StreamInfo> info, EncryptingFragmenter(scoped_refptr<StreamInfo> info,
TrackFragment* traf, TrackFragment* traf,
scoped_ptr<EncryptionKey> encryption_key, scoped_ptr<EncryptionKey> encryption_key,
int64_t clear_time); int64_t clear_time,
EncryptionMode encryption_mode);
~EncryptingFragmenter() override; ~EncryptingFragmenter() override;
@ -56,7 +58,7 @@ class EncryptingFragmenter : public Fragmenter {
Status CreateEncryptor(); Status CreateEncryptor();
EncryptionKey* encryption_key() { return encryption_key_.get(); } EncryptionKey* encryption_key() { return encryption_key_.get(); }
AesCtrEncryptor* encryptor() { return encryptor_.get(); } AesEncryptor* encryptor() { return encryptor_.get(); }
void set_encryption_key(scoped_ptr<EncryptionKey> encryption_key) { void set_encryption_key(scoped_ptr<EncryptionKey> encryption_key) {
encryption_key_ = encryption_key.Pass(); encryption_key_ = encryption_key.Pass();
@ -71,12 +73,13 @@ class EncryptingFragmenter : public Fragmenter {
scoped_refptr<StreamInfo> info_; scoped_refptr<StreamInfo> info_;
scoped_ptr<EncryptionKey> encryption_key_; scoped_ptr<EncryptionKey> encryption_key_;
scoped_ptr<AesCtrEncryptor> encryptor_; scoped_ptr<AesEncryptor> encryptor_;
// If this stream contains AVC, subsample encryption specifies that the size // If this stream contains AVC, subsample encryption specifies that the size
// and type of NAL units remain unencrypted. This function returns the size of // and type of NAL units remain unencrypted. This function returns the size of
// the size field in bytes. Can be 1, 2 or 4 bytes. // the size field in bytes. Can be 1, 2 or 4 bytes.
const uint8_t nalu_length_size_; const uint8_t nalu_length_size_;
int64_t clear_time_; int64_t clear_time_;
EncryptionMode encryption_mode_;
scoped_ptr<VPxParser> vpx_parser_; scoped_ptr<VPxParser> vpx_parser_;
scoped_ptr<VideoSliceHeaderParser> header_parser_; scoped_ptr<VideoSliceHeaderParser> header_parser_;

View File

@ -20,6 +20,7 @@ enum FourCC {
FOURCC_AVC1 = 0x61766331, FOURCC_AVC1 = 0x61766331,
FOURCC_AVCC = 0x61766343, FOURCC_AVCC = 0x61766343,
FOURCC_BLOC = 0x626C6F63, FOURCC_BLOC = 0x626C6F63,
FOURCC_CBC1 = 0x63626331,
FOURCC_CENC = 0x63656e63, FOURCC_CENC = 0x63656e63,
FOURCC_CO64 = 0x636f3634, FOURCC_CO64 = 0x636f3634,
FOURCC_CTTS = 0x63747473, FOURCC_CTTS = 0x63747473,

View File

@ -24,11 +24,13 @@ KeyRotationFragmenter::KeyRotationFragmenter(MovieFragment* moof,
KeySource::TrackType track_type, KeySource::TrackType track_type,
int64_t crypto_period_duration, int64_t crypto_period_duration,
int64_t clear_time, int64_t clear_time,
MuxerListener* muxer_listener) MuxerListener* muxer_listener,
EncryptionMode encryption_mode)
: EncryptingFragmenter(info, : EncryptingFragmenter(info,
traf, traf,
scoped_ptr<EncryptionKey>(new EncryptionKey()), scoped_ptr<EncryptionKey>(new EncryptionKey()),
clear_time), clear_time,
encryption_mode),
moof_(moof), moof_(moof),
encryption_key_source_(encryption_key_source), encryption_key_source_(encryption_key_source),
track_type_(track_type), track_type_(track_type),

View File

@ -7,6 +7,7 @@
#ifndef MEDIA_FORMATS_MP4_KEY_ROTATION_FRAGMENTER_H_ #ifndef MEDIA_FORMATS_MP4_KEY_ROTATION_FRAGMENTER_H_
#define MEDIA_FORMATS_MP4_KEY_ROTATION_FRAGMENTER_H_ #define MEDIA_FORMATS_MP4_KEY_ROTATION_FRAGMENTER_H_
#include "packager/media/base/encryption_modes.h"
#include "packager/media/base/key_source.h" #include "packager/media/base/key_source.h"
#include "packager/media/event/muxer_listener.h" #include "packager/media/event/muxer_listener.h"
#include "packager/media/formats/mp4/encrypting_fragmenter.h" #include "packager/media/formats/mp4/encrypting_fragmenter.h"
@ -40,7 +41,8 @@ class KeyRotationFragmenter : public EncryptingFragmenter {
KeySource::TrackType track_type, KeySource::TrackType track_type,
int64_t crypto_period_duration, int64_t crypto_period_duration,
int64_t clear_time, int64_t clear_time,
MuxerListener* muxer_listener); MuxerListener* muxer_listener,
EncryptionMode encryption_mode);
~KeyRotationFragmenter() override; ~KeyRotationFragmenter() override;
protected: protected:

View File

@ -154,7 +154,8 @@ Status MP4Muxer::Initialize() {
encryption_key_source(), encryption_key_source(),
max_sd_pixels(), max_sd_pixels(),
clear_lead_in_seconds(), clear_lead_in_seconds(),
crypto_period_duration_in_seconds()); crypto_period_duration_in_seconds(),
encryption_mode());
if (!segmenter_initialized.ok()) if (!segmenter_initialized.ok())
return segmenter_initialized; return segmenter_initialized;

View File

@ -38,6 +38,7 @@ const uint8_t kKeyRotationDefaultKeyId[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 0, 0, 0, 0, 0, 0, 0, 0
}; };
COMPILE_ASSERT(arraysize(kKeyRotationDefaultKeyId) == kCencKeyIdSize, COMPILE_ASSERT(arraysize(kKeyRotationDefaultKeyId) == kCencKeyIdSize,
cenc_key_id_must_be_size_16); cenc_key_id_must_be_size_16);
@ -49,9 +50,16 @@ uint64_t Rescale(uint64_t time_in_old_scale,
void GenerateSinf(const EncryptionKey& encryption_key, void GenerateSinf(const EncryptionKey& encryption_key,
FourCC old_type, FourCC old_type,
EncryptionMode encryption_mode,
ProtectionSchemeInfo* sinf) { ProtectionSchemeInfo* sinf) {
sinf->format.format = old_type; sinf->format.format = old_type;
if (encryption_mode == kEncryptionModeAesCtr){
sinf->type.type = FOURCC_CENC; sinf->type.type = FOURCC_CENC;
} else if (encryption_mode == kEncryptionModeAesCbc) {
sinf->type.type = FOURCC_CBC1;
}
sinf->type.version = kCencSchemeVersion; sinf->type.version = kCencSchemeVersion;
sinf->info.track_encryption.is_encrypted = true; sinf->info.track_encryption.is_encrypted = true;
sinf->info.track_encryption.default_iv_size = sinf->info.track_encryption.default_iv_size =
@ -61,6 +69,7 @@ void GenerateSinf(const EncryptionKey& encryption_key,
void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key, void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key,
double clear_lead_in_seconds, double clear_lead_in_seconds,
EncryptionMode encryption_mode,
SampleDescription* description) { SampleDescription* description) {
DCHECK(description); DCHECK(description);
if (description->type == kVideo) { if (description->type == kVideo) {
@ -72,7 +81,7 @@ void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key,
// Convert the first entry to an encrypted entry. // Convert the first entry to an encrypted entry.
VideoSampleEntry& entry = description->video_entries[0]; VideoSampleEntry& entry = description->video_entries[0];
GenerateSinf(encryption_key, entry.format, &entry.sinf); GenerateSinf(encryption_key, entry.format, encryption_mode, &entry.sinf);
entry.format = FOURCC_ENCV; entry.format = FOURCC_ENCV;
} else { } else {
DCHECK_EQ(kAudio, description->type); DCHECK_EQ(kAudio, description->type);
@ -84,7 +93,7 @@ void GenerateEncryptedSampleEntry(const EncryptionKey& encryption_key,
// Convert the first entry to an encrypted entry. // Convert the first entry to an encrypted entry.
AudioSampleEntry& entry = description->audio_entries[0]; AudioSampleEntry& entry = description->audio_entries[0];
GenerateSinf(encryption_key, entry.format, &entry.sinf); GenerateSinf(encryption_key, entry.format, encryption_mode, &entry.sinf);
entry.format = FOURCC_ENCA; entry.format = FOURCC_ENCA;
} }
} }
@ -127,7 +136,8 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
KeySource* encryption_key_source, KeySource* encryption_key_source,
uint32_t max_sd_pixels, uint32_t max_sd_pixels,
double clear_lead_in_seconds, double clear_lead_in_seconds,
double crypto_period_duration_in_seconds) { double crypto_period_duration_in_seconds,
EncryptionMode encryption_mode) {
DCHECK_LT(0u, streams.size()); DCHECK_LT(0u, streams.size());
muxer_listener_ = muxer_listener; muxer_listener_ = muxer_listener;
progress_listener_ = progress_listener; progress_listener_ = progress_listener;
@ -164,8 +174,7 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
kKeyRotationDefaultKeyId, kKeyRotationDefaultKeyId,
kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId)); kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId));
GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds, GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds,
&description); encryption_mode, &description);
if (muxer_listener_) { if (muxer_listener_) {
muxer_listener_->OnEncryptionInfoReady( muxer_listener_->OnEncryptionInfoReady(
kInitialEncryptionInfo, encryption_key.key_id, kInitialEncryptionInfo, encryption_key.key_id,
@ -177,7 +186,7 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
encryption_key_source, track_type, encryption_key_source, track_type,
crypto_period_duration_in_seconds * streams[i]->info()->time_scale(), crypto_period_duration_in_seconds * streams[i]->info()->time_scale(),
clear_lead_in_seconds * streams[i]->info()->time_scale(), clear_lead_in_seconds * streams[i]->info()->time_scale(),
muxer_listener_); muxer_listener_, encryption_mode);
continue; continue;
} }
@ -188,7 +197,7 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
return status; return status;
GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds, GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds,
&description); encryption_mode, &description);
if (moov_->pssh.empty()) { if (moov_->pssh.empty()) {
moov_->pssh.resize(encryption_key->key_system_info.size()); moov_->pssh.resize(encryption_key->key_system_info.size());
@ -205,7 +214,8 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
fragmenters_[i] = new EncryptingFragmenter( fragmenters_[i] = new EncryptingFragmenter(
streams[i]->info(), &moof_->tracks[i], encryption_key.Pass(), streams[i]->info(), &moof_->tracks[i], encryption_key.Pass(),
clear_lead_in_seconds * streams[i]->info()->time_scale()); clear_lead_in_seconds * streams[i]->info()->time_scale(),
encryption_mode);
} }
// Choose the first stream if there is no VIDEO. // Choose the first stream if there is no VIDEO.

View File

@ -12,6 +12,7 @@
#include "packager/base/memory/ref_counted.h" #include "packager/base/memory/ref_counted.h"
#include "packager/base/memory/scoped_ptr.h" #include "packager/base/memory/scoped_ptr.h"
#include "packager/media/base/encryption_modes.h"
#include "packager/media/base/status.h" #include "packager/media/base/status.h"
#include "packager/media/formats/mp4/box_definitions.h" #include "packager/media/formats/mp4/box_definitions.h"
@ -65,7 +66,8 @@ class Segmenter {
KeySource* encryption_key_source, KeySource* encryption_key_source,
uint32_t max_sd_pixels, uint32_t max_sd_pixels,
double clear_lead_in_seconds, double clear_lead_in_seconds,
double crypto_period_duration_in_seconds); double crypto_period_duration_in_seconds,
EncryptionMode encryption_mode);
/// Finalize the segmenter. /// Finalize the segmenter.
/// @return OK on success, an error status otherwise. /// @return OK on success, an error status otherwise.

View File

@ -8,6 +8,7 @@
#include <limits> #include <limits>
#include "packager/media/base/buffer_reader.h" #include "packager/media/base/buffer_reader.h"
#include "packager/media/base/encryption_modes.h"
#include "packager/media/formats/mp4/chunk_info_iterator.h" #include "packager/media/formats/mp4/chunk_info_iterator.h"
#include "packager/media/formats/mp4/composition_offset_iterator.h" #include "packager/media/formats/mp4/composition_offset_iterator.h"
#include "packager/media/formats/mp4/decoding_time_iterator.h" #include "packager/media/formats/mp4/decoding_time_iterator.h"
@ -597,10 +598,26 @@ scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
return scoped_ptr<DecryptConfig>(); return scoped_ptr<DecryptConfig>();
} }
FourCC protection_scheme = is_audio() ? audio_description().sinf.type.type
: video_description().sinf.type.type;
EncryptionMode decryption_mode;
switch (protection_scheme) {
case FOURCC_CENC:
decryption_mode = kEncryptionModeAesCtr;
break;
case FOURCC_CBC1:
decryption_mode = kEncryptionModeAesCbc;
break;
default:
LOG(ERROR) << "Unsupported protection scheme.";
return scoped_ptr<DecryptConfig>();
}
return scoped_ptr<DecryptConfig>(new DecryptConfig( return scoped_ptr<DecryptConfig>(new DecryptConfig(
track_encryption().default_kid, track_encryption().default_kid,
sample_encryption_entry.initialization_vector, sample_encryption_entry.initialization_vector,
sample_encryption_entry.subsamples)); sample_encryption_entry.subsamples,
decryption_mode));
} }
} // namespace mp4 } // namespace mp4

View File

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

View File

@ -6,7 +6,7 @@
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/base/sys_byteorder.h" #include "packager/base/sys_byteorder.h"
#include "packager/media/base/decrypt_config.h" #include "packager/media/base/encryption_modes.h"
#include "packager/media/formats/webm/webm_constants.h" #include "packager/media/formats/webm/webm_constants.h"
namespace edash_packager { namespace edash_packager {
@ -56,7 +56,8 @@ bool WebMCreateDecryptConfig(const uint8_t* data,
decrypt_config->reset(new DecryptConfig( decrypt_config->reset(new DecryptConfig(
std::vector<uint8_t>(key_id, key_id + key_id_size), std::vector<uint8_t>(key_id, key_id + key_id_size),
std::vector<uint8_t>(counter_block.begin(), counter_block.end()), std::vector<uint8_t>(counter_block.begin(), counter_block.end()),
std::vector<SubsampleEntry>())); std::vector<SubsampleEntry>(),
kEncryptionModeAesCtr));
*data_offset = frame_offset; *data_offset = frame_offset;
return true; return true;

View File

@ -30,6 +30,14 @@ Status WebMMuxer::Initialize() {
"Key rotation is not implemented for WebM"); "Key rotation is not implemented for WebM");
} }
if (encryption_key_source() && (encryption_mode() != kEncryptionModeAesCtr)) {
NOTIMPLEMENTED()
<< "WebM muxer does not support encryption mode other than AES-CTR.";
return Status(
error::UNIMPLEMENTED,
"WebM muxer does not support encryption mode other than AES-CTR.");
}
scoped_ptr<MkvWriter> writer(new MkvWriter); scoped_ptr<MkvWriter> writer(new MkvWriter);
Status status = writer->Open(options().output_file_name); Status status = writer->Open(options().output_file_name);
if (!status.ok()) if (!status.ok())

View File

@ -9,7 +9,7 @@
#include <vector> #include <vector>
#include "packager/base/strings/string_number_conversions.h" #include "packager/base/strings/string_number_conversions.h"
#include "packager/media/base/aes_encryptor.h" #include "packager/media/base/aes_decryptor.h"
#include "packager/media/base/audio_stream_info.h" #include "packager/media/base/audio_stream_info.h"
#include "packager/media/base/key_source.h" #include "packager/media/base/key_source.h"
#include "packager/media/base/media_sample.h" #include "packager/media/base/media_sample.h"

View File

@ -11,6 +11,7 @@
#include "packager/base/strings/stringprintf.h" #include "packager/base/strings/stringprintf.h"
#include "packager/base/time/clock.h" #include "packager/base/time/clock.h"
#include "packager/media/base/demuxer.h" #include "packager/media/base/demuxer.h"
#include "packager/media/base/encryption_modes.h"
#include "packager/media/base/key_source.h" #include "packager/media/base/key_source.h"
#include "packager/media/base/media_stream.h" #include "packager/media/base/media_stream.h"
#include "packager/media/base/muxer.h" #include "packager/media/base/muxer.h"
@ -180,7 +181,8 @@ void PackagerTestBasic::Remux(const std::string& input,
muxer_video->SetKeySource(encryption_key_source.get(), muxer_video->SetKeySource(encryption_key_source.get(),
KeySource::TRACK_TYPE_SD, KeySource::TRACK_TYPE_SD,
kClearLeadInSeconds, kClearLeadInSeconds,
kCryptoDurationInSeconds); kCryptoDurationInSeconds,
kEncryptionModeAesCtr);
} }
} }
@ -201,7 +203,8 @@ void PackagerTestBasic::Remux(const std::string& input,
muxer_audio->SetKeySource(encryption_key_source.get(), muxer_audio->SetKeySource(encryption_key_source.get(),
KeySource::TRACK_TYPE_SD, KeySource::TRACK_TYPE_SD,
kClearLeadInSeconds, kClearLeadInSeconds,
kCryptoDurationInSeconds); kCryptoDurationInSeconds,
kEncryptionModeAesCtr);
} }
} }