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