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 // Adds one or more subsamples to |*subsamples|. This may add more than one
30 // if one of the values overflows the integer in the subsample.
31 void AddSubsample(uint64_t clear_bytes,
32  uint64_t cipher_bytes,
33  DecryptConfig* decrypt_config) {
34  CHECK_LT(cipher_bytes, std::numeric_limits<uint32_t>::max());
35  const uint64_t kUInt16Max = std::numeric_limits<uint16_t>::max();
36  while (clear_bytes > kUInt16Max) {
37  decrypt_config->AddSubsample(kUInt16Max, 0);
38  clear_bytes -= kUInt16Max;
39  }
40 
41  if (clear_bytes > 0 || cipher_bytes > 0)
42  decrypt_config->AddSubsample(clear_bytes, cipher_bytes);
43 }
44 
45 Codec GetVideoCodec(const StreamInfo& stream_info) {
46  if (stream_info.stream_type() != kStreamVideo) return kUnknownCodec;
47  const VideoStreamInfo& video_stream_info =
48  static_cast<const VideoStreamInfo&>(stream_info);
49  return video_stream_info.codec();
50 }
51 
52 uint8_t GetNaluLengthSize(const StreamInfo& stream_info) {
53  if (stream_info.stream_type() != kStreamVideo)
54  return 0;
55 
56  const VideoStreamInfo& video_stream_info =
57  static_cast<const VideoStreamInfo&>(stream_info);
58  return video_stream_info.nalu_length_size();
59 }
60 
61 KeySource::TrackType GetTrackTypeForEncryption(const StreamInfo& stream_info,
62  uint32_t max_sd_pixels,
63  uint32_t max_hd_pixels,
64  uint32_t max_uhd1_pixels) {
65  if (stream_info.stream_type() == kStreamAudio)
66  return KeySource::TRACK_TYPE_AUDIO;
67 
68  if (stream_info.stream_type() != kStreamVideo)
69  return KeySource::TRACK_TYPE_UNKNOWN;
70 
71  DCHECK_EQ(kStreamVideo, stream_info.stream_type());
72  const VideoStreamInfo& video_stream_info =
73  static_cast<const VideoStreamInfo&>(stream_info);
74  uint32_t pixels = video_stream_info.width() * video_stream_info.height();
75  if (pixels <= max_sd_pixels) {
76  return KeySource::TRACK_TYPE_SD;
77  } else if (pixels <= max_hd_pixels) {
78  return KeySource::TRACK_TYPE_HD;
79  } else if (pixels <= max_uhd1_pixels) {
80  return KeySource::TRACK_TYPE_UHD1;
81  }
82  return KeySource::TRACK_TYPE_UHD2;
83 }
84 } // namespace
85 
86 EncryptionHandler::EncryptionHandler(
87  const EncryptionOptions& encryption_options,
88  KeySource* key_source)
89  : encryption_options_(encryption_options), key_source_(key_source) {}
90 
91 EncryptionHandler::~EncryptionHandler() {}
92 
94  if (num_input_streams() != 1 || next_output_stream_index() != 1) {
95  return Status(error::INVALID_ARGUMENT,
96  "Expects exactly one input and output.");
97  }
98  return Status::OK;
99 }
100 
101 Status EncryptionHandler::Process(std::unique_ptr<StreamData> stream_data) {
102  Status status;
103  switch (stream_data->stream_data_type) {
104  case StreamDataType::kStreamInfo:
105  status = ProcessStreamInfo(stream_data->stream_info.get());
106  break;
107  case StreamDataType::kSegmentInfo:
108  if (!stream_data->segment_info->is_subsegment) {
109  new_segment_ = true;
110  if (remaining_clear_lead_ > 0)
111  remaining_clear_lead_ -= stream_data->segment_info->duration;
112  else
113  stream_data->segment_info->is_encrypted = true;
114  }
115  break;
116  case StreamDataType::kMediaSample:
117  status = ProcessMediaSample(stream_data->media_sample.get());
118  break;
119  default:
120  VLOG(3) << "Stream data type "
121  << static_cast<int>(stream_data->stream_data_type) << " ignored.";
122  break;
123  }
124  return status.ok() ? Dispatch(std::move(stream_data)) : status;
125 }
126 
127 Status EncryptionHandler::ProcessStreamInfo(StreamInfo* stream_info) {
128  if (stream_info->is_encrypted()) {
129  return Status(error::INVALID_ARGUMENT,
130  "Input stream is already encrypted.");
131  }
132 
133  remaining_clear_lead_ =
134  encryption_options_.clear_lead_in_seconds * stream_info->time_scale();
135  crypto_period_duration_ =
136  encryption_options_.crypto_period_duration_in_seconds *
137  stream_info->time_scale();
138  nalu_length_size_ = GetNaluLengthSize(*stream_info);
139  video_codec_ = GetVideoCodec(*stream_info);
140  track_type_ = GetTrackTypeForEncryption(
141  *stream_info, encryption_options_.max_sd_pixels,
142  encryption_options_.max_hd_pixels, encryption_options_.max_uhd1_pixels);
143  switch (video_codec_) {
144  case kCodecVP8:
145  vpx_parser_.reset(new VP8Parser);
146  break;
147  case kCodecVP9:
148  vpx_parser_.reset(new VP9Parser);
149  break;
150  case kCodecH264:
151  header_parser_.reset(new H264VideoSliceHeaderParser);
152  break;
153  case kCodecHVC1:
154  FALLTHROUGH_INTENDED;
155  case kCodecHEV1:
156  header_parser_.reset(new H265VideoSliceHeaderParser);
157  break;
158  default:
159  // Expect an audio codec with nalu length size == 0.
160  if (nalu_length_size_ > 0) {
161  LOG(WARNING) << "Unknown video codec '" << video_codec_ << "'";
162  return Status(error::ENCRYPTION_FAILURE, "Unknown video codec.");
163  }
164  }
165  if (header_parser_ &&
166  !header_parser_->Initialize(stream_info->codec_config())) {
167  return Status(error::ENCRYPTION_FAILURE, "Fail to read SPS and PPS data.");
168  }
169 
170  // Set up protection pattern.
171  if (encryption_options_.protection_scheme == FOURCC_cbcs ||
172  encryption_options_.protection_scheme == FOURCC_cens) {
173  if (stream_info->stream_type() == kStreamVideo) {
174  // Use 1:9 pattern for video.
175  crypt_byte_block_ = 1u;
176  skip_byte_block_ = 9u;
177  } else {
178  // Tracks other than video are protected using whole-block full-sample
179  // encryption, which is essentially a pattern of 1:0. Note that this may
180  // not be the same as the non-pattern based encryption counterparts, e.g.
181  // in 'cens' for full sample encryption, the whole sample is encrypted up
182  // to the last 16-byte boundary, see 23001-7:2016(E) 9.7; while in 'cenc'
183  // for full sample encryption, the last partial 16-byte block is also
184  // encrypted, see 23001-7:2016(E) 9.4.2. Another difference is the use of
185  // constant iv.
186  crypt_byte_block_ = 1u;
187  skip_byte_block_ = 0u;
188  }
189  } else {
190  // Not using pattern encryption.
191  crypt_byte_block_ = 0u;
192  skip_byte_block_ = 0u;
193  }
194 
195  stream_info->set_is_encrypted(true);
196  return Status::OK;
197 }
198 
199 Status EncryptionHandler::ProcessMediaSample(MediaSample* sample) {
200  // We need to parse the frame (which also updates the vpx parser) even if the
201  // frame is not encrypted as the next (encrypted) frame may be dependent on
202  // this clear frame.
203  std::vector<VPxFrameInfo> vpx_frames;
204  if (vpx_parser_ &&
205  !vpx_parser_->Parse(sample->data(), sample->data_size(), &vpx_frames)) {
206  return Status(error::ENCRYPTION_FAILURE, "Failed to parse vpx frame.");
207  }
208  if (remaining_clear_lead_ > 0)
209  return Status::OK;
210 
211  Status status;
212  if (new_segment_) {
213  EncryptionKey encryption_key;
214  bool create_encryptor = false;
215  if (crypto_period_duration_ != 0) {
216  const int64_t current_crypto_period_index =
217  sample->dts() / crypto_period_duration_;
218  if (current_crypto_period_index != prev_crypto_period_index_) {
219  status = key_source_->GetCryptoPeriodKey(current_crypto_period_index,
220  track_type_, &encryption_key);
221  if (!status.ok())
222  return status;
223  create_encryptor = true;
224  }
225  } else if (!encryptor_) {
226  status = key_source_->GetKey(track_type_, &encryption_key);
227  if (!status.ok())
228  return status;
229  create_encryptor = true;
230  }
231  if (create_encryptor && !CreateEncryptor(&encryption_key))
232  return Status(error::ENCRYPTION_FAILURE, "Failed to create encryptor");
233  new_segment_ = false;
234  }
235 
236  std::unique_ptr<DecryptConfig> decrypt_config(new DecryptConfig(
237  key_id_, encryptor_->iv(), std::vector<SubsampleEntry>(),
238  encryption_options_.protection_scheme, crypt_byte_block_,
239  skip_byte_block_));
240  if (vpx_parser_) {
241  if (!EncryptVpxFrame(vpx_frames, sample, decrypt_config.get()))
242  return Status(error::ENCRYPTION_FAILURE, "Failed to encrypt VPx frames.");
243  DCHECK_EQ(decrypt_config->GetTotalSizeOfSubsamples(), sample->data_size());
244  } else if (nalu_length_size_ > 0) {
245  if (!EncryptNalFrame(sample, decrypt_config.get())) {
246  return Status(error::ENCRYPTION_FAILURE,
247  "Failed to encrypt video frames.");
248  }
249  DCHECK_EQ(decrypt_config->GetTotalSizeOfSubsamples(), sample->data_size());
250  } else {
251  DCHECK_LE(crypt_byte_block_, 1u);
252  DCHECK_EQ(skip_byte_block_, 0u);
253  EncryptBytes(sample->writable_data(), sample->data_size());
254  }
255  sample->set_decrypt_config(std::move(decrypt_config));
256  encryptor_->UpdateIv();
257  return Status::OK;
258 }
259 
260 bool EncryptionHandler::CreateEncryptor(EncryptionKey* encryption_key) {
261  std::unique_ptr<AesCryptor> encryptor;
262  switch (encryption_options_.protection_scheme) {
263  case FOURCC_cenc:
264  encryptor.reset(new AesCtrEncryptor);
265  break;
266  case FOURCC_cbc1:
267  encryptor.reset(new AesCbcEncryptor(kNoPadding));
268  break;
269  case FOURCC_cens:
270  encryptor.reset(new AesPatternCryptor(
271  crypt_byte_block_, skip_byte_block_,
273  AesCryptor::kDontUseConstantIv,
274  std::unique_ptr<AesCryptor>(new AesCtrEncryptor())));
275  break;
276  case FOURCC_cbcs:
277  encryptor.reset(new AesPatternCryptor(
278  crypt_byte_block_, skip_byte_block_,
280  AesCryptor::kUseConstantIv,
281  std::unique_ptr<AesCryptor>(new AesCbcEncryptor(kNoPadding))));
282  break;
283  default:
284  LOG(ERROR) << "Unsupported protection scheme.";
285  return false;
286  }
287 
288  if (encryption_key->iv.empty()) {
289  if (!AesCryptor::GenerateRandomIv(encryption_options_.protection_scheme,
290  &encryption_key->iv)) {
291  LOG(ERROR) << "Failed to generate random iv.";
292  return false;
293  }
294  }
295  const bool initialized =
296  encryptor->InitializeWithIv(encryption_key->key, encryption_key->iv);
297  encryptor_ = std::move(encryptor);
298  key_id_ = encryption_key->key_id;
299  return initialized;
300 }
301 
302 bool EncryptionHandler::EncryptVpxFrame(const std::vector<VPxFrameInfo>& vpx_frames,
303  MediaSample* sample,
304  DecryptConfig* decrypt_config) {
305  uint8_t* data = sample->writable_data();
306  const bool is_superframe = vpx_frames.size() > 1;
307  for (const VPxFrameInfo& frame : vpx_frames) {
308  uint16_t clear_bytes =
309  static_cast<uint16_t>(frame.uncompressed_header_size);
310  uint32_t cipher_bytes = static_cast<uint32_t>(
311  frame.frame_size - frame.uncompressed_header_size);
312 
313  // "VP Codec ISO Media File Format Binding" document requires that the
314  // encrypted bytes of each frame within the superframe must be block
315  // aligned so that the counter state can be computed for each frame
316  // within the superframe.
317  // ISO/IEC 23001-7:2016 10.2 'cbc1' 10.3 'cens'
318  // The BytesOfProtectedData size SHALL be a multiple of 16 bytes to
319  // avoid partial blocks in Subsamples.
320  if (is_superframe || encryption_options_.protection_scheme == FOURCC_cbc1 ||
321  encryption_options_.protection_scheme == FOURCC_cens) {
322  const uint16_t misalign_bytes = cipher_bytes % kCencBlockSize;
323  clear_bytes += misalign_bytes;
324  cipher_bytes -= misalign_bytes;
325  }
326 
327  decrypt_config->AddSubsample(clear_bytes, cipher_bytes);
328  if (cipher_bytes > 0)
329  EncryptBytes(data + clear_bytes, cipher_bytes);
330  data += frame.frame_size;
331  }
332  // Add subsample for the superframe index if exists.
333  if (is_superframe) {
334  size_t index_size = sample->data() + sample->data_size() - data;
335  DCHECK_LE(index_size, 2 + vpx_frames.size() * 4);
336  DCHECK_GE(index_size, 2 + vpx_frames.size() * 1);
337  uint16_t clear_bytes = static_cast<uint16_t>(index_size);
338  uint32_t cipher_bytes = 0;
339  decrypt_config->AddSubsample(clear_bytes, cipher_bytes);
340  }
341  return true;
342 }
343 
344 bool EncryptionHandler::EncryptNalFrame(MediaSample* sample,
345  DecryptConfig* decrypt_config) {
346  const Nalu::CodecType nalu_type =
347  (video_codec_ == kCodecHVC1 || video_codec_ == kCodecHEV1) ? Nalu::kH265
348  : Nalu::kH264;
349  NaluReader reader(nalu_type, nalu_length_size_, sample->writable_data(),
350  sample->data_size());
351 
352  // Store the current length of clear data. This is used to squash
353  // multiple unencrypted NAL units into fewer subsample entries.
354  uint64_t accumulated_clear_bytes = 0;
355 
356  Nalu nalu;
357  NaluReader::Result result;
358  while ((result = reader.Advance(&nalu)) == NaluReader::kOk) {
359  if (nalu.is_video_slice()) {
360  // For video-slice NAL units, encrypt the video slice. This skips
361  // the frame header. If this is an unrecognized codec, the whole NAL unit
362  // will be encrypted.
363  const int64_t video_slice_header_size =
364  header_parser_ ? header_parser_->GetHeaderSize(nalu) : 0;
365  if (video_slice_header_size < 0) {
366  LOG(ERROR) << "Failed to read slice header.";
367  return false;
368  }
369 
370  uint64_t current_clear_bytes =
371  nalu.header_size() + video_slice_header_size;
372  uint64_t cipher_bytes = nalu.payload_size() - video_slice_header_size;
373 
374  // ISO/IEC 23001-7:2016 10.2 'cbc1' 10.3 'cens'
375  // The BytesOfProtectedData size SHALL be a multiple of 16 bytes to
376  // avoid partial blocks in Subsamples.
377  if (encryption_options_.protection_scheme == FOURCC_cbc1 ||
378  encryption_options_.protection_scheme == FOURCC_cens) {
379  const uint16_t misalign_bytes = cipher_bytes % kCencBlockSize;
380  current_clear_bytes += misalign_bytes;
381  cipher_bytes -= misalign_bytes;
382  }
383 
384  const uint8_t* nalu_data = nalu.data() + current_clear_bytes;
385  EncryptBytes(const_cast<uint8_t*>(nalu_data), cipher_bytes);
386 
387  AddSubsample(
388  accumulated_clear_bytes + nalu_length_size_ + current_clear_bytes,
389  cipher_bytes, decrypt_config);
390  accumulated_clear_bytes = 0;
391  } else {
392  // For non-video-slice NAL units, don't encrypt.
393  accumulated_clear_bytes +=
394  nalu_length_size_ + nalu.header_size() + nalu.payload_size();
395  }
396  }
397  if (result != NaluReader::kEOStream) {
398  LOG(ERROR) << "Failed to parse NAL units.";
399  return false;
400  }
401  AddSubsample(accumulated_clear_bytes, 0, decrypt_config);
402  return true;
403 }
404 
405 void EncryptionHandler::EncryptBytes(uint8_t* data, size_t size) {
406  DCHECK(encryptor_);
407  CHECK(encryptor_->Crypt(data, size, data));
408 }
409 
410 void EncryptionHandler::InjectVpxParserForTesting(
411  std::unique_ptr<VPxParser> vpx_parser) {
412  vpx_parser_ = std::move(vpx_parser);
413 }
414 
415 void EncryptionHandler::InjectVideoSliceHeaderParserForTesting(
416  std::unique_ptr<VideoSliceHeaderParser> header_parser) {
417  header_parser_ = std::move(header_parser);
418 }
419 
420 } // namespace media
421 } // namespace shaka
Abstract class holds stream information.
Definition: stream_info.h:57
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'.