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_promise.h"
17 #include "media/base/decoder_buffer.h"
18 #include "media/base/decrypt_config.h"
19 #include "media/base/video_decoder_config.h"
20 #include "media/base/video_frame.h"
21 #include "media/cdm/json_web_key.h"
25 // Keeps track of the session IDs and DecryptionKeys. The keys are ordered by
26 // insertion time (last insertion is first). It takes ownership of the
28 class AesDecryptor::SessionIdDecryptionKeyMap
{
29 // Use a std::list to actually hold the data. Insertion is always done
30 // at the front, so the "latest" decryption key is always the first one
32 typedef std::list
<std::pair
<std::string
, DecryptionKey
*> > KeyList
;
35 SessionIdDecryptionKeyMap() {}
36 ~SessionIdDecryptionKeyMap() { STLDeleteValues(&key_list_
); }
38 // Replaces value if |session_id| is already present, or adds it if not.
39 // This |decryption_key| becomes the latest until another insertion or
40 // |session_id| is erased.
41 void Insert(const std::string
& web_session_id
,
42 scoped_ptr
<DecryptionKey
> decryption_key
);
44 // Deletes the entry for |session_id| if present.
45 void Erase(const std::string
& web_session_id
);
47 // Returns whether the list is empty
48 bool Empty() const { return key_list_
.empty(); }
50 // Returns the last inserted DecryptionKey.
51 DecryptionKey
* LatestDecryptionKey() {
52 DCHECK(!key_list_
.empty());
53 return key_list_
.begin()->second
;
56 bool Contains(const std::string
& web_session_id
) {
57 return Find(web_session_id
) != key_list_
.end();
61 // Searches the list for an element with |web_session_id|.
62 KeyList::iterator
Find(const std::string
& web_session_id
);
64 // Deletes the entry pointed to by |position|.
65 void Erase(KeyList::iterator position
);
69 DISALLOW_COPY_AND_ASSIGN(SessionIdDecryptionKeyMap
);
72 void AesDecryptor::SessionIdDecryptionKeyMap::Insert(
73 const std::string
& web_session_id
,
74 scoped_ptr
<DecryptionKey
> decryption_key
) {
75 KeyList::iterator it
= Find(web_session_id
);
76 if (it
!= key_list_
.end())
78 DecryptionKey
* raw_ptr
= decryption_key
.release();
79 key_list_
.push_front(std::make_pair(web_session_id
, raw_ptr
));
82 void AesDecryptor::SessionIdDecryptionKeyMap::Erase(
83 const std::string
& web_session_id
) {
84 KeyList::iterator it
= Find(web_session_id
);
85 if (it
== key_list_
.end())
90 AesDecryptor::SessionIdDecryptionKeyMap::KeyList::iterator
91 AesDecryptor::SessionIdDecryptionKeyMap::Find(
92 const std::string
& web_session_id
) {
93 for (KeyList::iterator it
= key_list_
.begin(); it
!= key_list_
.end(); ++it
) {
94 if (it
->first
== web_session_id
)
97 return key_list_
.end();
100 void AesDecryptor::SessionIdDecryptionKeyMap::Erase(
101 KeyList::iterator position
) {
102 DCHECK(position
->second
);
103 delete position
->second
;
104 key_list_
.erase(position
);
107 uint32
AesDecryptor::next_web_session_id_
= 1;
109 enum ClearBytesBufferSel
{
110 kSrcContainsClearBytes
,
111 kDstContainsClearBytes
114 static void CopySubsamples(const std::vector
<SubsampleEntry
>& subsamples
,
115 const ClearBytesBufferSel sel
,
118 for (size_t i
= 0; i
< subsamples
.size(); i
++) {
119 const SubsampleEntry
& subsample
= subsamples
[i
];
120 if (sel
== kSrcContainsClearBytes
) {
121 src
+= subsample
.clear_bytes
;
123 dst
+= subsample
.clear_bytes
;
125 memcpy(dst
, src
, subsample
.cypher_bytes
);
126 src
+= subsample
.cypher_bytes
;
127 dst
+= subsample
.cypher_bytes
;
131 // Decrypts |input| using |key|. Returns a DecoderBuffer with the decrypted
132 // data if decryption succeeded or NULL if decryption failed.
133 static scoped_refptr
<DecoderBuffer
> DecryptData(const DecoderBuffer
& input
,
134 crypto::SymmetricKey
* key
) {
135 CHECK(input
.data_size());
136 CHECK(input
.decrypt_config());
139 crypto::Encryptor encryptor
;
140 if (!encryptor
.Init(key
, crypto::Encryptor::CTR
, "")) {
141 DVLOG(1) << "Could not initialize decryptor.";
145 DCHECK_EQ(input
.decrypt_config()->iv().size(),
146 static_cast<size_t>(DecryptConfig::kDecryptionKeySize
));
147 if (!encryptor
.SetCounter(input
.decrypt_config()->iv())) {
148 DVLOG(1) << "Could not set counter block.";
152 const char* sample
= reinterpret_cast<const char*>(input
.data());
153 size_t sample_size
= static_cast<size_t>(input
.data_size());
155 DCHECK_GT(sample_size
, 0U) << "No sample data to be decrypted.";
156 if (sample_size
== 0)
159 if (input
.decrypt_config()->subsamples().empty()) {
160 std::string decrypted_text
;
161 base::StringPiece
encrypted_text(sample
, sample_size
);
162 if (!encryptor
.Decrypt(encrypted_text
, &decrypted_text
)) {
163 DVLOG(1) << "Could not decrypt data.";
167 // TODO(xhwang): Find a way to avoid this data copy.
168 return DecoderBuffer::CopyFrom(
169 reinterpret_cast<const uint8
*>(decrypted_text
.data()),
170 decrypted_text
.size());
173 const std::vector
<SubsampleEntry
>& subsamples
=
174 input
.decrypt_config()->subsamples();
176 size_t total_clear_size
= 0;
177 size_t total_encrypted_size
= 0;
178 for (size_t i
= 0; i
< subsamples
.size(); i
++) {
179 total_clear_size
+= subsamples
[i
].clear_bytes
;
180 total_encrypted_size
+= subsamples
[i
].cypher_bytes
;
181 // Check for overflow. This check is valid because *_size is unsigned.
182 DCHECK(total_clear_size
>= subsamples
[i
].clear_bytes
);
183 if (total_encrypted_size
< subsamples
[i
].cypher_bytes
)
186 size_t total_size
= total_clear_size
+ total_encrypted_size
;
187 if (total_size
< total_clear_size
|| total_size
!= sample_size
) {
188 DVLOG(1) << "Subsample sizes do not equal input size";
192 // No need to decrypt if there is no encrypted data.
193 if (total_encrypted_size
<= 0) {
194 return DecoderBuffer::CopyFrom(reinterpret_cast<const uint8
*>(sample
),
198 // The encrypted portions of all subsamples must form a contiguous block,
199 // such that an encrypted subsample that ends away from a block boundary is
200 // immediately followed by the start of the next encrypted subsample. We
201 // copy all encrypted subsamples to a contiguous buffer, decrypt them, then
202 // copy the decrypted bytes over the encrypted bytes in the output.
203 // TODO(strobe): attempt to reduce number of memory copies
204 scoped_ptr
<uint8
[]> encrypted_bytes(new uint8
[total_encrypted_size
]);
205 CopySubsamples(subsamples
, kSrcContainsClearBytes
,
206 reinterpret_cast<const uint8
*>(sample
), encrypted_bytes
.get());
208 base::StringPiece
encrypted_text(
209 reinterpret_cast<const char*>(encrypted_bytes
.get()),
210 total_encrypted_size
);
211 std::string decrypted_text
;
212 if (!encryptor
.Decrypt(encrypted_text
, &decrypted_text
)) {
213 DVLOG(1) << "Could not decrypt data.";
216 DCHECK_EQ(decrypted_text
.size(), encrypted_text
.size());
218 scoped_refptr
<DecoderBuffer
> output
= DecoderBuffer::CopyFrom(
219 reinterpret_cast<const uint8
*>(sample
), sample_size
);
220 CopySubsamples(subsamples
, kDstContainsClearBytes
,
221 reinterpret_cast<const uint8
*>(decrypted_text
.data()),
222 output
->writable_data());
226 AesDecryptor::AesDecryptor(const SessionMessageCB
& session_message_cb
,
227 const SessionClosedCB
& session_closed_cb
,
228 const SessionKeysChangeCB
& session_keys_change_cb
)
229 : session_message_cb_(session_message_cb
),
230 session_closed_cb_(session_closed_cb
),
231 session_keys_change_cb_(session_keys_change_cb
) {
232 DCHECK(!session_message_cb_
.is_null());
233 DCHECK(!session_closed_cb_
.is_null());
234 DCHECK(!session_keys_change_cb_
.is_null());
237 AesDecryptor::~AesDecryptor() {
241 void AesDecryptor::SetServerCertificate(const uint8
* certificate_data
,
242 int certificate_data_length
,
243 scoped_ptr
<SimpleCdmPromise
> promise
) {
245 NOT_SUPPORTED_ERROR
, 0, "SetServerCertificate() is not supported.");
248 void AesDecryptor::CreateSession(const std::string
& init_data_type
,
249 const uint8
* init_data
,
250 int init_data_length
,
251 SessionType session_type
,
252 scoped_ptr
<NewSessionCdmPromise
> promise
) {
253 std::string
web_session_id(base::UintToString(next_web_session_id_
++));
254 valid_sessions_
.insert(web_session_id
);
256 // For now, the AesDecryptor does not care about |init_data_type| or
257 // |session_type|; just resolve the promise and then fire a message event
258 // using the |init_data| as the key ID in the license request.
259 // TODO(jrummell): Validate |init_data_type| and |session_type|.
260 std::vector
<uint8
> message
;
261 if (init_data
&& init_data_length
)
262 CreateLicenseRequest(init_data
, init_data_length
, session_type
, &message
);
264 promise
->resolve(web_session_id
);
266 session_message_cb_
.Run(web_session_id
, message
, GURL());
269 void AesDecryptor::LoadSession(const std::string
& web_session_id
,
270 scoped_ptr
<NewSessionCdmPromise
> promise
) {
271 // TODO(xhwang): Change this to NOTREACHED() when blink checks for key systems
272 // that do not support loadSession. See http://crbug.com/342481
273 promise
->reject(NOT_SUPPORTED_ERROR
, 0, "LoadSession() is not supported.");
276 void AesDecryptor::UpdateSession(const std::string
& web_session_id
,
277 const uint8
* response
,
279 scoped_ptr
<SimpleCdmPromise
> promise
) {
281 CHECK_GT(response_length
, 0);
283 // TODO(jrummell): Convert back to a DCHECK once prefixed EME is removed.
284 if (valid_sessions_
.find(web_session_id
) == valid_sessions_
.end()) {
285 promise
->reject(INVALID_ACCESS_ERROR
, 0, "Session does not exist.");
289 std::string
key_string(reinterpret_cast<const char*>(response
),
292 KeyIdAndKeyPairs keys
;
293 SessionType session_type
= MediaKeys::TEMPORARY_SESSION
;
294 if (!ExtractKeysFromJWKSet(key_string
, &keys
, &session_type
)) {
296 INVALID_ACCESS_ERROR
, 0, "Response is not a valid JSON Web Key Set.");
300 // Make sure that at least one key was extracted.
303 INVALID_ACCESS_ERROR
, 0, "Response does not contain any keys.");
307 for (KeyIdAndKeyPairs::iterator it
= keys
.begin(); it
!= keys
.end(); ++it
) {
308 if (it
->second
.length() !=
309 static_cast<size_t>(DecryptConfig::kDecryptionKeySize
)) {
310 DVLOG(1) << "Invalid key length: " << it
->second
.length();
311 promise
->reject(INVALID_ACCESS_ERROR
, 0, "Invalid key length.");
314 if (!AddDecryptionKey(web_session_id
, it
->first
, it
->second
)) {
315 promise
->reject(INVALID_ACCESS_ERROR
, 0, "Unable to add key.");
321 base::AutoLock
auto_lock(new_key_cb_lock_
);
323 if (!new_audio_key_cb_
.is_null())
324 new_audio_key_cb_
.Run();
326 if (!new_video_key_cb_
.is_null())
327 new_video_key_cb_
.Run();
332 // Assume that at least 1 new key has been successfully added and thus
334 session_keys_change_cb_
.Run(web_session_id
, true);
337 void AesDecryptor::CloseSession(const std::string
& web_session_id
,
338 scoped_ptr
<SimpleCdmPromise
> promise
) {
339 // Validate that this is a reference to an active session and then forget it.
340 std::set
<std::string
>::iterator it
= valid_sessions_
.find(web_session_id
);
341 DCHECK(it
!= valid_sessions_
.end());
343 valid_sessions_
.erase(it
);
345 // Close the session.
346 DeleteKeysForSession(web_session_id
);
348 session_closed_cb_
.Run(web_session_id
);
351 void AesDecryptor::RemoveSession(const std::string
& web_session_id
,
352 scoped_ptr
<SimpleCdmPromise
> promise
) {
353 // AesDecryptor doesn't keep any persistent data, so this should be
355 // TODO(jrummell): Make sure persistent session types are rejected.
356 // http://crbug.com/384152.
358 // However, v0.1b calls to CancelKeyRequest() will call this, so close the
359 // session, if it exists.
360 // TODO(jrummell): Remove the close() call when prefixed EME is removed.
361 // http://crbug.com/249976.
362 if (valid_sessions_
.find(web_session_id
) != valid_sessions_
.end()) {
363 CloseSession(web_session_id
, promise
.Pass());
367 promise
->reject(INVALID_ACCESS_ERROR
, 0, "Session does not exist.");
370 CdmContext
* AesDecryptor::GetCdmContext() {
374 Decryptor
* AesDecryptor::GetDecryptor() {
378 #if defined(ENABLE_BROWSER_CDMS)
379 int AesDecryptor::GetCdmId() const {
380 return kInvalidCdmId
;
382 #endif // defined(ENABLE_BROWSER_CDMS)
384 void AesDecryptor::RegisterNewKeyCB(StreamType stream_type
,
385 const NewKeyCB
& new_key_cb
) {
386 base::AutoLock
auto_lock(new_key_cb_lock_
);
388 switch (stream_type
) {
390 new_audio_key_cb_
= new_key_cb
;
393 new_video_key_cb_
= new_key_cb
;
400 void AesDecryptor::Decrypt(StreamType stream_type
,
401 const scoped_refptr
<DecoderBuffer
>& encrypted
,
402 const DecryptCB
& decrypt_cb
) {
403 CHECK(encrypted
->decrypt_config());
405 scoped_refptr
<DecoderBuffer
> decrypted
;
406 // An empty iv string signals that the frame is unencrypted.
407 if (encrypted
->decrypt_config()->iv().empty()) {
408 decrypted
= DecoderBuffer::CopyFrom(encrypted
->data(),
409 encrypted
->data_size());
411 const std::string
& key_id
= encrypted
->decrypt_config()->key_id();
412 DecryptionKey
* key
= GetKey(key_id
);
414 DVLOG(1) << "Could not find a matching key for the given key ID.";
415 decrypt_cb
.Run(kNoKey
, NULL
);
419 crypto::SymmetricKey
* decryption_key
= key
->decryption_key();
420 decrypted
= DecryptData(*encrypted
.get(), decryption_key
);
421 if (!decrypted
.get()) {
422 DVLOG(1) << "Decryption failed.";
423 decrypt_cb
.Run(kError
, NULL
);
428 decrypted
->set_timestamp(encrypted
->timestamp());
429 decrypted
->set_duration(encrypted
->duration());
430 decrypt_cb
.Run(kSuccess
, decrypted
);
433 void AesDecryptor::CancelDecrypt(StreamType stream_type
) {
434 // Decrypt() calls the DecryptCB synchronously so there's nothing to cancel.
437 void AesDecryptor::InitializeAudioDecoder(const AudioDecoderConfig
& config
,
438 const DecoderInitCB
& init_cb
) {
439 // AesDecryptor does not support audio decoding.
443 void AesDecryptor::InitializeVideoDecoder(const VideoDecoderConfig
& config
,
444 const DecoderInitCB
& init_cb
) {
445 // AesDecryptor does not support video decoding.
449 void AesDecryptor::DecryptAndDecodeAudio(
450 const scoped_refptr
<DecoderBuffer
>& encrypted
,
451 const AudioDecodeCB
& audio_decode_cb
) {
452 NOTREACHED() << "AesDecryptor does not support audio decoding";
455 void AesDecryptor::DecryptAndDecodeVideo(
456 const scoped_refptr
<DecoderBuffer
>& encrypted
,
457 const VideoDecodeCB
& video_decode_cb
) {
458 NOTREACHED() << "AesDecryptor does not support video decoding";
461 void AesDecryptor::ResetDecoder(StreamType stream_type
) {
462 NOTREACHED() << "AesDecryptor does not support audio/video decoding";
465 void AesDecryptor::DeinitializeDecoder(StreamType stream_type
) {
466 NOTREACHED() << "AesDecryptor does not support audio/video decoding";
469 bool AesDecryptor::AddDecryptionKey(const std::string
& web_session_id
,
470 const std::string
& key_id
,
471 const std::string
& key_string
) {
472 scoped_ptr
<DecryptionKey
> decryption_key(new DecryptionKey(key_string
));
473 if (!decryption_key
->Init()) {
474 DVLOG(1) << "Could not initialize decryption key.";
478 base::AutoLock
auto_lock(key_map_lock_
);
479 KeyIdToSessionKeysMap::iterator key_id_entry
= key_map_
.find(key_id
);
480 if (key_id_entry
!= key_map_
.end()) {
481 key_id_entry
->second
->Insert(web_session_id
, decryption_key
.Pass());
485 // |key_id| not found, so need to create new entry.
486 scoped_ptr
<SessionIdDecryptionKeyMap
> inner_map(
487 new SessionIdDecryptionKeyMap());
488 inner_map
->Insert(web_session_id
, decryption_key
.Pass());
489 key_map_
.add(key_id
, inner_map
.Pass());
493 AesDecryptor::DecryptionKey
* AesDecryptor::GetKey(
494 const std::string
& key_id
) const {
495 base::AutoLock
auto_lock(key_map_lock_
);
496 KeyIdToSessionKeysMap::const_iterator key_id_found
= key_map_
.find(key_id
);
497 if (key_id_found
== key_map_
.end())
500 // Return the key from the "latest" session_id entry.
501 return key_id_found
->second
->LatestDecryptionKey();
504 void AesDecryptor::DeleteKeysForSession(const std::string
& web_session_id
) {
505 base::AutoLock
auto_lock(key_map_lock_
);
507 // Remove all keys associated with |web_session_id|. Since the data is
508 // optimized for access in GetKey(), we need to look at each entry in
510 KeyIdToSessionKeysMap::iterator it
= key_map_
.begin();
511 while (it
!= key_map_
.end()) {
512 it
->second
->Erase(web_session_id
);
513 if (it
->second
->Empty()) {
514 // Need to get rid of the entry for this key_id. This will mess up the
515 // iterator, so we need to increment it first.
516 KeyIdToSessionKeysMap::iterator current
= it
;
518 key_map_
.erase(current
);
525 AesDecryptor::DecryptionKey::DecryptionKey(const std::string
& secret
)
529 AesDecryptor::DecryptionKey::~DecryptionKey() {}
531 bool AesDecryptor::DecryptionKey::Init() {
532 CHECK(!secret_
.empty());
533 decryption_key_
.reset(crypto::SymmetricKey::Import(
534 crypto::SymmetricKey::AES
, secret_
));
535 if (!decryption_key_
)