DASH Media Packaging SDK
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
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 <gflags/gflags.h>
10 #include "packager/media/base/aes_encryptor.h"
11 #include "packager/media/base/buffer_writer.h"
12 #include "packager/media/base/fourccs.h"
13 #include "packager/media/base/media_sample.h"
14 #include "packager/media/codecs/vp9_parser.h"
15 #include "packager/media/formats/webm/webm_constants.h"
16 
17 namespace shaka {
18 namespace media {
19 namespace webm {
20 namespace {
21 
22 const size_t kAesBlockSize = 16;
23 
24 Status CreateContentEncryption(mkvmuxer::Track* track, EncryptionKey* key) {
25  if (!track->AddContentEncoding()) {
26  return Status(error::INTERNAL_ERROR,
27  "Could not add ContentEncoding to track.");
28  }
29 
30  mkvmuxer::ContentEncoding* const encoding =
31  track->GetContentEncodingByIndex(0);
32  if (!encoding) {
33  return Status(error::INTERNAL_ERROR,
34  "Could not add ContentEncoding to track.");
35  }
36 
37  mkvmuxer::ContentEncAESSettings* const aes = encoding->enc_aes_settings();
38  if (!aes) {
39  return Status(error::INTERNAL_ERROR,
40  "Error getting ContentEncAESSettings.");
41  }
42  if (aes->cipher_mode() != mkvmuxer::ContentEncAESSettings::kCTR) {
43  return Status(error::INTERNAL_ERROR, "Cipher Mode is not CTR.");
44  }
45 
46  if (!key->key_id.empty() &&
47  !encoding->SetEncryptionID(
48  reinterpret_cast<const uint8_t*>(key->key_id.data()),
49  key->key_id.size())) {
50  return Status(error::INTERNAL_ERROR, "Error setting encryption ID.");
51  }
52  return Status::OK;
53 }
54 
55 } // namespace
56 
57 Encryptor::Encryptor() {}
58 
59 Encryptor::~Encryptor() {}
60 
62  KeySource::TrackType track_type,
63  Codec codec,
64  KeySource* key_source,
65  bool webm_subsample_encryption) {
66  DCHECK(key_source);
67  return CreateEncryptor(muxer_listener, track_type, codec, key_source,
68  webm_subsample_encryption);
69 }
70 
71 Status Encryptor::AddTrackInfo(mkvmuxer::Track* track) {
72  DCHECK(key_);
73  return CreateContentEncryption(track, key_.get());
74 }
75 
76 Status Encryptor::EncryptFrame(std::shared_ptr<MediaSample> sample,
77  bool encrypt_frame) {
78  DCHECK(encryptor_);
79 
80  const size_t sample_size = sample->data_size();
81  // We need to parse the frame (which also updates the vpx parser) even if the
82  // frame is not encrypted as the next (encrypted) frame may be dependent on
83  // this clear frame.
84  std::vector<VPxFrameInfo> vpx_frames;
85  if (vpx_parser_) {
86  if (!vpx_parser_->Parse(sample->data(), sample_size, &vpx_frames)) {
87  return Status(error::MUXER_FAILURE, "Failed to parse VPx frame.");
88  }
89  }
90 
91  if (encrypt_frame) {
92  const size_t iv_size = encryptor_->iv().size();
93  if (iv_size != kWebMIvSize) {
94  return Status(error::MUXER_FAILURE,
95  "Incorrect size WebM encryption IV.");
96  }
97  if (vpx_frames.size()) {
98  // Use partitioned subsample encryption: | signal_byte(3) | iv
99  // | num_partitions | partition_offset * n | enc_data |
100 
101  if (vpx_frames.size() > kWebMMaxSubsamples) {
102  return Status(error::MUXER_FAILURE,
103  "Maximum number of VPx encryption partitions exceeded.");
104  }
105  size_t num_partitions =
106  vpx_frames.size() == 1 ? 1 : vpx_frames.size() * 2;
107  size_t header_size = kWebMSignalByteSize + iv_size +
108  kWebMNumPartitionsSize +
109  (kWebMPartitionOffsetSize * num_partitions);
110  sample->resize_data(header_size + sample_size);
111  uint8_t* sample_data = sample->writable_data();
112  memmove(sample_data + header_size, sample_data, sample_size);
113  sample_data[0] = kWebMEncryptedSignal | kWebMPartitionedSignal;
114  memcpy(sample_data + kWebMSignalByteSize, encryptor_->iv().data(),
115  iv_size);
116  sample_data[kWebMSignalByteSize + kWebMIvSize] =
117  static_cast<uint8_t>(num_partitions);
118  uint32_t partition_offset = 0;
119  BufferWriter offsets_buffer(kWebMPartitionOffsetSize * num_partitions);
120  for (const auto& vpx_frame : vpx_frames) {
121  uint32_t encrypted_size = static_cast<uint32_t>(
122  vpx_frame.frame_size - vpx_frame.uncompressed_header_size);
123  encrypted_size -= encrypted_size % kAesBlockSize;
124  uint32_t clear_size =
125  static_cast<uint32_t>(vpx_frame.frame_size - encrypted_size);
126  partition_offset += clear_size;
127  offsets_buffer.AppendInt(partition_offset);
128  if (encrypted_size > 0) {
129  uint8_t* encrypted_ptr = sample_data + header_size + partition_offset;
130  if (!encryptor_->Crypt(encrypted_ptr, encrypted_size, encrypted_ptr)) {
131  return Status(error::MUXER_FAILURE, "Failed to encrypt the frame.");
132  }
133  partition_offset += encrypted_size;
134  }
135  if (num_partitions > 1) {
136  offsets_buffer.AppendInt(partition_offset);
137  }
138  }
139  DCHECK_EQ(num_partitions * kWebMPartitionOffsetSize,
140  offsets_buffer.Size());
141  memcpy(sample_data + kWebMSignalByteSize + kWebMIvSize +
142  kWebMNumPartitionsSize,
143  offsets_buffer.Buffer(), offsets_buffer.Size());
144  } else {
145  // Use whole-frame encryption: | signal_byte(1) | iv | enc_data |
146 
147  sample->resize_data(sample_size + iv_size + kWebMSignalByteSize);
148  uint8_t* sample_data = sample->writable_data();
149 
150  // Encrypt the data in-place.
151  if (!encryptor_->Crypt(sample_data, sample_size, sample_data)) {
152  return Status(error::MUXER_FAILURE, "Failed to encrypt the frame.");
153  }
154 
155  // First move the sample data to after the IV; then write the IV and
156  // signal byte.
157  memmove(sample_data + iv_size + kWebMSignalByteSize, sample_data,
158  sample_size);
159  sample_data[0] = kWebMEncryptedSignal;
160  memcpy(sample_data + 1, encryptor_->iv().data(), iv_size);
161  }
162  encryptor_->UpdateIv();
163  } else {
164  // Clear sample: | signal_byte(0) | data |
165  sample->resize_data(sample_size + 1);
166  uint8_t* sample_data = sample->writable_data();
167  memmove(sample_data + 1, sample_data, sample_size);
168  sample_data[0] = 0x00;
169  }
170 
171  return Status::OK;
172 }
173 
174 Status Encryptor::CreateEncryptor(MuxerListener* muxer_listener,
175  KeySource::TrackType track_type,
176  Codec codec,
177  KeySource* key_source,
178  bool webm_subsample_encryption) {
179  std::unique_ptr<EncryptionKey> encryption_key(new EncryptionKey());
180  Status status = key_source->GetKey(track_type, encryption_key.get());
181  if (!status.ok())
182  return status;
183  if (encryption_key->iv.empty()) {
184  if (!AesCryptor::GenerateRandomIv(FOURCC_cenc, &encryption_key->iv))
185  return Status(error::INTERNAL_ERROR, "Failed to generate random iv.");
186  }
187  DCHECK_EQ(kWebMIvSize, encryption_key->iv.size());
188  std::unique_ptr<AesCtrEncryptor> encryptor(new AesCtrEncryptor());
189  const bool initialized =
190  encryptor->InitializeWithIv(encryption_key->key, encryption_key->iv);
191  if (!initialized)
192  return Status(error::INTERNAL_ERROR, "Failed to create the encryptor.");
193 
194  if (webm_subsample_encryption && codec == kCodecVP9) {
195  // Allocate VP9 parser to do subsample encryption of VP9.
196  vpx_parser_.reset(new VP9Parser);
197  }
198 
199  if (muxer_listener) {
200  const bool kInitialEncryptionInfo = true;
201  muxer_listener->OnEncryptionInfoReady(
202  kInitialEncryptionInfo, FOURCC_cenc, encryption_key->key_id,
203  encryptor->iv(), encryption_key->key_system_info);
204  }
205 
206  key_ = std::move(encryption_key);
207  encryptor_ = std::move(encryptor);
208  return Status::OK;
209 }
210 
211 } // namespace webm
212 } // namespace media
213 } // namespace shaka
virtual void OnEncryptionInfoReady(bool is_initial_encryption_info, FourCC protection_scheme, const std::vector< uint8_t > &key_id, const std::vector< uint8_t > &iv, const std::vector< ProtectionSystemSpecificInfo > &key_system_info)=0
Status AddTrackInfo(mkvmuxer::Track *track)
Definition: encryptor.cc:71
virtual Status GetKey(TrackType track_type, EncryptionKey *key)=0
const uint8_t * Buffer() const
Definition: buffer_writer.h:59
Status EncryptFrame(std::shared_ptr< MediaSample > sample, bool encrypt_frame)
Definition: encryptor.cc:76
Status Initialize(MuxerListener *muxer_listener, KeySource::TrackType track_type, Codec codec, KeySource *key_source, bool webm_subsample_encryption)
Definition: encryptor.cc:61
static bool GenerateRandomIv(FourCC protection_scheme, std::vector< uint8_t > *iv)
Definition: aes_cryptor.cc:107
Class to parse a vp9 bit stream.
Definition: vp9_parser.h:20
KeySource is responsible for encryption key acquisition.
Definition: key_source.h:30