Home | History | Annotate | Download | only in crypto
      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 "content/renderer/media/crypto/proxy_decryptor.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback_helpers.h"
      9 #include "base/logging.h"
     10 #include "base/strings/string_util.h"
     11 #include "content/renderer/media/crypto/content_decryption_module_factory.h"
     12 #if defined(OS_ANDROID)
     13 #include "content/renderer/media/android/renderer_media_player_manager.h"
     14 #endif  // defined(OS_ANDROID)
     15 #include "media/cdm/json_web_key.h"
     16 #include "media/cdm/key_system_names.h"
     17 
     18 namespace content {
     19 
     20 // Since these reference IDs may conflict with the ones generated in
     21 // WebContentDecryptionModuleSessionImpl for the short time both paths are
     22 // active, start with 100000 and generate the IDs from there.
     23 // TODO(jrummell): Only allow one path http://crbug.com/306680.
     24 uint32 ProxyDecryptor::next_session_id_ = 100000;
     25 
     26 const uint32 kInvalidSessionId = 0;
     27 
     28 #if defined(ENABLE_PEPPER_CDMS)
     29 void ProxyDecryptor::DestroyHelperPlugin() {
     30   ContentDecryptionModuleFactory::DestroyHelperPlugin(
     31       web_media_player_client_, web_frame_);
     32 }
     33 #endif  // defined(ENABLE_PEPPER_CDMS)
     34 
     35 ProxyDecryptor::ProxyDecryptor(
     36 #if defined(ENABLE_PEPPER_CDMS)
     37     blink::WebMediaPlayerClient* web_media_player_client,
     38     blink::WebFrame* web_frame,
     39 #elif defined(OS_ANDROID)
     40     RendererMediaPlayerManager* manager,
     41     int media_keys_id,
     42 #endif  // defined(ENABLE_PEPPER_CDMS)
     43     const KeyAddedCB& key_added_cb,
     44     const KeyErrorCB& key_error_cb,
     45     const KeyMessageCB& key_message_cb)
     46     : weak_ptr_factory_(this),
     47 #if defined(ENABLE_PEPPER_CDMS)
     48       web_media_player_client_(web_media_player_client),
     49       web_frame_(web_frame),
     50 #elif defined(OS_ANDROID)
     51       manager_(manager),
     52       media_keys_id_(media_keys_id),
     53 #endif  // defined(ENABLE_PEPPER_CDMS)
     54       key_added_cb_(key_added_cb),
     55       key_error_cb_(key_error_cb),
     56       key_message_cb_(key_message_cb),
     57       is_clear_key_(false) {
     58   DCHECK(!key_added_cb_.is_null());
     59   DCHECK(!key_error_cb_.is_null());
     60   DCHECK(!key_message_cb_.is_null());
     61 }
     62 
     63 ProxyDecryptor::~ProxyDecryptor() {
     64   // Destroy the decryptor explicitly before destroying the plugin.
     65   {
     66     base::AutoLock auto_lock(lock_);
     67     media_keys_.reset();
     68   }
     69 }
     70 
     71 // TODO(xhwang): Support multiple decryptor notification request (e.g. from
     72 // video and audio decoders). The current implementation is okay for the current
     73 // media pipeline since we initialize audio and video decoders in sequence.
     74 // But ProxyDecryptor should not depend on media pipeline's implementation
     75 // detail.
     76 void ProxyDecryptor::SetDecryptorReadyCB(
     77      const media::DecryptorReadyCB& decryptor_ready_cb) {
     78   base::AutoLock auto_lock(lock_);
     79 
     80   // Cancels the previous decryptor request.
     81   if (decryptor_ready_cb.is_null()) {
     82     if (!decryptor_ready_cb_.is_null())
     83       base::ResetAndReturn(&decryptor_ready_cb_).Run(NULL);
     84     return;
     85   }
     86 
     87   // Normal decryptor request.
     88   DCHECK(decryptor_ready_cb_.is_null());
     89   if (media_keys_) {
     90     decryptor_ready_cb.Run(media_keys_->GetDecryptor());
     91     return;
     92   }
     93   decryptor_ready_cb_ = decryptor_ready_cb;
     94 }
     95 
     96 bool ProxyDecryptor::InitializeCDM(const std::string& key_system,
     97                                    const GURL& frame_url) {
     98   DVLOG(1) << "InitializeCDM: key_system = " << key_system;
     99 
    100   base::AutoLock auto_lock(lock_);
    101 
    102   DCHECK(!media_keys_);
    103   media_keys_ = CreateMediaKeys(key_system, frame_url);
    104   if (!media_keys_)
    105     return false;
    106 
    107   if (!decryptor_ready_cb_.is_null())
    108     base::ResetAndReturn(&decryptor_ready_cb_).Run(media_keys_->GetDecryptor());
    109 
    110   is_clear_key_ =
    111       media::IsClearKey(key_system) || media::IsExternalClearKey(key_system);
    112   return true;
    113 }
    114 
    115 bool ProxyDecryptor::GenerateKeyRequest(const std::string& type,
    116                                         const uint8* init_data,
    117                                         int init_data_length) {
    118   // Use a unique reference id for this request.
    119   uint32 session_id = next_session_id_++;
    120   if (!media_keys_->CreateSession(
    121            session_id, type, init_data, init_data_length)) {
    122     media_keys_.reset();
    123     return false;
    124   }
    125 
    126   return true;
    127 }
    128 
    129 void ProxyDecryptor::AddKey(const uint8* key,
    130                             int key_length,
    131                             const uint8* init_data,
    132                             int init_data_length,
    133                             const std::string& web_session_id) {
    134   DVLOG(1) << "AddKey()";
    135 
    136   // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called.
    137   uint32 session_id = LookupSessionId(web_session_id);
    138   if (session_id == kInvalidSessionId) {
    139     // Session hasn't been referenced before, so it is an error.
    140     // Note that the specification says "If sessionId is not null and is
    141     // unrecognized, throw an INVALID_ACCESS_ERR." However, for backwards
    142     // compatibility the error is not thrown, but rather reported as a
    143     // KeyError.
    144     key_error_cb_.Run(std::string(), media::MediaKeys::kUnknownError, 0);
    145     return;
    146   }
    147 
    148   // EME WD spec only supports a single array passed to the CDM. For
    149   // Clear Key using v0.1b, both arrays are used (|init_data| is key_id).
    150   // Since the EME WD spec supports the key as a JSON Web Key,
    151   // convert the 2 arrays to a JWK and pass it as the single array.
    152   if (is_clear_key_) {
    153     // Decryptor doesn't support empty key ID (see http://crbug.com/123265).
    154     // So ensure a non-empty value is passed.
    155     if (!init_data) {
    156       static const uint8 kDummyInitData[1] = {0};
    157       init_data = kDummyInitData;
    158       init_data_length = arraysize(kDummyInitData);
    159     }
    160 
    161     std::string jwk =
    162         media::GenerateJWKSet(key, key_length, init_data, init_data_length);
    163     DCHECK(!jwk.empty());
    164     media_keys_->UpdateSession(
    165         session_id, reinterpret_cast<const uint8*>(jwk.data()), jwk.size());
    166     return;
    167   }
    168 
    169   media_keys_->UpdateSession(session_id, key, key_length);
    170 }
    171 
    172 void ProxyDecryptor::CancelKeyRequest(const std::string& session_id) {
    173   DVLOG(1) << "CancelKeyRequest()";
    174 
    175   // WebMediaPlayerImpl ensures GenerateKeyRequest() has been called.
    176   uint32 session_reference_id = LookupSessionId(session_id);
    177   if (session_reference_id == kInvalidSessionId) {
    178     // Session hasn't been created, so it is an error.
    179     key_error_cb_.Run(
    180         std::string(), media::MediaKeys::kUnknownError, 0);
    181   }
    182   else {
    183     media_keys_->ReleaseSession(session_reference_id);
    184   }
    185 }
    186 
    187 scoped_ptr<media::MediaKeys> ProxyDecryptor::CreateMediaKeys(
    188     const std::string& key_system,
    189     const GURL& frame_url) {
    190   return ContentDecryptionModuleFactory::Create(
    191       key_system,
    192 #if defined(ENABLE_PEPPER_CDMS)
    193       web_media_player_client_,
    194       web_frame_,
    195       base::Bind(&ProxyDecryptor::DestroyHelperPlugin,
    196                  weak_ptr_factory_.GetWeakPtr()),
    197 #elif defined(OS_ANDROID)
    198       manager_,
    199       media_keys_id_,
    200       frame_url,
    201 #endif  // defined(ENABLE_PEPPER_CDMS)
    202       base::Bind(&ProxyDecryptor::OnSessionCreated,
    203                  weak_ptr_factory_.GetWeakPtr()),
    204       base::Bind(&ProxyDecryptor::OnSessionMessage,
    205                  weak_ptr_factory_.GetWeakPtr()),
    206       base::Bind(&ProxyDecryptor::OnSessionReady,
    207                  weak_ptr_factory_.GetWeakPtr()),
    208       base::Bind(&ProxyDecryptor::OnSessionClosed,
    209                  weak_ptr_factory_.GetWeakPtr()),
    210       base::Bind(&ProxyDecryptor::OnSessionError,
    211                  weak_ptr_factory_.GetWeakPtr()));
    212 }
    213 
    214 void ProxyDecryptor::OnSessionCreated(uint32 session_id,
    215                                       const std::string& web_session_id) {
    216   // Due to heartbeat messages, OnSessionCreated() can get called multiple
    217   // times.
    218   SessionIdMap::iterator it = sessions_.find(session_id);
    219   DCHECK(it == sessions_.end() || it->second == web_session_id);
    220   if (it == sessions_.end())
    221     sessions_[session_id] = web_session_id;
    222 }
    223 
    224 void ProxyDecryptor::OnSessionMessage(uint32 session_id,
    225                                       const std::vector<uint8>& message,
    226                                       const std::string& destination_url) {
    227   // Assumes that OnSessionCreated() has been called before this.
    228   key_message_cb_.Run(LookupWebSessionId(session_id), message, destination_url);
    229 }
    230 
    231 void ProxyDecryptor::OnSessionReady(uint32 session_id) {
    232   // Assumes that OnSessionCreated() has been called before this.
    233   key_added_cb_.Run(LookupWebSessionId(session_id));
    234 }
    235 
    236 void ProxyDecryptor::OnSessionClosed(uint32 session_id) {
    237   // No closed event in EME v0.1b.
    238 }
    239 
    240 void ProxyDecryptor::OnSessionError(uint32 session_id,
    241                                     media::MediaKeys::KeyError error_code,
    242                                     int system_code) {
    243   // Assumes that OnSessionCreated() has been called before this.
    244   key_error_cb_.Run(LookupWebSessionId(session_id), error_code, system_code);
    245 }
    246 
    247 uint32 ProxyDecryptor::LookupSessionId(const std::string& session_id) {
    248   for (SessionIdMap::iterator it = sessions_.begin();
    249        it != sessions_.end();
    250        ++it) {
    251     if (it->second == session_id)
    252       return it->first;
    253   }
    254 
    255   // If |session_id| is null, then use the single reference id.
    256   if (session_id.empty() && sessions_.size() == 1)
    257     return sessions_.begin()->first;
    258 
    259   return kInvalidSessionId;
    260 }
    261 
    262 const std::string& ProxyDecryptor::LookupWebSessionId(uint32 session_id) {
    263   DCHECK_NE(session_id, kInvalidSessionId);
    264 
    265   // Session may not exist if error happens during GenerateKeyRequest().
    266   SessionIdMap::iterator it = sessions_.find(session_id);
    267   return (it != sessions_.end()) ? it->second : base::EmptyString();
    268 }
    269 
    270 }  // namespace content
    271