Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(114)

Side by Side Diff: media/crypto/aes_decryptor.cc

Issue 10651006: Add Common Encryption support to BMFF, including subsample decryption. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Satisfy mac_rel buildbot Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/crypto/aes_decryptor.h ('k') | media/crypto/aes_decryptor_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/crypto/aes_decryptor.h" 5 #include "media/crypto/aes_decryptor.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "base/stl_util.h" 8 #include "base/stl_util.h"
9 #include "base/string_number_conversions.h" 9 #include "base/string_number_conversions.h"
10 #include "crypto/encryptor.h" 10 #include "crypto/encryptor.h"
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
52 key_size); 52 key_size);
53 } 53 }
54 54
55 // Checks data in |input| matches the HMAC in |input|. The check is using the 55 // Checks data in |input| matches the HMAC in |input|. The check is using the
56 // SHA1 algorithm. |hmac_key| is the key of the HMAC algorithm. Returns true if 56 // SHA1 algorithm. |hmac_key| is the key of the HMAC algorithm. Returns true if
57 // the integrity check passes. 57 // the integrity check passes.
58 static bool CheckData(const DecoderBuffer& input, 58 static bool CheckData(const DecoderBuffer& input,
59 const base::StringPiece& hmac_key) { 59 const base::StringPiece& hmac_key) {
60 CHECK(input.GetDataSize()); 60 CHECK(input.GetDataSize());
61 CHECK(input.GetDecryptConfig()); 61 CHECK(input.GetDecryptConfig());
62 CHECK_GT(input.GetDecryptConfig()->checksum_size(), 0); 62 CHECK_GT(input.GetDecryptConfig()->checksum().size(), 0u);
63 CHECK(!hmac_key.empty()); 63 CHECK(!hmac_key.empty());
64 64
65 crypto::HMAC hmac(crypto::HMAC::SHA1); 65 crypto::HMAC hmac(crypto::HMAC::SHA1);
66 if (!hmac.Init(hmac_key)) 66 if (!hmac.Init(hmac_key))
67 return false; 67 return false;
68 68
69 // The component that initializes |input.GetDecryptConfig()| is responsible 69 // The component that initializes |input.GetDecryptConfig()| is responsible
70 // for checking that |input.GetDecryptConfig()->checksum_size()| matches 70 // for checking that |input.GetDecryptConfig()->checksum_size()| matches
71 // what is defined by the format. 71 // what is defined by the format.
72 72
73 // Here, check that checksum size is not greater than the hash 73 // Here, check that checksum size is not greater than the hash
74 // algorithm's digest length. 74 // algorithm's digest length.
75 DCHECK_LE(input.GetDecryptConfig()->checksum_size(), 75 DCHECK_LE(input.GetDecryptConfig()->checksum().size(),
76 static_cast<int>(hmac.DigestLength())); 76 hmac.DigestLength());
77 77
78 base::StringPiece data_to_check( 78 base::StringPiece data_to_check(
79 reinterpret_cast<const char*>(input.GetData()), input.GetDataSize()); 79 reinterpret_cast<const char*>(input.GetData()), input.GetDataSize());
80 base::StringPiece digest(
81 reinterpret_cast<const char*>(input.GetDecryptConfig()->checksum()),
82 input.GetDecryptConfig()->checksum_size());
83 80
84 return hmac.VerifyTruncated(data_to_check, digest); 81 return hmac.VerifyTruncated(data_to_check,
82 input.GetDecryptConfig()->checksum());
85 } 83 }
86 84
87 // Decrypts |input| using |key|. |encrypted_data_offset| is the number of bytes 85 enum ClearBytesBufferSel {
88 // into |input| that the encrypted data starts. 86 kSrcContainsClearBytes,
89 // Returns a DecoderBuffer with the decrypted data if decryption succeeded or 87 kDstContainsClearBytes,
90 // NULL if decryption failed. 88 };
89
90 static void CopySubsamples(const std::vector<SubsampleEntry>& subsamples,
91 const ClearBytesBufferSel sel,
92 const uint8* src,
93 uint8* dst) {
94 for (size_t i = 0; i < subsamples.size(); i++) {
95 const SubsampleEntry& subsample = subsamples[i];
96 if (sel == kSrcContainsClearBytes) {
97 src += subsample.clear_bytes;
98 } else {
99 dst += subsample.clear_bytes;
100 }
101 memcpy(dst, src, subsample.cypher_bytes);
102 src += subsample.cypher_bytes;
103 dst += subsample.cypher_bytes;
104 }
105 }
106
107 // Decrypts |input| using |key|. Returns a DecoderBuffer with the decrypted
108 // data if decryption succeeded or NULL if decryption failed.
91 static scoped_refptr<DecoderBuffer> DecryptData(const DecoderBuffer& input, 109 static scoped_refptr<DecoderBuffer> DecryptData(const DecoderBuffer& input,
92 crypto::SymmetricKey* key, 110 crypto::SymmetricKey* key) {
93 int encrypted_data_offset) {
94 CHECK(input.GetDataSize()); 111 CHECK(input.GetDataSize());
95 CHECK(input.GetDecryptConfig()); 112 CHECK(input.GetDecryptConfig());
96 CHECK(key); 113 CHECK(key);
97 114
98 // Initialize decryptor.
99 crypto::Encryptor encryptor; 115 crypto::Encryptor encryptor;
100 if (!encryptor.Init(key, crypto::Encryptor::CTR, "")) { 116 if (!encryptor.Init(key, crypto::Encryptor::CTR, "")) {
101 DVLOG(1) << "Could not initialize decryptor."; 117 DVLOG(1) << "Could not initialize decryptor.";
102 return NULL; 118 return NULL;
103 } 119 }
104 120
105 DCHECK_EQ(input.GetDecryptConfig()->iv_size(), 121 DCHECK_EQ(input.GetDecryptConfig()->iv().size(),
106 DecryptConfig::kDecryptionKeySize); 122 static_cast<size_t>(DecryptConfig::kDecryptionKeySize));
107 // Set the counter block. 123 if (!encryptor.SetCounter(input.GetDecryptConfig()->iv())) {
108 base::StringPiece counter_block(
109 reinterpret_cast<const char*>(input.GetDecryptConfig()->iv()),
110 input.GetDecryptConfig()->iv_size());
111 if (counter_block.empty()) {
112 DVLOG(1) << "Could not generate counter block.";
113 return NULL;
114 }
115 if (!encryptor.SetCounter(counter_block)) {
116 DVLOG(1) << "Could not set counter block."; 124 DVLOG(1) << "Could not set counter block.";
117 return NULL; 125 return NULL;
118 } 126 }
119 127
128 const int data_offset = input.GetDecryptConfig()->data_offset();
129 const char* sample =
130 reinterpret_cast<const char*>(input.GetData() + data_offset);
131 int sample_size = input.GetDataSize() - data_offset;
132
133 if (input.GetDecryptConfig()->subsamples().empty()) {
134 std::string decrypted_text;
135 base::StringPiece encrypted_text(sample, sample_size);
136 if (!encryptor.Decrypt(encrypted_text, &decrypted_text)) {
137 DVLOG(1) << "Could not decrypt data.";
138 return NULL;
139 }
140
141 // TODO(xhwang): Find a way to avoid this data copy.
142 return DecoderBuffer::CopyFrom(
143 reinterpret_cast<const uint8*>(decrypted_text.data()),
144 decrypted_text.size());
145 }
146
147 const std::vector<SubsampleEntry>& subsamples =
148 input.GetDecryptConfig()->subsamples();
149
150 int total_clear_size = 0;
151 int total_encrypted_size = 0;
152 for (size_t i = 0; i < subsamples.size(); i++) {
153 total_clear_size += subsamples[i].clear_bytes;
154 total_encrypted_size += subsamples[i].cypher_bytes;
155 }
156 if (total_clear_size + total_encrypted_size != sample_size) {
157 DVLOG(1) << "Subsample sizes do not equal input size";
158 return NULL;
159 }
160
161 // The encrypted portions of all subsamples must form a contiguous block,
162 // such that an encrypted subsample that ends away from a block boundary is
163 // immediately followed by the start of the next encrypted subsample. We
164 // copy all encrypted subsamples to a contiguous buffer, decrypt them, then
165 // copy the decrypted bytes over the encrypted bytes in the output.
166 // TODO(strobe): attempt to reduce number of memory copies
167 scoped_array<uint8> encrypted_bytes(new uint8[total_encrypted_size]);
168 CopySubsamples(subsamples, kSrcContainsClearBytes,
169 reinterpret_cast<const uint8*>(sample), encrypted_bytes.get());
170
171 base::StringPiece encrypted_text(
172 reinterpret_cast<const char*>(encrypted_bytes.get()),
173 total_encrypted_size);
120 std::string decrypted_text; 174 std::string decrypted_text;
121 const char* frame =
122 reinterpret_cast<const char*>(input.GetData() + encrypted_data_offset);
123 int frame_size = input.GetDataSize() - encrypted_data_offset;
124 base::StringPiece encrypted_text(frame, frame_size);
125 if (!encryptor.Decrypt(encrypted_text, &decrypted_text)) { 175 if (!encryptor.Decrypt(encrypted_text, &decrypted_text)) {
126 DVLOG(1) << "Could not decrypt data."; 176 DVLOG(1) << "Could not decrypt data.";
127 return NULL; 177 return NULL;
128 } 178 }
129 179
130 // TODO(xhwang): Find a way to avoid this data copy. 180 scoped_refptr<DecoderBuffer> output = DecoderBuffer::CopyFrom(
131 return DecoderBuffer::CopyFrom( 181 reinterpret_cast<const uint8*>(sample), sample_size);
132 reinterpret_cast<const uint8*>(decrypted_text.data()), 182 CopySubsamples(subsamples, kDstContainsClearBytes,
133 decrypted_text.size()); 183 reinterpret_cast<const uint8*>(decrypted_text.data()),
184 output->GetWritableData());
185 return output;
134 } 186 }
135 187
136 AesDecryptor::AesDecryptor(DecryptorClient* client) 188 AesDecryptor::AesDecryptor(DecryptorClient* client)
137 : client_(client) { 189 : client_(client) {
138 } 190 }
139 191
140 AesDecryptor::~AesDecryptor() { 192 AesDecryptor::~AesDecryptor() {
141 STLDeleteValues(&key_map_); 193 STLDeleteValues(&key_map_);
142 } 194 }
143 195
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
185 std::string key_id_string(reinterpret_cast<const char*>(init_data), 237 std::string key_id_string(reinterpret_cast<const char*>(init_data),
186 init_data_length); 238 init_data_length);
187 std::string key_string(reinterpret_cast<const char*>(key) , key_length); 239 std::string key_string(reinterpret_cast<const char*>(key) , key_length);
188 scoped_ptr<DecryptionKey> decryption_key(new DecryptionKey(key_string)); 240 scoped_ptr<DecryptionKey> decryption_key(new DecryptionKey(key_string));
189 if (!decryption_key.get()) { 241 if (!decryption_key.get()) {
190 DVLOG(1) << "Could not create key."; 242 DVLOG(1) << "Could not create key.";
191 client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0); 243 client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0);
192 return; 244 return;
193 } 245 }
194 246
195 // TODO(fgalligan): When ISO is added we will need to figure out how to 247 if (!decryption_key->Init()) {
196 // detect if the encrypted data will contain an HMAC.
197 if (!decryption_key->Init(true)) {
198 DVLOG(1) << "Could not initialize decryption key."; 248 DVLOG(1) << "Could not initialize decryption key.";
199 client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0); 249 client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0);
200 return; 250 return;
201 } 251 }
202 252
203 { 253 {
204 base::AutoLock auto_lock(key_map_lock_); 254 base::AutoLock auto_lock(key_map_lock_);
205 KeyMap::iterator found = key_map_.find(key_id_string); 255 KeyMap::iterator found = key_map_.find(key_id_string);
206 if (found != key_map_.end()) { 256 if (found != key_map_.end()) {
207 delete found->second; 257 delete found->second;
208 key_map_.erase(found); 258 key_map_.erase(found);
209 } 259 }
210 key_map_[key_id_string] = decryption_key.release(); 260 key_map_[key_id_string] = decryption_key.release();
211 } 261 }
212 262
213 client_->KeyAdded(key_system, session_id); 263 client_->KeyAdded(key_system, session_id);
214 } 264 }
215 265
216 void AesDecryptor::CancelKeyRequest(const std::string& key_system, 266 void AesDecryptor::CancelKeyRequest(const std::string& key_system,
217 const std::string& session_id) { 267 const std::string& session_id) {
218 } 268 }
219 269
220 void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, 270 void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted,
221 const DecryptCB& decrypt_cb) { 271 const DecryptCB& decrypt_cb) {
222 CHECK(encrypted->GetDecryptConfig()); 272 CHECK(encrypted->GetDecryptConfig());
223 const uint8* key_id = encrypted->GetDecryptConfig()->key_id(); 273 const std::string& key_id = encrypted->GetDecryptConfig()->key_id();
224 const int key_id_size = encrypted->GetDecryptConfig()->key_id_size();
225
226 // TODO(xhwang): Avoid always constructing a string with StringPiece?
227 std::string key_id_string(reinterpret_cast<const char*>(key_id), key_id_size);
228 274
229 DecryptionKey* key = NULL; 275 DecryptionKey* key = NULL;
230 { 276 {
231 base::AutoLock auto_lock(key_map_lock_); 277 base::AutoLock auto_lock(key_map_lock_);
232 KeyMap::const_iterator found = key_map_.find(key_id_string); 278 KeyMap::const_iterator found = key_map_.find(key_id);
233 if (found != key_map_.end()) 279 if (found != key_map_.end())
234 key = found->second; 280 key = found->second;
235 } 281 }
236 282
237 if (!key) { 283 if (!key) {
238 // TODO(fgalligan): Fire a need_key event here and add a test. 284 // TODO(fgalligan): Fire a need_key event here and add a test.
239 DVLOG(1) << "Could not find a matching key for given key ID."; 285 DVLOG(1) << "Could not find a matching key for given key ID.";
240 decrypt_cb.Run(kError, NULL); 286 decrypt_cb.Run(kError, NULL);
241 return; 287 return;
242 } 288 }
243 289
244 int checksum_size = encrypted->GetDecryptConfig()->checksum_size(); 290 int checksum_size = encrypted->GetDecryptConfig()->checksum().size();
245 // According to the WebM encrypted specification, it is an open question 291 // According to the WebM encrypted specification, it is an open question
246 // what should happen when a frame fails the integrity check. 292 // what should happen when a frame fails the integrity check.
247 // http://wiki.webmproject.org/encryption/webm-encryption-rfc 293 // http://wiki.webmproject.org/encryption/webm-encryption-rfc
248 if (checksum_size > 0 && 294 if (checksum_size > 0 &&
249 !key->hmac_key().empty() && 295 !key->hmac_key().empty() &&
250 !CheckData(*encrypted, key->hmac_key())) { 296 !CheckData(*encrypted, key->hmac_key())) {
251 DVLOG(1) << "Integrity check failed."; 297 DVLOG(1) << "Integrity check failed.";
252 decrypt_cb.Run(kError, NULL); 298 decrypt_cb.Run(kError, NULL);
253 return; 299 return;
254 } 300 }
255 301
302 // TODO(strobe): Currently, presence of checksum is used to indicate the use
303 // of normal or WebM decryption keys. Consider a more explicit signaling
304 // mechanism and the removal of the webm_decryption_key member.
305 crypto::SymmetricKey* decryption_key = (checksum_size > 0) ?
306 key->webm_decryption_key() : key->decryption_key();
256 scoped_refptr<DecoderBuffer> decrypted = 307 scoped_refptr<DecoderBuffer> decrypted =
257 DecryptData(*encrypted, 308 DecryptData(*encrypted, decryption_key);
258 key->decryption_key(),
259 encrypted->GetDecryptConfig()->encrypted_frame_offset());
260 if (!decrypted) { 309 if (!decrypted) {
261 DVLOG(1) << "Decryption failed."; 310 DVLOG(1) << "Decryption failed.";
262 decrypt_cb.Run(kError, NULL); 311 decrypt_cb.Run(kError, NULL);
263 return; 312 return;
264 } 313 }
265 314
266 decrypted->SetTimestamp(encrypted->GetTimestamp()); 315 decrypted->SetTimestamp(encrypted->GetTimestamp());
267 decrypted->SetDuration(encrypted->GetDuration()); 316 decrypted->SetDuration(encrypted->GetDuration());
268 decrypt_cb.Run(kSuccess, decrypted); 317 decrypt_cb.Run(kSuccess, decrypted);
269 } 318 }
270 319
271 AesDecryptor::DecryptionKey::DecryptionKey( 320 AesDecryptor::DecryptionKey::DecryptionKey(
272 const std::string& secret) 321 const std::string& secret)
273 : secret_(secret) { 322 : secret_(secret) {
274 } 323 }
275 324
276 AesDecryptor::DecryptionKey::~DecryptionKey() {} 325 AesDecryptor::DecryptionKey::~DecryptionKey() {}
277 326
278 bool AesDecryptor::DecryptionKey::Init(bool derive_webm_keys) { 327 bool AesDecryptor::DecryptionKey::Init() {
279 CHECK(!secret_.empty()); 328 CHECK(!secret_.empty());
280
281 if (derive_webm_keys) {
282 std::string raw_key = DeriveKey(secret_,
283 kWebmEncryptionSeed,
284 secret_.length());
285 if (raw_key.empty()) {
286 return false;
287 }
288 decryption_key_.reset(
289 crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key));
290 if (!decryption_key_.get()) {
291 return false;
292 }
293
294 hmac_key_ = DeriveKey(secret_, kWebmHmacSeed, kWebmSha1DigestSize);
295 if (hmac_key_.empty()) {
296 return false;
297 }
298 return true;
299 }
300
301 decryption_key_.reset( 329 decryption_key_.reset(
302 crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, secret_)); 330 crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, secret_));
303 if (!decryption_key_.get()) { 331 if (!decryption_key_.get()) {
304 return false; 332 return false;
305 } 333 }
334
335 std::string raw_key = DeriveKey(secret_,
336 kWebmEncryptionSeed,
337 secret_.length());
338 if (raw_key.empty()) {
339 return false;
340 }
341 webm_decryption_key_.reset(
342 crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key));
343 if (!webm_decryption_key_.get()) {
344 return false;
345 }
346
347 hmac_key_ = DeriveKey(secret_, kWebmHmacSeed, kWebmSha1DigestSize);
348 if (hmac_key_.empty()) {
349 return false;
350 }
351
306 return true; 352 return true;
307 } 353 }
308 354
309 } // namespace media 355 } // namespace media
OLDNEW
« no previous file with comments | « media/crypto/aes_decryptor.h ('k') | media/crypto/aes_decryptor_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698