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(scoped_refptr<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  uint8_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] = num_partitions;
117  uint32_t partition_offset = 0;
118  BufferWriter offsets_buffer(kWebMPartitionOffsetSize * num_partitions);
119  for (const auto& vpx_frame : vpx_frames) {
120  uint32_t encrypted_size =
121  vpx_frame.frame_size - vpx_frame.uncompressed_header_size;
122  encrypted_size -= encrypted_size % kAesBlockSize;
123  uint32_t clear_size = vpx_frame.frame_size - encrypted_size;
124  partition_offset += clear_size;
125  offsets_buffer.AppendInt(partition_offset);
126  if (encrypted_size > 0) {
127  uint8_t* encrypted_ptr = sample_data + header_size + partition_offset;
128  if (!encryptor_->Crypt(encrypted_ptr, encrypted_size, encrypted_ptr)) {
129  return Status(error::MUXER_FAILURE, "Failed to encrypt the frame.");
130  }
131  partition_offset += encrypted_size;
132  }
133  if (num_partitions > 1) {
134  offsets_buffer.AppendInt(partition_offset);
135  }
136  }
137  DCHECK_EQ(num_partitions * kWebMPartitionOffsetSize,
138  offsets_buffer.Size());
139  memcpy(sample_data + kWebMSignalByteSize + kWebMIvSize +
140  kWebMNumPartitionsSize,
141  offsets_buffer.Buffer(), offsets_buffer.Size());
142  } else {
143  // Use whole-frame encryption: | signal_byte(1) | iv | enc_data |
144 
145  sample->resize_data(sample_size + iv_size + kWebMSignalByteSize);
146  uint8_t* sample_data = sample->writable_data();
147 
148  // Encrypt the data in-place.
149  if (!encryptor_->Crypt(sample_data, sample_size, sample_data)) {
150  return Status(error::MUXER_FAILURE, "Failed to encrypt the frame.");
151  }
152 
153  // First move the sample data to after the IV; then write the IV and
154  // signal byte.
155  memmove(sample_data + iv_size + kWebMSignalByteSize, sample_data,
156  sample_size);
157  sample_data[0] = kWebMEncryptedSignal;
158  memcpy(sample_data + 1, encryptor_->iv().data(), iv_size);
159  }
160  encryptor_->UpdateIv();
161  } else {
162  // Clear sample: | signal_byte(0) | data |
163  sample->resize_data(sample_size + 1);
164  uint8_t* sample_data = sample->writable_data();
165  memmove(sample_data + 1, sample_data, sample_size);
166  sample_data[0] = 0x00;
167  }
168 
169  return Status::OK;
170 }
171 
172 Status Encryptor::CreateEncryptor(MuxerListener* muxer_listener,
173  KeySource::TrackType track_type,
174  Codec codec,
175  KeySource* key_source,
176  bool webm_subsample_encryption) {
177  std::unique_ptr<EncryptionKey> encryption_key(new EncryptionKey());
178  Status status = key_source->GetKey(track_type, encryption_key.get());
179  if (!status.ok())
180  return status;
181  if (encryption_key->iv.empty()) {
182  if (!AesCryptor::GenerateRandomIv(FOURCC_cenc, &encryption_key->iv))
183  return Status(error::INTERNAL_ERROR, "Failed to generate random iv.");
184  }
185  DCHECK_EQ(kWebMIvSize, encryption_key->iv.size());
186  std::unique_ptr<AesCtrEncryptor> encryptor(new AesCtrEncryptor());
187  const bool initialized =
188  encryptor->InitializeWithIv(encryption_key->key, encryption_key->iv);
189  if (!initialized)
190  return Status(error::INTERNAL_ERROR, "Failed to create the encryptor.");
191 
192  if (webm_subsample_encryption && codec == kCodecVP9) {
193  // Allocate VP9 parser to do subsample encryption of VP9.
194  vpx_parser_.reset(new VP9Parser);
195  }
196 
197  if (muxer_listener) {
198  const bool kInitialEncryptionInfo = true;
199  muxer_listener->OnEncryptionInfoReady(
200  kInitialEncryptionInfo, FOURCC_cenc, encryption_key->key_id,
201  encryptor->iv(), encryption_key->key_system_info);
202  }
203 
204  key_ = std::move(encryption_key);
205  encryptor_ = std::move(encryptor);
206  return Status::OK;
207 }
208 
209 } // namespace webm
210 } // namespace media
211 } // 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 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:109
Class to parse a vp9 bit stream.
Definition: vp9_parser.h:20
Status EncryptFrame(scoped_refptr< MediaSample > sample, bool encrypt_frame)
Definition: encryptor.cc:76
KeySource is responsible for encryption key acquisition.
Definition: key_source.h:30