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.");
DEFINE_string(protection_scheme,
"cenc",
"Choose protection scheme. Currently support cenc and cbc1. "
"Default is cenc.");
"Choose protection scheme, 'cenc' or 'cbc1' or pattern-based "
"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 {

View File

@ -12,16 +12,20 @@ namespace media {
DecryptConfig::DecryptConfig(const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& iv,
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,
const std::vector<uint8_t>& iv,
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),
iv_(iv),
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);
}

View File

@ -60,10 +60,16 @@ class DecryptConfig {
/// in size to the sum of the subsample sizes.
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
/// '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,
const std::vector<uint8_t>& iv,
const std::vector<SubsampleEntry>& subsamples,
FourCC protection_scheme);
FourCC protection_scheme,
uint8_t crypt_byte_block,
uint8_t skip_byte_block);
~DecryptConfig();
@ -71,6 +77,8 @@ class DecryptConfig {
const std::vector<uint8_t>& iv() const { return iv_; }
const std::vector<SubsampleEntry>& subsamples() const { return subsamples_; }
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:
const std::vector<uint8_t> key_id_;
@ -83,6 +91,9 @@ class DecryptConfig {
const std::vector<SubsampleEntry> subsamples_;
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);
};

View File

@ -9,6 +9,7 @@
#include "packager/base/logging.h"
#include "packager/base/stl_util.h"
#include "packager/media/base/aes_decryptor.h"
#include "packager/media/base/aes_pattern_cryptor.h"
namespace edash_packager {
namespace media {
@ -47,6 +48,21 @@ bool DecryptorSource::DecryptSampleBuffer(const DecryptConfig* decrypt_config,
case FOURCC_cbc1:
aes_decryptor.reset(new AesCbcDecryptor(kNoPadding, kChainAcrossCalls));
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:
LOG(ERROR) << "Unsupported protection scheme: "
<< decrypt_config->protection_scheme();

View File

@ -9,6 +9,7 @@
#include <limits>
#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/key_source.h"
#include "packager/media/base/media_sample.h"
@ -63,24 +64,35 @@ EncryptingFragmenter::EncryptingFragmenter(
TrackFragment* traf,
scoped_ptr<EncryptionKey> encryption_key,
int64_t clear_time,
FourCC protection_scheme)
FourCC protection_scheme,
uint8_t crypt_byte_block,
uint8_t skip_byte_block)
: Fragmenter(traf),
info_(info),
encryption_key_(encryption_key.Pass()),
nalu_length_size_(GetNaluLengthSize(*info)),
video_codec_(GetVideoCodec(*info)),
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_);
if (video_codec_ == kCodecVP8) {
switch (video_codec_) {
case kCodecVP8:
vpx_parser_.reset(new VP8Parser);
} else if (video_codec_ == kCodecVP9) {
break;
case kCodecVP9:
vpx_parser_.reset(new VP9Parser);
} else if (video_codec_ == kCodecH264) {
break;
case kCodecH264:
header_parser_.reset(new H264VideoSliceHeaderParser);
} else if (video_codec_ == kCodecHVC1 || video_codec_ == kCodecHEV1) {
break;
case kCodecHVC1:
FALLTHROUGH_INTENDED;
case kCodecHEV1:
header_parser_.reset(new H265VideoSliceHeaderParser);
} else if (nalu_length_size_ > 0) {
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.
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.
SampleAuxiliaryInformationSize& saiz = traf()->auxiliary_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,
// otherwise |sample_info_size| is just the IV size.
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() {
@ -180,6 +199,19 @@ Status EncryptingFragmenter::CreateEncryptor() {
case FOURCC_cbc1:
encryptor.reset(new AesCbcEncryptor(kNoPadding, kChainAcrossCalls));
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:
return Status(error::MUXER_FAILURE, "Unsupported protection scheme.");
}
@ -202,6 +234,8 @@ Status EncryptingFragmenter::EncryptSample(scoped_refptr<MediaSample> sample) {
DCHECK(encryptor_);
SampleEncryptionEntry sample_encryption_entry;
// 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();
if (IsSubsampleEncryptionRequired()) {

View File

@ -32,11 +32,17 @@ class EncryptingFragmenter : public Fragmenter {
/// track's timescale.
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
/// '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,
TrackFragment* traf,
scoped_ptr<EncryptionKey> encryption_key,
int64_t clear_time,
FourCC protection_scheme);
FourCC protection_scheme,
uint8_t crypt_byte_block,
uint8_t skip_byte_block);
~EncryptingFragmenter() override;
@ -62,6 +68,8 @@ class EncryptingFragmenter : public Fragmenter {
const EncryptionKey* encryption_key() const { return encryption_key_.get(); }
AesCryptor* encryptor() { return encryptor_.get(); }
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) {
encryption_key_ = encryption_key.Pass();
@ -83,7 +91,9 @@ class EncryptingFragmenter : public Fragmenter {
const uint8_t nalu_length_size_;
const VideoCodec video_codec_;
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<VideoSliceHeaderParser> header_parser_;

View File

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

View File

@ -34,6 +34,10 @@ class KeyRotationFragmenter : public EncryptingFragmenter {
/// track's timescale.
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
/// '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
/// muxer related events. This may be null.
KeyRotationFragmenter(MovieFragment* moof,
@ -44,6 +48,8 @@ class KeyRotationFragmenter : public EncryptingFragmenter {
int64_t crypto_period_duration,
int64_t clear_time,
FourCC protection_scheme,
uint8_t crypt_byte_block,
uint8_t skip_byte_block,
MuxerListener* muxer_listener);
~KeyRotationFragmenter() override;

View File

@ -26,6 +26,9 @@ namespace media {
namespace mp4 {
namespace {
// For pattern-based encryption.
const uint8_t kCryptByteBlock = 1u;
const uint8_t kSkipByteBlock = 9u;
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;
}
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,
FourCC old_type,
FourCC protection_scheme,
@ -60,7 +75,18 @@ void GenerateSinf(const EncryptionKey& encryption_key,
auto& track_encryption = sinf->info.track_encryption;
track_encryption.default_is_protected = 1;
DCHECK(!encryption_key.iv.empty());
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;
}
@ -159,6 +185,16 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
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 =
GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels);
SampleDescription& description =
@ -170,10 +206,12 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
encryption_key.key_id.assign(
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.");
}
GenerateEncryptedSampleEntry(encryption_key, clear_lead_in_seconds,
protection_scheme, &description);
local_protection_scheme, &description);
if (muxer_listener_) {
muxer_listener_->OnEncryptionInfoReady(
kInitialEncryptionInfo, encryption_key.key_id,
@ -185,7 +223,8 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
encryption_key_source, track_type,
crypto_period_duration_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;
}
@ -195,12 +234,14 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
if (!status.ok())
return status;
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.");
}
}
GenerateEncryptedSampleEntry(*encryption_key, clear_lead_in_seconds,
protection_scheme, &description);
local_protection_scheme, &description);
if (moov_->pssh.empty()) {
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(
streams[i]->info(), &moof_->tracks[i], encryption_key.Pass(),
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.

View File

@ -606,10 +606,22 @@ scoped_ptr<DecryptConfig> TrackRunIterator::GetDecryptConfig() {
FourCC protection_scheme = is_audio() ? audio_description().sinf.type.type
: video_description().sinf.type.type;
return scoped_ptr<DecryptConfig>(
new DecryptConfig(track_encryption().default_kid,
sample_encryption_entry.initialization_vector,
sample_encryption_entry.subsamples, protection_scheme));
std::vector<uint8_t> iv = sample_encryption_entry.initialization_vector;
if (iv.empty()) {
if (protection_scheme != FOURCC_cbcs) {
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

View File

@ -19,6 +19,9 @@ const int kSumAscending1 = 45;
const int kAudioScale = 48000;
const int kVideoScale = 25;
const uint8_t kDefaultCryptByteBlock = 2;
const uint8_t kDefaultSkipByteBlock = 8;
const uint8_t kAuxInfo[] = {
// Sample 1: IV (no subsumples).
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
@ -29,7 +32,8 @@ const uint8_t kAuxInfo[] = {
// Sample 2: Subsample 1.
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
// Sample 2: Subsample 2.
0x00, 0x03, 0x00, 0x00, 0x00, 0x04};
0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
};
const uint8_t kSampleEncryptionDataWithSubsamples[] = {
// Sample count.
@ -47,7 +51,8 @@ const uint8_t kSampleEncryptionDataWithSubsamples[] = {
// Sample 2: Subsample 1.
0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
// Sample 2: Subsample 2.
0x00, 0x03, 0x00, 0x00, 0x00, 0x04};
0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
};
const uint8_t kSampleEncryptionDataWithoutSubsamples[] = {
// Sample count.
@ -55,13 +60,37 @@ const uint8_t kSampleEncryptionDataWithoutSubsamples[] = {
// Sample 1: IV.
0x41, 0x54, 0x65, 0x73, 0x74, 0x49, 0x76, 0x31,
// 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 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,
0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44};
const uint8_t kKeyId[] = {
0x41, 0x47, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x54,
0x65, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x49, 0x44,
};
} // namespace
@ -142,7 +171,7 @@ class TrackRunIteratorTest : public testing::Test {
}
// Update the first sample description of a Track to indicate encryption
void AddEncryption(Track* track) {
void AddEncryption(FourCC protection_scheme, Track* track) {
SampleDescription* stsd =
&track->media.information.sample_table.description;
ProtectionSchemeInfo* sinf;
@ -152,9 +181,20 @@ class TrackRunIteratorTest : public testing::Test {
sinf = &stsd->audio_entries[0].sinf;
}
sinf->type.type = FOURCC_cenc;
sinf->type.type = protection_scheme;
sinf->info.track_encryption.default_is_protected = 1;
// 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,
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) {
vec->resize(10);
for (size_t i = 0; i < vec->size(); i++)
@ -364,7 +437,7 @@ TEST_F(TrackRunIteratorTest, IgnoreUnknownAuxInfoTest) {
TEST_F(TrackRunIteratorTest,
DecryptConfigTestWithSampleEncryptionAndSubsample) {
AddEncryption(&moov_.tracks[1]);
AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
iter_.reset(new TrackRunIterator(&moov_));
MovieFragment moof = CreateFragment();
@ -402,7 +475,7 @@ TEST_F(TrackRunIteratorTest,
TEST_F(TrackRunIteratorTest,
DecryptConfigTestWithSampleEncryptionAndNoSubsample) {
AddEncryption(&moov_.tracks[1]);
AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
iter_.reset(new TrackRunIterator(&moov_));
MovieFragment moof = CreateFragment();
@ -432,8 +505,84 @@ TEST_F(TrackRunIteratorTest,
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) {
AddEncryption(&moov_.tracks[1]);
AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
iter_.reset(new TrackRunIterator(&moov_));
MovieFragment moof = CreateFragment();
@ -470,8 +619,8 @@ TEST_F(TrackRunIteratorTest, DecryptConfigTestWithAuxInfo) {
// It is legal for aux info blocks to be shared among multiple formats.
TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) {
AddEncryption(&moov_.tracks[0]);
AddEncryption(&moov_.tracks[1]);
AddEncryption(FOURCC_cenc, &moov_.tracks[0]);
AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
iter_.reset(new TrackRunIterator(&moov_));
MovieFragment moof = CreateFragment();
@ -512,8 +661,8 @@ TEST_F(TrackRunIteratorTest, SharedAuxInfoTest) {
// byte 10000: track 1, run 2 data
// byte 20000: track 1, run 1 aux info
TEST_F(TrackRunIteratorTest, UnexpectedOrderingTest) {
AddEncryption(&moov_.tracks[0]);
AddEncryption(&moov_.tracks[1]);
AddEncryption(FOURCC_cenc, &moov_.tracks[0]);
AddEncryption(FOURCC_cenc, &moov_.tracks[1]);
iter_.reset(new TrackRunIterator(&moov_));
MovieFragment moof = CreateFragment();