Shaka Packager SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends
encryptor.cc
1 // Copyright 2015 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/formats/webm/encryptor.h"
8 
9 #include "packager/media/base/buffer_writer.h"
10 #include "packager/media/base/media_sample.h"
11 #include "packager/media/formats/webm/webm_constants.h"
12 
13 namespace shaka {
14 namespace media {
15 namespace webm {
16 
17 Status UpdateTrackForEncryption(const std::vector<uint8_t>& key_id,
18  mkvmuxer::Track* track) {
19  DCHECK_EQ(track->content_encoding_entries_size(), 0u);
20 
21  if (!track->AddContentEncoding()) {
22  return Status(error::INTERNAL_ERROR,
23  "Could not add ContentEncoding to track.");
24  }
25 
26  mkvmuxer::ContentEncoding* const encoding =
27  track->GetContentEncodingByIndex(0);
28  if (!encoding) {
29  return Status(error::INTERNAL_ERROR,
30  "Could not add ContentEncoding to track.");
31  }
32 
33  mkvmuxer::ContentEncAESSettings* const aes = encoding->enc_aes_settings();
34  if (!aes) {
35  return Status(error::INTERNAL_ERROR,
36  "Error getting ContentEncAESSettings.");
37  }
38  if (aes->cipher_mode() != mkvmuxer::ContentEncAESSettings::kCTR) {
39  return Status(error::INTERNAL_ERROR, "Cipher Mode is not CTR.");
40  }
41 
42  if (!encoding->SetEncryptionID(key_id.data(), key_id.size())) {
43  return Status(error::INTERNAL_ERROR, "Error setting encryption ID.");
44  }
45  return Status::OK;
46 }
47 
48 void UpdateFrameForEncryption(MediaSample* sample) {
49  const size_t sample_size = sample->data_size();
50  if (sample->decrypt_config()) {
51  auto* decrypt_config = sample->decrypt_config();
52  const size_t iv_size = decrypt_config->iv().size();
53  DCHECK_EQ(iv_size, kWebMIvSize);
54  if (!decrypt_config->subsamples().empty()) {
55  auto& subsamples = decrypt_config->subsamples();
56  // Use partitioned subsample encryption: | signal_byte(3) | iv
57  // | num_partitions | partition_offset * n | enc_data |
58  DCHECK_LT(subsamples.size(), kWebMMaxSubsamples);
59  const size_t num_partitions =
60  2 * subsamples.size() - 1 -
61  (subsamples.back().cipher_bytes == 0 ? 1 : 0);
62  const size_t header_size = kWebMSignalByteSize + iv_size +
63  kWebMNumPartitionsSize +
64  (kWebMPartitionOffsetSize * num_partitions);
65  sample->resize_data(header_size + sample_size);
66  uint8_t* sample_data = sample->writable_data();
67  memmove(sample_data + header_size, sample_data, sample_size);
68  sample_data[0] = kWebMEncryptedSignal | kWebMPartitionedSignal;
69  memcpy(sample_data + kWebMSignalByteSize, decrypt_config->iv().data(),
70  iv_size);
71  sample_data[kWebMSignalByteSize + kWebMIvSize] =
72  static_cast<uint8_t>(num_partitions);
73 
74  BufferWriter offsets_buffer;
75  uint32_t partition_offset = 0;
76  for (size_t i = 0; i < subsamples.size() - 1; ++i) {
77  partition_offset += subsamples[i].clear_bytes;
78  offsets_buffer.AppendInt(partition_offset);
79  partition_offset += subsamples[i].cipher_bytes;
80  offsets_buffer.AppendInt(partition_offset);
81  }
82  // Add another partition between the clear bytes and cipher bytes if
83  // cipher bytes is not zero.
84  if (subsamples.back().cipher_bytes != 0) {
85  partition_offset += subsamples.back().clear_bytes;
86  offsets_buffer.AppendInt(partition_offset);
87  }
88  DCHECK_EQ(num_partitions * kWebMPartitionOffsetSize,
89  offsets_buffer.Size());
90  memcpy(sample_data + kWebMSignalByteSize + kWebMIvSize +
91  kWebMNumPartitionsSize,
92  offsets_buffer.Buffer(), offsets_buffer.Size());
93  } else {
94  // Use whole-frame encryption: | signal_byte(1) | iv | enc_data |
95  sample->resize_data(sample_size + iv_size + kWebMSignalByteSize);
96  uint8_t* sample_data = sample->writable_data();
97 
98  // First move the sample data to after the IV; then write the IV and
99  // signal byte.
100  memmove(sample_data + iv_size + kWebMSignalByteSize, sample_data,
101  sample_size);
102  sample_data[0] = kWebMEncryptedSignal;
103  memcpy(sample_data + 1, decrypt_config->iv().data(), iv_size);
104  }
105  } else {
106  // Clear sample: | signal_byte(0) | data |
107  sample->resize_data(sample_size + 1);
108  uint8_t* sample_data = sample->writable_data();
109  memmove(sample_data + 1, sample_data, sample_size);
110  sample_data[0] = 0x00;
111  }
112 }
113 
114 } // namespace webm
115 } // namespace media
116 } // namespace shaka