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

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: Convert key_id to string Created 8 years, 5 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
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 HMAC covers the IV and the frame data. 69 // The HMAC covers the IV and the frame data.
70 base::StringPiece data_to_check( 70 base::StringPiece data_to_check(
71 reinterpret_cast<const char*>(input.GetData()), input.GetDataSize()); 71 reinterpret_cast<const char*>(input.GetData()), input.GetDataSize());
72 72
73 scoped_array<uint8> calculated_hmac(new uint8[hmac.DigestLength()]); 73 scoped_array<uint8> calculated_hmac(new uint8[hmac.DigestLength()]);
74 if (!hmac.Sign(data_to_check, calculated_hmac.get(), hmac.DigestLength())) 74 if (!hmac.Sign(data_to_check, calculated_hmac.get(), hmac.DigestLength()))
75 return false; 75 return false;
76 76
77 DCHECK(input.GetDecryptConfig()->checksum_size() <= 77 DCHECK(input.GetDecryptConfig()->checksum().size() <= hmac.DigestLength());
78 static_cast<int>(hmac.DigestLength())); 78 if (memcmp(input.GetDecryptConfig()->checksum().data(),
ddorwin 2012/07/24 01:00:10 Please add: TODO(fgalligan): Use hmac.Verify().
strobe_ 2012/07/25 01:05:13 Done.
79 if (memcmp(input.GetDecryptConfig()->checksum(),
80 calculated_hmac.get(), 79 calculated_hmac.get(),
81 input.GetDecryptConfig()->checksum_size()) != 0) 80 input.GetDecryptConfig()->checksum().size()) != 0)
82 return false; 81 return false;
83 return true; 82 return true;
84 } 83 }
85 84
86 // Decrypts |input| using |key|. |encrypted_data_offset| is the number of bytes 85 enum ClearBytesBufferSel {
87 // into |input| that the encrypted data starts. 86 kSrcContainsClearBytes,
88 // Returns a DecoderBuffer with the decrypted data if decryption succeeded or 87 kDstContainsClearBytes,
89 // 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.
90 static scoped_refptr<DecoderBuffer> DecryptData(const DecoderBuffer& input, 109 static scoped_refptr<DecoderBuffer> DecryptData(const DecoderBuffer& input,
91 crypto::SymmetricKey* key, 110 crypto::SymmetricKey* key) {
92 int encrypted_data_offset) {
93 CHECK(input.GetDataSize()); 111 CHECK(input.GetDataSize());
94 CHECK(input.GetDecryptConfig()); 112 CHECK(input.GetDecryptConfig());
95 CHECK(key); 113 CHECK(key);
96 114
97 // Initialize decryptor.
98 crypto::Encryptor encryptor; 115 crypto::Encryptor encryptor;
99 if (!encryptor.Init(key, crypto::Encryptor::CTR, "")) { 116 if (!encryptor.Init(key, crypto::Encryptor::CTR, "")) {
100 DVLOG(1) << "Could not initialize decryptor."; 117 DVLOG(1) << "Could not initialize decryptor.";
101 return NULL; 118 return NULL;
102 } 119 }
103 120
104 DCHECK_EQ(input.GetDecryptConfig()->iv_size(), 121 DCHECK_EQ(input.GetDecryptConfig()->iv().size(),
105 DecryptConfig::kDecryptionKeySize); 122 static_cast<size_t>(DecryptConfig::kDecryptionKeySize));
106 // Set the counter block. 123 if (!encryptor.SetCounter(input.GetDecryptConfig()->iv())) {
107 base::StringPiece counter_block(
108 reinterpret_cast<const char*>(input.GetDecryptConfig()->iv()),
109 input.GetDecryptConfig()->iv_size());
110 if (counter_block.empty()) {
111 DVLOG(1) << "Could not generate counter block.";
112 return NULL;
113 }
114 if (!encryptor.SetCounter(counter_block)) {
115 DVLOG(1) << "Could not set counter block."; 124 DVLOG(1) << "Could not set counter block.";
116 return NULL; 125 return NULL;
117 } 126 }
118 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);
119 std::string decrypted_text; 174 std::string decrypted_text;
120 const char* frame =
121 reinterpret_cast<const char*>(input.GetData() + encrypted_data_offset);
122 int frame_size = input.GetDataSize() - encrypted_data_offset;
123 base::StringPiece encrypted_text(frame, frame_size);
124 if (!encryptor.Decrypt(encrypted_text, &decrypted_text)) { 175 if (!encryptor.Decrypt(encrypted_text, &decrypted_text)) {
125 DVLOG(1) << "Could not decrypt data."; 176 DVLOG(1) << "Could not decrypt data.";
126 return NULL; 177 return NULL;
127 } 178 }
128 179
129 // TODO(xhwang): Find a way to avoid this data copy. 180 scoped_refptr<DecoderBuffer> output = DecoderBuffer::CopyFrom(
130 return DecoderBuffer::CopyFrom( 181 reinterpret_cast<const uint8*>(sample), sample_size);
131 reinterpret_cast<const uint8*>(decrypted_text.data()), 182 CopySubsamples(subsamples, kDstContainsClearBytes,
132 decrypted_text.size()); 183 reinterpret_cast<const uint8*>(decrypted_text.data()),
184 output->GetWritableData());
185 return output;
133 } 186 }
134 187
135 AesDecryptor::AesDecryptor(DecryptorClient* client) 188 AesDecryptor::AesDecryptor(DecryptorClient* client)
136 : client_(client) { 189 : client_(client) {
137 } 190 }
138 191
139 AesDecryptor::~AesDecryptor() { 192 AesDecryptor::~AesDecryptor() {
140 STLDeleteValues(&key_map_); 193 STLDeleteValues(&key_map_);
141 } 194 }
142 195
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
184 std::string key_id_string(reinterpret_cast<const char*>(init_data), 237 std::string key_id_string(reinterpret_cast<const char*>(init_data),
185 init_data_length); 238 init_data_length);
186 std::string key_string(reinterpret_cast<const char*>(key) , key_length); 239 std::string key_string(reinterpret_cast<const char*>(key) , key_length);
187 scoped_ptr<DecryptionKey> decryption_key(new DecryptionKey(key_string)); 240 scoped_ptr<DecryptionKey> decryption_key(new DecryptionKey(key_string));
188 if (!decryption_key.get()) { 241 if (!decryption_key.get()) {
189 DVLOG(1) << "Could not create key."; 242 DVLOG(1) << "Could not create key.";
190 client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0); 243 client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0);
191 return; 244 return;
192 } 245 }
193 246
194 // TODO(fgalligan): When ISO is added we will need to figure out how to 247 // TODO(fgalligan): When ISO is added we will need to figure out how to
ddorwin 2012/07/24 01:00:10 This is probably redundant with the TODO below.
strobe_ 2012/07/25 01:05:13 Done.
195 // detect if the encrypted data will contain an HMAC. 248 // detect if the encrypted data will contain an HMAC.
196 if (!decryption_key->Init(true)) { 249 if (!decryption_key->Init()) {
197 DVLOG(1) << "Could not initialize decryption key."; 250 DVLOG(1) << "Could not initialize decryption key.";
198 client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0); 251 client_->KeyError(key_system, session_id, Decryptor::kUnknownError, 0);
199 return; 252 return;
200 } 253 }
201 254
202 { 255 {
203 base::AutoLock auto_lock(key_map_lock_); 256 base::AutoLock auto_lock(key_map_lock_);
204 KeyMap::iterator found = key_map_.find(key_id_string); 257 KeyMap::iterator found = key_map_.find(key_id_string);
205 if (found != key_map_.end()) { 258 if (found != key_map_.end()) {
206 delete found->second; 259 delete found->second;
207 key_map_.erase(found); 260 key_map_.erase(found);
208 } 261 }
209 key_map_[key_id_string] = decryption_key.release(); 262 key_map_[key_id_string] = decryption_key.release();
210 } 263 }
211 264
212 client_->KeyAdded(key_system, session_id); 265 client_->KeyAdded(key_system, session_id);
213 } 266 }
214 267
215 void AesDecryptor::CancelKeyRequest(const std::string& key_system, 268 void AesDecryptor::CancelKeyRequest(const std::string& key_system,
216 const std::string& session_id) { 269 const std::string& session_id) {
217 } 270 }
218 271
219 void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted, 272 void AesDecryptor::Decrypt(const scoped_refptr<DecoderBuffer>& encrypted,
220 const DecryptCB& decrypt_cb) { 273 const DecryptCB& decrypt_cb) {
221 CHECK(encrypted->GetDecryptConfig()); 274 CHECK(encrypted->GetDecryptConfig());
222 const uint8* key_id = encrypted->GetDecryptConfig()->key_id(); 275 const std::string& key_id = encrypted->GetDecryptConfig()->key_id();
223 const int key_id_size = encrypted->GetDecryptConfig()->key_id_size();
224
225 // TODO(xhwang): Avoid always constructing a string with StringPiece?
226 std::string key_id_string(reinterpret_cast<const char*>(key_id), key_id_size);
227 276
228 DecryptionKey* key = NULL; 277 DecryptionKey* key = NULL;
229 { 278 {
230 base::AutoLock auto_lock(key_map_lock_); 279 base::AutoLock auto_lock(key_map_lock_);
231 KeyMap::const_iterator found = key_map_.find(key_id_string); 280 KeyMap::const_iterator found = key_map_.find(key_id);
232 if (found != key_map_.end()) 281 if (found != key_map_.end())
233 key = found->second; 282 key = found->second;
234 } 283 }
235 284
236 if (!key) { 285 if (!key) {
237 // TODO(fgalligan): Fire a need_key event here and add a test. 286 // TODO(fgalligan): Fire a need_key event here and add a test.
238 DVLOG(1) << "Could not find a matching key for given key ID."; 287 DVLOG(1) << "Could not find a matching key for given key ID.";
239 decrypt_cb.Run(kError, NULL); 288 decrypt_cb.Run(kError, NULL);
240 return; 289 return;
241 } 290 }
242 291
243 int checksum_size = encrypted->GetDecryptConfig()->checksum_size(); 292 int checksum_size = encrypted->GetDecryptConfig()->checksum().size();
244 // According to the WebM encrypted specification, it is an open question 293 // According to the WebM encrypted specification, it is an open question
245 // what should happen when a frame fails the integrity check. 294 // what should happen when a frame fails the integrity check.
246 // http://wiki.webmproject.org/encryption/webm-encryption-rfc 295 // http://wiki.webmproject.org/encryption/webm-encryption-rfc
247 if (checksum_size > 0 && 296 if (checksum_size > 0 &&
248 !key->hmac_key().empty() && 297 !key->hmac_key().empty() &&
249 !CheckData(*encrypted, key->hmac_key())) { 298 !CheckData(*encrypted, key->hmac_key())) {
250 DVLOG(1) << "Integrity check failed."; 299 DVLOG(1) << "Integrity check failed.";
251 decrypt_cb.Run(kError, NULL); 300 decrypt_cb.Run(kError, NULL);
252 return; 301 return;
253 } 302 }
254 303
304 // TODO(strobe): Currently, presence of checksum is used to indicate the use
305 // of normal or WebM decryption keys. Consider a more explicit signaling
306 // mechanism.
ddorwin 2012/07/24 01:00:10 ... and remove the webm_ member.
strobe_ 2012/07/25 01:05:13 Done.
307 crypto::SymmetricKey* decryption_key = (checksum_size > 0) ?
308 key->webm_decryption_key() : key->decryption_key();
255 scoped_refptr<DecoderBuffer> decrypted = 309 scoped_refptr<DecoderBuffer> decrypted =
256 DecryptData(*encrypted, 310 DecryptData(*encrypted, decryption_key);
257 key->decryption_key(),
258 encrypted->GetDecryptConfig()->encrypted_frame_offset());
259 if (!decrypted) { 311 if (!decrypted) {
260 DVLOG(1) << "Decryption failed."; 312 DVLOG(1) << "Decryption failed.";
261 decrypt_cb.Run(kError, NULL); 313 decrypt_cb.Run(kError, NULL);
262 return; 314 return;
263 } 315 }
264 316
265 decrypted->SetTimestamp(encrypted->GetTimestamp()); 317 decrypted->SetTimestamp(encrypted->GetTimestamp());
266 decrypted->SetDuration(encrypted->GetDuration()); 318 decrypted->SetDuration(encrypted->GetDuration());
267 decrypt_cb.Run(kSuccess, decrypted); 319 decrypt_cb.Run(kSuccess, decrypted);
268 } 320 }
269 321
270 AesDecryptor::DecryptionKey::DecryptionKey( 322 AesDecryptor::DecryptionKey::DecryptionKey(
271 const std::string& secret) 323 const std::string& secret)
272 : secret_(secret) { 324 : secret_(secret) {
273 } 325 }
274 326
275 AesDecryptor::DecryptionKey::~DecryptionKey() {} 327 AesDecryptor::DecryptionKey::~DecryptionKey() {}
276 328
277 bool AesDecryptor::DecryptionKey::Init(bool derive_webm_keys) { 329 bool AesDecryptor::DecryptionKey::Init() {
278 CHECK(!secret_.empty()); 330 CHECK(!secret_.empty());
279
280 if (derive_webm_keys) {
281 std::string raw_key = DeriveKey(secret_,
282 kWebmEncryptionSeed,
283 secret_.length());
284 if (raw_key.empty()) {
285 return false;
286 }
287 decryption_key_.reset(
288 crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key));
289 if (!decryption_key_.get()) {
290 return false;
291 }
292
293 hmac_key_ = DeriveKey(secret_, kWebmHmacSeed, kWebmSha1DigestSize);
294 if (hmac_key_.empty()) {
295 return false;
296 }
297 return true;
298 }
299
300 decryption_key_.reset( 331 decryption_key_.reset(
301 crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, secret_)); 332 crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, secret_));
302 if (!decryption_key_.get()) { 333 if (!decryption_key_.get()) {
303 return false; 334 return false;
304 } 335 }
336
337 std::string raw_key = DeriveKey(secret_,
338 kWebmEncryptionSeed,
339 secret_.length());
340 if (raw_key.empty()) {
341 return false;
342 }
343 webm_decryption_key_.reset(
344 crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, raw_key));
345 if (!webm_decryption_key_.get()) {
346 return false;
347 }
348
349 hmac_key_ = DeriveKey(secret_, kWebmHmacSeed, kWebmSha1DigestSize);
350 if (hmac_key_.empty()) {
351 return false;
352 }
353
305 return true; 354 return true;
306 } 355 }
307 356
308 } // namespace media 357 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698