Roll PDFium 6f34355..bca779d
[chromium-blink-merge.git] / media / cdm / aes_decryptor.cc
blob8c110fa0b2bbe8b90e5246a608258a1ade8d8ca4
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
3 // found in the LICENSE file.
5 #include "media/cdm/aes_decryptor.h"
7 #include <list>
8 #include <vector>
10 #include "base/logging.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "crypto/encryptor.h"
14 #include "crypto/symmetric_key.h"
15 #include "media/base/audio_decoder_config.h"
16 #include "media/base/cdm_key_information.h"
17 #include "media/base/cdm_promise.h"
18 #include "media/base/decoder_buffer.h"
19 #include "media/base/decrypt_config.h"
20 #include "media/base/video_decoder_config.h"
21 #include "media/base/video_frame.h"
22 #include "media/cdm/json_web_key.h"
24 #if defined(USE_PROPRIETARY_CODECS)
25 #include "media/cdm/cenc_utils.h"
26 #endif
28 namespace media {
30 // Keeps track of the session IDs and DecryptionKeys. The keys are ordered by
31 // insertion time (last insertion is first). It takes ownership of the
32 // DecryptionKeys.
33 class AesDecryptor::SessionIdDecryptionKeyMap {
34 // Use a std::list to actually hold the data. Insertion is always done
35 // at the front, so the "latest" decryption key is always the first one
36 // in the list.
37 typedef std::list<std::pair<std::string, DecryptionKey*> > KeyList;
39 public:
40 SessionIdDecryptionKeyMap() {}
41 ~SessionIdDecryptionKeyMap() { STLDeleteValues(&key_list_); }
43 // Replaces value if |session_id| is already present, or adds it if not.
44 // This |decryption_key| becomes the latest until another insertion or
45 // |session_id| is erased.
46 void Insert(const std::string& session_id,
47 scoped_ptr<DecryptionKey> decryption_key);
49 // Deletes the entry for |session_id| if present.
50 void Erase(const std::string& session_id);
52 // Returns whether the list is empty
53 bool Empty() const { return key_list_.empty(); }
55 // Returns the last inserted DecryptionKey.
56 DecryptionKey* LatestDecryptionKey() {
57 DCHECK(!key_list_.empty());
58 return key_list_.begin()->second;
61 bool Contains(const std::string& session_id) {
62 return Find(session_id) != key_list_.end();
65 private:
66 // Searches the list for an element with |session_id|.
67 KeyList::iterator Find(const std::string& session_id);
69 // Deletes the entry pointed to by |position|.
70 void Erase(KeyList::iterator position);
72 KeyList key_list_;
74 DISALLOW_COPY_AND_ASSIGN(SessionIdDecryptionKeyMap);
77 void AesDecryptor::SessionIdDecryptionKeyMap::Insert(
78 const std::string& session_id,
79 scoped_ptr<DecryptionKey> decryption_key) {
80 KeyList::iterator it = Find(session_id);
81 if (it != key_list_.end())
82 Erase(it);
83 DecryptionKey* raw_ptr = decryption_key.release();
84 key_list_.push_front(std::make_pair(session_id, raw_ptr));
87 void AesDecryptor::SessionIdDecryptionKeyMap::Erase(
88 const std::string& session_id) {
89 KeyList::iterator it = Find(session_id);
90 if (it == key_list_.end())
91 return;
92 Erase(it);
95 AesDecryptor::SessionIdDecryptionKeyMap::KeyList::iterator
96 AesDecryptor::SessionIdDecryptionKeyMap::Find(const std::string& session_id) {
97 for (KeyList::iterator it = key_list_.begin(); it != key_list_.end(); ++it) {
98 if (it->first == session_id)
99 return it;
101 return key_list_.end();
104 void AesDecryptor::SessionIdDecryptionKeyMap::Erase(
105 KeyList::iterator position) {
106 DCHECK(position->second);
107 delete position->second;
108 key_list_.erase(position);
111 uint32_t AesDecryptor::next_session_id_ = 1;
113 enum ClearBytesBufferSel {
114 kSrcContainsClearBytes,
115 kDstContainsClearBytes
118 static void CopySubsamples(const std::vector<SubsampleEntry>& subsamples,
119 const ClearBytesBufferSel sel,
120 const uint8_t* src,
121 uint8_t* dst) {
122 for (size_t i = 0; i < subsamples.size(); i++) {
123 const SubsampleEntry& subsample = subsamples[i];
124 if (sel == kSrcContainsClearBytes) {
125 src += subsample.clear_bytes;
126 } else {
127 dst += subsample.clear_bytes;
129 memcpy(dst, src, subsample.cypher_bytes);
130 src += subsample.cypher_bytes;
131 dst += subsample.cypher_bytes;
135 // Decrypts |input| using |key|. Returns a DecoderBuffer with the decrypted
136 // data if decryption succeeded or NULL if decryption failed.
137 static scoped_refptr<DecoderBuffer> DecryptData(const DecoderBuffer& input,
138 crypto::SymmetricKey* key) {
139 CHECK(input.data_size());
140 CHECK(input.decrypt_config());
141 CHECK(key);
143 crypto::Encryptor encryptor;
144 if (!encryptor.Init(key, crypto::Encryptor::CTR, "")) {
145 DVLOG(1) << "Could not initialize decryptor.";
146 return NULL;
149 DCHECK_EQ(input.decrypt_config()->iv().size(),
150 static_cast<size_t>(DecryptConfig::kDecryptionKeySize));
151 if (!encryptor.SetCounter(input.decrypt_config()->iv())) {
152 DVLOG(1) << "Could not set counter block.";
153 return NULL;
156 const char* sample = reinterpret_cast<const char*>(input.data());
157 size_t sample_size = static_cast<size_t>(input.data_size());
159 DCHECK_GT(sample_size, 0U) << "No sample data to be decrypted.";
160 if (sample_size == 0)
161 return NULL;
163 if (input.decrypt_config()->subsamples().empty()) {
164 std::string decrypted_text;
165 base::StringPiece encrypted_text(sample, sample_size);
166 if (!encryptor.Decrypt(encrypted_text, &decrypted_text)) {
167 DVLOG(1) << "Could not decrypt data.";
168 return NULL;
171 // TODO(xhwang): Find a way to avoid this data copy.
172 return DecoderBuffer::CopyFrom(
173 reinterpret_cast<const uint8_t*>(decrypted_text.data()),
174 decrypted_text.size());
177 const std::vector<SubsampleEntry>& subsamples =
178 input.decrypt_config()->subsamples();
180 size_t total_clear_size = 0;
181 size_t total_encrypted_size = 0;
182 for (size_t i = 0; i < subsamples.size(); i++) {
183 total_clear_size += subsamples[i].clear_bytes;
184 total_encrypted_size += subsamples[i].cypher_bytes;
185 // Check for overflow. This check is valid because *_size is unsigned.
186 DCHECK(total_clear_size >= subsamples[i].clear_bytes);
187 if (total_encrypted_size < subsamples[i].cypher_bytes)
188 return NULL;
190 size_t total_size = total_clear_size + total_encrypted_size;
191 if (total_size < total_clear_size || total_size != sample_size) {
192 DVLOG(1) << "Subsample sizes do not equal input size";
193 return NULL;
196 // No need to decrypt if there is no encrypted data.
197 if (total_encrypted_size <= 0) {
198 return DecoderBuffer::CopyFrom(reinterpret_cast<const uint8_t*>(sample),
199 sample_size);
202 // The encrypted portions of all subsamples must form a contiguous block,
203 // such that an encrypted subsample that ends away from a block boundary is
204 // immediately followed by the start of the next encrypted subsample. We
205 // copy all encrypted subsamples to a contiguous buffer, decrypt them, then
206 // copy the decrypted bytes over the encrypted bytes in the output.
207 // TODO(strobe): attempt to reduce number of memory copies
208 scoped_ptr<uint8_t[]> encrypted_bytes(new uint8_t[total_encrypted_size]);
209 CopySubsamples(subsamples, kSrcContainsClearBytes,
210 reinterpret_cast<const uint8_t*>(sample),
211 encrypted_bytes.get());
213 base::StringPiece encrypted_text(
214 reinterpret_cast<const char*>(encrypted_bytes.get()),
215 total_encrypted_size);
216 std::string decrypted_text;
217 if (!encryptor.Decrypt(encrypted_text, &decrypted_text)) {
218 DVLOG(1) << "Could not decrypt data.";
219 return NULL;
221 DCHECK_EQ(decrypted_text.size(), encrypted_text.size());
223 scoped_refptr<DecoderBuffer> output = DecoderBuffer::CopyFrom(
224 reinterpret_cast<const uint8_t*>(sample), sample_size);
225 CopySubsamples(subsamples, kDstContainsClearBytes,
226 reinterpret_cast<const uint8_t*>(decrypted_text.data()),
227 output->writable_data());
228 return output;
231 AesDecryptor::AesDecryptor(const GURL& /* security_origin */,
232 const SessionMessageCB& session_message_cb,
233 const SessionClosedCB& session_closed_cb,
234 const SessionKeysChangeCB& session_keys_change_cb)
235 : session_message_cb_(session_message_cb),
236 session_closed_cb_(session_closed_cb),
237 session_keys_change_cb_(session_keys_change_cb) {
238 // AesDecryptor doesn't keep any persistent data, so no need to do anything
239 // with |security_origin|.
240 DCHECK(!session_message_cb_.is_null());
241 DCHECK(!session_closed_cb_.is_null());
242 DCHECK(!session_keys_change_cb_.is_null());
245 AesDecryptor::~AesDecryptor() {
246 key_map_.clear();
249 void AesDecryptor::SetServerCertificate(const std::vector<uint8_t>& certificate,
250 scoped_ptr<SimpleCdmPromise> promise) {
251 promise->reject(
252 NOT_SUPPORTED_ERROR, 0, "SetServerCertificate() is not supported.");
255 void AesDecryptor::CreateSessionAndGenerateRequest(
256 SessionType session_type,
257 EmeInitDataType init_data_type,
258 const std::vector<uint8_t>& init_data,
259 scoped_ptr<NewSessionCdmPromise> promise) {
260 std::string session_id(base::UintToString(next_session_id_++));
261 valid_sessions_.insert(session_id);
263 // For now, the AesDecryptor does not care about |session_type|.
264 // TODO(jrummell): Validate |session_type|.
266 std::vector<uint8_t> message;
267 // TODO(jrummell): Since unprefixed will never send NULL, remove this check
268 // when prefixed EME is removed (http://crbug.com/249976).
269 if (!init_data.empty()) {
270 std::vector<std::vector<uint8_t>> keys;
271 switch (init_data_type) {
272 case EmeInitDataType::WEBM:
273 // |init_data| is simply the key needed.
274 keys.push_back(init_data);
275 break;
276 case EmeInitDataType::CENC:
277 #if defined(USE_PROPRIETARY_CODECS)
278 // |init_data| is a set of 0 or more concatenated 'pssh' boxes.
279 if (!GetKeyIdsForCommonSystemId(init_data, &keys)) {
280 promise->reject(NOT_SUPPORTED_ERROR, 0,
281 "No supported PSSH box found.");
282 return;
284 break;
285 #else
286 promise->reject(NOT_SUPPORTED_ERROR, 0,
287 "Initialization data type CENC is not supported.");
288 return;
289 #endif
290 case EmeInitDataType::KEYIDS: {
291 std::string init_data_string(init_data.begin(), init_data.end());
292 std::string error_message;
293 if (!ExtractKeyIdsFromKeyIdsInitData(init_data_string, &keys,
294 &error_message)) {
295 promise->reject(NOT_SUPPORTED_ERROR, 0, error_message);
296 return;
298 break;
300 default:
301 NOTREACHED();
302 promise->reject(NOT_SUPPORTED_ERROR, 0,
303 "init_data_type not supported.");
304 return;
306 CreateLicenseRequest(keys, session_type, &message);
309 promise->resolve(session_id);
311 // No URL needed for license requests.
312 session_message_cb_.Run(session_id, LICENSE_REQUEST, message,
313 GURL::EmptyGURL());
316 void AesDecryptor::LoadSession(SessionType session_type,
317 const std::string& session_id,
318 scoped_ptr<NewSessionCdmPromise> promise) {
319 // TODO(xhwang): Change this to NOTREACHED() when blink checks for key systems
320 // that do not support loadSession. See http://crbug.com/342481
321 promise->reject(NOT_SUPPORTED_ERROR, 0, "LoadSession() is not supported.");
324 void AesDecryptor::UpdateSession(const std::string& session_id,
325 const std::vector<uint8_t>& response,
326 scoped_ptr<SimpleCdmPromise> promise) {
327 CHECK(!response.empty());
329 // TODO(jrummell): Convert back to a DCHECK once prefixed EME is removed.
330 if (valid_sessions_.find(session_id) == valid_sessions_.end()) {
331 promise->reject(INVALID_ACCESS_ERROR, 0, "Session does not exist.");
332 return;
335 std::string key_string(response.begin(), response.end());
337 KeyIdAndKeyPairs keys;
338 SessionType session_type = MediaKeys::TEMPORARY_SESSION;
339 if (!ExtractKeysFromJWKSet(key_string, &keys, &session_type)) {
340 promise->reject(
341 INVALID_ACCESS_ERROR, 0, "Response is not a valid JSON Web Key Set.");
342 return;
345 // Make sure that at least one key was extracted.
346 if (keys.empty()) {
347 promise->reject(
348 INVALID_ACCESS_ERROR, 0, "Response does not contain any keys.");
349 return;
352 bool key_added = false;
353 for (KeyIdAndKeyPairs::iterator it = keys.begin(); it != keys.end(); ++it) {
354 if (it->second.length() !=
355 static_cast<size_t>(DecryptConfig::kDecryptionKeySize)) {
356 DVLOG(1) << "Invalid key length: " << it->second.length();
357 promise->reject(INVALID_ACCESS_ERROR, 0, "Invalid key length.");
358 return;
361 // If this key_id doesn't currently exist in this session,
362 // a new key is added.
363 if (!HasKey(session_id, it->first))
364 key_added = true;
366 if (!AddDecryptionKey(session_id, it->first, it->second)) {
367 promise->reject(INVALID_ACCESS_ERROR, 0, "Unable to add key.");
368 return;
373 base::AutoLock auto_lock(new_key_cb_lock_);
375 if (!new_audio_key_cb_.is_null())
376 new_audio_key_cb_.Run();
378 if (!new_video_key_cb_.is_null())
379 new_video_key_cb_.Run();
382 promise->resolve();
384 // Create the list of all available keys for this session.
385 CdmKeysInfo keys_info;
387 base::AutoLock auto_lock(key_map_lock_);
388 for (const auto& item : key_map_) {
389 if (item.second->Contains(session_id)) {
390 scoped_ptr<CdmKeyInformation> key_info(new CdmKeyInformation);
391 key_info->key_id.assign(item.first.begin(), item.first.end());
392 key_info->status = CdmKeyInformation::USABLE;
393 key_info->system_code = 0;
394 keys_info.push_back(key_info.release());
399 session_keys_change_cb_.Run(session_id, key_added, keys_info.Pass());
402 void AesDecryptor::CloseSession(const std::string& session_id,
403 scoped_ptr<SimpleCdmPromise> promise) {
404 // Validate that this is a reference to an active session and then forget it.
405 std::set<std::string>::iterator it = valid_sessions_.find(session_id);
406 DCHECK(it != valid_sessions_.end());
408 valid_sessions_.erase(it);
410 // Close the session.
411 DeleteKeysForSession(session_id);
412 promise->resolve();
413 session_closed_cb_.Run(session_id);
416 void AesDecryptor::RemoveSession(const std::string& session_id,
417 scoped_ptr<SimpleCdmPromise> promise) {
418 // AesDecryptor doesn't keep any persistent data, so this should be
419 // NOT_REACHED().
420 // TODO(jrummell): Make sure persistent session types are rejected.
421 // http://crbug.com/384152.
423 // However, v0.1b calls to CancelKeyRequest() will call this, so close the
424 // session, if it exists.
425 // TODO(jrummell): Remove the close() call when prefixed EME is removed.
426 // http://crbug.com/249976.
427 if (valid_sessions_.find(session_id) != valid_sessions_.end()) {
428 CloseSession(session_id, promise.Pass());
429 return;
432 promise->reject(INVALID_ACCESS_ERROR, 0, "Session does not exist.");
435 CdmContext* AesDecryptor::GetCdmContext() {
436 return this;
439 Decryptor* AesDecryptor::GetDecryptor() {
440 return this;
443 int AesDecryptor::GetCdmId() const {
444 return kInvalidCdmId;
447 void AesDecryptor::RegisterNewKeyCB(StreamType stream_type,
448 const NewKeyCB& new_key_cb) {
449 base::AutoLock auto_lock(new_key_cb_lock_);
451 switch (stream_type) {
452 case kAudio:
453 new_audio_key_cb_ = new_key_cb;
454 break;
455 case kVideo:
456 new_video_key_cb_ = new_key_cb;
457 break;
458 default:
459 NOTREACHED();
463 void AesDecryptor::Decrypt(StreamType stream_type,
464 const scoped_refptr<DecoderBuffer>& encrypted,
465 const DecryptCB& decrypt_cb) {
466 CHECK(encrypted->decrypt_config());
468 scoped_refptr<DecoderBuffer> decrypted;
469 // An empty iv string signals that the frame is unencrypted.
470 if (encrypted->decrypt_config()->iv().empty()) {
471 decrypted = DecoderBuffer::CopyFrom(encrypted->data(),
472 encrypted->data_size());
473 } else {
474 const std::string& key_id = encrypted->decrypt_config()->key_id();
475 base::AutoLock auto_lock(key_map_lock_);
476 DecryptionKey* key = GetKey_Locked(key_id);
477 if (!key) {
478 DVLOG(1) << "Could not find a matching key for the given key ID.";
479 decrypt_cb.Run(kNoKey, NULL);
480 return;
483 crypto::SymmetricKey* decryption_key = key->decryption_key();
484 decrypted = DecryptData(*encrypted.get(), decryption_key);
485 if (!decrypted.get()) {
486 DVLOG(1) << "Decryption failed.";
487 decrypt_cb.Run(kError, NULL);
488 return;
492 decrypted->set_timestamp(encrypted->timestamp());
493 decrypted->set_duration(encrypted->duration());
494 decrypt_cb.Run(kSuccess, decrypted);
497 void AesDecryptor::CancelDecrypt(StreamType stream_type) {
498 // Decrypt() calls the DecryptCB synchronously so there's nothing to cancel.
501 void AesDecryptor::InitializeAudioDecoder(const AudioDecoderConfig& config,
502 const DecoderInitCB& init_cb) {
503 // AesDecryptor does not support audio decoding.
504 init_cb.Run(false);
507 void AesDecryptor::InitializeVideoDecoder(const VideoDecoderConfig& config,
508 const DecoderInitCB& init_cb) {
509 // AesDecryptor does not support video decoding.
510 init_cb.Run(false);
513 void AesDecryptor::DecryptAndDecodeAudio(
514 const scoped_refptr<DecoderBuffer>& encrypted,
515 const AudioDecodeCB& audio_decode_cb) {
516 NOTREACHED() << "AesDecryptor does not support audio decoding";
519 void AesDecryptor::DecryptAndDecodeVideo(
520 const scoped_refptr<DecoderBuffer>& encrypted,
521 const VideoDecodeCB& video_decode_cb) {
522 NOTREACHED() << "AesDecryptor does not support video decoding";
525 void AesDecryptor::ResetDecoder(StreamType stream_type) {
526 NOTREACHED() << "AesDecryptor does not support audio/video decoding";
529 void AesDecryptor::DeinitializeDecoder(StreamType stream_type) {
530 // AesDecryptor does not support audio/video decoding, but since this can be
531 // called any time after InitializeAudioDecoder/InitializeVideoDecoder,
532 // nothing to be done here.
535 bool AesDecryptor::AddDecryptionKey(const std::string& session_id,
536 const std::string& key_id,
537 const std::string& key_string) {
538 scoped_ptr<DecryptionKey> decryption_key(new DecryptionKey(key_string));
539 if (!decryption_key->Init()) {
540 DVLOG(1) << "Could not initialize decryption key.";
541 return false;
544 base::AutoLock auto_lock(key_map_lock_);
545 KeyIdToSessionKeysMap::iterator key_id_entry = key_map_.find(key_id);
546 if (key_id_entry != key_map_.end()) {
547 key_id_entry->second->Insert(session_id, decryption_key.Pass());
548 return true;
551 // |key_id| not found, so need to create new entry.
552 scoped_ptr<SessionIdDecryptionKeyMap> inner_map(
553 new SessionIdDecryptionKeyMap());
554 inner_map->Insert(session_id, decryption_key.Pass());
555 key_map_.add(key_id, inner_map.Pass());
556 return true;
559 AesDecryptor::DecryptionKey* AesDecryptor::GetKey_Locked(
560 const std::string& key_id) const {
561 key_map_lock_.AssertAcquired();
562 KeyIdToSessionKeysMap::const_iterator key_id_found = key_map_.find(key_id);
563 if (key_id_found == key_map_.end())
564 return NULL;
566 // Return the key from the "latest" session_id entry.
567 return key_id_found->second->LatestDecryptionKey();
570 bool AesDecryptor::HasKey(const std::string& session_id,
571 const std::string& key_id) {
572 base::AutoLock auto_lock(key_map_lock_);
573 KeyIdToSessionKeysMap::const_iterator key_id_found = key_map_.find(key_id);
574 if (key_id_found == key_map_.end())
575 return false;
577 return key_id_found->second->Contains(session_id);
580 void AesDecryptor::DeleteKeysForSession(const std::string& session_id) {
581 base::AutoLock auto_lock(key_map_lock_);
583 // Remove all keys associated with |session_id|. Since the data is
584 // optimized for access in GetKey_Locked(), we need to look at each entry in
585 // |key_map_|.
586 KeyIdToSessionKeysMap::iterator it = key_map_.begin();
587 while (it != key_map_.end()) {
588 it->second->Erase(session_id);
589 if (it->second->Empty()) {
590 // Need to get rid of the entry for this key_id. This will mess up the
591 // iterator, so we need to increment it first.
592 KeyIdToSessionKeysMap::iterator current = it;
593 ++it;
594 key_map_.erase(current);
595 } else {
596 ++it;
601 AesDecryptor::DecryptionKey::DecryptionKey(const std::string& secret)
602 : secret_(secret) {
605 AesDecryptor::DecryptionKey::~DecryptionKey() {}
607 bool AesDecryptor::DecryptionKey::Init() {
608 CHECK(!secret_.empty());
609 decryption_key_.reset(crypto::SymmetricKey::Import(
610 crypto::SymmetricKey::AES, secret_));
611 if (!decryption_key_)
612 return false;
613 return true;
616 } // namespace media