Add support for 'cbcs' and 'cens' protection schemes

Issue #78

Change-Id: I9f71b9a92067e2f6b388092494a7d6a84986cdc0
This commit is contained in:
KongQun Yang 2016-04-05 17:28:09 -07:00
parent c8819cb257
commit e253747453
11 changed files with 355 additions and 55 deletions

View File

@ -57,8 +57,12 @@ DEFINE_int32(crypto_period_duration,
"rotation is enabled."); "rotation is enabled.");
DEFINE_string(protection_scheme, DEFINE_string(protection_scheme,
"cenc", "cenc",
"Choose protection scheme. Currently support cenc and cbc1. " "Choose protection scheme, 'cenc' or 'cbc1' or pattern-based "
"Default is cenc."); "protection schemes 'cens' or 'cbcs'. Note that if a "
"pattern-based protection scheme only applies to video stream; "
"audio stream will be encrypted using the corresponding "
"non-pattern-based encryption schemes, i.e. 'cenc' for 'cens', "
"'cbc1' for 'cbcs'.");
namespace edash_packager { namespace edash_packager {

View File

@ -12,16 +12,20 @@ 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)
: DecryptConfig(key_id, iv, subsamples, FOURCC_cenc) {} : DecryptConfig(key_id, iv, subsamples, FOURCC_cenc, 0, 0) {}
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,
FourCC protection_scheme) FourCC protection_scheme,
uint8_t crypt_byte_block,
uint8_t skip_byte_block)
: key_id_(key_id), : key_id_(key_id),
iv_(iv), iv_(iv),
subsamples_(subsamples), subsamples_(subsamples),
protection_scheme_(protection_scheme) { protection_scheme_(protection_scheme),
crypt_byte_block_(crypt_byte_block),
skip_byte_block_(skip_byte_block) {
CHECK_GT(key_id.size(), 0u); CHECK_GT(key_id.size(), 0u);
} }

View File

@ -60,10 +60,16 @@ class DecryptConfig {
/// in size to the sum of the subsample sizes. /// in size to the sum of the subsample sizes.
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens', /// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
/// 'cbc1', 'cbcs'. /// 'cbc1', 'cbcs'.
/// @param crypt_byte_block indicates number of encrypted blocks (16-byte) in
/// pattern based encryption, 'cens' and 'cbcs'. Ignored otherwise.
/// @param skip_byte_block indicates number of unencrypted blocks (16-byte)
/// in pattern based encryption, 'cens' and 'cbcs'. Ignored otherwise.
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,
FourCC protection_scheme); FourCC protection_scheme,
uint8_t crypt_byte_block,
uint8_t skip_byte_block);
~DecryptConfig(); ~DecryptConfig();
@ -71,6 +77,8 @@ class DecryptConfig {
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_; }
FourCC protection_scheme() const { return protection_scheme_; } FourCC protection_scheme() const { return protection_scheme_; }
uint8_t crypt_byte_block() const { return crypt_byte_block_; }
uint8_t skip_byte_block() const { return skip_byte_block_; }
private: private:
const std::vector<uint8_t> key_id_; const std::vector<uint8_t> key_id_;
@ -83,6 +91,9 @@ class DecryptConfig {
const std::vector<SubsampleEntry> subsamples_; const std::vector<SubsampleEntry> subsamples_;
const FourCC protection_scheme_; const FourCC protection_scheme_;
// For pattern-based protection schemes, like CENS and CBCS.
const uint8_t crypt_byte_block_;
const uint8_t skip_byte_block_;
DISALLOW_COPY_AND_ASSIGN(DecryptConfig); DISALLOW_COPY_AND_ASSIGN(DecryptConfig);
}; };

View File

@ -9,6 +9,7 @@
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/base/stl_util.h" #include "packager/base/stl_util.h"
#include "packager/media/base/aes_decryptor.h" #include "packager/media/base/aes_decryptor.h"
#include "packager/media/base/aes_pattern_cryptor.h"
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
@ -47,6 +48,21 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config,
case FOURCC_cbc1: case FOURCC_cbc1:
aes_decryptor.reset(new AesCbcDecryptor(kNoPadding, kChainAcrossCalls)); aes_decryptor.reset(new AesCbcDecryptor(kNoPadding, kChainAcrossCalls));
break; break;
case FOURCC_cens:
aes_decryptor.reset(
new AesPatternCryptor(decrypt_config->crypt_byte_block(),
decrypt_config->skip_byte_block(),
AesPatternCryptor::kDontUseConstantIv,
scoped_ptr<AesCryptor>(new AesCtrDecryptor)));
break;
case FOURCC_cbcs:
aes_decryptor.reset(
new AesPatternCryptor(decrypt_config->crypt_byte_block(),
decrypt_config->skip_byte_block(),
AesPatternCryptor::kUseConstantIv,
scoped_ptr<AesCryptor>(new AesCbcDecryptor(
kNoPadding, kChainAcrossCalls))));
break;
default: default:
LOG(ERROR) << "Unsupported protection scheme: " LOG(ERROR) << "Unsupported protection scheme: "
<< decrypt_config->protection_scheme(); << decrypt_config->protection_scheme();

View File

@ -9,6 +9,7 @@
#include <limits> #include <limits>
#include "packager/media/base/aes_encryptor.h" #include "packager/media/base/aes_encryptor.h"
#include "packager/media/base/aes_pattern_cryptor.h"
#include "packager/media/base/buffer_reader.h" #include "packager/media/base/buffer_reader.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"
@ -63,26 +64,37 @@ EncryptingFragmenter::EncryptingFragmenter(
TrackFragment* traf, TrackFragment* traf,
scoped_ptr<EncryptionKey> encryption_key, scoped_ptr<EncryptionKey> encryption_key,
int64_t clear_time, int64_t clear_time,
FourCC protection_scheme) FourCC protection_scheme,
uint8_t crypt_byte_block,
uint8_t skip_byte_block)
: 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)),
video_codec_(GetVideoCodec(*info)), video_codec_(GetVideoCodec(*info)),
clear_time_(clear_time), clear_time_(clear_time),
protection_scheme_(protection_scheme) { protection_scheme_(protection_scheme),
crypt_byte_block_(crypt_byte_block),
skip_byte_block_(skip_byte_block) {
DCHECK(encryption_key_); DCHECK(encryption_key_);
if (video_codec_ == kCodecVP8) { switch (video_codec_) {
vpx_parser_.reset(new VP8Parser); case kCodecVP8:
} else if (video_codec_ == kCodecVP9) { vpx_parser_.reset(new VP8Parser);
vpx_parser_.reset(new VP9Parser); break;
} else if (video_codec_ == kCodecH264) { case kCodecVP9:
header_parser_.reset(new H264VideoSliceHeaderParser); vpx_parser_.reset(new VP9Parser);
} else if (video_codec_ == kCodecHVC1 || video_codec_ == kCodecHEV1) { break;
header_parser_.reset(new H265VideoSliceHeaderParser); case kCodecH264:
} else if (nalu_length_size_ > 0) { header_parser_.reset(new H264VideoSliceHeaderParser);
LOG(WARNING) << "Unknown video codec '" << video_codec_ break;
<< "', whole subsamples will be encrypted."; case kCodecHVC1:
FALLTHROUGH_INTENDED;
case kCodecHEV1:
header_parser_.reset(new H265VideoSliceHeaderParser);
break;
default:
LOG(WARNING) << "Unknown video codec '" << video_codec_
<< "', whole subsamples will be encrypted.";
} }
} }
@ -153,6 +165,11 @@ void EncryptingFragmenter::FinalizeFragmentForEncryption() {
// The offset will be adjusted in Segmenter after knowing moof size. // The offset will be adjusted in Segmenter after knowing moof size.
traf()->auxiliary_offset.offsets.push_back(0); traf()->auxiliary_offset.offsets.push_back(0);
// For 'cbcs' scheme, Constant IVs SHALL be used.
const size_t per_sample_iv_size =
(protection_scheme_ == FOURCC_cbcs) ? 0 : encryptor_->iv().size();
traf()->sample_encryption.iv_size = per_sample_iv_size;
// Optimize saiz box. // Optimize saiz box.
SampleAuxiliaryInformationSize& saiz = traf()->auxiliary_size; SampleAuxiliaryInformationSize& saiz = traf()->auxiliary_size;
saiz.sample_count = traf()->runs[0].sample_sizes.size(); saiz.sample_count = traf()->runs[0].sample_sizes.size();
@ -165,9 +182,11 @@ void EncryptingFragmenter::FinalizeFragmentForEncryption() {
// |sample_info_sizes| table is filled in only for subsample encryption, // |sample_info_sizes| table is filled in only for subsample encryption,
// otherwise |sample_info_size| is just the IV size. // otherwise |sample_info_size| is just the IV size.
DCHECK(!IsSubsampleEncryptionRequired()); DCHECK(!IsSubsampleEncryptionRequired());
saiz.default_sample_info_size = encryptor_->iv().size(); saiz.default_sample_info_size = per_sample_iv_size;
} }
traf()->sample_encryption.iv_size = encryptor_->iv().size(); // It should only happen with full sample encryption + constant iv, which is
// not a legal combination.
CHECK(!saiz.sample_info_sizes.empty() || saiz.default_sample_info_size != 0);
} }
Status EncryptingFragmenter::CreateEncryptor() { Status EncryptingFragmenter::CreateEncryptor() {
@ -180,6 +199,19 @@ Status EncryptingFragmenter::CreateEncryptor() {
case FOURCC_cbc1: case FOURCC_cbc1:
encryptor.reset(new AesCbcEncryptor(kNoPadding, kChainAcrossCalls)); encryptor.reset(new AesCbcEncryptor(kNoPadding, kChainAcrossCalls));
break; break;
case FOURCC_cens:
encryptor.reset(
new AesPatternCryptor(crypt_byte_block(), skip_byte_block(),
AesPatternCryptor::kDontUseConstantIv,
scoped_ptr<AesCryptor>(new AesCtrEncryptor)));
break;
case FOURCC_cbcs:
encryptor.reset(
new AesPatternCryptor(crypt_byte_block(), skip_byte_block(),
AesPatternCryptor::kUseConstantIv,
scoped_ptr<AesCryptor>(new AesCbcEncryptor(
kNoPadding, kChainAcrossCalls))));
break;
default: default:
return Status(error::MUXER_FAILURE, "Unsupported protection scheme."); return Status(error::MUXER_FAILURE, "Unsupported protection scheme.");
} }
@ -202,7 +234,9 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
DCHECK(encryptor_); DCHECK(encryptor_);
SampleEncryptionEntry sample_encryption_entry; SampleEncryptionEntry sample_encryption_entry;
sample_encryption_entry.initialization_vector = encryptor_->iv(); // For 'cbcs' scheme, Constant IVs SHALL be used.
if (protection_scheme_ != FOURCC_cbcs)
sample_encryption_entry.initialization_vector = encryptor_->iv();
uint8_t* data = sample->writable_data(); uint8_t* data = sample->writable_data();
if (IsSubsampleEncryptionRequired()) { if (IsSubsampleEncryptionRequired()) {
if (vpx_parser_) { if (vpx_parser_) {

View File

@ -32,11 +32,17 @@ class EncryptingFragmenter : public Fragmenter {
/// track's timescale. /// track's timescale.
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens', /// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
/// 'cbc1', 'cbcs'. /// 'cbc1', 'cbcs'.
/// @param crypt_byte_block indicates number of encrypted blocks (16-byte) in
/// pattern based encryption.
/// @param skip_byte_block indicates number of unencrypted blocks (16-byte)
/// in pattern based encryption.
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,
FourCC protection_scheme); FourCC protection_scheme,
uint8_t crypt_byte_block,
uint8_t skip_byte_block);
~EncryptingFragmenter() override; ~EncryptingFragmenter() override;
@ -62,6 +68,8 @@ class EncryptingFragmenter : public Fragmenter {
const EncryptionKey* encryption_key() const { return encryption_key_.get(); } const EncryptionKey* encryption_key() const { return encryption_key_.get(); }
AesCryptor* encryptor() { return encryptor_.get(); } AesCryptor* encryptor() { return encryptor_.get(); }
FourCC protection_scheme() const { return protection_scheme_; } FourCC protection_scheme() const { return protection_scheme_; }
uint8_t crypt_byte_block() const { return crypt_byte_block_; }
uint8_t skip_byte_block() const { return skip_byte_block_; }
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();
@ -83,7 +91,9 @@ class EncryptingFragmenter : public Fragmenter {
const uint8_t nalu_length_size_; const uint8_t nalu_length_size_;
const VideoCodec video_codec_; const VideoCodec video_codec_;
int64_t clear_time_; int64_t clear_time_;
FourCC protection_scheme_; const FourCC protection_scheme_;
const uint8_t crypt_byte_block_;
const uint8_t skip_byte_block_;
scoped_ptr<VPxParser> vpx_parser_; scoped_ptr<VPxParser> vpx_parser_;
scoped_ptr<VideoSliceHeaderParser> header_parser_; scoped_ptr<VideoSliceHeaderParser> header_parser_;

View File

@ -25,12 +25,16 @@ KeyRotationFragmenter::KeyRotationFragmenter(MovieFragment* moof,
int64_t crypto_period_duration, int64_t crypto_period_duration,
int64_t clear_time, int64_t clear_time,
FourCC protection_scheme, FourCC protection_scheme,
uint8_t crypt_byte_block,
uint8_t skip_byte_block,
MuxerListener* muxer_listener) MuxerListener* muxer_listener)
: EncryptingFragmenter(info, : EncryptingFragmenter(info,
traf, traf,
scoped_ptr<EncryptionKey>(new EncryptionKey()), scoped_ptr<EncryptionKey>(new EncryptionKey()),
clear_time, clear_time,
protection_scheme), protection_scheme,
crypt_byte_block,
skip_byte_block),
moof_(moof), moof_(moof),
encryption_key_source_(encryption_key_source), encryption_key_source_(encryption_key_source),
track_type_(track_type), track_type_(track_type),
@ -105,10 +109,18 @@ Status KeyRotationFragmenter::PrepareFragmentForEncryption(
// Fill in SampleGroupDescription box information. // Fill in SampleGroupDescription box information.
traf()->sample_group_description.grouping_type = FOURCC_seig; traf()->sample_group_description.grouping_type = FOURCC_seig;
traf()->sample_group_description.entries.resize(1); traf()->sample_group_description.entries.resize(1);
traf()->sample_group_description.entries[0].is_protected = 1; auto& sample_group_entry = traf()->sample_group_description.entries[0];
traf()->sample_group_description.entries[0].per_sample_iv_size = sample_group_entry.is_protected = 1;
encryptor()->iv().size(); if (protection_scheme() == FOURCC_cbcs) {
traf()->sample_group_description.entries[0].key_id = encryption_key()->key_id; // For 'cbcs' scheme, Constant IVs SHALL be used.
sample_group_entry.per_sample_iv_size = 0;
sample_group_entry.constant_iv = encryptor()->iv();
} else {
sample_group_entry.per_sample_iv_size = encryptor()->iv().size();
}
sample_group_entry.crypt_byte_block = crypt_byte_block();
sample_group_entry.skip_byte_block = skip_byte_block();
sample_group_entry.key_id = encryption_key()->key_id;
// Fill in SampleToGroup box information. // Fill in SampleToGroup box information.
traf()->sample_to_group.grouping_type = FOURCC_seig; traf()->sample_to_group.grouping_type = FOURCC_seig;

View File

@ -34,6 +34,10 @@ class KeyRotationFragmenter : public EncryptingFragmenter {
/// track's timescale. /// track's timescale.
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens', /// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
/// 'cbc1', 'cbcs'. /// 'cbc1', 'cbcs'.
/// @param crypt_byte_block indicates number of encrypted blocks (16-byte) in
/// pattern based encryption.
/// @param skip_byte_block indicates number of unencrypted blocks (16-byte)
/// in pattern based encryption.
/// @param muxer_listener is a pointer to MuxerListener for notifying /// @param muxer_listener is a pointer to MuxerListener for notifying
/// muxer related events. This may be null. /// muxer related events. This may be null.
KeyRotationFragmenter(MovieFragment* moof, KeyRotationFragmenter(MovieFragment* moof,
@ -44,6 +48,8 @@ class KeyRotationFragmenter : public EncryptingFragmenter {
int64_t crypto_period_duration, int64_t crypto_period_duration,
int64_t clear_time, int64_t clear_time,
FourCC protection_scheme, FourCC protection_scheme,
uint8_t crypt_byte_block,
uint8_t skip_byte_block,
MuxerListener* muxer_listener); MuxerListener* muxer_listener);
~KeyRotationFragmenter() override; ~KeyRotationFragmenter() override;

View File

@ -26,6 +26,9 @@ namespace media {
namespace mp4 { namespace mp4 {
namespace { namespace {
// For pattern-based encryption.
const uint8_t kCryptByteBlock = 1u;
const uint8_t kSkipByteBlock = 9u;
const size_t kCencKeyIdSize = 16u; const size_t kCencKeyIdSize = 16u;
@ -47,6 +50,18 @@ uint64_t Rescale(uint64_t time_in_old_scale,
return static_cast<double>(time_in_old_scale) / old_scale * new_scale; return static_cast<double>(time_in_old_scale) / old_scale * new_scale;
} }
uint8_t GetCryptByteBlock(FourCC protection_scheme) {
return (protection_scheme == FOURCC_cbcs || protection_scheme == FOURCC_cens)
? kCryptByteBlock
: 0;
}
uint8_t GetSkipByteBlock(FourCC protection_scheme) {
return (protection_scheme == FOURCC_cbcs || protection_scheme == FOURCC_cens)
? kSkipByteBlock
: 0;
}
void GenerateSinf(const EncryptionKey& encryption_key, void GenerateSinf(const EncryptionKey& encryption_key,
FourCC old_type, FourCC old_type,
FourCC protection_scheme, FourCC protection_scheme,
@ -60,7 +75,18 @@ void GenerateSinf(const EncryptionKey& encryption_key,
auto& track_encryption = sinf->info.track_encryption; auto& track_encryption = sinf->info.track_encryption;
track_encryption.default_is_protected = 1; track_encryption.default_is_protected = 1;
DCHECK(!encryption_key.iv.empty()); DCHECK(!encryption_key.iv.empty());
track_encryption.default_per_sample_iv_size = encryption_key.iv.size(); if (protection_scheme == FOURCC_cbcs) {
// ISO/IEC 23001-7:2016 10.4.1
// For 'cbcs' scheme, Constant IVs SHALL be used.
track_encryption.default_per_sample_iv_size = 0;
track_encryption.default_constant_iv = encryption_key.iv;
} else {
track_encryption.default_per_sample_iv_size = encryption_key.iv.size();
}
track_encryption.default_crypt_byte_block =
GetCryptByteBlock(protection_scheme);
track_encryption.default_skip_byte_block =
GetSkipByteBlock(protection_scheme);
track_encryption.default_kid = encryption_key.key_id; track_encryption.default_kid = encryption_key.key_id;
} }
@ -159,6 +185,16 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
continue; continue;
} }
FourCC local_protection_scheme = protection_scheme;
if (streams[i]->info()->stream_type() != kStreamVideo) {
// Pattern encryption should only be used with video. Replaces with the
// corresponding non-pattern encryption scheme.
if (protection_scheme == FOURCC_cbcs)
local_protection_scheme = FOURCC_cbc1;
else if (protection_scheme == FOURCC_cens)
local_protection_scheme = FOURCC_cenc;
}
KeySource::TrackType track_type = KeySource::TrackType track_type =
GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels); GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels);
SampleDescription& description = SampleDescription& description =
@ -170,10 +206,12 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
encryption_key.key_id.assign( encryption_key.key_id.assign(
kKeyRotationDefaultKeyId, kKeyRotationDefaultKeyId,
kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId)); kKeyRotationDefaultKeyId + arraysize(kKeyRotationDefaultKeyId));
if (!AesCryptor::GenerateRandomIv(protection_scheme, &encryption_key.iv)) if (!AesCryptor::GenerateRandomIv(local_protection_scheme,
&encryption_key.iv)) {
return Status(error::INTERNAL_ERROR, "Failed to generate random iv."); return Status(error::INTERNAL_ERROR, "Failed to generate random iv.");
}
GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds, GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds,
protection_scheme, &description); local_protection_scheme, &description);
if (muxer_listener_) { if (muxer_listener_) {
muxer_listener_->OnEncryptionInfoReady( muxer_listener_->OnEncryptionInfoReady(
kInitialEncryptionInfo, encryption_key.key_id, kInitialEncryptionInfo, encryption_key.key_id,
@ -185,7 +223,8 @@ 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(),
protection_scheme, muxer_listener_); local_protection_scheme, GetCryptByteBlock(local_protection_scheme),
GetSkipByteBlock(local_protection_scheme), muxer_listener_);
continue; continue;
} }
@ -195,12 +234,14 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
if (!status.ok()) if (!status.ok())
return status; return status;
if (encryption_key->iv.empty()) { if (encryption_key->iv.empty()) {
if (!AesCryptor::GenerateRandomIv(protection_scheme, &encryption_key->iv)) if (!AesCryptor::GenerateRandomIv(local_protection_scheme,
&encryption_key->iv)) {
return Status(error::INTERNAL_ERROR, "Failed to generate random iv."); return Status(error::INTERNAL_ERROR, "Failed to generate random iv.");
}
} }
GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds, GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds,
protection_scheme, &description); local_protection_scheme, &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());
@ -218,7 +259,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(),
protection_scheme); local_protection_scheme, GetCryptByteBlock(local_protection_scheme),
GetSkipByteBlock(local_protection_scheme));
} }
// Choose the first stream if there is no VIDEO. // Choose the first stream if there is no VIDEO.

View File

@ -606,10 +606,22 @@ scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
FourCC protection_scheme = is_audio() ? audio_description().sinf.type.type FourCC protection_scheme = is_audio() ? audio_description().sinf.type.type
: video_description().sinf.type.type; : video_description().sinf.type.type;
return scoped_ptr<DecryptConfig>( std::vector<uint8_t> iv = sample_encryption_entry.initialization_vector;
new DecryptConfig(track_encryption().default_kid, if (iv.empty()) {
sample_encryption_entry.initialization_vector, if (protection_scheme != FOURCC_cbcs) {
sample_encryption_entry.subsamples, protection_scheme)); LOG(WARNING)
<< "Constant IV should only be used with 'cbcs' protection scheme.";
}
iv = track_encryption().default_constant_iv;
if (iv.empty()) {
LOG(ERROR) << "IV cannot be empty.";
return scoped_ptr<DecryptConfig>();
}
}
return scoped_ptr<DecryptConfig>(new DecryptConfig(
track_encryption().default_kid, iv, sample_encryption_entry.subsamples,
protection_scheme, track_encryption().default_crypt_byte_block,
track_encryption().default_skip_byte_block));
} }
} // namespace mp4 } // namespace mp4

View File

@ -19,6 +19,9 @@ const int kSumAscending1 = 45;
const int kAudioScale = 48000; const int kAudioScale = 48000;
const int kVideoScale = 25; const int kVideoScale = 25;
const uint8_t kDefaultCryptByteBlock = 2;
const uint8_t kDefaultSkipByteBlock = 8;
const uint8_t kAuxInfo[] = { const uint8_t kAuxInfo[] = {
// Sample 1: IV (no subsumples). // Sample 1: IV (no subsumples).
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31, 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
@ -29,7 +32,8 @@ const uint8_t kAuxInfo[] = {
// Sample 2: Subsample 1. // Sample 2: Subsample 1.
0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
// Sample 2: Subsample 2. // Sample 2: Subsample 2.
0x00, 0x03, 0x00, 0x00, 0x00, 0x04}; 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
};
const uint8_t kSampleEncryptionDataWithSubsamples[] = { const uint8_t kSampleEncryptionDataWithSubsamples[] = {
// Sample count. // Sample count.
@ -47,7 +51,8 @@ const uint8_t kSampleEncryptionDataWithSubsamples[] = {
// Sample 2: Subsample 1. // Sample 2: Subsample 1.
0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
// Sample 2: Subsample 2. // Sample 2: Subsample 2.
0x00, 0x03, 0x00, 0x00, 0x00, 0x04}; 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
};
const uint8_t kSampleEncryptionDataWithoutSubsamples[] = { const uint8_t kSampleEncryptionDataWithoutSubsamples[] = {
// Sample count. // Sample count.
@ -55,13 +60,37 @@ const uint8_t kSampleEncryptionDataWithoutSubsamples[] = {
// Sample 1: IV. // Sample 1: IV.
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31, 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
// Sample 2: IV. // Sample 2: IV.
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32}; 0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32,
};
const uint8_t kSampleEncryptionDataWithConstantIvAndSubsamples[] = {
// Sample count.
0x00, 0x00, 0x00, 0x02,
// Sample 1: Subsample count.
0x00, 0x01,
// Sample 1: Subsample 1.
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
// Sample 2: Subsample count.
0x00, 0x02,
// Sample 2: Subsample 1.
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
// Sample 2: Subsample 2.
0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
};
const uint8_t kSampleEncryptionDataWithConstantIvWithoutSubsamples[] = {
// Sample count.
0x00, 0x00, 0x00, 0x02,
};
const char kIv1[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31}; const char kIv1[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31};
const char kIv2[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32}; const char kIv2[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x32};
const char kConstantIv[] = {0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x33};
const uint8_t kKeyId[] = {0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54, const uint8_t kKeyId[] = {
0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44}; 0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54,
0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44,
};
} // namespace } // namespace
@ -142,7 +171,7 @@ class TrackRunIteratorTest : public testing::Test {
} }
// Update the first sample description of a Track to indicate encryption // Update the first sample description of a Track to indicate encryption
void AddEncryption(Track* track) { void AddEncryption(FourCC protection_scheme, Track* track) {
SampleDescription* stsd = SampleDescription* stsd =
&track->media.information.sample_table.description; &track->media.information.sample_table.description;
ProtectionSchemeInfo* sinf; ProtectionSchemeInfo* sinf;
@ -152,9 +181,20 @@ class TrackRunIteratorTest : public testing::Test {
sinf = &stsd->audio_entries[0].sinf; sinf = &stsd->audio_entries[0].sinf;
} }
sinf->type.type = FOURCC_cenc; sinf->type.type = protection_scheme;
sinf->info.track_encryption.default_is_protected = 1; sinf->info.track_encryption.default_is_protected = 1;
sinf->info.track_encryption.default_per_sample_iv_size = 8; // Use constant IV for CBCS protection scheme.
if (protection_scheme == FOURCC_cbcs) {
sinf->info.track_encryption.default_per_sample_iv_size = 0;
sinf->info.track_encryption.default_constant_iv.assign(
kConstantIv, kConstantIv + arraysize(kConstantIv));
sinf->info.track_encryption.default_crypt_byte_block =
kDefaultCryptByteBlock;
sinf->info.track_encryption.default_skip_byte_block =
kDefaultSkipByteBlock;
} else {
sinf->info.track_encryption.default_per_sample_iv_size = 8;
}
sinf->info.track_encryption.default_kid.assign(kKeyId, sinf->info.track_encryption.default_kid.assign(kKeyId,
kKeyId + arraysize(kKeyId)); kKeyId + arraysize(kKeyId));
} }
@ -202,6 +242,39 @@ class TrackRunIteratorTest : public testing::Test {
} }
} }
void AddSampleEncryptionWithConstantIv(uint8_t use_subsample_flag,
TrackFragment* frag) {
frag->sample_encryption.iv_size = 0;
frag->sample_encryption.flags = use_subsample_flag;
if (use_subsample_flag) {
frag->sample_encryption.sample_encryption_data.assign(
kSampleEncryptionDataWithConstantIvAndSubsamples,
kSampleEncryptionDataWithConstantIvAndSubsamples +
arraysize(kSampleEncryptionDataWithConstantIvAndSubsamples));
} else {
frag->sample_encryption.sample_encryption_data.assign(
kSampleEncryptionDataWithConstantIvWithoutSubsamples,
kSampleEncryptionDataWithConstantIvWithoutSubsamples +
arraysize(kSampleEncryptionDataWithConstantIvWithoutSubsamples));
}
// Update sample sizes and aux info header.
frag->runs.resize(1);
frag->runs[0].sample_count = 2;
if (use_subsample_flag) {
// Update sample sizes to match with subsample entries above.
frag->runs[0].sample_sizes[0] = 3;
frag->runs[0].sample_sizes[1] = 10;
// Set aux info header.
frag->auxiliary_offset.offsets.push_back(0);
frag->auxiliary_size.sample_count = 2;
frag->auxiliary_size.sample_info_sizes.push_back(16);
frag->auxiliary_size.sample_info_sizes.push_back(22);
} else {
// No aux info needed for constant iv and full sample encryption.
}
}
void SetAscending(std::vector<uint32_t>* vec) { void SetAscending(std::vector<uint32_t>* vec) {
vec->resize(10); vec->resize(10);
for (size_t i = 0; i < vec->size(); i++) for (size_t i = 0; i < vec->size(); i++)
@ -364,7 +437,7 @@ TEST_F(TrackRunIteratorTest, IgnoreUnknownAuxInfoTest) {
TEST_F(TrackRunIteratorTest, TEST_F(TrackRunIteratorTest,
DecryptConfigTestWithSampleEncryptionAndSubsample) { DecryptConfigTestWithSampleEncryptionAndSubsample) {
AddEncryption(&moov_.tracks[1]); AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
iter_.reset(new TrackRunIterator(&moov_)); iter_.reset(new TrackRunIterator(&moov_));
MovieFragment moof = CreateFragment(); MovieFragment moof = CreateFragment();
@ -402,7 +475,7 @@ TEST_F(TrackRunIteratorTest,
TEST_F(TrackRunIteratorTest, TEST_F(TrackRunIteratorTest,
DecryptConfigTestWithSampleEncryptionAndNoSubsample) { DecryptConfigTestWithSampleEncryptionAndNoSubsample) {
AddEncryption(&moov_.tracks[1]); AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
iter_.reset(new TrackRunIterator(&moov_)); iter_.reset(new TrackRunIterator(&moov_));
MovieFragment moof = CreateFragment(); MovieFragment moof = CreateFragment();
@ -432,8 +505,84 @@ TEST_F(TrackRunIteratorTest,
EXPECT_EQ(config->subsamples().size(), 0u); EXPECT_EQ(config->subsamples().size(), 0u);
} }
TEST_F(TrackRunIteratorTest,
DecryptConfigTestWithSampleEncryptionAndConstantIvAndSubsample) {
AddEncryption(FOURCC_cbcs, &moov_.tracks[1]);
iter_.reset(new TrackRunIterator(&moov_));
MovieFragment moof = CreateFragment();
AddSampleEncryptionWithConstantIv(SampleEncryption::kUseSubsampleEncryption,
&moof.tracks[1]);
ASSERT_TRUE(iter_->Init(moof));
// The run for track 2 will be the second, which is parsed according to
// data_offset.
iter_->AdvanceRun();
EXPECT_EQ(iter_->track_id(), 2u);
EXPECT_TRUE(iter_->is_encrypted());
EXPECT_EQ(iter_->sample_offset(), 200);
EXPECT_EQ(iter_->GetMaxClearOffset(), moof.tracks[1].runs[0].data_offset);
scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
EXPECT_EQ(FOURCC_cbcs, config->protection_scheme());
EXPECT_EQ(kDefaultCryptByteBlock, config->crypt_byte_block());
EXPECT_EQ(kDefaultSkipByteBlock, config->skip_byte_block());
EXPECT_EQ(std::vector<uint8_t>(kKeyId, kKeyId + arraysize(kKeyId)),
config->key_id());
EXPECT_EQ(
std::vector<uint8_t>(kConstantIv, kConstantIv + arraysize(kConstantIv)),
config->iv());
EXPECT_EQ(config->subsamples().size(), 1u);
EXPECT_EQ(config->subsamples()[0].clear_bytes, 1u);
EXPECT_EQ(config->subsamples()[0].cipher_bytes, 2u);
iter_->AdvanceSample();
config = iter_->GetDecryptConfig();
EXPECT_EQ(
std::vector<uint8_t>(kConstantIv, kConstantIv + arraysize(kConstantIv)),
config->iv());
EXPECT_EQ(config->subsamples().size(), 2u);
EXPECT_EQ(config->subsamples()[0].clear_bytes, 1u);
EXPECT_EQ(config->subsamples()[0].cipher_bytes, 2u);
EXPECT_EQ(config->subsamples()[1].clear_bytes, 3u);
EXPECT_EQ(config->subsamples()[1].cipher_bytes, 4u);
}
TEST_F(TrackRunIteratorTest,
DecryptConfigTestWithSampleEncryptionAndConstantIvAndNoSubsample) {
AddEncryption(FOURCC_cbcs, &moov_.tracks[1]);
iter_.reset(new TrackRunIterator(&moov_));
MovieFragment moof = CreateFragment();
AddSampleEncryptionWithConstantIv(!SampleEncryption::kUseSubsampleEncryption,
&moof.tracks[1]);
ASSERT_TRUE(iter_->Init(moof));
// The run for track 2 will be the second, which is parsed according to
// data_offset.
iter_->AdvanceRun();
EXPECT_EQ(iter_->track_id(), 2u);
EXPECT_TRUE(iter_->is_encrypted());
EXPECT_EQ(iter_->sample_offset(), 200);
EXPECT_EQ(iter_->GetMaxClearOffset(), moof.tracks[1].runs[0].data_offset);
scoped_ptr<DecryptConfig> config = iter_->GetDecryptConfig();
EXPECT_EQ(FOURCC_cbcs, config->protection_scheme());
EXPECT_EQ(std::vector<uint8_t>(kKeyId, kKeyId + arraysize(kKeyId)),
config->key_id());
EXPECT_EQ(
std::vector<uint8_t>(kConstantIv, kConstantIv + arraysize(kConstantIv)),
config->iv());
EXPECT_EQ(config->subsamples().size(), 0u);
iter_->AdvanceSample();
config = iter_->GetDecryptConfig();
EXPECT_EQ(
std::vector<uint8_t>(kConstantIv, kConstantIv + arraysize(kConstantIv)),
config->iv());
EXPECT_EQ(config->subsamples().size(), 0u);
}
TEST_F(TrackRunIteratorTest, DecryptConfigTestWithAuxInfo) { TEST_F(TrackRunIteratorTest, DecryptConfigTestWithAuxInfo) {
AddEncryption(&moov_.tracks[1]); AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
iter_.reset(new TrackRunIterator(&moov_)); iter_.reset(new TrackRunIterator(&moov_));
MovieFragment moof = CreateFragment(); MovieFragment moof = CreateFragment();
@ -470,8 +619,8 @@ TEST_F(TrackRunIteratorTest, DecryptConfigTestWithAuxInfo) {
// It is legal for aux info blocks to be shared among multiple formats. // It is legal for aux info blocks to be shared among multiple formats.
TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) { TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) {
AddEncryption(&moov_.tracks[0]); AddEncryption(FOURCC_cenc, &moov_.tracks[0]);
AddEncryption(&moov_.tracks[1]); AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
iter_.reset(new TrackRunIterator(&moov_)); iter_.reset(new TrackRunIterator(&moov_));
MovieFragment moof = CreateFragment(); MovieFragment moof = CreateFragment();
@ -512,8 +661,8 @@ TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) {
// byte 10000: track 1, run 2 data // byte 10000: track 1, run 2 data
// byte 20000: track 1, run 1 aux info // byte 20000: track 1, run 1 aux info
TEST_F(TrackRunIteratorTest, UnexpectedOrderingTest) { TEST_F(TrackRunIteratorTest, UnexpectedOrderingTest) {
AddEncryption(&moov_.tracks[0]); AddEncryption(FOURCC_cenc, &moov_.tracks[0]);
AddEncryption(&moov_.tracks[1]); AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
iter_.reset(new TrackRunIterator(&moov_)); iter_.reset(new TrackRunIterator(&moov_));
MovieFragment moof = CreateFragment(); MovieFragment moof = CreateFragment();