forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcenc_decryptor.cc
152 lines (128 loc) · 5.51 KB
/
cenc_decryptor.cc
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
146
147
148
149
150
151
152
// Copyright 2018 The Chromium Authors. All rights reserved.
// 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 <vector>
#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "crypto/encryptor.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 {
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,
const uint8_t* src,
uint8_t* dst) {
for (size_t i = 0; i < subsamples.size(); i++) {
const SubsampleEntry& subsample = subsamples[i];
if (sel == kSrcContainsClearBytes) {
src += subsample.clear_bytes;
} else {
dst += subsample.clear_bytes;
}
memcpy(dst, src, subsample.cypher_bytes);
src += subsample.cypher_bytes;
dst += subsample.cypher_bytes;
}
}
// TODO(crbug.com/840983): 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());
output->CopySideDataFrom(input.side_data(), input.side_data_size());
}
} // namespace
scoped_refptr<DecoderBuffer> DecryptCencBuffer(
const DecoderBuffer& input,
const crypto::SymmetricKey& key) {
const char* sample = reinterpret_cast<const char*>(input.data());
const size_t sample_size = input.data_size();
DCHECK(sample_size) << "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());
const std::string& iv = decrypt_config->iv();
DCHECK_EQ(iv.size(), static_cast<size_t>(DecryptConfig::kDecryptionKeySize));
crypto::Encryptor encryptor;
if (!encryptor.Init(&key, crypto::Encryptor::CTR, "")) {
DVLOG(1) << "Could not initialize decryptor.";
return nullptr;
}
if (!encryptor.SetCounter(iv)) {
DVLOG(1) << "Could not set counter block.";
return nullptr;
}
const std::vector<SubsampleEntry>& subsamples = decrypt_config->subsamples();
if (subsamples.empty()) {
std::string decrypted_text;
base::StringPiece encrypted_text(sample, sample_size);
if (!encryptor.Decrypt(encrypted_text, &decrypted_text)) {
DVLOG(1) << "Could not decrypt data.";
return nullptr;
}
// TODO(xhwang): Find a way to avoid this data copy.
auto output = DecoderBuffer::CopyFrom(
reinterpret_cast<const uint8_t*>(decrypted_text.data()),
decrypted_text.size());
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(input.data(), sample_size);
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
std::unique_ptr<uint8_t[]> encrypted_bytes(new uint8_t[total_encrypted_size]);
CopySubsamples(subsamples, kSrcContainsClearBytes,
reinterpret_cast<const uint8_t*>(sample),
encrypted_bytes.get());
base::StringPiece encrypted_text(
reinterpret_cast<const char*>(encrypted_bytes.get()),
total_encrypted_size);
std::string decrypted_text;
if (!encryptor.Decrypt(encrypted_text, &decrypted_text)) {
DVLOG(1) << "Could not decrypt data.";
return nullptr;
}
DCHECK_EQ(decrypted_text.size(), encrypted_text.size());
scoped_refptr<DecoderBuffer> output = DecoderBuffer::CopyFrom(
reinterpret_cast<const uint8_t*>(sample), sample_size);
CopySubsamples(subsamples, kDstContainsClearBytes,
reinterpret_cast<const uint8_t*>(decrypted_text.data()),
output->writable_data());
CopyExtraSettings(input, output.get());
return output;
}
} // namespace media