Shaka Packager SDK
encryption_handler.cc
1 // Copyright 2017 Google Inc. 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 <stddef.h>
10 #include <stdint.h>
11 
12 #include <algorithm>
13 
14 #include "packager/media/base/aes_encryptor.h"
15 #include "packager/media/base/audio_stream_info.h"
16 #include "packager/media/base/key_source.h"
17 #include "packager/media/base/macros.h"
18 #include "packager/media/base/media_sample.h"
19 #include "packager/media/base/video_stream_info.h"
20 #include "packager/media/crypto/aes_encryptor_factory.h"
21 #include "packager/media/crypto/subsample_generator.h"
22 #include "packager/status_macros.h"
23 
24 namespace shaka {
25 namespace media {
26 
27 namespace {
28 // The encryption handler only supports a single output.
29 const size_t kStreamIndex = 0;
30 
31 // The default KID, KEY and IV for key rotation are all 0s.
32 // They are placeholders and are not really being used to encrypt data.
33 const uint8_t kKeyRotationDefaultKeyId[] = {
34  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
35 };
36 const uint8_t kKeyRotationDefaultKey[] = {
37  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
38 };
39 const uint8_t kKeyRotationDefaultIv[] = {
40  0, 0, 0, 0, 0, 0, 0, 0,
41 };
42 
43 std::string GetStreamLabelForEncryption(
44  const StreamInfo& stream_info,
45  const std::function<std::string(
46  const EncryptionParams::EncryptedStreamAttributes& stream_attributes)>&
47  stream_label_func) {
48  EncryptionParams::EncryptedStreamAttributes stream_attributes;
49  if (stream_info.stream_type() == kStreamAudio) {
50  stream_attributes.stream_type =
51  EncryptionParams::EncryptedStreamAttributes::kAudio;
52  } else if (stream_info.stream_type() == kStreamVideo) {
53  const VideoStreamInfo& video_stream_info =
54  static_cast<const VideoStreamInfo&>(stream_info);
55  stream_attributes.stream_type =
56  EncryptionParams::EncryptedStreamAttributes::kVideo;
57  stream_attributes.oneof.video.width = video_stream_info.width();
58  stream_attributes.oneof.video.height = video_stream_info.height();
59  }
60  return stream_label_func(stream_attributes);
61 }
62 
63 bool IsPatternEncryptionScheme(FourCC protection_scheme) {
64  return protection_scheme == kAppleSampleAesProtectionScheme ||
65  protection_scheme == FOURCC_cbcs || protection_scheme == FOURCC_cens;
66 }
67 
68 } // namespace
69 
70 EncryptionHandler::EncryptionHandler(const EncryptionParams& encryption_params,
71  KeySource* key_source)
72  : encryption_params_(encryption_params),
73  protection_scheme_(
74  static_cast<FourCC>(encryption_params.protection_scheme)),
75  key_source_(key_source),
76  subsample_generator_(
77  new SubsampleGenerator(encryption_params.vp9_subsample_encryption)),
78  encryptor_factory_(new AesEncryptorFactory) {}
79 
80 EncryptionHandler::~EncryptionHandler() = default;
81 
83  if (!encryption_params_.stream_label_func) {
84  return Status(error::INVALID_ARGUMENT, "Stream label function not set.");
85  }
86  if (num_input_streams() != 1 || next_output_stream_index() != 1) {
87  return Status(error::INVALID_ARGUMENT,
88  "Expects exactly one input and output.");
89  }
90  return Status::OK;
91 }
92 
93 Status EncryptionHandler::Process(std::unique_ptr<StreamData> stream_data) {
94  switch (stream_data->stream_data_type) {
95  case StreamDataType::kStreamInfo:
96  return ProcessStreamInfo(*stream_data->stream_info);
97  case StreamDataType::kSegmentInfo: {
98  std::shared_ptr<SegmentInfo> segment_info(new SegmentInfo(
99  *stream_data->segment_info));
100 
101  segment_info->is_encrypted = remaining_clear_lead_ <= 0;
102 
103  const bool key_rotation_enabled = crypto_period_duration_ != 0;
104  if (key_rotation_enabled)
105  segment_info->key_rotation_encryption_config = encryption_config_;
106  if (!segment_info->is_subsegment) {
107  if (key_rotation_enabled)
108  check_new_crypto_period_ = true;
109  if (remaining_clear_lead_ > 0)
110  remaining_clear_lead_ -= segment_info->duration;
111  }
112 
113  return DispatchSegmentInfo(kStreamIndex, segment_info);
114  }
115  case StreamDataType::kMediaSample:
116  return ProcessMediaSample(std::move(stream_data->media_sample));
117  default:
118  VLOG(3) << "Stream data type "
119  << static_cast<int>(stream_data->stream_data_type) << " ignored.";
120  return Dispatch(std::move(stream_data));
121  }
122 }
123 
124 Status EncryptionHandler::ProcessStreamInfo(const StreamInfo& clear_info) {
125  if (clear_info.is_encrypted()) {
126  return Status(error::INVALID_ARGUMENT,
127  "Input stream is already encrypted.");
128  }
129 
130  DCHECK_NE(kStreamUnknown, clear_info.stream_type());
131  DCHECK_NE(kStreamText, clear_info.stream_type());
132  std::shared_ptr<StreamInfo> stream_info = clear_info.Clone();
133  RETURN_IF_ERROR(
134  subsample_generator_->Initialize(protection_scheme_, *stream_info));
135 
136  remaining_clear_lead_ =
137  encryption_params_.clear_lead_in_seconds * stream_info->time_scale();
138  crypto_period_duration_ =
139  encryption_params_.crypto_period_duration_in_seconds *
140  stream_info->time_scale();
141  codec_ = stream_info->codec();
142  stream_label_ = GetStreamLabelForEncryption(
143  *stream_info, encryption_params_.stream_label_func);
144 
145  SetupProtectionPattern(stream_info->stream_type());
146 
147  EncryptionKey encryption_key;
148  const bool key_rotation_enabled = crypto_period_duration_ != 0;
149  if (key_rotation_enabled) {
150  check_new_crypto_period_ = true;
151  // Setup dummy key id, key and iv to signal encryption for key rotation.
152  encryption_key.key_id.assign(std::begin(kKeyRotationDefaultKeyId),
153  std::end(kKeyRotationDefaultKeyId));
154  encryption_key.key.assign(std::begin(kKeyRotationDefaultKey),
155  std::end(kKeyRotationDefaultKey));
156  encryption_key.iv.assign(std::begin(kKeyRotationDefaultIv),
157  std::end(kKeyRotationDefaultIv));
158  } else {
159  RETURN_IF_ERROR(key_source_->GetKey(stream_label_, &encryption_key));
160  }
161  if (!CreateEncryptor(encryption_key))
162  return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor");
163 
164  stream_info->set_is_encrypted(true);
165  stream_info->set_has_clear_lead(encryption_params_.clear_lead_in_seconds > 0);
166  stream_info->set_encryption_config(*encryption_config_);
167 
168  return DispatchStreamInfo(kStreamIndex, stream_info);
169 }
170 
171 Status EncryptionHandler::ProcessMediaSample(
172  std::shared_ptr<const MediaSample> clear_sample) {
173  DCHECK(clear_sample);
174 
175  // Process the frame even if the frame is not encrypted as the next
176  // (encrypted) frame may be dependent on this clear frame.
177  std::vector<SubsampleEntry> subsamples;
178  RETURN_IF_ERROR(subsample_generator_->GenerateSubsamples(
179  clear_sample->data(), clear_sample->data_size(), &subsamples));
180 
181  // Need to setup the encryptor for new segments even if this segment does not
182  // need to be encrypted, so we can signal encryption metadata earlier to
183  // allows clients to prefetch the keys.
184  if (check_new_crypto_period_) {
185  // |dts| can be negative, e.g. after EditList adjustments. Normalized to 0
186  // in that case.
187  const int64_t dts = std::max(clear_sample->dts(), static_cast<int64_t>(0));
188  const int64_t current_crypto_period_index = dts / crypto_period_duration_;
189  if (current_crypto_period_index != prev_crypto_period_index_) {
190  EncryptionKey encryption_key;
191  RETURN_IF_ERROR(key_source_->GetCryptoPeriodKey(
192  current_crypto_period_index, stream_label_, &encryption_key));
193  if (!CreateEncryptor(encryption_key))
194  return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor");
195  prev_crypto_period_index_ = current_crypto_period_index;
196  }
197  check_new_crypto_period_ = false;
198  }
199 
200  // Since there is no encryption needed right now, send the clear copy
201  // downstream so we can save the costs of copying it.
202  if (remaining_clear_lead_ > 0) {
203  return DispatchMediaSample(kStreamIndex, std::move(clear_sample));
204  }
205 
206  std::shared_ptr<uint8_t> cipher_sample_data(
207  new uint8_t[clear_sample->data_size()], std::default_delete<uint8_t[]>());
208 
209  const uint8_t* source = clear_sample->data();
210  uint8_t* dest = cipher_sample_data.get();
211  if (!subsamples.empty()) {
212  size_t total_size = 0;
213  for (const SubsampleEntry& subsample : subsamples) {
214  if (subsample.clear_bytes > 0) {
215  memcpy(dest, source, subsample.clear_bytes);
216  source += subsample.clear_bytes;
217  dest += subsample.clear_bytes;
218  total_size += subsample.clear_bytes;
219  }
220  if (subsample.cipher_bytes > 0) {
221  EncryptBytes(source, subsample.cipher_bytes, dest);
222  source += subsample.cipher_bytes;
223  dest += subsample.cipher_bytes;
224  total_size += subsample.cipher_bytes;
225  }
226  }
227  DCHECK_EQ(total_size, clear_sample->data_size());
228  } else {
229  EncryptBytes(source, clear_sample->data_size(), dest);
230  }
231 
232  std::shared_ptr<MediaSample> cipher_sample(clear_sample->Clone());
233  cipher_sample->TransferData(std::move(cipher_sample_data),
234  clear_sample->data_size());
235 
236  // Finish initializing the sample before sending it downstream. We must
237  // wait until now to finish the initialization as we will lose access to
238  // |decrypt_config| once we set it.
239  cipher_sample->set_is_encrypted(true);
240  std::unique_ptr<DecryptConfig> decrypt_config(new DecryptConfig(
241  encryption_config_->key_id, encryptor_->iv(), subsamples,
242  protection_scheme_, crypt_byte_block_, skip_byte_block_));
243  cipher_sample->set_decrypt_config(std::move(decrypt_config));
244 
245  encryptor_->UpdateIv();
246 
247  return DispatchMediaSample(kStreamIndex, std::move(cipher_sample));
248 }
249 
250 void EncryptionHandler::SetupProtectionPattern(StreamType stream_type) {
251  if (stream_type == kStreamVideo &&
252  IsPatternEncryptionScheme(protection_scheme_)) {
253  // Use 1:9 pattern.
254  crypt_byte_block_ = 1u;
255  skip_byte_block_ = 9u;
256  } else {
257  // Audio stream in pattern encryption scheme does not use pattern; it uses
258  // whole-block full sample encryption instead. Non-pattern encryption does
259  // not have pattern.
260  crypt_byte_block_ = 0u;
261  skip_byte_block_ = 0u;
262  }
263 }
264 
265 bool EncryptionHandler::CreateEncryptor(const EncryptionKey& encryption_key) {
266  std::unique_ptr<AesCryptor> encryptor = encryptor_factory_->CreateEncryptor(
267  protection_scheme_, crypt_byte_block_, skip_byte_block_, codec_,
268  encryption_key.key, encryption_key.iv);
269  if (!encryptor)
270  return false;
271  encryptor_ = std::move(encryptor);
272 
273  encryption_config_.reset(new EncryptionConfig);
274  encryption_config_->protection_scheme = protection_scheme_;
275  encryption_config_->crypt_byte_block = crypt_byte_block_;
276  encryption_config_->skip_byte_block = skip_byte_block_;
277 
278  const std::vector<uint8_t>& iv = encryptor_->iv();
279  if (encryptor_->use_constant_iv()) {
280  encryption_config_->per_sample_iv_size = 0;
281  encryption_config_->constant_iv = iv;
282  } else {
283  encryption_config_->per_sample_iv_size = static_cast<uint8_t>(iv.size());
284  }
285 
286  encryption_config_->key_id = encryption_key.key_id;
287  encryption_config_->key_system_info = encryption_key.key_system_info;
288  return true;
289 }
290 
291 void EncryptionHandler::EncryptBytes(const uint8_t* source,
292  size_t source_size,
293  uint8_t* dest) {
294  DCHECK(source);
295  DCHECK(dest);
296  DCHECK(encryptor_);
297  CHECK(encryptor_->Crypt(source, source_size, dest));
298 }
299 
300 void EncryptionHandler::InjectSubsampleGeneratorForTesting(
301  std::unique_ptr<SubsampleGenerator> generator) {
302  subsample_generator_ = std::move(generator);
303 }
304 
305 void EncryptionHandler::InjectEncryptorFactoryForTesting(
306  std::unique_ptr<AesEncryptorFactory> encryptor_factory) {
307  encryptor_factory_ = std::move(encryptor_factory);
308 }
309 
310 } // namespace media
311 } // namespace shaka
Abstract class holds stream information.
Definition: stream_info.h:61
virtual std::unique_ptr< StreamInfo > Clone() const =0
All the methods that are virtual are virtual for mocking.
Status Process(std::unique_ptr< StreamData > stream_data) override