DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
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 <limits>
13 
14 #include "packager/media/base/aes_encryptor.h"
15 #include "packager/media/base/aes_pattern_cryptor.h"
16 #include "packager/media/base/key_source.h"
17 #include "packager/media/base/media_sample.h"
18 #include "packager/media/base/video_stream_info.h"
19 #include "packager/media/codecs/video_slice_header_parser.h"
20 #include "packager/media/codecs/vp8_parser.h"
21 #include "packager/media/codecs/vp9_parser.h"
22 
23 namespace shaka {
24 namespace media {
25 
26 namespace {
27 const size_t kCencBlockSize = 16u;
28 
29 // The default KID for key rotation is all 0s.
30 const uint8_t kKeyRotationDefaultKeyId[] = {
31  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
32 };
33 
34 // Adds one or more subsamples to |*subsamples|. This may add more than one
35 // if one of the values overflows the integer in the subsample.
36 void AddSubsample(uint64_t clear_bytes,
37  uint64_t cipher_bytes,
38  DecryptConfig* decrypt_config) {
39  CHECK_LT(cipher_bytes, std::numeric_limits<uint32_t>::max());
40  const uint64_t kUInt16Max = std::numeric_limits<uint16_t>::max();
41  while (clear_bytes > kUInt16Max) {
42  decrypt_config->AddSubsample(kUInt16Max, 0);
43  clear_bytes -= kUInt16Max;
44  }
45 
46  if (clear_bytes > 0 || cipher_bytes > 0)
47  decrypt_config->AddSubsample(clear_bytes, cipher_bytes);
48 }
49 
50 uint8_t GetNaluLengthSize(const StreamInfo& stream_info) {
51  if (stream_info.stream_type() != kStreamVideo)
52  return 0;
53 
54  const VideoStreamInfo& video_stream_info =
55  static_cast<const VideoStreamInfo&>(stream_info);
56  return video_stream_info.nalu_length_size();
57 }
58 
59 // TODO(kqyang): Update KeySource to accept string base stream label.
60 KeySource::TrackType ToTrackType(const std::string& track_type) {
61  if (track_type == "SD")
62  return KeySource::TRACK_TYPE_SD;
63  if (track_type == "HD")
64  return KeySource::TRACK_TYPE_HD;
65  if (track_type == "AUDIO")
66  return KeySource::TRACK_TYPE_AUDIO;
67  return KeySource::TRACK_TYPE_SD;
68 }
69 
70 KeySource::TrackType GetTrackTypeForEncryption(
71  const StreamInfo& stream_info,
72  const std::function<std::string(
73  const EncryptionParams::EncryptedStreamAttributes& stream_attributes)>&
74  stream_label_func) {
75  EncryptionParams::EncryptedStreamAttributes stream_attributes;
76  if (stream_info.stream_type() == kStreamAudio) {
77  stream_attributes.stream_type =
78  EncryptionParams::EncryptedStreamAttributes::kAudio;
79  } else if (stream_info.stream_type() == kStreamVideo) {
80  const VideoStreamInfo& video_stream_info =
81  static_cast<const VideoStreamInfo&>(stream_info);
82  stream_attributes.stream_type =
83  EncryptionParams::EncryptedStreamAttributes::kVideo;
84  stream_attributes.oneof.video.width = video_stream_info.width();
85  stream_attributes.oneof.video.height = video_stream_info.height();
86  }
87  return ToTrackType(stream_label_func(stream_attributes));
88 }
89 } // namespace
90 
91 EncryptionHandler::EncryptionHandler(
92  const EncryptionOptions& encryption_options,
93  KeySource* key_source)
94  : encryption_options_(encryption_options), key_source_(key_source) {}
95 
96 EncryptionHandler::~EncryptionHandler() {}
97 
99  if (!encryption_options_.stream_label_func) {
100  return Status(error::INVALID_ARGUMENT, "Stream label function not set.");
101  }
102  if (num_input_streams() != 1 || next_output_stream_index() != 1) {
103  return Status(error::INVALID_ARGUMENT,
104  "Expects exactly one input and output.");
105  }
106  return Status::OK;
107 }
108 
109 Status EncryptionHandler::Process(std::unique_ptr<StreamData> stream_data) {
110  Status status;
111  switch (stream_data->stream_data_type) {
112  case StreamDataType::kStreamInfo:
113  status = ProcessStreamInfo(stream_data->stream_info.get());
114  break;
115  case StreamDataType::kSegmentInfo: {
116  SegmentInfo* segment_info = stream_data->segment_info.get();
117  segment_info->is_encrypted = remaining_clear_lead_ <= 0;
118 
119  const bool key_rotation_enabled = crypto_period_duration_ != 0;
120  if (key_rotation_enabled)
121  segment_info->key_rotation_encryption_config = encryption_config_;
122  if (!segment_info->is_subsegment) {
123  if (key_rotation_enabled)
124  check_new_crypto_period_ = true;
125  if (remaining_clear_lead_ > 0)
126  remaining_clear_lead_ -= segment_info->duration;
127  }
128  break;
129  }
130  case StreamDataType::kMediaSample:
131  status = ProcessMediaSample(stream_data->media_sample.get());
132  break;
133  default:
134  VLOG(3) << "Stream data type "
135  << static_cast<int>(stream_data->stream_data_type) << " ignored.";
136  break;
137  }
138  return status.ok() ? Dispatch(std::move(stream_data)) : status;
139 }
140 
141 Status EncryptionHandler::ProcessStreamInfo(StreamInfo* stream_info) {
142  if (stream_info->is_encrypted()) {
143  return Status(error::INVALID_ARGUMENT,
144  "Input stream is already encrypted.");
145  }
146 
147  remaining_clear_lead_ =
148  encryption_options_.clear_lead_in_seconds * stream_info->time_scale();
149  crypto_period_duration_ =
150  encryption_options_.crypto_period_duration_in_seconds *
151  stream_info->time_scale();
152  codec_ = stream_info->codec();
153  nalu_length_size_ = GetNaluLengthSize(*stream_info);
154  track_type_ = GetTrackTypeForEncryption(
155  *stream_info, encryption_options_.stream_label_func);
156  switch (codec_) {
157  case kCodecVP9:
158  if (encryption_options_.vp9_subsample_encryption)
159  vpx_parser_.reset(new VP9Parser);
160  break;
161  case kCodecH264:
162  header_parser_.reset(new H264VideoSliceHeaderParser);
163  break;
164  case kCodecH265:
165  header_parser_.reset(new H265VideoSliceHeaderParser);
166  break;
167  default:
168  // Other codecs should have nalu length size == 0.
169  if (nalu_length_size_ > 0) {
170  LOG(WARNING) << "Unknown video codec '" << codec_ << "'";
171  return Status(error::ENCRYPTION_FAILURE, "Unknown video codec.");
172  }
173  }
174  if (header_parser_) {
175  CHECK_NE(nalu_length_size_, 0u) << "AnnexB stream is not supported yet";
176  if (!header_parser_->Initialize(stream_info->codec_config())) {
177  return Status(error::ENCRYPTION_FAILURE,
178  "Fail to read SPS and PPS data.");
179  }
180  }
181 
182  Status status = SetupProtectionPattern(stream_info->stream_type());
183  if (!status.ok())
184  return status;
185 
186  EncryptionKey encryption_key;
187  const bool key_rotation_enabled = crypto_period_duration_ != 0;
188  if (key_rotation_enabled) {
189  check_new_crypto_period_ = true;
190  // Setup dummy key id and key to signal encryption for key rotation.
191  encryption_key.key_id.assign(
192  kKeyRotationDefaultKeyId,
193  kKeyRotationDefaultKeyId + sizeof(kKeyRotationDefaultKeyId));
194  // The key is not really used to encrypt any data. It is there just for
195  // convenience.
196  encryption_key.key = encryption_key.key_id;
197  } else {
198  status = key_source_->GetKey(track_type_, &encryption_key);
199  if (!status.ok())
200  return status;
201  }
202  if (!CreateEncryptor(encryption_key))
203  return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor");
204 
205  stream_info->set_is_encrypted(true);
206  stream_info->set_has_clear_lead(encryption_options_.clear_lead_in_seconds >
207  0);
208  stream_info->set_encryption_config(*encryption_config_);
209  return Status::OK;
210 }
211 
212 Status EncryptionHandler::ProcessMediaSample(MediaSample* sample) {
213  // We need to parse the frame (which also updates the vpx parser) even if the
214  // frame is not encrypted as the next (encrypted) frame may be dependent on
215  // this clear frame.
216  std::vector<VPxFrameInfo> vpx_frames;
217  if (vpx_parser_ &&
218  !vpx_parser_->Parse(sample->data(), sample->data_size(), &vpx_frames)) {
219  return Status(error::ENCRYPTION_FAILURE, "Failed to parse vpx frame.");
220  }
221 
222  // Need to setup the encryptor for new segments even if this segment does not
223  // need to be encrypted, so we can signal encryption metadata earlier to
224  // allows clients to prefetch the keys.
225  if (check_new_crypto_period_) {
226  const int64_t current_crypto_period_index =
227  sample->dts() / crypto_period_duration_;
228  if (current_crypto_period_index != prev_crypto_period_index_) {
229  EncryptionKey encryption_key;
230  Status status = key_source_->GetCryptoPeriodKey(
231  current_crypto_period_index, track_type_, &encryption_key);
232  if (!status.ok())
233  return status;
234  if (!CreateEncryptor(encryption_key))
235  return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor");
236  }
237  check_new_crypto_period_ = false;
238  }
239 
240  if (remaining_clear_lead_ > 0)
241  return Status::OK;
242 
243  std::unique_ptr<DecryptConfig> decrypt_config(new DecryptConfig(
244  encryption_config_->key_id, encryptor_->iv(),
245  std::vector<SubsampleEntry>(), encryption_options_.protection_scheme,
246  crypt_byte_block_, skip_byte_block_));
247  bool result = true;
248  if (vpx_parser_) {
249  result = EncryptVpxFrame(vpx_frames, sample, decrypt_config.get());
250  if (result) {
251  DCHECK_EQ(decrypt_config->GetTotalSizeOfSubsamples(),
252  sample->data_size());
253  }
254  } else if (header_parser_) {
255  result = EncryptNalFrame(sample, decrypt_config.get());
256  if (result) {
257  DCHECK_EQ(decrypt_config->GetTotalSizeOfSubsamples(),
258  sample->data_size());
259  }
260  } else {
261  if (sample->data_size() > leading_clear_bytes_size_) {
262  EncryptBytes(sample->writable_data() + leading_clear_bytes_size_,
263  sample->data_size() - leading_clear_bytes_size_);
264  }
265  }
266  if (!result)
267  return Status(error::ENCRYPTION_FAILURE, "Failed to encrypt samples.");
268  sample->set_is_encrypted(true);
269  sample->set_decrypt_config(std::move(decrypt_config));
270  encryptor_->UpdateIv();
271  return Status::OK;
272 }
273 
274 Status EncryptionHandler::SetupProtectionPattern(StreamType stream_type) {
275  switch (encryption_options_.protection_scheme) {
276  case kAppleSampleAesProtectionScheme: {
277  const size_t kH264LeadingClearBytesSize = 32u;
278  const size_t kSmallNalUnitSize = 32u + 16u;
279  const size_t kAudioLeadingClearBytesSize = 16u;
280  switch (codec_) {
281  case kCodecH264:
282  // Apple Sample AES uses 1:9 pattern for video.
283  crypt_byte_block_ = 1u;
284  skip_byte_block_ = 9u;
285  leading_clear_bytes_size_ = kH264LeadingClearBytesSize;
286  min_protected_data_size_ = kSmallNalUnitSize + 1u;
287  break;
288  case kCodecAAC:
289  FALLTHROUGH_INTENDED;
290  case kCodecAC3:
291  // Audio is whole sample encrypted. We could not use a
292  // crypto_byte_block_ of 1 here as if there is one crypto block
293  // remaining, it need not be encrypted for video but it needs to be
294  // encrypted for audio.
295  crypt_byte_block_ = 0u;
296  skip_byte_block_ = 0u;
297  leading_clear_bytes_size_ = kAudioLeadingClearBytesSize;
298  min_protected_data_size_ = leading_clear_bytes_size_ + 1u;
299  break;
300  default:
301  return Status(error::ENCRYPTION_FAILURE,
302  "Only AAC/AC3 and H264 are supported in Sample AES.");
303  }
304  break;
305  }
306  case FOURCC_cbcs:
307  FALLTHROUGH_INTENDED;
308  case FOURCC_cens:
309  if (stream_type == kStreamVideo) {
310  // Use 1:9 pattern for video.
311  crypt_byte_block_ = 1u;
312  skip_byte_block_ = 9u;
313  } else {
314  // Tracks other than video are protected using whole-block full-sample
315  // encryption, which is essentially a pattern of 1:0. Note that this may
316  // not be the same as the non-pattern based encryption counterparts,
317  // e.g. in 'cens' for full sample encryption, the whole sample is
318  // encrypted up to the last 16-byte boundary, see 23001-7:2016(E) 9.7;
319  // while in 'cenc' for full sample encryption, the last partial 16-byte
320  // block is also encrypted, see 23001-7:2016(E) 9.4.2. Another
321  // difference is the use of constant iv.
322  crypt_byte_block_ = 1u;
323  skip_byte_block_ = 0u;
324  }
325  break;
326  default:
327  // Not using pattern encryption.
328  crypt_byte_block_ = 0u;
329  skip_byte_block_ = 0u;
330  }
331  return Status::OK;
332 }
333 
334 bool EncryptionHandler::CreateEncryptor(const EncryptionKey& encryption_key) {
335  std::unique_ptr<AesCryptor> encryptor;
336  switch (encryption_options_.protection_scheme) {
337  case FOURCC_cenc:
338  encryptor.reset(new AesCtrEncryptor);
339  break;
340  case FOURCC_cbc1:
341  encryptor.reset(new AesCbcEncryptor(kNoPadding));
342  break;
343  case FOURCC_cens:
344  encryptor.reset(new AesPatternCryptor(
345  crypt_byte_block_, skip_byte_block_,
347  AesCryptor::kDontUseConstantIv,
348  std::unique_ptr<AesCryptor>(new AesCtrEncryptor())));
349  break;
350  case FOURCC_cbcs:
351  encryptor.reset(new AesPatternCryptor(
352  crypt_byte_block_, skip_byte_block_,
354  AesCryptor::kUseConstantIv,
355  std::unique_ptr<AesCryptor>(new AesCbcEncryptor(kNoPadding))));
356  break;
357  case kAppleSampleAesProtectionScheme:
358  if (crypt_byte_block_ == 0 && skip_byte_block_ == 0) {
359  encryptor.reset(
360  new AesCbcEncryptor(kNoPadding, AesCryptor::kUseConstantIv));
361  } else {
362  encryptor.reset(new AesPatternCryptor(
363  crypt_byte_block_, skip_byte_block_,
365  AesCryptor::kUseConstantIv,
366  std::unique_ptr<AesCryptor>(new AesCbcEncryptor(kNoPadding))));
367  }
368  break;
369  default:
370  LOG(ERROR) << "Unsupported protection scheme.";
371  return false;
372  }
373 
374  std::vector<uint8_t> iv = encryption_key.iv;
375  if (iv.empty()) {
376  if (!AesCryptor::GenerateRandomIv(encryption_options_.protection_scheme,
377  &iv)) {
378  LOG(ERROR) << "Failed to generate random iv.";
379  return false;
380  }
381  }
382  const bool initialized =
383  encryptor->InitializeWithIv(encryption_key.key, iv);
384  encryptor_ = std::move(encryptor);
385 
386  encryption_config_.reset(new EncryptionConfig);
387  encryption_config_->protection_scheme = encryption_options_.protection_scheme;
388  encryption_config_->crypt_byte_block = crypt_byte_block_;
389  encryption_config_->skip_byte_block = skip_byte_block_;
390  if (encryptor_->use_constant_iv()) {
391  encryption_config_->per_sample_iv_size = 0;
392  encryption_config_->constant_iv = iv;
393  } else {
394  encryption_config_->per_sample_iv_size = static_cast<uint8_t>(iv.size());
395  }
396  encryption_config_->key_id = encryption_key.key_id;
397  encryption_config_->key_system_info = encryption_key.key_system_info;
398  return initialized;
399 }
400 
401 bool EncryptionHandler::EncryptVpxFrame(
402  const std::vector<VPxFrameInfo>& vpx_frames,
403  MediaSample* sample,
404  DecryptConfig* decrypt_config) {
405  uint8_t* data = sample->writable_data();
406  for (const VPxFrameInfo& frame : vpx_frames) {
407  uint16_t clear_bytes =
408  static_cast<uint16_t>(frame.uncompressed_header_size);
409  uint32_t cipher_bytes = static_cast<uint32_t>(
410  frame.frame_size - frame.uncompressed_header_size);
411 
412  // "VP Codec ISO Media File Format Binding" document requires that the
413  // encrypted bytes of each frame within the superframe must be block
414  // aligned so that the counter state can be computed for each frame
415  // within the superframe.
416  // ISO/IEC 23001-7:2016 10.2 'cbc1' 10.3 'cens'
417  // The BytesOfProtectedData size SHALL be a multiple of 16 bytes to
418  // avoid partial blocks in Subsamples.
419  // For consistency, apply block alignment to all frames.
420  const uint16_t misalign_bytes = cipher_bytes % kCencBlockSize;
421  clear_bytes += misalign_bytes;
422  cipher_bytes -= misalign_bytes;
423 
424  decrypt_config->AddSubsample(clear_bytes, cipher_bytes);
425  if (cipher_bytes > 0)
426  EncryptBytes(data + clear_bytes, cipher_bytes);
427  data += frame.frame_size;
428  }
429  // Add subsample for the superframe index if exists.
430  const bool is_superframe = vpx_frames.size() > 1;
431  if (is_superframe) {
432  size_t index_size = sample->data() + sample->data_size() - data;
433  DCHECK_LE(index_size, 2 + vpx_frames.size() * 4);
434  DCHECK_GE(index_size, 2 + vpx_frames.size() * 1);
435  uint16_t clear_bytes = static_cast<uint16_t>(index_size);
436  uint32_t cipher_bytes = 0;
437  decrypt_config->AddSubsample(clear_bytes, cipher_bytes);
438  }
439  return true;
440 }
441 
442 bool EncryptionHandler::EncryptNalFrame(MediaSample* sample,
443  DecryptConfig* decrypt_config) {
444  DCHECK_NE(nalu_length_size_, 0u);
445  DCHECK(header_parser_);
446  const Nalu::CodecType nalu_type =
447  (codec_ == kCodecH265) ? Nalu::kH265 : Nalu::kH264;
448  NaluReader reader(nalu_type, nalu_length_size_, sample->writable_data(),
449  sample->data_size());
450 
451  // Store the current length of clear data. This is used to squash
452  // multiple unencrypted NAL units into fewer subsample entries.
453  uint64_t accumulated_clear_bytes = 0;
454 
455  Nalu nalu;
456  NaluReader::Result result;
457  while ((result = reader.Advance(&nalu)) == NaluReader::kOk) {
458  const uint64_t nalu_total_size = nalu.header_size() + nalu.payload_size();
459  if (nalu.is_video_slice() && nalu_total_size >= min_protected_data_size_) {
460  uint64_t current_clear_bytes = leading_clear_bytes_size_;
461  if (current_clear_bytes == 0) {
462  // For video-slice NAL units, encrypt the video slice. This skips
463  // the frame header.
464  const int64_t video_slice_header_size =
465  header_parser_->GetHeaderSize(nalu);
466  if (video_slice_header_size < 0) {
467  LOG(ERROR) << "Failed to read slice header.";
468  return false;
469  }
470  current_clear_bytes = nalu.header_size() + video_slice_header_size;
471  }
472  uint64_t cipher_bytes = nalu_total_size - current_clear_bytes;
473 
474  // ISO/IEC 23001-7:2016 10.2 'cbc1' 10.3 'cens'
475  // The BytesOfProtectedData size SHALL be a multiple of 16 bytes to
476  // avoid partial blocks in Subsamples.
477  // CMAF requires 'cenc' scheme BytesOfProtectedData SHALL be a multiple
478  // of 16 bytes; while 'cbcs' scheme BytesOfProtectedData SHALL start on
479  // the first byte of video data following the slice header.
480  if (encryption_options_.protection_scheme == FOURCC_cbc1 ||
481  encryption_options_.protection_scheme == FOURCC_cens ||
482  encryption_options_.protection_scheme == FOURCC_cenc) {
483  const uint16_t misalign_bytes = cipher_bytes % kCencBlockSize;
484  current_clear_bytes += misalign_bytes;
485  cipher_bytes -= misalign_bytes;
486  }
487 
488  const uint8_t* nalu_data = nalu.data() + current_clear_bytes;
489  EncryptBytes(const_cast<uint8_t*>(nalu_data), cipher_bytes);
490 
491  AddSubsample(
492  accumulated_clear_bytes + nalu_length_size_ + current_clear_bytes,
493  cipher_bytes, decrypt_config);
494  accumulated_clear_bytes = 0;
495  } else {
496  // For non-video-slice or small NAL units, don't encrypt.
497  accumulated_clear_bytes += nalu_length_size_ + nalu_total_size;
498  }
499  }
500  if (result != NaluReader::kEOStream) {
501  LOG(ERROR) << "Failed to parse NAL units.";
502  return false;
503  }
504  AddSubsample(accumulated_clear_bytes, 0, decrypt_config);
505  return true;
506 }
507 
508 void EncryptionHandler::EncryptBytes(uint8_t* data, size_t size) {
509  DCHECK(encryptor_);
510  CHECK(encryptor_->Crypt(data, size, data));
511 }
512 
513 void EncryptionHandler::InjectVpxParserForTesting(
514  std::unique_ptr<VPxParser> vpx_parser) {
515  vpx_parser_ = std::move(vpx_parser);
516 }
517 
518 void EncryptionHandler::InjectVideoSliceHeaderParserForTesting(
519  std::unique_ptr<VideoSliceHeaderParser> header_parser) {
520  header_parser_ = std::move(header_parser);
521 }
522 
523 } // namespace media
524 } // namespace shaka
std::function< std::string(const EncryptionParams::EncryptedStreamAttributes &stream_attributes)> stream_label_func
Abstract class holds stream information.
Definition: stream_info.h:57
bool vp9_subsample_encryption
Enable/disable subsample encryption for VP9.
Status Dispatch(std::unique_ptr< StreamData > stream_data)
virtual Status GetCryptoPeriodKey(uint32_t crypto_period_index, TrackType track_type, EncryptionKey *key)=0
virtual Status GetKey(TrackType track_type, EncryptionKey *key)=0
Status Process(std::unique_ptr< StreamData > stream_data) override
static bool GenerateRandomIv(FourCC protection_scheme, std::vector< uint8_t > *iv)
Definition: aes_cryptor.cc:107
double clear_lead_in_seconds
Clear lead duration in seconds.
FourCC protection_scheme
The protection scheme: 'cenc', 'cens', 'cbc1', 'cbcs'.