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:
parent
ef81be5f7b
commit
e39c3572af
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
};
|
};
|
||||||
|
|
|
@ -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()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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_
|
|
@ -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',
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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_;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue