Add multi-key support in fixed/raw keys

The keys can be specified in --keys option, with the form of
  label=<label>:key_id=<32-digit hex string>:key=<32-digit hex string>,label=...

There can be multiple "label=..." block. The DRM label can be one
of "AUDIO, SD, HD, UHD1, UHD2" or a custom label.

Mark --key and --key_id as deprecated. We can achieve the same result
with --keys=label=:key_id=<key_id>:key=<key>.

Also add a new optional field in stream descriptor: drm_label, which
is used to overwrite the internally generated DRM label for the stream.
If not provided, the DRM label is generated automatically based on
audio/video information, e.g. resolution.

Code changes:
- Merged RawKeyEncryptionParams and RawKeyDecryptionParams into
  RawKeyParams.
- Make FixedKeySource accepts RawKeyParams as input.

Change-Id: Ic8c2f071cc71188e13f14bc6396fc2b3ffa5cac6
This commit is contained in:
KongQun Yang 2017-09-20 15:49:00 -07:00
parent 27c1900f12
commit 1bf1ec2445
17 changed files with 365 additions and 172 deletions

View File

@ -9,14 +9,27 @@ Raw key encryption options
Enable decryption with fixed key. Enable decryption with fixed key.
--keys <key_info_string[,key_info_string][,key_info_string]...>
**key_info_string** is of the form::
label={label}:key_id={key_id}:key={key}
*label* can be an arbitrary string or a predefined DRM label like AUDIO,
SD, HD, etc. Label with an empty string indicates the default key and
key_id. The *drm_label* in :doc:`/options/stream_descriptors`,
which can be implicit, determines which key info is applied to the stream
by matching the *drm_label* with the *label* in key info.
*key_id* and *key* should be 32-digit hex strings.
--key_id <32-digit hex string> --key_id <32-digit hex string>
The key id in hex string format. The key id in hex string format. Deprecated. Use --keys instead.
HEX.
--key <32-digit hex string> --key <32-digit hex string>
The key in hex string format. The key in hex string format. Deprecated. Use --keys instead.
--iv <16-digit or 32-digit hex string> --iv <16-digit or 32-digit hex string>

View File

@ -62,6 +62,14 @@ These are the available fields:
Optional. Defaults to 0 if not specified. If it is set to 1, no encryption Optional. Defaults to 0 if not specified. If it is set to 1, no encryption
of the stream will be made. of the stream will be made.
:drm_label:
Optional value for custom DRM label, which defines the encryption key
applied to the stream. Typically values include AUDIO, SD, HD, UHD1, UHD2.
For raw key, it should be a label defined in --keys. If not provided, the
DRM label is derived from stream type (video, audio), resolutions, etc.
Note that it is case sensitive.
:trick_play_factor (tpf): :trick_play_factor (tpf):
Optional value which specifies the trick play, a.k.a. trick mode, stream Optional value which specifies the trick play, a.k.a. trick mode, stream

View File

@ -18,6 +18,11 @@ DEFINE_bool(enable_fixed_key_decryption,
"Enable decryption with fixed key."); "Enable decryption with fixed key.");
DEFINE_hex_bytes(key_id, "", "Key id in hex string format."); DEFINE_hex_bytes(key_id, "", "Key id in hex string format.");
DEFINE_hex_bytes(key, "", "Key in hex string format."); DEFINE_hex_bytes(key, "", "Key in hex string format.");
DEFINE_string(keys,
"",
"A list of key information in the form of label=<drm "
"label>:key_id=<32-digit key id in hex>:key=<32-digit key in "
"hex>,label=...");
DEFINE_hex_bytes( DEFINE_hex_bytes(
iv, iv,
"", "",
@ -40,6 +45,7 @@ bool ValidateFixedCryptoFlags() {
const char fixed_crypto_label[] = "--enable_fixed_key_encryption/decryption"; const char fixed_crypto_label[] = "--enable_fixed_key_encryption/decryption";
// --key_id and --key are associated with --enable_fixed_key_encryption and // --key_id and --key are associated with --enable_fixed_key_encryption and
// --enable_fixed_key_decryption. // --enable_fixed_key_decryption.
if (FLAGS_keys.empty()) {
if (!ValidateFlag("key_id", FLAGS_key_id_bytes, fixed_crypto, false, if (!ValidateFlag("key_id", FLAGS_key_id_bytes, fixed_crypto, false,
fixed_crypto_label)) { fixed_crypto_label)) {
success = false; success = false;
@ -48,6 +54,17 @@ bool ValidateFixedCryptoFlags() {
fixed_crypto_label)) { fixed_crypto_label)) {
success = false; success = false;
} }
if (success && (!FLAGS_key_id_bytes.empty() || !FLAGS_key_bytes.empty())) {
PrintWarning(
"--key_id and --key are going to be deprecated. Please switch to "
"--keys as soon as possible.");
}
} else {
if (!FLAGS_key_id_bytes.empty() || !FLAGS_key_bytes.empty()) {
PrintError("--key_id or --key cannot be used together with --keys.");
success = false;
}
}
if (!ValidateFlag("iv", FLAGS_iv_bytes, FLAGS_enable_fixed_key_encryption, if (!ValidateFlag("iv", FLAGS_iv_bytes, FLAGS_enable_fixed_key_encryption,
true, "--enable_fixed_key_encryption")) { true, "--enable_fixed_key_encryption")) {
success = false; success = false;

View File

@ -18,6 +18,7 @@ DECLARE_bool(enable_fixed_key_encryption);
DECLARE_bool(enable_fixed_key_decryption); DECLARE_bool(enable_fixed_key_decryption);
DECLARE_hex_bytes(key_id); DECLARE_hex_bytes(key_id);
DECLARE_hex_bytes(key); DECLARE_hex_bytes(key);
DECLARE_string(keys);
DECLARE_hex_bytes(iv); DECLARE_hex_bytes(iv);
DECLARE_hex_bytes(pssh); DECLARE_hex_bytes(pssh);

View File

@ -82,6 +82,12 @@ const char kUsage[] =
" derived from the file extension of the output file.\n" " derived from the file extension of the output file.\n"
" - skip_encryption=0|1: Optional. Defaults to 0 if not specified. If\n" " - skip_encryption=0|1: Optional. Defaults to 0 if not specified. If\n"
" it is set to 1, no encryption of the stream will be made.\n" " it is set to 1, no encryption of the stream will be made.\n"
" - drm_label: Optional value for custom DRM label, which defines the\n"
" encryption key applied to the stream. Typical values include AUDIO,\n"
" SD, HD, UHD1, UHD2. For raw key, it should be a label defined in\n"
" --keys. If not provided, the DRM label is derived from stream type\n"
" (video, audio), resolution, etc.\n"
" Note that it is case sensitive.\n"
" - trick_play_factor (tpf): Optional value which specifies the trick\n" " - trick_play_factor (tpf): Optional value which specifies the trick\n"
" play, a.k.a. trick mode, stream sampling rate among key frames.\n" " play, a.k.a. trick mode, stream sampling rate among key frames.\n"
" If specified, the output is a trick play stream.\n" " If specified, the output is a trick play stream.\n"
@ -93,6 +99,11 @@ const char kUsage[] =
" Usually ends with '.m3u8'. If unspecified, defaults to something of\n" " Usually ends with '.m3u8'. If unspecified, defaults to something of\n"
" the form 'stream_0.m3u8', 'stream_1.m3u8', 'stream_2.m3u8', etc.\n"; " the form 'stream_0.m3u8', 'stream_1.m3u8', 'stream_2.m3u8', etc.\n";
// Labels for parameters in RawKey key info.
const char kDrmLabelLabel[] = "label";
const char kKeyIdLabel[] = "key_id";
const char kKeyLabel[] = "key";
enum ExitStatus { enum ExitStatus {
kSuccess = 0, kSuccess = 0,
kArgumentValidationFailed, kArgumentValidationFailed,
@ -154,6 +165,50 @@ bool GetProtectionScheme(uint32_t* protection_scheme) {
return false; return false;
} }
bool ParseKeys(const std::string& keys, RawKeyParams* raw_key) {
for (const std::string& key_data : base::SplitString(
keys, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
base::StringPairs string_pairs;
base::SplitStringIntoKeyValuePairs(key_data, '=', ':', &string_pairs);
std::map<std::string, std::string> value_map;
for (const auto& string_pair : string_pairs)
value_map[string_pair.first] = string_pair.second;
const std::string drm_label = value_map[kDrmLabelLabel];
if (raw_key->key_map.find(drm_label) != raw_key->key_map.end()) {
LOG(ERROR) << "Seeing duplicated DRM label '" << drm_label << "'.";
return false;
}
auto& key_info = raw_key->key_map[drm_label];
if (value_map[kKeyIdLabel].empty() ||
!base::HexStringToBytes(value_map[kKeyIdLabel], &key_info.key_id)) {
return false;
}
if (value_map[kKeyLabel].empty() ||
!base::HexStringToBytes(value_map[kKeyLabel], &key_info.key)) {
return false;
}
}
return true;
}
bool GetRawKeyParams(RawKeyParams* raw_key) {
raw_key->iv = FLAGS_iv_bytes;
raw_key->pssh = FLAGS_pssh_bytes;
if (!FLAGS_keys.empty()) {
if (!ParseKeys(FLAGS_keys, raw_key)) {
LOG(ERROR) << "Failed to parse --keys " << FLAGS_keys;
return false;
}
} else {
// An empty StreamLabel specifies the default key info.
RawKeyParams::KeyInfo& key_info = raw_key->key_map[""];
key_info.key_id = FLAGS_key_id_bytes;
key_info.key = FLAGS_key_bytes;
}
return true;
}
base::Optional<PackagingParams> GetPackagingParams() { base::Optional<PackagingParams> GetPackagingParams() {
PackagingParams packaging_params; PackagingParams packaging_params;
@ -223,13 +278,8 @@ base::Optional<PackagingParams> GetPackagingParams() {
break; break;
} }
case KeyProvider::kRawKey: { case KeyProvider::kRawKey: {
RawKeyEncryptionParams& raw_key = encryption_params.raw_key; if (!GetRawKeyParams(&encryption_params.raw_key))
raw_key.iv = FLAGS_iv_bytes; return base::nullopt;
raw_key.pssh = FLAGS_pssh_bytes;
// An empty StreamLabel specifies the default KeyPair.
RawKeyEncryptionParams::KeyPair& key_pair = raw_key.key_map[""];
key_pair.key_id = FLAGS_key_id_bytes;
key_pair.key = FLAGS_key_bytes;
break; break;
} }
case KeyProvider::kNone: case KeyProvider::kNone:
@ -260,11 +310,8 @@ base::Optional<PackagingParams> GetPackagingParams() {
break; break;
} }
case KeyProvider::kRawKey: { case KeyProvider::kRawKey: {
RawKeyDecryptionParams& raw_key = decryption_params.raw_key; if (!GetRawKeyParams(&decryption_params.raw_key))
// An empty StreamLabel specifies the default KeyPair. return base::nullopt;
RawKeyDecryptionParams::KeyPair& key_pair = raw_key.key_map[""];
key_pair.key_id = FLAGS_key_id_bytes;
key_pair.key = FLAGS_key_bytes;
break; break;
} }
case KeyProvider::kPlayready: case KeyProvider::kPlayready:

View File

@ -85,12 +85,7 @@ std::unique_ptr<KeySource> CreateEncryptionKeySource(
break; break;
} }
case KeyProvider::kRawKey: { case KeyProvider::kRawKey: {
const RawKeyEncryptionParams& raw_key = encryption_params.raw_key; encryption_key_source = FixedKeySource::Create(encryption_params.raw_key);
const std::string kDefaultTrackType;
// TODO(kqyang): Refactor FixedKeySource.
encryption_key_source = FixedKeySource::Create(
raw_key.key_map.find("")->second.key_id,
raw_key.key_map.find("")->second.key, raw_key.pssh, raw_key.iv);
break; break;
} }
case KeyProvider::kPlayready: { case KeyProvider::kPlayready: {
@ -171,12 +166,7 @@ std::unique_ptr<KeySource> CreateDecryptionKeySource(
break; break;
} }
case KeyProvider::kRawKey: { case KeyProvider::kRawKey: {
const RawKeyDecryptionParams& raw_key = decryption_params.raw_key; decryption_key_source = FixedKeySource::Create(decryption_params.raw_key);
const std::vector<uint8_t> kNoPssh;
const std::vector<uint8_t> kNoIv;
decryption_key_source = FixedKeySource::Create(
raw_key.key_map.find("")->second.key_id,
raw_key.key_map.find("")->second.key, kNoPssh, kNoIv);
break; break;
} }
case KeyProvider::kNone: case KeyProvider::kNone:

View File

@ -28,6 +28,7 @@ enum FieldType {
kHlsPlaylistNameField, kHlsPlaylistNameField,
kTrickPlayFactorField, kTrickPlayFactorField,
kSkipEncryptionField, kSkipEncryptionField,
kDrmStreamLabelField,
}; };
struct FieldNameToTypeMapping { struct FieldNameToTypeMapping {
@ -58,6 +59,8 @@ const FieldNameToTypeMapping kFieldNameTypeMappings[] = {
{"trick_play_factor", kTrickPlayFactorField}, {"trick_play_factor", kTrickPlayFactorField},
{"tpf", kTrickPlayFactorField}, {"tpf", kTrickPlayFactorField},
{"skip_encryption", kSkipEncryptionField}, {"skip_encryption", kSkipEncryptionField},
{"drm_stream_label", kDrmStreamLabelField},
{"drm_label", kDrmStreamLabelField},
}; };
FieldType GetFieldType(const std::string& field_name) { FieldType GetFieldType(const std::string& field_name) {
@ -154,6 +157,10 @@ base::Optional<StreamDescriptor> ParseStreamDescriptor(
descriptor.skip_encryption = skip_encryption_value > 0; descriptor.skip_encryption = skip_encryption_value > 0;
break; break;
} }
case kDrmStreamLabelField: {
descriptor.drm_label = iter->second;
break;
}
default: default:
LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first LOG(ERROR) << "Unknown field in stream descriptor (\"" << iter->first
<< "\")."; << "\").";

View File

@ -151,10 +151,12 @@ class PackagerAppTest(unittest.TestCase):
'--content_id=' + self.widevine_content_id, '--content_id=' + self.widevine_content_id,
] ]
elif encryption: elif encryption:
flags += ['--enable_fixed_key_encryption', flags += [
'--key_id=' + self.encryption_key_id, '--enable_fixed_key_encryption',
'--key=' + self.encryption_key, '--keys=label=:key_id={0}:key={1}'.format(self.encryption_key_id,
'--clear_lead={0}'.format(clear_lead)] self.encryption_key),
'--clear_lead={0}'.format(clear_lead)
]
if not random_iv: if not random_iv:
flags.append('--iv=' + self.encryption_iv) flags.append('--iv=' + self.encryption_iv)
@ -164,9 +166,11 @@ class PackagerAppTest(unittest.TestCase):
flags += ['--vp9_subsample_encryption=false'] flags += ['--vp9_subsample_encryption=false']
if decryption: if decryption:
flags += ['--enable_fixed_key_decryption', flags += [
'--key_id=' + self.encryption_key_id, '--enable_fixed_key_decryption',
'--key=' + self.encryption_key] '--keys=label=:key_id={0}:key={1}'.format(self.encryption_key_id,
self.encryption_key)
]
if key_rotation: if key_rotation:
flags.append('--crypto_period_duration=1') flags.append('--crypto_period_duration=1')
@ -567,6 +571,50 @@ class PackagerFunctionalTest(PackagerAppTest):
self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4') self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4')
self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4') self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4')
def testPackageWithEncryptionMultiKeys(self):
audio_key_id = '10111213141516171819202122232425'
audio_key = '11121314151617181920212223242526'
video_key_id = '20212223242526272829303132333435'
video_key = '21222324252627282930313233343536'
flags = self._GetFlags() + [
'--enable_fixed_key_encryption',
'--keys=label=AUDIO:key_id={0}:key={1},label=SD:key_id={2}:key={3}'.
format(audio_key_id, audio_key, video_key_id, video_key),
'--clear_lead={0}'.format(1), '--iv={0}'.format(self.encryption_iv)
]
self.assertPackageSuccess(self._GetStreams(['audio', 'video']), flags)
self.encryption_key_id = audio_key_id
self.encryption_key = audio_key
self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4')
self.encryption_key_id = video_key_id
self.encryption_key = video_key
self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4')
def testPackageWithEncryptionMultiKeysWithStreamLabel(self):
audio_key_id = '20212223242526272829303132333435'
audio_key = '21222324252627282930313233343536'
video_key_id = '10111213141516171819202122232425'
video_key = '11121314151617181920212223242526'
flags = self._GetFlags() + [
'--enable_fixed_key_encryption',
'--keys=label=MyAudio:key_id={0}:key={1},label=:key_id={2}:key={3}'.
format(audio_key_id, audio_key, video_key_id, video_key),
'--clear_lead={0}'.format(1), '--iv={0}'.format(self.encryption_iv)
]
# DRM label 'MyVideo' is not defined, will fall back to the key for the
# empty default label.
self.assertPackageSuccess(
self._GetStreams(['audio,drm_label=MyAudio',
'video,drm_label=MyVideo']), flags)
self.encryption_key_id = audio_key_id
self.encryption_key = audio_key
self._VerifyDecryption(self.output[0], 'bear-640x360-a-golden.mp4')
self.encryption_key_id = video_key_id
self.encryption_key = video_key
self._VerifyDecryption(self.output[1], 'bear-640x360-v-golden.mp4')
def testPackageWithEncryptionOfOnlyVideoStream(self): def testPackageWithEncryptionOfOnlyVideoStream(self):
self.assertPackageSuccess( self.assertPackageSuccess(
self._GetStreams(['audio,skip_encryption=1', 'video']), self._GetStreams(['audio,skip_encryption=1', 'video']),

View File

@ -16,4 +16,8 @@ void PrintError(const std::string& error_message) {
fprintf(stderr, "ERROR: %s\n", error_message.c_str()); fprintf(stderr, "ERROR: %s\n", error_message.c_str());
} }
void PrintWarning(const std::string& warning_message) {
fprintf(stderr, "WARNING: %s\n", warning_message.c_str());
}
} // namespace shaka } // namespace shaka

View File

@ -19,6 +19,10 @@ namespace shaka {
/// @param error_message specifies the error message. /// @param error_message specifies the error message.
void PrintError(const std::string& error_message); void PrintError(const std::string& error_message);
/// Format and print warning message.
/// @param warning_message specifies the warning message.
void PrintWarning(const std::string& warning_message);
/// Validate a flag against the given condition. /// Validate a flag against the given condition.
/// @param flag_name is the name of the flag. /// @param flag_name is the name of the flag.
/// @param flag_value is the value of the flag. /// @param flag_value is the value of the flag.

View File

@ -10,6 +10,10 @@
#include "packager/base/logging.h" #include "packager/base/logging.h"
#include "packager/base/strings/string_number_conversions.h" #include "packager/base/strings/string_number_conversions.h"
namespace {
const char kEmptyDrmLabel[] = "";
} // namespace
namespace shaka { namespace shaka {
namespace media { namespace media {
@ -24,30 +28,40 @@ Status FixedKeySource::FetchKeys(EmeInitDataType init_data_type,
Status FixedKeySource::GetKey(const std::string& stream_label, Status FixedKeySource::GetKey(const std::string& stream_label,
EncryptionKey* key) { EncryptionKey* key) {
DCHECK(key); DCHECK(key);
DCHECK(encryption_key_); // Try to find the key with label |stream_label|. If it is not available,
*key = *encryption_key_; // fall back to the default empty label if it is available.
auto iter = encryption_key_map_.find(stream_label);
if (iter == encryption_key_map_.end()) {
iter = encryption_key_map_.find(kEmptyDrmLabel);
if (iter == encryption_key_map_.end()) {
return Status(error::NOT_FOUND,
"Key for '" + stream_label + "' was not found.");
}
}
*key = *iter->second;
return Status::OK; return Status::OK;
} }
Status FixedKeySource::GetKey(const std::vector<uint8_t>& key_id, Status FixedKeySource::GetKey(const std::vector<uint8_t>& key_id,
EncryptionKey* key) { EncryptionKey* key) {
DCHECK(key); DCHECK(key);
DCHECK(encryption_key_); for (const auto& pair : encryption_key_map_) {
if (key_id != encryption_key_->key_id) { if (pair.second->key_id == key_id) {
return Status(error::NOT_FOUND, *key = *pair.second;
std::string("Key for key ID ") +
base::HexEncode(&key_id[0], key_id.size()) +
" was not found.");
}
*key = *encryption_key_;
return Status::OK; return Status::OK;
}
}
return Status(error::INTERNAL_ERROR,
"Key for key_id=" + base::HexEncode(&key_id[0], key_id.size()) +
" was not found.");
} }
Status FixedKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index, Status FixedKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
const std::string& stream_label, const std::string& stream_label,
EncryptionKey* key) { EncryptionKey* key) {
// Create a copy of the key. Status status = GetKey(stream_label, key);
*key = *encryption_key_; if (!status.ok())
return status;
// A naive key rotation algorithm is implemented here by left rotating the // A naive key rotation algorithm is implemented here by left rotating the
// key, key_id and pssh. Note that this implementation is only intended for // key, key_id and pssh. Note that this implementation is only intended for
@ -88,51 +102,59 @@ Status FixedKeySource::GetCryptoPeriodKey(uint32_t crypto_period_index,
} }
std::unique_ptr<FixedKeySource> FixedKeySource::Create( std::unique_ptr<FixedKeySource> FixedKeySource::Create(
const std::vector<uint8_t>& key_id, const RawKeyParams& raw_key) {
const std::vector<uint8_t>& key, std::vector<ProtectionSystemSpecificInfo> key_system_info;
const std::vector<uint8_t>& pssh_boxes, if (!raw_key.pssh.empty()) {
const std::vector<uint8_t>& iv) {
std::unique_ptr<EncryptionKey> encryption_key(new EncryptionKey());
if (key_id.size() != 16) {
LOG(ERROR) << "Invalid key ID size '" << key_id.size()
<< "', must be 16 bytes.";
return std::unique_ptr<FixedKeySource>();
}
if (key.size() != 16) {
// CENC only supports AES-128, i.e. 16 bytes.
LOG(ERROR) << "Invalid key size '" << key.size() << "', must be 16 bytes.";
return std::unique_ptr<FixedKeySource>();
}
encryption_key->key_id = key_id;
encryption_key->key = key;
encryption_key->iv = iv;
if (!ProtectionSystemSpecificInfo::ParseBoxes( if (!ProtectionSystemSpecificInfo::ParseBoxes(
pssh_boxes.data(), pssh_boxes.size(), raw_key.pssh.data(), raw_key.pssh.size(), &key_system_info)) {
&encryption_key->key_system_info)) {
LOG(ERROR) << "--pssh argument should be full PSSH boxes."; LOG(ERROR) << "--pssh argument should be full PSSH boxes.";
return std::unique_ptr<FixedKeySource>(); return std::unique_ptr<FixedKeySource>();
} }
} else {
// If there aren't any PSSH boxes given, create one with the common system
// ID.
key_system_info.resize(1);
for (const auto& entry : raw_key.key_map) {
const RawKeyParams::KeyInfo& key_pair = entry.second;
key_system_info.back().add_key_id(key_pair.key_id);
}
key_system_info.back().set_system_id(kCommonSystemId,
arraysize(kCommonSystemId));
key_system_info.back().set_pssh_box_version(1);
}
// If there aren't any PSSH boxes given, create one with the common system ID. EncryptionKeyMap encryption_key_map;
if (encryption_key->key_system_info.size() == 0) { for (const auto& entry : raw_key.key_map) {
ProtectionSystemSpecificInfo info; const std::string& drm_label = entry.first;
info.add_key_id(encryption_key->key_id); const RawKeyParams::KeyInfo& key_pair = entry.second;
info.set_system_id(kCommonSystemId, arraysize(kCommonSystemId));
info.set_pssh_box_version(1);
encryption_key->key_system_info.push_back(info); if (key_pair.key_id.size() != 16) {
LOG(ERROR) << "Invalid key ID size '" << key_pair.key_id.size()
<< "', must be 16 bytes.";
return std::unique_ptr<FixedKeySource>();
}
if (key_pair.key.size() != 16) {
// CENC only supports AES-128, i.e. 16 bytes.
LOG(ERROR) << "Invalid key size '" << key_pair.key.size()
<< "', must be 16 bytes.";
return std::unique_ptr<FixedKeySource>();
}
std::unique_ptr<EncryptionKey> encryption_key(new EncryptionKey);
encryption_key->key_id = key_pair.key_id;
encryption_key->key = key_pair.key;
encryption_key->iv = raw_key.iv;
encryption_key->key_system_info = key_system_info;
encryption_key_map[drm_label] = std::move(encryption_key);
} }
return std::unique_ptr<FixedKeySource>( return std::unique_ptr<FixedKeySource>(
new FixedKeySource(std::move(encryption_key))); new FixedKeySource(std::move(encryption_key_map)));
} }
FixedKeySource::FixedKeySource() {} FixedKeySource::FixedKeySource() {}
FixedKeySource::FixedKeySource(std::unique_ptr<EncryptionKey> key) FixedKeySource::FixedKeySource(EncryptionKeyMap&& encryption_key_map)
: encryption_key_(std::move(key)) {} : encryption_key_map_(std::move(encryption_key_map)) {}
} // namespace media } // namespace media
} // namespace shaka } // namespace shaka

View File

@ -12,6 +12,7 @@
#include <vector> #include <vector>
#include "packager/media/base/key_source.h" #include "packager/media/base/key_source.h"
#include "packager/media/public/crypto_params.h"
namespace shaka { namespace shaka {
namespace media { namespace media {
@ -47,30 +48,22 @@ class FixedKeySource : public KeySource {
/// @} /// @}
/// Creates a new FixedKeySource from the given data. Returns null /// Creates a new FixedKeySource from the given data. Returns null
/// if the strings are invalid. /// if the parameter is malformed.
/// @param key_id is the key identifier. Must be 16 bytes. /// @param raw_key contains parameters to setup the key source.
/// @param key is the encryption / decryption key. Must be 16 bytes. static std::unique_ptr<FixedKeySource> Create(const RawKeyParams& raw_key);
/// @param pssh_boxes is the concatenated pssh boxes.
/// @param iv is the initialization vector. If not specified, a randomly
/// generated IV with the default length will be used.
/// Note: GetKey on the created key source will always return the same key for
/// all track types.
static std::unique_ptr<FixedKeySource> Create(
const std::vector<uint8_t>& key_id,
const std::vector<uint8_t>& key,
const std::vector<uint8_t>& pssh_boxes,
const std::vector<uint8_t>& iv);
protected: protected:
// Allow default constructor for mock key sources. // Allow default constructor for mock key sources.
FixedKeySource(); FixedKeySource();
private: private:
explicit FixedKeySource(std::unique_ptr<EncryptionKey> key); typedef std::map<std::string, std::unique_ptr<EncryptionKey>>
EncryptionKeyMap;
explicit FixedKeySource(EncryptionKeyMap&& encryption_key_map);
FixedKeySource(const FixedKeySource&) = delete;
FixedKeySource& operator=(const FixedKeySource&) = delete;
std::unique_ptr<EncryptionKey> encryption_key_; EncryptionKeyMap encryption_key_map_;
DISALLOW_COPY_AND_ASSIGN(FixedKeySource);
}; };
} // namespace media } // namespace media

View File

@ -24,7 +24,11 @@ namespace media {
namespace { namespace {
const char kKeyIdHex[] = "0101020305080d1522375990e9000000"; const char kKeyIdHex[] = "0101020305080d1522375990e9000000";
const char kKeyHex[] = "00100100200300500801302103405500"; const char kKeyHex[] = "00100100200300500801302103405500";
const char kKeyId2Hex[] = "1111121315180d1522375990e9000000";
const char kKey2Hex[] = "10201110300300500801302103405500";
const char kIvHex[] = "000102030405060708090a0b0c0d0e0f"; const char kIvHex[] = "000102030405060708090a0b0c0d0e0f";
// PSSH boxes generated manually according to PSSH box syntax specified in
// ISO/IEC 23001-7:2016 8.1.2.
const char kPsshBox1Hex[] = const char kPsshBox1Hex[] =
"000000427073736801000000" "000000427073736801000000"
"020305070b0d1113171d1f25292b2f35" "020305070b0d1113171d1f25292b2f35"
@ -38,66 +42,103 @@ const char kPsshBox2Hex[] =
"0000000e" "0000000e"
"fffffffff000000000000ddddddd"; "fffffffff000000000000ddddddd";
const char kDefaultPsshBoxHex[] = const char kDefaultPsshBoxHex[] =
"000000347073736801000000" "000000447073736801000000"
"1077efecc0b24d02ace33c1e52e2fb4b" "1077efecc0b24d02ace33c1e52e2fb4b"
"00000001" "00000002"
"1111121315180d1522375990e9000000"
"0101020305080d1522375990e9000000" "0101020305080d1522375990e9000000"
"00000000"; "00000000";
const char kDrmLabel[] = "SomeDrmLabel";
const char kAnotherDrmLabel[] = "AnotherDrmLabel";
const char kEmptyDrmLabel[] = "";
std::vector<uint8_t> HexStringToVector(const std::string& str) { std::vector<uint8_t> HexStringToVector(const std::string& str) {
std::vector<uint8_t> vec; std::vector<uint8_t> vec;
CHECK(base::HexStringToBytes(str, &vec)); CHECK(base::HexStringToBytes(str, &vec));
return vec; return vec;
} }
} } // namespace
TEST(FixedKeySourceTest, Success) { TEST(FixedKeySourceTest, Success) {
std::string pssh_boxes = std::string(kPsshBox1Hex) + kPsshBox2Hex; RawKeyParams raw_key_params;
std::unique_ptr<FixedKeySource> key_source = FixedKeySource::Create( raw_key_params.key_map[kDrmLabel].key_id = HexStringToVector(kKeyIdHex);
HexStringToVector(kKeyIdHex), HexStringToVector(kKeyHex), raw_key_params.key_map[kDrmLabel].key = HexStringToVector(kKeyHex);
HexStringToVector(pssh_boxes), HexStringToVector(kIvHex)); raw_key_params.key_map[kEmptyDrmLabel].key_id = HexStringToVector(kKeyId2Hex);
raw_key_params.key_map[kEmptyDrmLabel].key = HexStringToVector(kKey2Hex);
raw_key_params.iv = HexStringToVector(kIvHex);
raw_key_params.pssh =
HexStringToVector(std::string(kPsshBox1Hex) + kPsshBox2Hex);
std::unique_ptr<FixedKeySource> key_source =
FixedKeySource::Create(raw_key_params);
ASSERT_NE(nullptr, key_source); ASSERT_NE(nullptr, key_source);
EncryptionKey key; EncryptionKey key_from_drm_label;
ASSERT_OK(key_source->GetKey("SomeStreamLabel", &key)); ASSERT_OK(key_source->GetKey(kDrmLabel, &key_from_drm_label));
EXPECT_HEX_EQ(kKeyIdHex, key_from_drm_label.key_id);
EXPECT_HEX_EQ(kKeyHex, key_from_drm_label.key);
EXPECT_HEX_EQ(kIvHex, key_from_drm_label.iv);
ASSERT_EQ(2u, key_from_drm_label.key_system_info.size());
EXPECT_HEX_EQ(kPsshBox1Hex,
key_from_drm_label.key_system_info[0].CreateBox());
EXPECT_HEX_EQ(kPsshBox2Hex,
key_from_drm_label.key_system_info[1].CreateBox());
EXPECT_HEX_EQ(kKeyIdHex, key.key_id); // Using Key ID.
EXPECT_HEX_EQ(kKeyHex, key.key); EncryptionKey key_from_key_id;
EXPECT_HEX_EQ(kIvHex, key.iv); ASSERT_OK(key_source->GetKey(HexStringToVector(kKeyIdHex), &key_from_key_id));
EXPECT_EQ(key_from_key_id.key_id, key_from_drm_label.key_id);
ASSERT_EQ(2u, key.key_system_info.size()); // |kAnotherDrmLabel| is not present in the key source, but |kEmptyDrmLabel|
EXPECT_HEX_EQ(kPsshBox1Hex, key.key_system_info[0].CreateBox()); // is in the key source, the associated key for |kEmptyDrmLabel| will be
EXPECT_HEX_EQ(kPsshBox2Hex, key.key_system_info[1].CreateBox()); // returned.
ASSERT_OK(key_source->GetKey(kAnotherDrmLabel, &key_from_drm_label));
EXPECT_HEX_EQ(kKeyId2Hex, key_from_drm_label.key_id);
EXPECT_HEX_EQ(kKey2Hex, key_from_drm_label.key);
EXPECT_HEX_EQ(kIvHex, key_from_drm_label.iv);
ASSERT_EQ(2u, key_from_drm_label.key_system_info.size());
EXPECT_HEX_EQ(kPsshBox1Hex,
key_from_drm_label.key_system_info[0].CreateBox());
EXPECT_HEX_EQ(kPsshBox2Hex,
key_from_drm_label.key_system_info[1].CreateBox());
} }
TEST(FixedKeySourceTest, EmptyPssh) { TEST(FixedKeySourceTest, EmptyPssh) {
std::unique_ptr<FixedKeySource> key_source = FixedKeySource::Create( RawKeyParams raw_key_params;
HexStringToVector(kKeyIdHex), HexStringToVector(kKeyHex), raw_key_params.key_map[kDrmLabel].key_id = HexStringToVector(kKeyIdHex);
std::vector<uint8_t>(), HexStringToVector(kIvHex)); raw_key_params.key_map[kDrmLabel].key = HexStringToVector(kKeyHex);
raw_key_params.key_map[kAnotherDrmLabel].key_id =
HexStringToVector(kKeyId2Hex);
raw_key_params.key_map[kAnotherDrmLabel].key = HexStringToVector(kKey2Hex);
raw_key_params.iv = HexStringToVector(kIvHex);
std::unique_ptr<FixedKeySource> key_source =
FixedKeySource::Create(raw_key_params);
ASSERT_NE(nullptr, key_source); ASSERT_NE(nullptr, key_source);
EncryptionKey key; EncryptionKey key;
ASSERT_OK(key_source->GetKey("SomeStreamLabel", &key)); ASSERT_OK(key_source->GetKey(kDrmLabel, &key));
EXPECT_HEX_EQ(kKeyIdHex, key.key_id); EXPECT_HEX_EQ(kKeyIdHex, key.key_id);
EXPECT_HEX_EQ(kKeyHex, key.key); EXPECT_HEX_EQ(kKeyHex, key.key);
EXPECT_HEX_EQ(kIvHex, key.iv); EXPECT_HEX_EQ(kIvHex, key.iv);
ASSERT_EQ(1u, key.key_system_info.size()); ASSERT_EQ(1u, key.key_system_info.size());
EXPECT_HEX_EQ(kDefaultPsshBoxHex, key.key_system_info[0].CreateBox()); EXPECT_HEX_EQ(kDefaultPsshBoxHex, key.key_system_info[0].CreateBox());
} }
TEST(FixedKeySourceTest, Failure) { TEST(FixedKeySourceTest, Failure) {
// Invalid key id size. // Invalid key id size.
std::unique_ptr<FixedKeySource> key_source = FixedKeySource::Create( RawKeyParams raw_key_params;
HexStringToVector("000102030405"), HexStringToVector(kKeyHex), raw_key_params.key_map[kEmptyDrmLabel].key_id =
HexStringToVector(kPsshBox1Hex), HexStringToVector(kIvHex)); HexStringToVector("000102030405");
raw_key_params.key_map[kEmptyDrmLabel].key = HexStringToVector(kKeyHex);
raw_key_params.pssh = HexStringToVector(kPsshBox1Hex);
raw_key_params.iv = HexStringToVector(kIvHex);
std::unique_ptr<FixedKeySource> key_source =
FixedKeySource::Create(raw_key_params);
EXPECT_EQ(nullptr, key_source); EXPECT_EQ(nullptr, key_source);
// Invalid pssh box. // Invalid pssh box.
key_source = FixedKeySource::Create( raw_key_params.key_map[kEmptyDrmLabel].key_id = HexStringToVector(kKeyIdHex);
HexStringToVector(kKeyIdHex), HexStringToVector(kKeyHex), raw_key_params.pssh = HexStringToVector("000102030405");
HexStringToVector("000102030405"), HexStringToVector(kIvHex)); key_source = FixedKeySource::Create(raw_key_params);
EXPECT_EQ(nullptr, key_source); EXPECT_EQ(nullptr, key_source);
} }

View File

@ -50,7 +50,6 @@ class ProtectionSystemSpecificInfo {
system_id_.assign(system_id, system_id + system_id_size); system_id_.assign(system_id, system_id + system_id_size);
} }
void add_key_id(const std::vector<uint8_t>& key_id) { void add_key_id(const std::vector<uint8_t>& key_id) {
DCHECK_EQ(16u, key_id.size());
key_ids_.push_back(key_id); key_ids_.push_back(key_id);
} }
void clear_key_ids() { key_ids_.clear(); } void clear_key_ids() { key_ids_.clear(); }
@ -72,4 +71,3 @@ class ProtectionSystemSpecificInfo {
} // namespace shaka } // namespace shaka
#endif // MEDIA_BASE_PSSH_H_ #endif // MEDIA_BASE_PSSH_H_

View File

@ -72,7 +72,7 @@ struct WidevineEncryptionParams {
/// required. The presence of other parameters may be necessary depends /// required. The presence of other parameters may be necessary depends
/// on server configuration. /// on server configuration.
/// (2) Provide the raw key directly. Both `key_id` and `key` are required. /// (2) Provide the raw key directly. Both `key_id` and `key` are required.
/// We are planning to merge this mode with `RawKeyEncryptionParams`. /// We are planning to merge this mode with `RawKeyParams`.
struct PlayreadyEncryptionParams { struct PlayreadyEncryptionParams {
/// Playready license / key server URL. /// Playready license / key server URL.
std::string key_server_url; std::string key_server_url;
@ -95,24 +95,26 @@ struct PlayreadyEncryptionParams {
std::vector<uint8_t> key; std::vector<uint8_t> key;
}; };
/// Raw key encryption parameters, i.e. with key parameters provided. /// Raw key encryption/decryption parameters, i.e. with key parameters provided.
struct RawKeyEncryptionParams { struct RawKeyParams {
/// An optional initialization vector. If not provided, a random `iv` will be /// An optional initialization vector. If not provided, a random `iv` will be
/// generated. Note that this parameter should only be used during testing. /// generated. Note that this parameter should only be used during testing.
/// Not needed for decryption.
std::vector<uint8_t> iv; std::vector<uint8_t> iv;
/// Inject a custom `pssh` or multiple concatenated `psshs`. If not provided, /// Inject a custom `pssh` or multiple concatenated `psshs`. If not provided,
/// a common system pssh will be generated. /// a common system pssh will be generated.
/// Not needed for decryption.
std::vector<uint8_t> pssh; std::vector<uint8_t> pssh;
using StreamLabel = std::string; using StreamLabel = std::string;
struct KeyPair { struct KeyInfo {
std::vector<uint8_t> key_id; std::vector<uint8_t> key_id;
std::vector<uint8_t> key; std::vector<uint8_t> key;
}; };
/// Defines the KeyPair for the streams. An empty `StreamLabel` indicates the /// Defines the KeyInfo for the streams. An empty `StreamLabel` indicates the
/// default `KeyPair`, which applies to all the `StreamLabels` not present in /// default `KeyInfo`, which applies to all the `StreamLabels` not present in
/// `key_map`. /// `key_map`.
std::map<StreamLabel, KeyPair> key_map; std::map<StreamLabel, KeyInfo> key_map;
}; };
/// Encryption parameters. /// Encryption parameters.
@ -124,7 +126,7 @@ struct EncryptionParams {
// Only one of the three fields is valid. // Only one of the three fields is valid.
WidevineEncryptionParams widevine; WidevineEncryptionParams widevine;
PlayreadyEncryptionParams playready; PlayreadyEncryptionParams playready;
RawKeyEncryptionParams raw_key; RawKeyParams raw_key;
/// Clear lead duration in seconds. /// Clear lead duration in seconds.
double clear_lead_in_seconds = 0; double clear_lead_in_seconds = 0;
@ -182,19 +184,6 @@ struct WidevineDecryptionParams {
WidevineSigner signer; WidevineSigner signer;
}; };
/// Raw key decryption parameters, i.e. with key parameters provided.
struct RawKeyDecryptionParams {
using StreamLabel = std::string;
struct KeyPair {
std::vector<uint8_t> key_id;
std::vector<uint8_t> key;
};
/// Defines the KeyPair for the streams. An empty `StreamLabel` indicates the
/// default `KeyPair`, which applies to all the `StreamLabels` not present in
/// `key_map`.
std::map<StreamLabel, KeyPair> key_map;
};
/// Decryption parameters. /// Decryption parameters.
struct DecryptionParams { struct DecryptionParams {
/// Specifies the key provider, which determines which key provider is used /// Specifies the key provider, which determines which key provider is used
@ -203,7 +192,7 @@ struct DecryptionParams {
KeyProvider key_provider = KeyProvider::kNone; KeyProvider key_provider = KeyProvider::kNone;
// Only one of the two fields is valid. // Only one of the two fields is valid.
WidevineDecryptionParams widevine; WidevineDecryptionParams widevine;
RawKeyDecryptionParams raw_key; RawKeyParams raw_key;
}; };
} // namespace shaka } // namespace shaka

View File

@ -425,7 +425,13 @@ std::shared_ptr<MediaHandler> CreateEncryptionHandler(
encryption_params.protection_scheme = kAppleSampleAesProtectionScheme; encryption_params.protection_scheme = kAppleSampleAesProtectionScheme;
} }
if (!encryption_params.stream_label_func) { if (!stream.drm_label.empty()) {
const std::string& drm_label = stream.drm_label;
encryption_params.stream_label_func =
[drm_label](const EncryptionParams::EncryptedStreamAttributes&) {
return drm_label;
};
} else if (!encryption_params.stream_label_func) {
const int kDefaultMaxSdPixels = 768 * 576; const int kDefaultMaxSdPixels = 768 * 576;
const int kDefaultMaxHdPixels = 1920 * 1080; const int kDefaultMaxHdPixels = 1920 * 1080;
const int kDefaultMaxUhd1Pixels = 4096 * 2160; const int kDefaultMaxUhd1Pixels = 4096 * 2160;

View File

@ -82,6 +82,11 @@ struct StreamDescriptor {
/// If set to true, the stream will not be encrypted. This is useful, e.g. to /// If set to true, the stream will not be encrypted. This is useful, e.g. to
/// encrypt only video streams. /// encrypt only video streams.
bool skip_encryption = false; bool skip_encryption = false;
/// Specifies a custom DRM stream label, which can be a DRM label defined by
/// the DRM system. Typically values include AUDIO, SD, HD, UHD1, UHD2. If not
/// provided, the DRM stream label is derived from stream type (video, audio),
/// resolutions etc.
std::string drm_label;
/// If set to a non-zero value, will generate a trick play / trick mode /// If set to a non-zero value, will generate a trick play / trick mode
/// stream with frames sampled from the key frames in the original stream. /// stream with frames sampled from the key frames in the original stream.
/// `trick_play_factor` defines the sampling rate. /// `trick_play_factor` defines the sampling rate.