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