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