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"
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/cenc_utils.h"
23 #include "media/cdm/json_web_key.h"
27 // Keeps track of the session IDs and DecryptionKeys. The keys are ordered by
28 // insertion time (last insertion is first). It takes ownership of the
30 class AesDecryptor::SessionIdDecryptionKeyMap
{
31 // Use a std::list to actually hold the data. Insertion is always done
32 // at the front, so the "latest" decryption key is always the first one
34 typedef std::list
<std::pair
<std::string
, DecryptionKey
*> > KeyList
;
37 SessionIdDecryptionKeyMap() {}
38 ~SessionIdDecryptionKeyMap() { STLDeleteValues(&key_list_
); }
40 // Replaces value if |session_id| is already present, or adds it if not.
41 // This |decryption_key| becomes the latest until another insertion or
42 // |session_id| is erased.
43 void Insert(const std::string
& session_id
,
44 scoped_ptr
<DecryptionKey
> decryption_key
);
46 // Deletes the entry for |session_id| if present.
47 void Erase(const std::string
& session_id
);
49 // Returns whether the list is empty
50 bool Empty() const { return key_list_
.empty(); }
52 // Returns the last inserted DecryptionKey.
53 DecryptionKey
* LatestDecryptionKey() {
54 DCHECK(!key_list_
.empty());
55 return key_list_
.begin()->second
;
58 bool Contains(const std::string
& session_id
) {
59 return Find(session_id
) != key_list_
.end();
63 // Searches the list for an element with |session_id|.
64 KeyList::iterator
Find(const std::string
& session_id
);
66 // Deletes the entry pointed to by |position|.
67 void Erase(KeyList::iterator position
);
71 DISALLOW_COPY_AND_ASSIGN(SessionIdDecryptionKeyMap
);
74 void AesDecryptor::SessionIdDecryptionKeyMap::Insert(
75 const std::string
& session_id
,
76 scoped_ptr
<DecryptionKey
> decryption_key
) {
77 KeyList::iterator it
= Find(session_id
);
78 if (it
!= key_list_
.end())
80 DecryptionKey
* raw_ptr
= decryption_key
.release();
81 key_list_
.push_front(std::make_pair(session_id
, raw_ptr
));
84 void AesDecryptor::SessionIdDecryptionKeyMap::Erase(
85 const std::string
& session_id
) {
86 KeyList::iterator it
= Find(session_id
);
87 if (it
== key_list_
.end())
92 AesDecryptor::SessionIdDecryptionKeyMap::KeyList::iterator
93 AesDecryptor::SessionIdDecryptionKeyMap::Find(const std::string
& session_id
) {
94 for (KeyList::iterator it
= key_list_
.begin(); it
!= key_list_
.end(); ++it
) {
95 if (it
->first
== session_id
)
98 return key_list_
.end();
101 void AesDecryptor::SessionIdDecryptionKeyMap::Erase(
102 KeyList::iterator position
) {
103 DCHECK(position
->second
);
104 delete position
->second
;
105 key_list_
.erase(position
);
108 uint32
AesDecryptor::next_session_id_
= 1;
110 enum ClearBytesBufferSel
{
111 kSrcContainsClearBytes
,
112 kDstContainsClearBytes
115 static void CopySubsamples(const std::vector
<SubsampleEntry
>& subsamples
,
116 const ClearBytesBufferSel sel
,
119 for (size_t i
= 0; i
< subsamples
.size(); i
++) {
120 const SubsampleEntry
& subsample
= subsamples
[i
];
121 if (sel
== kSrcContainsClearBytes
) {
122 src
+= subsample
.clear_bytes
;
124 dst
+= subsample
.clear_bytes
;
126 memcpy(dst
, src
, subsample
.cypher_bytes
);
127 src
+= subsample
.cypher_bytes
;
128 dst
+= subsample
.cypher_bytes
;
132 // Decrypts |input| using |key|. Returns a DecoderBuffer with the decrypted
133 // data if decryption succeeded or NULL if decryption failed.
134 static scoped_refptr
<DecoderBuffer
> DecryptData(const DecoderBuffer
& input
,
135 crypto::SymmetricKey
* key
) {
136 CHECK(input
.data_size());
137 CHECK(input
.decrypt_config());
140 crypto::Encryptor encryptor
;
141 if (!encryptor
.Init(key
, crypto::Encryptor::CTR
, "")) {
142 DVLOG(1) << "Could not initialize decryptor.";
146 DCHECK_EQ(input
.decrypt_config()->iv().size(),
147 static_cast<size_t>(DecryptConfig::kDecryptionKeySize
));
148 if (!encryptor
.SetCounter(input
.decrypt_config()->iv())) {
149 DVLOG(1) << "Could not set counter block.";
153 const char* sample
= reinterpret_cast<const char*>(input
.data());
154 size_t sample_size
= static_cast<size_t>(input
.data_size());
156 DCHECK_GT(sample_size
, 0U) << "No sample data to be decrypted.";
157 if (sample_size
== 0)
160 if (input
.decrypt_config()->subsamples().empty()) {
161 std::string decrypted_text
;
162 base::StringPiece
encrypted_text(sample
, sample_size
);
163 if (!encryptor
.Decrypt(encrypted_text
, &decrypted_text
)) {
164 DVLOG(1) << "Could not decrypt data.";
168 // TODO(xhwang): Find a way to avoid this data copy.
169 return DecoderBuffer::CopyFrom(
170 reinterpret_cast<const uint8
*>(decrypted_text
.data()),
171 decrypted_text
.size());
174 const std::vector
<SubsampleEntry
>& subsamples
=
175 input
.decrypt_config()->subsamples();
177 size_t total_clear_size
= 0;
178 size_t total_encrypted_size
= 0;
179 for (size_t i
= 0; i
< subsamples
.size(); i
++) {
180 total_clear_size
+= subsamples
[i
].clear_bytes
;
181 total_encrypted_size
+= subsamples
[i
].cypher_bytes
;
182 // Check for overflow. This check is valid because *_size is unsigned.
183 DCHECK(total_clear_size
>= subsamples
[i
].clear_bytes
);
184 if (total_encrypted_size
< subsamples
[i
].cypher_bytes
)
187 size_t total_size
= total_clear_size
+ total_encrypted_size
;
188 if (total_size
< total_clear_size
|| total_size
!= sample_size
) {
189 DVLOG(1) << "Subsample sizes do not equal input size";
193 // No need to decrypt if there is no encrypted data.
194 if (total_encrypted_size
<= 0) {
195 return DecoderBuffer::CopyFrom(reinterpret_cast<const uint8
*>(sample
),
199 // The encrypted portions of all subsamples must form a contiguous block,
200 // such that an encrypted subsample that ends away from a block boundary is
201 // immediately followed by the start of the next encrypted subsample. We
202 // copy all encrypted subsamples to a contiguous buffer, decrypt them, then
203 // copy the decrypted bytes over the encrypted bytes in the output.
204 // TODO(strobe): attempt to reduce number of memory copies
205 scoped_ptr
<uint8
[]> encrypted_bytes(new uint8
[total_encrypted_size
]);
206 CopySubsamples(subsamples
, kSrcContainsClearBytes
,
207 reinterpret_cast<const uint8
*>(sample
), encrypted_bytes
.get());
209 base::StringPiece
encrypted_text(
210 reinterpret_cast<const char*>(encrypted_bytes
.get()),
211 total_encrypted_size
);
212 std::string decrypted_text
;
213 if (!encryptor
.Decrypt(encrypted_text
, &decrypted_text
)) {
214 DVLOG(1) << "Could not decrypt data.";
217 DCHECK_EQ(decrypted_text
.size(), encrypted_text
.size());
219 scoped_refptr
<DecoderBuffer
> output
= DecoderBuffer::CopyFrom(
220 reinterpret_cast<const uint8
*>(sample
), sample_size
);
221 CopySubsamples(subsamples
, kDstContainsClearBytes
,
222 reinterpret_cast<const uint8
*>(decrypted_text
.data()),
223 output
->writable_data());
227 AesDecryptor::AesDecryptor(const GURL
& /* security_origin */,
228 const SessionMessageCB
& session_message_cb
,
229 const SessionClosedCB
& session_closed_cb
,
230 const SessionKeysChangeCB
& session_keys_change_cb
)
231 : session_message_cb_(session_message_cb
),
232 session_closed_cb_(session_closed_cb
),
233 session_keys_change_cb_(session_keys_change_cb
) {
234 // AesDecryptor doesn't keep any persistent data, so no need to do anything
235 // with |security_origin|.
236 DCHECK(!session_message_cb_
.is_null());
237 DCHECK(!session_closed_cb_
.is_null());
238 DCHECK(!session_keys_change_cb_
.is_null());
241 AesDecryptor::~AesDecryptor() {
245 void AesDecryptor::SetServerCertificate(const uint8
* certificate_data
,
246 int certificate_data_length
,
247 scoped_ptr
<SimpleCdmPromise
> promise
) {
249 NOT_SUPPORTED_ERROR
, 0, "SetServerCertificate() is not supported.");
252 void AesDecryptor::CreateSessionAndGenerateRequest(
253 SessionType session_type
,
254 EmeInitDataType init_data_type
,
255 const uint8
* init_data
,
256 int init_data_length
,
257 scoped_ptr
<NewSessionCdmPromise
> promise
) {
258 std::string
session_id(base::UintToString(next_session_id_
++));
259 valid_sessions_
.insert(session_id
);
261 // For now, the AesDecryptor does not care about |session_type|.
262 // TODO(jrummell): Validate |session_type|.
264 std::vector
<uint8
> message
;
265 // TODO(jrummell): Since unprefixed will never send NULL, remove this check
266 // when prefixed EME is removed (http://crbug.com/249976).
267 if (init_data
&& init_data_length
) {
268 std::vector
<std::vector
<uint8
>> keys
;
269 switch (init_data_type
) {
270 case EmeInitDataType::WEBM
:
271 // |init_data| is simply the key needed.
273 std::vector
<uint8
>(init_data
, init_data
+ init_data_length
));
275 case EmeInitDataType::CENC
:
276 // |init_data| is a set of 0 or more concatenated 'pssh' boxes.
277 if (!GetKeyIdsForCommonSystemId(init_data
, init_data_length
, &keys
)) {
278 promise
->reject(NOT_SUPPORTED_ERROR
, 0,
279 "No supported PSSH box found.");
283 case EmeInitDataType::KEYIDS
: {
284 std::string
init_data_string(init_data
, init_data
+ init_data_length
);
285 std::string error_message
;
286 if (!ExtractKeyIdsFromKeyIdsInitData(init_data_string
, &keys
,
288 promise
->reject(NOT_SUPPORTED_ERROR
, 0, error_message
);
295 promise
->reject(NOT_SUPPORTED_ERROR
, 0,
296 "init_data_type not supported.");
299 CreateLicenseRequest(keys
, session_type
, &message
);
302 promise
->resolve(session_id
);
304 // No URL needed for license requests.
305 session_message_cb_
.Run(session_id
, LICENSE_REQUEST
, message
,
309 void AesDecryptor::LoadSession(SessionType session_type
,
310 const std::string
& session_id
,
311 scoped_ptr
<NewSessionCdmPromise
> promise
) {
312 // TODO(xhwang): Change this to NOTREACHED() when blink checks for key systems
313 // that do not support loadSession. See http://crbug.com/342481
314 promise
->reject(NOT_SUPPORTED_ERROR
, 0, "LoadSession() is not supported.");
317 void AesDecryptor::UpdateSession(const std::string
& session_id
,
318 const uint8
* response
,
320 scoped_ptr
<SimpleCdmPromise
> promise
) {
322 CHECK_GT(response_length
, 0);
324 // TODO(jrummell): Convert back to a DCHECK once prefixed EME is removed.
325 if (valid_sessions_
.find(session_id
) == valid_sessions_
.end()) {
326 promise
->reject(INVALID_ACCESS_ERROR
, 0, "Session does not exist.");
330 std::string
key_string(reinterpret_cast<const char*>(response
),
333 KeyIdAndKeyPairs keys
;
334 SessionType session_type
= MediaKeys::TEMPORARY_SESSION
;
335 if (!ExtractKeysFromJWKSet(key_string
, &keys
, &session_type
)) {
337 INVALID_ACCESS_ERROR
, 0, "Response is not a valid JSON Web Key Set.");
341 // Make sure that at least one key was extracted.
344 INVALID_ACCESS_ERROR
, 0, "Response does not contain any keys.");
348 bool key_added
= false;
349 for (KeyIdAndKeyPairs::iterator it
= keys
.begin(); it
!= keys
.end(); ++it
) {
350 if (it
->second
.length() !=
351 static_cast<size_t>(DecryptConfig::kDecryptionKeySize
)) {
352 DVLOG(1) << "Invalid key length: " << it
->second
.length();
353 promise
->reject(INVALID_ACCESS_ERROR
, 0, "Invalid key length.");
357 // If this key_id doesn't currently exist in this session,
358 // a new key is added.
359 if (!HasKey(session_id
, it
->first
))
362 if (!AddDecryptionKey(session_id
, it
->first
, it
->second
)) {
363 promise
->reject(INVALID_ACCESS_ERROR
, 0, "Unable to add key.");
369 base::AutoLock
auto_lock(new_key_cb_lock_
);
371 if (!new_audio_key_cb_
.is_null())
372 new_audio_key_cb_
.Run();
374 if (!new_video_key_cb_
.is_null())
375 new_video_key_cb_
.Run();
380 // Create the list of all available keys for this session.
381 CdmKeysInfo keys_info
;
383 base::AutoLock
auto_lock(key_map_lock_
);
384 for (const auto& item
: key_map_
) {
385 if (item
.second
->Contains(session_id
)) {
386 scoped_ptr
<CdmKeyInformation
> key_info(new CdmKeyInformation
);
387 key_info
->key_id
.assign(item
.first
.begin(), item
.first
.end());
388 key_info
->status
= CdmKeyInformation::USABLE
;
389 key_info
->system_code
= 0;
390 keys_info
.push_back(key_info
.release());
395 session_keys_change_cb_
.Run(session_id
, key_added
, keys_info
.Pass());
398 void AesDecryptor::CloseSession(const std::string
& session_id
,
399 scoped_ptr
<SimpleCdmPromise
> promise
) {
400 // Validate that this is a reference to an active session and then forget it.
401 std::set
<std::string
>::iterator it
= valid_sessions_
.find(session_id
);
402 DCHECK(it
!= valid_sessions_
.end());
404 valid_sessions_
.erase(it
);
406 // Close the session.
407 DeleteKeysForSession(session_id
);
409 session_closed_cb_
.Run(session_id
);
412 void AesDecryptor::RemoveSession(const std::string
& session_id
,
413 scoped_ptr
<SimpleCdmPromise
> promise
) {
414 // AesDecryptor doesn't keep any persistent data, so this should be
416 // TODO(jrummell): Make sure persistent session types are rejected.
417 // http://crbug.com/384152.
419 // However, v0.1b calls to CancelKeyRequest() will call this, so close the
420 // session, if it exists.
421 // TODO(jrummell): Remove the close() call when prefixed EME is removed.
422 // http://crbug.com/249976.
423 if (valid_sessions_
.find(session_id
) != valid_sessions_
.end()) {
424 CloseSession(session_id
, promise
.Pass());
428 promise
->reject(INVALID_ACCESS_ERROR
, 0, "Session does not exist.");
431 CdmContext
* AesDecryptor::GetCdmContext() {
435 Decryptor
* AesDecryptor::GetDecryptor() {
439 int AesDecryptor::GetCdmId() const {
440 return kInvalidCdmId
;
443 void AesDecryptor::RegisterNewKeyCB(StreamType stream_type
,
444 const NewKeyCB
& new_key_cb
) {
445 base::AutoLock
auto_lock(new_key_cb_lock_
);
447 switch (stream_type
) {
449 new_audio_key_cb_
= new_key_cb
;
452 new_video_key_cb_
= new_key_cb
;
459 void AesDecryptor::Decrypt(StreamType stream_type
,
460 const scoped_refptr
<DecoderBuffer
>& encrypted
,
461 const DecryptCB
& decrypt_cb
) {
462 CHECK(encrypted
->decrypt_config());
464 scoped_refptr
<DecoderBuffer
> decrypted
;
465 // An empty iv string signals that the frame is unencrypted.
466 if (encrypted
->decrypt_config()->iv().empty()) {
467 decrypted
= DecoderBuffer::CopyFrom(encrypted
->data(),
468 encrypted
->data_size());
470 const std::string
& key_id
= encrypted
->decrypt_config()->key_id();
471 DecryptionKey
* key
= GetKey(key_id
);
473 DVLOG(1) << "Could not find a matching key for the given key ID.";
474 decrypt_cb
.Run(kNoKey
, NULL
);
478 crypto::SymmetricKey
* decryption_key
= key
->decryption_key();
479 decrypted
= DecryptData(*encrypted
.get(), decryption_key
);
480 if (!decrypted
.get()) {
481 DVLOG(1) << "Decryption failed.";
482 decrypt_cb
.Run(kError
, NULL
);
487 decrypted
->set_timestamp(encrypted
->timestamp());
488 decrypted
->set_duration(encrypted
->duration());
489 decrypt_cb
.Run(kSuccess
, decrypted
);
492 void AesDecryptor::CancelDecrypt(StreamType stream_type
) {
493 // Decrypt() calls the DecryptCB synchronously so there's nothing to cancel.
496 void AesDecryptor::InitializeAudioDecoder(const AudioDecoderConfig
& config
,
497 const DecoderInitCB
& init_cb
) {
498 // AesDecryptor does not support audio decoding.
502 void AesDecryptor::InitializeVideoDecoder(const VideoDecoderConfig
& config
,
503 const DecoderInitCB
& init_cb
) {
504 // AesDecryptor does not support video decoding.
508 void AesDecryptor::DecryptAndDecodeAudio(
509 const scoped_refptr
<DecoderBuffer
>& encrypted
,
510 const AudioDecodeCB
& audio_decode_cb
) {
511 NOTREACHED() << "AesDecryptor does not support audio decoding";
514 void AesDecryptor::DecryptAndDecodeVideo(
515 const scoped_refptr
<DecoderBuffer
>& encrypted
,
516 const VideoDecodeCB
& video_decode_cb
) {
517 NOTREACHED() << "AesDecryptor does not support video decoding";
520 void AesDecryptor::ResetDecoder(StreamType stream_type
) {
521 NOTREACHED() << "AesDecryptor does not support audio/video decoding";
524 void AesDecryptor::DeinitializeDecoder(StreamType stream_type
) {
525 NOTREACHED() << "AesDecryptor does not support audio/video decoding";
528 bool AesDecryptor::AddDecryptionKey(const std::string
& session_id
,
529 const std::string
& key_id
,
530 const std::string
& key_string
) {
531 scoped_ptr
<DecryptionKey
> decryption_key(new DecryptionKey(key_string
));
532 if (!decryption_key
->Init()) {
533 DVLOG(1) << "Could not initialize decryption key.";
537 base::AutoLock
auto_lock(key_map_lock_
);
538 KeyIdToSessionKeysMap::iterator key_id_entry
= key_map_
.find(key_id
);
539 if (key_id_entry
!= key_map_
.end()) {
540 key_id_entry
->second
->Insert(session_id
, decryption_key
.Pass());
544 // |key_id| not found, so need to create new entry.
545 scoped_ptr
<SessionIdDecryptionKeyMap
> inner_map(
546 new SessionIdDecryptionKeyMap());
547 inner_map
->Insert(session_id
, decryption_key
.Pass());
548 key_map_
.add(key_id
, inner_map
.Pass());
552 AesDecryptor::DecryptionKey
* AesDecryptor::GetKey(
553 const std::string
& key_id
) const {
554 base::AutoLock
auto_lock(key_map_lock_
);
555 KeyIdToSessionKeysMap::const_iterator key_id_found
= key_map_
.find(key_id
);
556 if (key_id_found
== key_map_
.end())
559 // Return the key from the "latest" session_id entry.
560 return key_id_found
->second
->LatestDecryptionKey();
563 bool AesDecryptor::HasKey(const std::string
& session_id
,
564 const std::string
& key_id
) {
565 base::AutoLock
auto_lock(key_map_lock_
);
566 KeyIdToSessionKeysMap::const_iterator key_id_found
= key_map_
.find(key_id
);
567 if (key_id_found
== key_map_
.end())
570 return key_id_found
->second
->Contains(session_id
);
573 void AesDecryptor::DeleteKeysForSession(const std::string
& session_id
) {
574 base::AutoLock
auto_lock(key_map_lock_
);
576 // Remove all keys associated with |session_id|. Since the data is
577 // optimized for access in GetKey(), we need to look at each entry in
579 KeyIdToSessionKeysMap::iterator it
= key_map_
.begin();
580 while (it
!= key_map_
.end()) {
581 it
->second
->Erase(session_id
);
582 if (it
->second
->Empty()) {
583 // Need to get rid of the entry for this key_id. This will mess up the
584 // iterator, so we need to increment it first.
585 KeyIdToSessionKeysMap::iterator current
= it
;
587 key_map_
.erase(current
);
594 AesDecryptor::DecryptionKey::DecryptionKey(const std::string
& secret
)
598 AesDecryptor::DecryptionKey::~DecryptionKey() {}
600 bool AesDecryptor::DecryptionKey::Init() {
601 CHECK(!secret_
.empty());
602 decryption_key_
.reset(crypto::SymmetricKey::Import(
603 crypto::SymmetricKey::AES
, secret_
));
604 if (!decryption_key_
)