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