1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
media / cdm / cenc_decryptor.cc [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/cdm/cenc_decryptor.h"
#include <stdint.h>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include "base/containers/heap_array.h"
#include "base/containers/span.h"
#include "base/logging.h"
#include "crypto/aes_ctr.h"
#include "crypto/symmetric_key.h"
#include "media/base/decoder_buffer.h"
#include "media/base/decrypt_config.h"
#include "media/base/subsample_entry.h"
namespace media {
namespace {
constexpr size_t kRequiredKeyBytes = 16;
enum ClearBytesBufferSel { kSrcContainsClearBytes, kDstContainsClearBytes };
// Copy the cypher bytes as specified by |subsamples| from |src| to |dst|.
// If |sel| == kSrcContainsClearBytes, then |src| is expected to contain any
// clear bytes specified by |subsamples| and will be skipped. This is used
// when copying all the protected data out of a sample. If |sel| ==
// kDstContainsClearBytes, then any clear bytes mentioned in |subsamples|
// will be skipped in |dst|. This is used when copying the decrypted bytes
// back into the buffer, replacing the encrypted portions.
void CopySubsamples(const std::vector<SubsampleEntry>& subsamples,
const ClearBytesBufferSel sel,
base::span<const uint8_t> src,
base::span<uint8_t> dst) {
size_t src_i = 0;
size_t dst_i = 0;
for (const auto& subsample : subsamples) {
if (sel == kSrcContainsClearBytes) {
src_i += subsample.clear_bytes;
} else {
dst_i += subsample.clear_bytes;
}
auto src_view = src.subspan(src_i, subsample.cypher_bytes);
auto dst_view = dst.subspan(dst_i, subsample.cypher_bytes);
dst_view.copy_from(src_view);
src_i += subsample.cypher_bytes;
dst_i += subsample.cypher_bytes;
}
}
// TODO(crbug.com/40575437): This should be done in DecoderBuffer so that
// additional fields are more easily handled.
void CopyExtraSettings(const DecoderBuffer& input, DecoderBuffer* output) {
output->set_timestamp(input.timestamp());
output->set_duration(input.duration());
output->set_is_key_frame(input.is_key_frame());
if (input.side_data()) {
output->set_side_data(input.side_data()->Clone());
}
}
} // namespace
scoped_refptr<DecoderBuffer> DecryptCencBuffer(
const DecoderBuffer& input,
const crypto::SymmetricKey& wrapped_key) {
return DecryptCencBuffer(input, base::as_byte_span(wrapped_key.key()));
}
scoped_refptr<DecoderBuffer> DecryptCencBuffer(const DecoderBuffer& input,
base::span<const uint8_t> key) {
base::span<const uint8_t> sample = input;
DCHECK(!sample.empty()) << "No data to decrypt.";
const DecryptConfig* decrypt_config = input.decrypt_config();
DCHECK(decrypt_config) << "No need to call Decrypt() on unencrypted buffer.";
DCHECK_EQ(EncryptionScheme::kCenc, decrypt_config->encryption_scheme());
if (key.size() != kRequiredKeyBytes) {
DVLOG(1) << "Supplied key is the wrong size for CENC";
return nullptr;
}
auto iv = base::as_byte_span(decrypt_config->iv())
.to_fixed_extent<crypto::aes_ctr::kCounterSize>();
if (!iv) {
DVLOG(1) << "Supplied IV is the wrong size for CENC";
return nullptr;
}
const std::vector<SubsampleEntry>& subsamples = decrypt_config->subsamples();
if (subsamples.empty()) {
auto decrypted = base::HeapArray<uint8_t>::Uninit(sample.size());
crypto::aes_ctr::Decrypt(key, *iv, sample, decrypted);
auto output = DecoderBuffer::FromArray(std::move(decrypted));
CopyExtraSettings(input, output.get());
return output;
}
if (!VerifySubsamplesMatchSize(subsamples, sample.size())) {
DVLOG(1) << "Subsample sizes do not equal input size";
return nullptr;
}
// Compute the size of the encrypted portion. Overflow, etc. checked by
// the call to VerifySubsamplesMatchSize().
size_t total_encrypted_size = 0;
for (const auto& subsample : subsamples)
total_encrypted_size += subsample.cypher_bytes;
// No need to decrypt if there is no encrypted data.
if (total_encrypted_size == 0) {
auto output = DecoderBuffer::CopyFrom(sample);
CopyExtraSettings(input, output.get());
return output;
}
// The encrypted portions of all subsamples must form a contiguous block,
// such that an encrypted subsample that ends away from a block boundary is
// immediately followed by the start of the next encrypted subsample. We
// copy all encrypted subsamples to a contiguous buffer, decrypt them, then
// copy the decrypted bytes over the encrypted bytes in the output.
// TODO(strobe): attempt to reduce number of memory copies
auto encrypted = base::HeapArray<uint8_t>::Uninit(total_encrypted_size);
auto decrypted = base::HeapArray<uint8_t>::Uninit(total_encrypted_size);
CopySubsamples(subsamples, kSrcContainsClearBytes, sample, encrypted);
crypto::aes_ctr::Decrypt(key, *iv, encrypted, decrypted);
scoped_refptr<DecoderBuffer> output = DecoderBuffer::CopyFrom(sample);
CopySubsamples(subsamples, kDstContainsClearBytes, decrypted,
output->writable_span());
CopyExtraSettings(input, output.get());
return output;
}
} // namespace media