Shaka Packager SDK
Loading...
Searching...
No Matches
encryption_handler.cc
1// Copyright 2017 Google LLC. All rights reserved.
2//
3// Use of this source code is governed by a BSD-style
4// license that can be found in the LICENSE file or at
5// https://developers.google.com/open-source/licenses/bsd
6
7#include <packager/media/crypto/encryption_handler.h>
8
9#include <algorithm>
10#include <cstddef>
11#include <cstdint>
12#include <cstring>
13#include <functional>
14#include <iterator>
15#include <memory>
16#include <string>
17#include <utility>
18#include <vector>
19
20#include <absl/log/check.h>
21#include <absl/log/log.h>
22
23#include <packager/crypto_params.h>
24#include <packager/macros/status.h>
25#include <packager/media/base/aes_cryptor.h>
26#include <packager/media/base/common_pssh_generator.h>
27#include <packager/media/base/decrypt_config.h>
28#include <packager/media/base/encryption_config.h>
29#include <packager/media/base/fourccs.h>
30#include <packager/media/base/key_source.h>
31#include <packager/media/base/media_handler.h>
32#include <packager/media/base/media_sample.h>
33#include <packager/media/base/playready_pssh_generator.h>
34#include <packager/media/base/protection_system_ids.h>
35#include <packager/media/base/protection_system_specific_info.h>
36#include <packager/media/base/pssh_generator.h>
37#include <packager/media/base/stream_info.h>
38#include <packager/media/base/video_stream_info.h>
39#include <packager/media/base/widevine_pssh_generator.h>
40#include <packager/media/crypto/aes_encryptor_factory.h>
41#include <packager/media/crypto/subsample_generator.h>
42#include <packager/status.h>
43
44namespace shaka {
45namespace media {
46
47namespace {
48// The encryption handler only supports a single output.
49const size_t kStreamIndex = 0;
50
51// The default KID, KEY and IV for key rotation are all 0s.
52// They are placeholders and are not really being used to encrypt data.
53const uint8_t kKeyRotationDefaultKeyId[] = {
54 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
55};
56const uint8_t kKeyRotationDefaultKey[] = {
57 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
58};
59const uint8_t kKeyRotationDefaultIv[] = {
60 0, 0, 0, 0, 0, 0, 0, 0,
61};
62
63// Whether a key restricted to |common_encryption_scheme| may be used with
64// |protection_scheme|. The scheme of a stream may differ from the Common
65// Encryption scheme it is signaled with, so this cannot be validated before
66// the stream's actual protection scheme is known.
67bool SchemeBindingAllows(const std::string& common_encryption_scheme,
68 FourCC protection_scheme) {
69 if (common_encryption_scheme.empty())
70 return true;
71 if (common_encryption_scheme == FourCCToString(protection_scheme))
72 return true;
73 // Apple Sample AES applies the 'cbcs' pattern scheme to TS streams.
74 if (protection_scheme == kAppleSampleAesProtectionScheme &&
75 common_encryption_scheme == "cbcs") {
76 return true;
77 }
78 // AES-128 full-segment encryption is not a Common Encryption scheme, so a
79 // Common Encryption scheme restriction cannot apply to it.
80 if (protection_scheme == kAes128ProtectionScheme)
81 return true;
82 return false;
83}
84
85std::string GetStreamLabelForEncryption(
86 const StreamInfo& stream_info,
87 const std::function<std::string(
88 const EncryptionParams::EncryptedStreamAttributes& stream_attributes)>&
89 stream_label_func) {
90 EncryptionParams::EncryptedStreamAttributes stream_attributes;
91 if (stream_info.stream_type() == kStreamAudio) {
92 stream_attributes.stream_type =
93 EncryptionParams::EncryptedStreamAttributes::kAudio;
94 } else if (stream_info.stream_type() == kStreamVideo) {
95 const VideoStreamInfo& video_stream_info =
96 static_cast<const VideoStreamInfo&>(stream_info);
97 stream_attributes.stream_type =
98 EncryptionParams::EncryptedStreamAttributes::kVideo;
99 stream_attributes.oneof.video.width = video_stream_info.width();
100 stream_attributes.oneof.video.height = video_stream_info.height();
101 }
102 return stream_label_func(stream_attributes);
103}
104
105bool IsPatternEncryptionScheme(FourCC protection_scheme) {
106 return protection_scheme == kAppleSampleAesProtectionScheme ||
107 protection_scheme == FOURCC_cbcs || protection_scheme == FOURCC_cens;
108}
109
110void FillPsshGenerators(
111 const EncryptionParams& encryption_params,
112 std::vector<std::unique_ptr<PsshGenerator>>* pssh_generators,
113 std::vector<std::vector<uint8_t>>* no_pssh_systems) {
114 if (has_flag(encryption_params.protection_systems,
115 ProtectionSystem::kCommon)) {
116 pssh_generators->emplace_back(new CommonPsshGenerator());
117 }
118
119 if (has_flag(encryption_params.protection_systems,
120 ProtectionSystem::kPlayReady)) {
121 pssh_generators->emplace_back(new PlayReadyPsshGenerator(
122 encryption_params.playready_extra_header_data,
123 static_cast<FourCC>(encryption_params.protection_scheme)));
124 }
125
126 if (has_flag(encryption_params.protection_systems,
127 ProtectionSystem::kWidevine)) {
128 pssh_generators->emplace_back(new WidevinePsshGenerator(
129 static_cast<FourCC>(encryption_params.protection_scheme)));
130 }
131
132 if (has_flag(encryption_params.protection_systems,
133 ProtectionSystem::kFairPlay)) {
134 no_pssh_systems->emplace_back(std::begin(kFairPlaySystemId),
135 std::end(kFairPlaySystemId));
136 }
137 // We only support Marlin Adaptive Streaming Specification – Simple Profile
138 // with Implicit Content ID Mapping, which does not need a PSSH. Marlin
139 // specific PSSH with Explicit Content ID Mapping is not generated.
140 if (has_flag(encryption_params.protection_systems,
141 ProtectionSystem::kMarlin)) {
142 no_pssh_systems->emplace_back(std::begin(kMarlinSystemId),
143 std::end(kMarlinSystemId));
144 }
145
146 // The DRM signaling in a CPIX document is authoritative, so no default
147 // PSSH is generated for the CPIX key provider; --protection_systems can
148 // still be used to generate signaling for additional systems.
149 if (pssh_generators->empty() && no_pssh_systems->empty() &&
150 (encryption_params.key_provider != KeyProvider::kRawKey ||
151 encryption_params.raw_key.pssh.empty()) &&
152 encryption_params.key_provider != KeyProvider::kCpix) {
153 pssh_generators->emplace_back(new CommonPsshGenerator());
154 }
155}
156
157void AddProtectionSystemIfNotExist(
158 const ProtectionSystemSpecificInfo& pssh_info,
159 EncryptionConfig* encryption_config) {
160 for (const auto& info : encryption_config->key_system_info) {
161 if (info.system_id == pssh_info.system_id)
162 return;
163 }
164 encryption_config->key_system_info.push_back(pssh_info);
165}
166
167Status FillProtectionSystemInfo(const EncryptionParams& encryption_params,
168 const EncryptionKey& encryption_key,
169 EncryptionConfig* encryption_config) {
170 // If generating dummy keys for key rotation, don't generate PSSH info.
171 if (encryption_key.key_ids.empty())
172 return Status::OK;
173
174 std::vector<std::unique_ptr<PsshGenerator>> pssh_generators;
175 std::vector<std::vector<uint8_t>> no_pssh_systems;
176 FillPsshGenerators(encryption_params, &pssh_generators, &no_pssh_systems);
177
178 encryption_config->key_system_info = encryption_key.key_system_info;
179 for (const auto& pssh_generator : pssh_generators) {
180 const bool support_multiple_keys = pssh_generator->SupportMultipleKeys();
181 if (support_multiple_keys) {
182 ProtectionSystemSpecificInfo info;
183 RETURN_IF_ERROR(pssh_generator->GeneratePsshFromKeyIds(
184 encryption_key.key_ids, &info));
185 AddProtectionSystemIfNotExist(info, encryption_config);
186 } else {
187 ProtectionSystemSpecificInfo info;
188 RETURN_IF_ERROR(pssh_generator->GeneratePsshFromKeyIdAndKey(
189 encryption_key.key_id, encryption_key.key, &info));
190 AddProtectionSystemIfNotExist(info, encryption_config);
191 }
192 }
193
194 for (const auto& no_pssh_system : no_pssh_systems) {
195 ProtectionSystemSpecificInfo info;
196 info.system_id = no_pssh_system;
197 AddProtectionSystemIfNotExist(info, encryption_config);
198 }
199
200 if (encryption_config->key_system_info.empty()) {
201 LOG(WARNING) << "The stream is encrypted but carries no DRM signaling "
202 "(PSSH); players may not be able to acquire the keys. "
203 "Add DRM signaling to the key source (e.g. a DRMSystem "
204 "element in the CPIX document), or use "
205 "--protection_systems to generate it.";
206 }
207
208 return Status::OK;
209}
210
211} // namespace
212
213EncryptionHandler::EncryptionHandler(const EncryptionParams& encryption_params,
214 KeySource* key_source)
215 : encryption_params_(encryption_params),
216 protection_scheme_(
217 static_cast<FourCC>(encryption_params.protection_scheme)),
218 key_source_(key_source),
219 subsample_generator_(
220 new SubsampleGenerator(encryption_params.vp9_subsample_encryption,
221 encryption_params.cencv1)),
222 encryptor_factory_(new AesEncryptorFactory) {}
223
224EncryptionHandler::~EncryptionHandler() = default;
225
226Status EncryptionHandler::InitializeInternal() {
227 if (!encryption_params_.stream_label_func) {
228 return Status(error::INVALID_ARGUMENT, "Stream label function not set.");
229 }
230 if (num_input_streams() != 1 || next_output_stream_index() != 1) {
231 return Status(error::INVALID_ARGUMENT,
232 "Expects exactly one input and output.");
233 }
234 return Status::OK;
235}
236
237Status EncryptionHandler::Process(std::unique_ptr<StreamData> stream_data) {
238 switch (stream_data->stream_data_type) {
239 case StreamDataType::kStreamInfo:
240 return ProcessStreamInfo(*stream_data->stream_info);
241 case StreamDataType::kSegmentInfo: {
242 std::shared_ptr<SegmentInfo> segment_info(
243 new SegmentInfo(*stream_data->segment_info));
244
245 segment_info->is_encrypted = remaining_clear_lead_ <= 0;
246
247 const bool key_rotation_enabled = crypto_period_duration_ != 0;
248 if (key_rotation_enabled)
249 segment_info->key_rotation_encryption_config = encryption_config_;
250 if (!segment_info->is_subsegment) {
251 if (key_rotation_enabled)
252 check_new_crypto_period_ = true;
253 if (remaining_clear_lead_ > 0)
254 remaining_clear_lead_ -= segment_info->duration;
255 }
256
257 return DispatchSegmentInfo(kStreamIndex, segment_info);
258 }
259 case StreamDataType::kMediaSample:
260 return ProcessMediaSample(std::move(stream_data->media_sample));
261 default:
262 VLOG(3) << "Stream data type "
263 << static_cast<int>(stream_data->stream_data_type) << " ignored.";
264 return Dispatch(std::move(stream_data));
265 }
266}
267
268Status EncryptionHandler::ProcessStreamInfo(const StreamInfo& clear_info) {
269 if (clear_info.is_encrypted()) {
270 return Status(error::INVALID_ARGUMENT,
271 "Input stream is already encrypted.");
272 }
273
274 DCHECK_NE(kStreamUnknown, clear_info.stream_type());
275 DCHECK_NE(kStreamText, clear_info.stream_type());
276 std::shared_ptr<StreamInfo> stream_info = clear_info.Clone();
277 RETURN_IF_ERROR(
278 subsample_generator_->Initialize(protection_scheme_, *stream_info));
279
280 remaining_clear_lead_ =
281 encryption_params_.clear_lead_in_seconds * stream_info->time_scale();
282 crypto_period_duration_ =
283 encryption_params_.crypto_period_duration_in_seconds *
284 stream_info->time_scale();
285 codec_ = stream_info->codec();
286 stream_label_ = GetStreamLabelForEncryption(
287 *stream_info, encryption_params_.stream_label_func);
288
289 SetupProtectionPattern(stream_info->stream_type(), stream_info->codec());
290
291 EncryptionKey encryption_key;
292 const bool key_rotation_enabled = crypto_period_duration_ != 0;
293 if (key_rotation_enabled) {
294 check_new_crypto_period_ = true;
295 // Setup dummy key id, key and iv to signal encryption for key rotation.
296 encryption_key.key_id.assign(std::begin(kKeyRotationDefaultKeyId),
297 std::end(kKeyRotationDefaultKeyId));
298 encryption_key.key.assign(std::begin(kKeyRotationDefaultKey),
299 std::end(kKeyRotationDefaultKey));
300 encryption_key.iv.assign(std::begin(kKeyRotationDefaultIv),
301 std::end(kKeyRotationDefaultIv));
302 } else {
303 RETURN_IF_ERROR(key_source_->GetKey(stream_label_, &encryption_key));
304 if (!SchemeBindingAllows(encryption_key.common_encryption_scheme,
305 protection_scheme_)) {
306 return Status(error::INVALID_ARGUMENT,
307 "The key for stream label '" + stream_label_ +
308 "' is restricted to common encryption scheme '" +
309 encryption_key.common_encryption_scheme +
310 "', but the stream uses protection scheme '" +
311 FourCCToString(protection_scheme_) +
312 "'. Use a key without the restriction or a matching "
313 "--protection_scheme.");
314 }
315 }
316 if (!CreateEncryptor(encryption_key))
317 return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor");
318
319 stream_info->set_is_encrypted(true);
320 stream_info->set_has_clear_lead(encryption_params_.clear_lead_in_seconds > 0);
321 stream_info->set_encryption_config(*encryption_config_);
322
323 return DispatchStreamInfo(kStreamIndex, stream_info);
324}
325
326Status EncryptionHandler::ProcessMediaSample(
327 std::shared_ptr<const MediaSample> clear_sample) {
328 DCHECK(clear_sample);
329
330 // AES-128 encrypts full TS segments in TsWriter, not individual samples.
331 // Pass samples through as clear so TsWriter can build the unencrypted
332 // transport stream buffer which is then encrypted as a whole unit.
333 if (protection_scheme_ == kAes128ProtectionScheme) {
334 return DispatchMediaSample(kStreamIndex, std::move(clear_sample));
335 }
336
337 // Process the frame even if the frame is not encrypted as the next
338 // (encrypted) frame may be dependent on this clear frame.
339 std::vector<SubsampleEntry> subsamples;
340 RETURN_IF_ERROR(subsample_generator_->GenerateSubsamples(
341 clear_sample->data(), clear_sample->data_size(), &subsamples));
342
343 // Need to setup the encryptor for new segments even if this segment does not
344 // need to be encrypted, so we can signal encryption metadata earlier to
345 // allows clients to prefetch the keys.
346 if (check_new_crypto_period_) {
347 // |dts| can be negative, e.g. after EditList adjustments. Normalized to 0
348 // in that case.
349 const int64_t dts = std::max(clear_sample->dts(), static_cast<int64_t>(0));
350 const int64_t current_crypto_period_index = dts / crypto_period_duration_;
351 const int32_t crypto_period_duration_in_seconds = static_cast<int32_t>(
352 encryption_params_.crypto_period_duration_in_seconds);
353 if (current_crypto_period_index != prev_crypto_period_index_) {
354 EncryptionKey encryption_key;
355 RETURN_IF_ERROR(key_source_->GetCryptoPeriodKey(
356 current_crypto_period_index, crypto_period_duration_in_seconds,
357 stream_label_, &encryption_key));
358 if (!CreateEncryptor(encryption_key))
359 return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor");
360 prev_crypto_period_index_ = current_crypto_period_index;
361 }
362 check_new_crypto_period_ = false;
363 }
364
365 // Since there is no encryption needed right now, send the clear copy
366 // downstream so we can save the costs of copying it.
367 if (remaining_clear_lead_ > 0) {
368 return DispatchMediaSample(kStreamIndex, std::move(clear_sample));
369 }
370
371 size_t ciphertext_size =
372 encryptor_->RequiredOutputSize(clear_sample->data_size());
373
374 std::shared_ptr<uint8_t> cipher_sample_data(new uint8_t[ciphertext_size],
375 std::default_delete<uint8_t[]>());
376
377 const uint8_t* source = clear_sample->data();
378 uint8_t* dest = cipher_sample_data.get();
379 if (!subsamples.empty()) {
380 size_t total_size = 0;
381 for (const SubsampleEntry& subsample : subsamples) {
382 if (subsample.clear_bytes > 0) {
383 // clear_bytes is the number of bytes to leave in the clear
384 memcpy(dest, source, subsample.clear_bytes);
385 source += subsample.clear_bytes;
386 dest += subsample.clear_bytes;
387 total_size += subsample.clear_bytes;
388 }
389 if (subsample.cipher_bytes > 0) {
390 // cipher_bytes is the number of bytes we want to encrypt
391 EncryptBytes(source, subsample.cipher_bytes, dest, ciphertext_size);
392 source += subsample.cipher_bytes;
393 dest += subsample.cipher_bytes;
394 total_size += subsample.cipher_bytes;
395 }
396 }
397 DCHECK_EQ(total_size, clear_sample->data_size());
398 } else {
399 EncryptBytes(source, clear_sample->data_size(), dest, ciphertext_size);
400 }
401
402 std::shared_ptr<MediaSample> cipher_sample(clear_sample->Clone());
403 cipher_sample->TransferData(std::move(cipher_sample_data),
404 clear_sample->data_size());
405
406 // Finish initializing the sample before sending it downstream. We must
407 // wait until now to finish the initialization as we will lose access to
408 // |decrypt_config| once we set it.
409 cipher_sample->set_is_encrypted(true);
410 std::unique_ptr<DecryptConfig> decrypt_config(new DecryptConfig(
411 encryption_config_->key_id, encryptor_->iv(), subsamples,
412 protection_scheme_, crypt_byte_block_, skip_byte_block_));
413 cipher_sample->set_decrypt_config(std::move(decrypt_config));
414
415 encryptor_->UpdateIv();
416
417 return DispatchMediaSample(kStreamIndex, std::move(cipher_sample));
418}
419
420void EncryptionHandler::SetupProtectionPattern(StreamType stream_type,
421 Codec codec) {
422 if ((stream_type == kStreamVideo || codec == kCodecAC4) &&
423 IsPatternEncryptionScheme(protection_scheme_)) {
424 crypt_byte_block_ = encryption_params_.crypt_byte_block;
425 skip_byte_block_ = encryption_params_.skip_byte_block;
426 } else {
427 // Audio stream in pattern encryption scheme does not use pattern; it uses
428 // whole-block full sample encryption instead. Non-pattern encryption does
429 // not have pattern.
430 crypt_byte_block_ = 0u;
431 skip_byte_block_ = 0u;
432 }
433}
434
435bool EncryptionHandler::CreateEncryptor(const EncryptionKey& encryption_key) {
436 std::unique_ptr<AesCryptor> encryptor = encryptor_factory_->CreateEncryptor(
437 protection_scheme_, crypt_byte_block_, skip_byte_block_, codec_,
438 encryption_key.key, encryption_key.iv);
439 if (!encryptor)
440 return false;
441 encryptor_ = std::move(encryptor);
442
443 encryption_config_.reset(new EncryptionConfig);
444 encryption_config_->protection_scheme = protection_scheme_;
445 encryption_config_->crypt_byte_block = crypt_byte_block_;
446 encryption_config_->skip_byte_block = skip_byte_block_;
447
448 const std::vector<uint8_t>& iv = encryptor_->iv();
449 if (encryptor_->use_constant_iv()) {
450 encryption_config_->per_sample_iv_size = 0;
451 encryption_config_->constant_iv = iv;
452 } else {
453 encryption_config_->per_sample_iv_size = static_cast<uint8_t>(iv.size());
454 }
455
456 encryption_config_->key_id = encryption_key.key_id;
457
458 // For HLS AES-128, store the raw key so TsWriter can encrypt full segments.
459 if (protection_scheme_ == kAes128ProtectionScheme) {
460 encryption_config_->key = encryption_key.key;
461 }
462
463 const auto status = FillProtectionSystemInfo(
464 encryption_params_, encryption_key, encryption_config_.get());
465 return status.ok();
466}
467
468void EncryptionHandler::EncryptBytes(const uint8_t* source,
469 size_t source_size,
470 uint8_t* dest,
471 size_t dest_size) {
472 DCHECK(source);
473 DCHECK(dest);
474 DCHECK(encryptor_);
475 CHECK(encryptor_->Crypt(source, source_size, dest, &dest_size));
476}
477
478void EncryptionHandler::InjectSubsampleGeneratorForTesting(
479 std::unique_ptr<SubsampleGenerator> generator) {
480 subsample_generator_ = std::move(generator);
481}
482
483void EncryptionHandler::InjectEncryptorFactoryForTesting(
484 std::unique_ptr<AesEncryptorFactory> encryptor_factory) {
485 encryptor_factory_ = std::move(encryptor_factory);
486}
487
488} // namespace media
489} // namespace shaka
Abstract class holds stream information.
Definition stream_info.h:73
virtual std::unique_ptr< StreamInfo > Clone() const =0
All the methods that are virtual are virtual for mocking.