aes_decryptor.cc revision 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38
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.
4
5#include "media/cdm/aes_decryptor.h"
6
7#include <list>
8#include <vector>
9
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"
22
23namespace media {
24
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
27// DecryptionKeys.
28class 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
31  // in the list.
32  typedef std::list<std::pair<std::string, DecryptionKey*> > KeyList;
33
34 public:
35  SessionIdDecryptionKeyMap() {}
36  ~SessionIdDecryptionKeyMap() { STLDeleteValues(&key_list_); }
37
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);
43
44  // Deletes the entry for |session_id| if present.
45  void Erase(const std::string& web_session_id);
46
47  // Returns whether the list is empty
48  bool Empty() const { return key_list_.empty(); }
49
50  // Returns the last inserted DecryptionKey.
51  DecryptionKey* LatestDecryptionKey() {
52    DCHECK(!key_list_.empty());
53    return key_list_.begin()->second;
54  }
55
56  bool Contains(const std::string& web_session_id) {
57    return Find(web_session_id) != key_list_.end();
58  }
59
60 private:
61  // Searches the list for an element with |web_session_id|.
62  KeyList::iterator Find(const std::string& web_session_id);
63
64  // Deletes the entry pointed to by |position|.
65  void Erase(KeyList::iterator position);
66
67  KeyList key_list_;
68
69  DISALLOW_COPY_AND_ASSIGN(SessionIdDecryptionKeyMap);
70};
71
72void 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())
77    Erase(it);
78  DecryptionKey* raw_ptr = decryption_key.release();
79  key_list_.push_front(std::make_pair(web_session_id, raw_ptr));
80}
81
82void AesDecryptor::SessionIdDecryptionKeyMap::Erase(
83    const std::string& web_session_id) {
84  KeyList::iterator it = Find(web_session_id);
85  if (it == key_list_.end())
86    return;
87  Erase(it);
88}
89
90AesDecryptor::SessionIdDecryptionKeyMap::KeyList::iterator
91AesDecryptor::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)
95      return it;
96  }
97  return key_list_.end();
98}
99
100void AesDecryptor::SessionIdDecryptionKeyMap::Erase(
101    KeyList::iterator position) {
102  DCHECK(position->second);
103  delete position->second;
104  key_list_.erase(position);
105}
106
107uint32 AesDecryptor::next_web_session_id_ = 1;
108
109enum ClearBytesBufferSel {
110  kSrcContainsClearBytes,
111  kDstContainsClearBytes
112};
113
114static void CopySubsamples(const std::vector<SubsampleEntry>& subsamples,
115                           const ClearBytesBufferSel sel,
116                           const uint8* src,
117                           uint8* dst) {
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;
122    } else {
123      dst += subsample.clear_bytes;
124    }
125    memcpy(dst, src, subsample.cypher_bytes);
126    src += subsample.cypher_bytes;
127    dst += subsample.cypher_bytes;
128  }
129}
130
131// Decrypts |input| using |key|.  Returns a DecoderBuffer with the decrypted
132// data if decryption succeeded or NULL if decryption failed.
133static scoped_refptr<DecoderBuffer> DecryptData(const DecoderBuffer& input,
134                                                crypto::SymmetricKey* key) {
135  CHECK(input.data_size());
136  CHECK(input.decrypt_config());
137  CHECK(key);
138
139  crypto::Encryptor encryptor;
140  if (!encryptor.Init(key, crypto::Encryptor::CTR, "")) {
141    DVLOG(1) << "Could not initialize decryptor.";
142    return NULL;
143  }
144
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.";
149    return NULL;
150  }
151
152  const char* sample = reinterpret_cast<const char*>(input.data());
153  size_t sample_size = static_cast<size_t>(input.data_size());
154
155  DCHECK_GT(sample_size, 0U) << "No sample data to be decrypted.";
156  if (sample_size == 0)
157    return NULL;
158
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.";
164      return NULL;
165    }
166
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());
171  }
172
173  const std::vector<SubsampleEntry>& subsamples =
174      input.decrypt_config()->subsamples();
175
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)
184      return NULL;
185  }
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";
189    return NULL;
190  }
191
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),
195                                   sample_size);
196  }
197
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());
207
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.";
214    return NULL;
215  }
216  DCHECK_EQ(decrypted_text.size(), encrypted_text.size());
217
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());
223  return output;
224}
225
226AesDecryptor::AesDecryptor(const SessionMessageCB& session_message_cb,
227                           const SessionClosedCB& session_closed_cb)
228    : session_message_cb_(session_message_cb),
229      session_closed_cb_(session_closed_cb) {
230  DCHECK(!session_message_cb_.is_null());
231  DCHECK(!session_closed_cb_.is_null());
232}
233
234AesDecryptor::~AesDecryptor() {
235  key_map_.clear();
236}
237
238void AesDecryptor::CreateSession(const std::string& init_data_type,
239                                 const uint8* init_data,
240                                 int init_data_length,
241                                 SessionType session_type,
242                                 scoped_ptr<NewSessionCdmPromise> promise) {
243  std::string web_session_id(base::UintToString(next_web_session_id_++));
244  valid_sessions_.insert(web_session_id);
245
246  // For now, the AesDecryptor does not care about |init_data_type| or
247  // |session_type|; just resolve the promise and then fire a message event
248  // with the |init_data| as the request.
249  // TODO(jrummell): Validate |init_data_type| and |session_type|.
250  std::vector<uint8> message;
251  if (init_data && init_data_length)
252    message.assign(init_data, init_data + init_data_length);
253
254  promise->resolve(web_session_id);
255
256  session_message_cb_.Run(web_session_id, message, GURL());
257}
258
259void AesDecryptor::LoadSession(const std::string& web_session_id,
260                               scoped_ptr<NewSessionCdmPromise> promise) {
261  // TODO(xhwang): Change this to NOTREACHED() when blink checks for key systems
262  // that do not support loadSession. See http://crbug.com/342481
263  promise->reject(NOT_SUPPORTED_ERROR, 0, "LoadSession() is not supported.");
264}
265
266void AesDecryptor::UpdateSession(const std::string& web_session_id,
267                                 const uint8* response,
268                                 int response_length,
269                                 scoped_ptr<SimpleCdmPromise> promise) {
270  CHECK(response);
271  CHECK_GT(response_length, 0);
272
273  // TODO(jrummell): Convert back to a DCHECK once prefixed EME is removed.
274  if (valid_sessions_.find(web_session_id) == valid_sessions_.end()) {
275    promise->reject(INVALID_ACCESS_ERROR, 0, "Session does not exist.");
276    return;
277  }
278
279  std::string key_string(reinterpret_cast<const char*>(response),
280                         response_length);
281
282  KeyIdAndKeyPairs keys;
283  if (!ExtractKeysFromJWKSet(key_string, &keys)) {
284    promise->reject(
285        INVALID_ACCESS_ERROR, 0, "response is not a valid JSON Web Key Set.");
286    return;
287  }
288
289  // Make sure that at least one key was extracted.
290  if (keys.empty()) {
291    promise->reject(
292        INVALID_ACCESS_ERROR, 0, "response does not contain any keys.");
293    return;
294  }
295
296  for (KeyIdAndKeyPairs::iterator it = keys.begin(); it != keys.end(); ++it) {
297    if (it->second.length() !=
298        static_cast<size_t>(DecryptConfig::kDecryptionKeySize)) {
299      DVLOG(1) << "Invalid key length: " << key_string.length();
300      promise->reject(INVALID_ACCESS_ERROR, 0, "Invalid key length.");
301      return;
302    }
303    if (!AddDecryptionKey(web_session_id, it->first, it->second)) {
304      promise->reject(INVALID_ACCESS_ERROR, 0, "Unable to add key.");
305      return;
306    }
307  }
308
309  {
310    base::AutoLock auto_lock(new_key_cb_lock_);
311
312    if (!new_audio_key_cb_.is_null())
313      new_audio_key_cb_.Run();
314
315    if (!new_video_key_cb_.is_null())
316      new_video_key_cb_.Run();
317  }
318
319  promise->resolve();
320}
321
322void AesDecryptor::GetUsableKeyIds(const std::string& web_session_id,
323                                   scoped_ptr<KeyIdsPromise> promise) {
324  // Since |web_session_id| is not provided by the user, this should never
325  // happen.
326  DCHECK(valid_sessions_.find(web_session_id) != valid_sessions_.end());
327
328  KeyIdsVector keyids;
329  base::AutoLock auto_lock(key_map_lock_);
330  for (KeyIdToSessionKeysMap::iterator it = key_map_.begin();
331       it != key_map_.end();
332       ++it) {
333    if (it->second->Contains(web_session_id))
334      keyids.push_back(std::vector<uint8>(it->first.begin(), it->first.end()));
335  }
336  promise->resolve(keyids);
337}
338
339void AesDecryptor::ReleaseSession(const std::string& web_session_id,
340                                  scoped_ptr<SimpleCdmPromise> promise) {
341  // Validate that this is a reference to an active session and then forget it.
342  std::set<std::string>::iterator it = valid_sessions_.find(web_session_id);
343  // TODO(jrummell): Convert back to a DCHECK once prefixed EME is removed.
344  if (it == valid_sessions_.end()) {
345    promise->reject(INVALID_ACCESS_ERROR, 0, "Session does not exist.");
346    return;
347  }
348
349  valid_sessions_.erase(it);
350
351  // Close the session.
352  DeleteKeysForSession(web_session_id);
353  promise->resolve();
354  session_closed_cb_.Run(web_session_id);
355}
356
357Decryptor* AesDecryptor::GetDecryptor() {
358  return this;
359}
360
361void AesDecryptor::RegisterNewKeyCB(StreamType stream_type,
362                                    const NewKeyCB& new_key_cb) {
363  base::AutoLock auto_lock(new_key_cb_lock_);
364
365  switch (stream_type) {
366    case kAudio:
367      new_audio_key_cb_ = new_key_cb;
368      break;
369    case kVideo:
370      new_video_key_cb_ = new_key_cb;
371      break;
372    default:
373      NOTREACHED();
374  }
375}
376
377void AesDecryptor::Decrypt(StreamType stream_type,
378                           const scoped_refptr<DecoderBuffer>& encrypted,
379                           const DecryptCB& decrypt_cb) {
380  CHECK(encrypted->decrypt_config());
381
382  scoped_refptr<DecoderBuffer> decrypted;
383  // An empty iv string signals that the frame is unencrypted.
384  if (encrypted->decrypt_config()->iv().empty()) {
385    decrypted = DecoderBuffer::CopyFrom(encrypted->data(),
386                                        encrypted->data_size());
387  } else {
388    const std::string& key_id = encrypted->decrypt_config()->key_id();
389    DecryptionKey* key = GetKey(key_id);
390    if (!key) {
391      DVLOG(1) << "Could not find a matching key for the given key ID.";
392      decrypt_cb.Run(kNoKey, NULL);
393      return;
394    }
395
396    crypto::SymmetricKey* decryption_key = key->decryption_key();
397    decrypted = DecryptData(*encrypted.get(), decryption_key);
398    if (!decrypted.get()) {
399      DVLOG(1) << "Decryption failed.";
400      decrypt_cb.Run(kError, NULL);
401      return;
402    }
403  }
404
405  decrypted->set_timestamp(encrypted->timestamp());
406  decrypted->set_duration(encrypted->duration());
407  decrypt_cb.Run(kSuccess, decrypted);
408}
409
410void AesDecryptor::CancelDecrypt(StreamType stream_type) {
411  // Decrypt() calls the DecryptCB synchronously so there's nothing to cancel.
412}
413
414void AesDecryptor::InitializeAudioDecoder(const AudioDecoderConfig& config,
415                                          const DecoderInitCB& init_cb) {
416  // AesDecryptor does not support audio decoding.
417  init_cb.Run(false);
418}
419
420void AesDecryptor::InitializeVideoDecoder(const VideoDecoderConfig& config,
421                                          const DecoderInitCB& init_cb) {
422  // AesDecryptor does not support video decoding.
423  init_cb.Run(false);
424}
425
426void AesDecryptor::DecryptAndDecodeAudio(
427    const scoped_refptr<DecoderBuffer>& encrypted,
428    const AudioDecodeCB& audio_decode_cb) {
429  NOTREACHED() << "AesDecryptor does not support audio decoding";
430}
431
432void AesDecryptor::DecryptAndDecodeVideo(
433    const scoped_refptr<DecoderBuffer>& encrypted,
434    const VideoDecodeCB& video_decode_cb) {
435  NOTREACHED() << "AesDecryptor does not support video decoding";
436}
437
438void AesDecryptor::ResetDecoder(StreamType stream_type) {
439  NOTREACHED() << "AesDecryptor does not support audio/video decoding";
440}
441
442void AesDecryptor::DeinitializeDecoder(StreamType stream_type) {
443  NOTREACHED() << "AesDecryptor does not support audio/video decoding";
444}
445
446bool AesDecryptor::AddDecryptionKey(const std::string& web_session_id,
447                                    const std::string& key_id,
448                                    const std::string& key_string) {
449  scoped_ptr<DecryptionKey> decryption_key(new DecryptionKey(key_string));
450  if (!decryption_key->Init()) {
451    DVLOG(1) << "Could not initialize decryption key.";
452    return false;
453  }
454
455  base::AutoLock auto_lock(key_map_lock_);
456  KeyIdToSessionKeysMap::iterator key_id_entry = key_map_.find(key_id);
457  if (key_id_entry != key_map_.end()) {
458    key_id_entry->second->Insert(web_session_id, decryption_key.Pass());
459    return true;
460  }
461
462  // |key_id| not found, so need to create new entry.
463  scoped_ptr<SessionIdDecryptionKeyMap> inner_map(
464      new SessionIdDecryptionKeyMap());
465  inner_map->Insert(web_session_id, decryption_key.Pass());
466  key_map_.add(key_id, inner_map.Pass());
467  return true;
468}
469
470AesDecryptor::DecryptionKey* AesDecryptor::GetKey(
471    const std::string& key_id) const {
472  base::AutoLock auto_lock(key_map_lock_);
473  KeyIdToSessionKeysMap::const_iterator key_id_found = key_map_.find(key_id);
474  if (key_id_found == key_map_.end())
475    return NULL;
476
477  // Return the key from the "latest" session_id entry.
478  return key_id_found->second->LatestDecryptionKey();
479}
480
481void AesDecryptor::DeleteKeysForSession(const std::string& web_session_id) {
482  base::AutoLock auto_lock(key_map_lock_);
483
484  // Remove all keys associated with |web_session_id|. Since the data is
485  // optimized for access in GetKey(), we need to look at each entry in
486  // |key_map_|.
487  KeyIdToSessionKeysMap::iterator it = key_map_.begin();
488  while (it != key_map_.end()) {
489    it->second->Erase(web_session_id);
490    if (it->second->Empty()) {
491      // Need to get rid of the entry for this key_id. This will mess up the
492      // iterator, so we need to increment it first.
493      KeyIdToSessionKeysMap::iterator current = it;
494      ++it;
495      key_map_.erase(current);
496    } else {
497      ++it;
498    }
499  }
500}
501
502AesDecryptor::DecryptionKey::DecryptionKey(const std::string& secret)
503    : secret_(secret) {
504}
505
506AesDecryptor::DecryptionKey::~DecryptionKey() {}
507
508bool AesDecryptor::DecryptionKey::Init() {
509  CHECK(!secret_.empty());
510  decryption_key_.reset(crypto::SymmetricKey::Import(
511      crypto::SymmetricKey::AES, secret_));
512  if (!decryption_key_)
513    return false;
514  return true;
515}
516
517}  // namespace media
518