OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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/cdm/aes_decryptor.h" | 5 #include "media/cdm/aes_decryptor.h" |
6 | 6 |
7 #include <list> | 7 #include <list> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/stl_util.h" | 11 #include "base/stl_util.h" |
12 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
13 #include "crypto/encryptor.h" | 13 #include "crypto/encryptor.h" |
14 #include "crypto/symmetric_key.h" | 14 #include "crypto/symmetric_key.h" |
15 #include "media/base/audio_decoder_config.h" | 15 #include "media/base/audio_decoder_config.h" |
| 16 #include "media/base/cdm_promise.h" |
16 #include "media/base/decoder_buffer.h" | 17 #include "media/base/decoder_buffer.h" |
17 #include "media/base/decrypt_config.h" | 18 #include "media/base/decrypt_config.h" |
18 #include "media/base/video_decoder_config.h" | 19 #include "media/base/video_decoder_config.h" |
19 #include "media/base/video_frame.h" | 20 #include "media/base/video_frame.h" |
20 #include "media/cdm/json_web_key.h" | 21 #include "media/cdm/json_web_key.h" |
21 | 22 |
22 namespace media { | 23 namespace media { |
23 | 24 |
24 // Keeps track of the session IDs and DecryptionKeys. The keys are ordered by | 25 // Keeps track of the session IDs and DecryptionKeys. The keys are ordered by |
25 // insertion time (last insertion is first). It takes ownership of the | 26 // insertion time (last insertion is first). It takes ownership of the |
26 // DecryptionKeys. | 27 // DecryptionKeys. |
27 class AesDecryptor::SessionIdDecryptionKeyMap { | 28 class AesDecryptor::SessionIdDecryptionKeyMap { |
28 // Use a std::list to actually hold the data. Insertion is always done | 29 // Use a std::list to actually hold the data. Insertion is always done |
29 // at the front, so the "latest" decryption key is always the first one | 30 // at the front, so the "latest" decryption key is always the first one |
30 // in the list. | 31 // in the list. |
31 typedef std::list<std::pair<uint32, DecryptionKey*> > KeyList; | 32 typedef std::list<std::pair<std::string, DecryptionKey*> > KeyList; |
32 | 33 |
33 public: | 34 public: |
34 SessionIdDecryptionKeyMap() {} | 35 SessionIdDecryptionKeyMap() {} |
35 ~SessionIdDecryptionKeyMap() { STLDeleteValues(&key_list_); } | 36 ~SessionIdDecryptionKeyMap() { STLDeleteValues(&key_list_); } |
36 | 37 |
37 // Replaces value if |session_id| is already present, or adds it if not. | 38 // Replaces value if |session_id| is already present, or adds it if not. |
38 // This |decryption_key| becomes the latest until another insertion or | 39 // This |decryption_key| becomes the latest until another insertion or |
39 // |session_id| is erased. | 40 // |session_id| is erased. |
40 void Insert(uint32 session_id, scoped_ptr<DecryptionKey> decryption_key); | 41 void Insert(const std::string& web_session_id, |
| 42 scoped_ptr<DecryptionKey> decryption_key); |
41 | 43 |
42 // Deletes the entry for |session_id| if present. | 44 // Deletes the entry for |session_id| if present. |
43 void Erase(const uint32 session_id); | 45 void Erase(const std::string& web_session_id); |
44 | 46 |
45 // Returns whether the list is empty | 47 // Returns whether the list is empty |
46 bool Empty() const { return key_list_.empty(); } | 48 bool Empty() const { return key_list_.empty(); } |
47 | 49 |
48 // Returns the last inserted DecryptionKey. | 50 // Returns the last inserted DecryptionKey. |
49 DecryptionKey* LatestDecryptionKey() { | 51 DecryptionKey* LatestDecryptionKey() { |
50 DCHECK(!key_list_.empty()); | 52 DCHECK(!key_list_.empty()); |
51 return key_list_.begin()->second; | 53 return key_list_.begin()->second; |
52 } | 54 } |
53 | 55 |
54 private: | 56 private: |
55 // Searches the list for an element with |session_id|. | 57 // Searches the list for an element with |web_session_id|. |
56 KeyList::iterator Find(const uint32 session_id); | 58 KeyList::iterator Find(const std::string& web_session_id); |
57 | 59 |
58 // Deletes the entry pointed to by |position|. | 60 // Deletes the entry pointed to by |position|. |
59 void Erase(KeyList::iterator position); | 61 void Erase(KeyList::iterator position); |
60 | 62 |
61 KeyList key_list_; | 63 KeyList key_list_; |
62 | 64 |
63 DISALLOW_COPY_AND_ASSIGN(SessionIdDecryptionKeyMap); | 65 DISALLOW_COPY_AND_ASSIGN(SessionIdDecryptionKeyMap); |
64 }; | 66 }; |
65 | 67 |
66 void AesDecryptor::SessionIdDecryptionKeyMap::Insert( | 68 void AesDecryptor::SessionIdDecryptionKeyMap::Insert( |
67 uint32 session_id, | 69 const std::string& web_session_id, |
68 scoped_ptr<DecryptionKey> decryption_key) { | 70 scoped_ptr<DecryptionKey> decryption_key) { |
69 KeyList::iterator it = Find(session_id); | 71 KeyList::iterator it = Find(web_session_id); |
70 if (it != key_list_.end()) | 72 if (it != key_list_.end()) |
71 Erase(it); | 73 Erase(it); |
72 DecryptionKey* raw_ptr = decryption_key.release(); | 74 DecryptionKey* raw_ptr = decryption_key.release(); |
73 key_list_.push_front(std::make_pair(session_id, raw_ptr)); | 75 key_list_.push_front(std::make_pair(web_session_id, raw_ptr)); |
74 } | 76 } |
75 | 77 |
76 void AesDecryptor::SessionIdDecryptionKeyMap::Erase(const uint32 session_id) { | 78 void AesDecryptor::SessionIdDecryptionKeyMap::Erase( |
77 KeyList::iterator it = Find(session_id); | 79 const std::string& web_session_id) { |
| 80 KeyList::iterator it = Find(web_session_id); |
78 if (it == key_list_.end()) | 81 if (it == key_list_.end()) |
79 return; | 82 return; |
80 Erase(it); | 83 Erase(it); |
81 } | 84 } |
82 | 85 |
83 AesDecryptor::SessionIdDecryptionKeyMap::KeyList::iterator | 86 AesDecryptor::SessionIdDecryptionKeyMap::KeyList::iterator |
84 AesDecryptor::SessionIdDecryptionKeyMap::Find(const uint32 session_id) { | 87 AesDecryptor::SessionIdDecryptionKeyMap::Find( |
| 88 const std::string& web_session_id) { |
85 for (KeyList::iterator it = key_list_.begin(); it != key_list_.end(); ++it) { | 89 for (KeyList::iterator it = key_list_.begin(); it != key_list_.end(); ++it) { |
86 if (it->first == session_id) | 90 if (it->first == web_session_id) |
87 return it; | 91 return it; |
88 } | 92 } |
89 return key_list_.end(); | 93 return key_list_.end(); |
90 } | 94 } |
91 | 95 |
92 void AesDecryptor::SessionIdDecryptionKeyMap::Erase( | 96 void AesDecryptor::SessionIdDecryptionKeyMap::Erase( |
93 KeyList::iterator position) { | 97 KeyList::iterator position) { |
94 DCHECK(position->second); | 98 DCHECK(position->second); |
95 delete position->second; | 99 delete position->second; |
96 key_list_.erase(position); | 100 key_list_.erase(position); |
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
208 DCHECK_EQ(decrypted_text.size(), encrypted_text.size()); | 212 DCHECK_EQ(decrypted_text.size(), encrypted_text.size()); |
209 | 213 |
210 scoped_refptr<DecoderBuffer> output = DecoderBuffer::CopyFrom( | 214 scoped_refptr<DecoderBuffer> output = DecoderBuffer::CopyFrom( |
211 reinterpret_cast<const uint8*>(sample), sample_size); | 215 reinterpret_cast<const uint8*>(sample), sample_size); |
212 CopySubsamples(subsamples, kDstContainsClearBytes, | 216 CopySubsamples(subsamples, kDstContainsClearBytes, |
213 reinterpret_cast<const uint8*>(decrypted_text.data()), | 217 reinterpret_cast<const uint8*>(decrypted_text.data()), |
214 output->writable_data()); | 218 output->writable_data()); |
215 return output; | 219 return output; |
216 } | 220 } |
217 | 221 |
218 AesDecryptor::AesDecryptor(const SessionCreatedCB& session_created_cb, | 222 AesDecryptor::AesDecryptor(const SessionMessageCB& session_message_cb) |
219 const SessionMessageCB& session_message_cb, | 223 : session_message_cb_(session_message_cb) { |
220 const SessionReadyCB& session_ready_cb, | 224 DCHECK(!session_message_cb_.is_null()); |
221 const SessionClosedCB& session_closed_cb, | 225 } |
222 const SessionErrorCB& session_error_cb) | |
223 : session_created_cb_(session_created_cb), | |
224 session_message_cb_(session_message_cb), | |
225 session_ready_cb_(session_ready_cb), | |
226 session_closed_cb_(session_closed_cb), | |
227 session_error_cb_(session_error_cb) {} | |
228 | 226 |
229 AesDecryptor::~AesDecryptor() { | 227 AesDecryptor::~AesDecryptor() { |
230 key_map_.clear(); | 228 key_map_.clear(); |
231 } | 229 } |
232 | 230 |
233 bool AesDecryptor::CreateSession(uint32 session_id, | 231 void AesDecryptor::CreateSession(const std::string& init_data_type, |
234 const std::string& content_type, | |
235 const uint8* init_data, | 232 const uint8* init_data, |
236 int init_data_length) { | 233 int init_data_length, |
237 // Validate that this is a new session. | 234 SessionType session_type, |
238 DCHECK(valid_sessions_.find(session_id) == valid_sessions_.end()); | 235 scoped_ptr<NewSessionCdmPromise> promise) { |
239 valid_sessions_.insert(session_id); | 236 std::string web_session_id(base::UintToString(next_web_session_id_++)); |
| 237 valid_sessions_.insert(web_session_id); |
240 | 238 |
241 std::string web_session_id_string(base::UintToString(next_web_session_id_++)); | 239 // For now, the AesDecryptor does not care about |init_data_type| or |
242 | 240 // |session_type|; just resolve the promise and then fire a message event |
243 // For now, the AesDecryptor does not care about |content_type|; | 241 // with the |init_data| as the request. |
244 // just fire the event with the |init_data| as the request. | 242 // TODO(jrummell): Validate |init_data_type| and |session_type|. |
245 std::vector<uint8> message; | 243 std::vector<uint8> message; |
246 if (init_data && init_data_length) | 244 if (init_data && init_data_length) |
247 message.assign(init_data, init_data + init_data_length); | 245 message.assign(init_data, init_data + init_data_length); |
248 | 246 |
249 session_created_cb_.Run(session_id, web_session_id_string); | 247 promise->resolve(web_session_id); |
250 session_message_cb_.Run(session_id, message, GURL()); | 248 |
251 return true; | 249 session_message_cb_.Run(web_session_id, message, GURL()); |
252 } | 250 } |
253 | 251 |
254 void AesDecryptor::LoadSession(uint32 session_id, | 252 void AesDecryptor::LoadSession(const std::string& web_session_id, |
255 const std::string& web_session_id) { | 253 scoped_ptr<NewSessionCdmPromise> promise) { |
256 // TODO(xhwang): Change this to NOTREACHED() when blink checks for key systems | 254 // TODO(xhwang): Change this to NOTREACHED() when blink checks for key systems |
257 // that do not support loadSession. See http://crbug.com/342481 | 255 // that do not support loadSession. See http://crbug.com/342481 |
258 session_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0); | 256 promise->reject(NOT_SUPPORTED_ERROR, 0, "LoadSession() is not supported."); |
259 } | 257 } |
260 | 258 |
261 void AesDecryptor::UpdateSession(uint32 session_id, | 259 void AesDecryptor::UpdateSession(const std::string& web_session_id, |
262 const uint8* response, | 260 const uint8* response, |
263 int response_length) { | 261 int response_length, |
| 262 scoped_ptr<SimpleCdmPromise> promise) { |
264 CHECK(response); | 263 CHECK(response); |
265 CHECK_GT(response_length, 0); | 264 CHECK_GT(response_length, 0); |
266 DCHECK(valid_sessions_.find(session_id) != valid_sessions_.end()); | 265 |
| 266 // TODO(jrummell): Convert back to a DCHECK once prefixed EME is removed. |
| 267 if (valid_sessions_.find(web_session_id) == valid_sessions_.end()) { |
| 268 promise->reject(INVALID_ACCESS_ERROR, 0, "Session does not exist."); |
| 269 return; |
| 270 } |
267 | 271 |
268 std::string key_string(reinterpret_cast<const char*>(response), | 272 std::string key_string(reinterpret_cast<const char*>(response), |
269 response_length); | 273 response_length); |
| 274 |
270 KeyIdAndKeyPairs keys; | 275 KeyIdAndKeyPairs keys; |
271 if (!ExtractKeysFromJWKSet(key_string, &keys)) { | 276 if (!ExtractKeysFromJWKSet(key_string, &keys)) { |
272 session_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0); | 277 promise->reject( |
| 278 INVALID_ACCESS_ERROR, 0, "response is not a valid JSON Web Key Set."); |
273 return; | 279 return; |
274 } | 280 } |
275 | 281 |
276 // Make sure that at least one key was extracted. | 282 // Make sure that at least one key was extracted. |
277 if (keys.empty()) { | 283 if (keys.empty()) { |
278 session_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0); | 284 promise->reject( |
| 285 INVALID_ACCESS_ERROR, 0, "response does not contain any keys."); |
279 return; | 286 return; |
280 } | 287 } |
281 | 288 |
282 for (KeyIdAndKeyPairs::iterator it = keys.begin(); it != keys.end(); ++it) { | 289 for (KeyIdAndKeyPairs::iterator it = keys.begin(); it != keys.end(); ++it) { |
283 if (it->second.length() != | 290 if (it->second.length() != |
284 static_cast<size_t>(DecryptConfig::kDecryptionKeySize)) { | 291 static_cast<size_t>(DecryptConfig::kDecryptionKeySize)) { |
285 DVLOG(1) << "Invalid key length: " << key_string.length(); | 292 DVLOG(1) << "Invalid key length: " << key_string.length(); |
286 session_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0); | 293 promise->reject(INVALID_ACCESS_ERROR, 0, "Invalid key length."); |
287 return; | 294 return; |
288 } | 295 } |
289 if (!AddDecryptionKey(session_id, it->first, it->second)) { | 296 if (!AddDecryptionKey(web_session_id, it->first, it->second)) { |
290 session_error_cb_.Run(session_id, MediaKeys::kUnknownError, 0); | 297 promise->reject(INVALID_ACCESS_ERROR, 0, "Unable to add key."); |
291 return; | 298 return; |
292 } | 299 } |
293 } | 300 } |
294 | 301 |
295 { | 302 { |
296 base::AutoLock auto_lock(new_key_cb_lock_); | 303 base::AutoLock auto_lock(new_key_cb_lock_); |
297 | 304 |
298 if (!new_audio_key_cb_.is_null()) | 305 if (!new_audio_key_cb_.is_null()) |
299 new_audio_key_cb_.Run(); | 306 new_audio_key_cb_.Run(); |
300 | 307 |
301 if (!new_video_key_cb_.is_null()) | 308 if (!new_video_key_cb_.is_null()) |
302 new_video_key_cb_.Run(); | 309 new_video_key_cb_.Run(); |
303 } | 310 } |
304 | 311 |
305 session_ready_cb_.Run(session_id); | 312 promise->resolve(); |
306 } | 313 } |
307 | 314 |
308 void AesDecryptor::ReleaseSession(uint32 session_id) { | 315 void AesDecryptor::ReleaseSession(const std::string& web_session_id, |
| 316 scoped_ptr<SimpleCdmPromise> promise) { |
309 // Validate that this is a reference to an active session and then forget it. | 317 // Validate that this is a reference to an active session and then forget it. |
310 std::set<uint32>::iterator it = valid_sessions_.find(session_id); | 318 std::set<std::string>::iterator it = valid_sessions_.find(web_session_id); |
311 DCHECK(it != valid_sessions_.end()); | 319 // TODO(jrummell): Convert back to a DCHECK once prefixed EME is removed. |
| 320 if (it == valid_sessions_.end()) { |
| 321 promise->reject(INVALID_ACCESS_ERROR, 0, "Session does not exist."); |
| 322 return; |
| 323 } |
| 324 |
312 valid_sessions_.erase(it); | 325 valid_sessions_.erase(it); |
313 | 326 |
314 DeleteKeysForSession(session_id); | 327 // Close the session. |
315 session_closed_cb_.Run(session_id); | 328 DeleteKeysForSession(web_session_id); |
| 329 promise->resolve(); |
316 } | 330 } |
317 | 331 |
318 Decryptor* AesDecryptor::GetDecryptor() { | 332 Decryptor* AesDecryptor::GetDecryptor() { |
319 return this; | 333 return this; |
320 } | 334 } |
321 | 335 |
322 void AesDecryptor::RegisterNewKeyCB(StreamType stream_type, | 336 void AesDecryptor::RegisterNewKeyCB(StreamType stream_type, |
323 const NewKeyCB& new_key_cb) { | 337 const NewKeyCB& new_key_cb) { |
324 base::AutoLock auto_lock(new_key_cb_lock_); | 338 base::AutoLock auto_lock(new_key_cb_lock_); |
325 | 339 |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
397 } | 411 } |
398 | 412 |
399 void AesDecryptor::ResetDecoder(StreamType stream_type) { | 413 void AesDecryptor::ResetDecoder(StreamType stream_type) { |
400 NOTREACHED() << "AesDecryptor does not support audio/video decoding"; | 414 NOTREACHED() << "AesDecryptor does not support audio/video decoding"; |
401 } | 415 } |
402 | 416 |
403 void AesDecryptor::DeinitializeDecoder(StreamType stream_type) { | 417 void AesDecryptor::DeinitializeDecoder(StreamType stream_type) { |
404 NOTREACHED() << "AesDecryptor does not support audio/video decoding"; | 418 NOTREACHED() << "AesDecryptor does not support audio/video decoding"; |
405 } | 419 } |
406 | 420 |
407 bool AesDecryptor::AddDecryptionKey(const uint32 session_id, | 421 bool AesDecryptor::AddDecryptionKey(const std::string& web_session_id, |
408 const std::string& key_id, | 422 const std::string& key_id, |
409 const std::string& key_string) { | 423 const std::string& key_string) { |
410 scoped_ptr<DecryptionKey> decryption_key(new DecryptionKey(key_string)); | 424 scoped_ptr<DecryptionKey> decryption_key(new DecryptionKey(key_string)); |
411 if (!decryption_key) { | |
412 DVLOG(1) << "Could not create key."; | |
413 return false; | |
414 } | |
415 | |
416 if (!decryption_key->Init()) { | 425 if (!decryption_key->Init()) { |
417 DVLOG(1) << "Could not initialize decryption key."; | 426 DVLOG(1) << "Could not initialize decryption key."; |
418 return false; | 427 return false; |
419 } | 428 } |
420 | 429 |
421 base::AutoLock auto_lock(key_map_lock_); | 430 base::AutoLock auto_lock(key_map_lock_); |
422 KeyIdToSessionKeysMap::iterator key_id_entry = key_map_.find(key_id); | 431 KeyIdToSessionKeysMap::iterator key_id_entry = key_map_.find(key_id); |
423 if (key_id_entry != key_map_.end()) { | 432 if (key_id_entry != key_map_.end()) { |
424 key_id_entry->second->Insert(session_id, decryption_key.Pass()); | 433 key_id_entry->second->Insert(web_session_id, decryption_key.Pass()); |
425 return true; | 434 return true; |
426 } | 435 } |
427 | 436 |
428 // |key_id| not found, so need to create new entry. | 437 // |key_id| not found, so need to create new entry. |
429 scoped_ptr<SessionIdDecryptionKeyMap> inner_map( | 438 scoped_ptr<SessionIdDecryptionKeyMap> inner_map( |
430 new SessionIdDecryptionKeyMap()); | 439 new SessionIdDecryptionKeyMap()); |
431 inner_map->Insert(session_id, decryption_key.Pass()); | 440 inner_map->Insert(web_session_id, decryption_key.Pass()); |
432 key_map_.add(key_id, inner_map.Pass()); | 441 key_map_.add(key_id, inner_map.Pass()); |
433 return true; | 442 return true; |
434 } | 443 } |
435 | 444 |
436 AesDecryptor::DecryptionKey* AesDecryptor::GetKey( | 445 AesDecryptor::DecryptionKey* AesDecryptor::GetKey( |
437 const std::string& key_id) const { | 446 const std::string& key_id) const { |
438 base::AutoLock auto_lock(key_map_lock_); | 447 base::AutoLock auto_lock(key_map_lock_); |
439 KeyIdToSessionKeysMap::const_iterator key_id_found = key_map_.find(key_id); | 448 KeyIdToSessionKeysMap::const_iterator key_id_found = key_map_.find(key_id); |
440 if (key_id_found == key_map_.end()) | 449 if (key_id_found == key_map_.end()) |
441 return NULL; | 450 return NULL; |
442 | 451 |
443 // Return the key from the "latest" session_id entry. | 452 // Return the key from the "latest" session_id entry. |
444 return key_id_found->second->LatestDecryptionKey(); | 453 return key_id_found->second->LatestDecryptionKey(); |
445 } | 454 } |
446 | 455 |
447 void AesDecryptor::DeleteKeysForSession(const uint32 session_id) { | 456 void AesDecryptor::DeleteKeysForSession(const std::string& web_session_id) { |
448 base::AutoLock auto_lock(key_map_lock_); | 457 base::AutoLock auto_lock(key_map_lock_); |
449 | 458 |
450 // Remove all keys associated with |session_id|. Since the data is optimized | 459 // Remove all keys associated with |web_session_id|. Since the data is |
451 // for access in GetKey(), we need to look at each entry in |key_map_|. | 460 // optimized for access in GetKey(), we need to look at each entry in |
| 461 // |key_map_|. |
452 KeyIdToSessionKeysMap::iterator it = key_map_.begin(); | 462 KeyIdToSessionKeysMap::iterator it = key_map_.begin(); |
453 while (it != key_map_.end()) { | 463 while (it != key_map_.end()) { |
454 it->second->Erase(session_id); | 464 it->second->Erase(web_session_id); |
455 if (it->second->Empty()) { | 465 if (it->second->Empty()) { |
456 // Need to get rid of the entry for this key_id. This will mess up the | 466 // Need to get rid of the entry for this key_id. This will mess up the |
457 // iterator, so we need to increment it first. | 467 // iterator, so we need to increment it first. |
458 KeyIdToSessionKeysMap::iterator current = it; | 468 KeyIdToSessionKeysMap::iterator current = it; |
459 ++it; | 469 ++it; |
460 key_map_.erase(current); | 470 key_map_.erase(current); |
461 } else { | 471 } else { |
462 ++it; | 472 ++it; |
463 } | 473 } |
464 } | 474 } |
465 } | 475 } |
466 | 476 |
467 AesDecryptor::DecryptionKey::DecryptionKey(const std::string& secret) | 477 AesDecryptor::DecryptionKey::DecryptionKey(const std::string& secret) |
468 : secret_(secret) { | 478 : secret_(secret) { |
469 } | 479 } |
470 | 480 |
471 AesDecryptor::DecryptionKey::~DecryptionKey() {} | 481 AesDecryptor::DecryptionKey::~DecryptionKey() {} |
472 | 482 |
473 bool AesDecryptor::DecryptionKey::Init() { | 483 bool AesDecryptor::DecryptionKey::Init() { |
474 CHECK(!secret_.empty()); | 484 CHECK(!secret_.empty()); |
475 decryption_key_.reset(crypto::SymmetricKey::Import( | 485 decryption_key_.reset(crypto::SymmetricKey::Import( |
476 crypto::SymmetricKey::AES, secret_)); | 486 crypto::SymmetricKey::AES, secret_)); |
477 if (!decryption_key_) | 487 if (!decryption_key_) |
478 return false; | 488 return false; |
479 return true; | 489 return true; |
480 } | 490 } |
481 | 491 |
482 } // namespace media | 492 } // namespace media |
OLD | NEW |