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