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