Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / renderer / media / crypto / proxy_decryptor.cc
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 "media/base/cdm_callback_promise.h"
14 #include "media/base/cdm_factory.h"
15 #include "media/cdm/json_web_key.h"
16 #include "media/cdm/key_system_names.h"
17
18 namespace content {
19
20 // Special system code to signal a closed persistent session in a SessionError()
21 // call. This is needed because there is no SessionClosed() call in the prefixed
22 // EME API.
23 const int kSessionClosedSystemCode = 29127;
24
25 ProxyDecryptor::ProxyDecryptor(const KeyAddedCB& key_added_cb,
26                                const KeyErrorCB& key_error_cb,
27                                const KeyMessageCB& key_message_cb)
28     : key_added_cb_(key_added_cb),
29       key_error_cb_(key_error_cb),
30       key_message_cb_(key_message_cb),
31       is_clear_key_(false),
32       weak_ptr_factory_(this) {
33   DCHECK(!key_added_cb_.is_null());
34   DCHECK(!key_error_cb_.is_null());
35   DCHECK(!key_message_cb_.is_null());
36 }
37
38 ProxyDecryptor::~ProxyDecryptor() {
39   // Destroy the decryptor explicitly before destroying the plugin.
40   media_keys_.reset();
41 }
42
43 media::Decryptor* ProxyDecryptor::GetDecryptor() {
44   return media_keys_ ? media_keys_->GetDecryptor() : NULL;
45 }
46
47 #if defined(ENABLE_BROWSER_CDMS)
48 int ProxyDecryptor::GetCdmId() {
49   return media_keys_->GetCdmId();
50 }
51 #endif
52
53 bool ProxyDecryptor::InitializeCDM(media::CdmFactory* cdm_factory,
54                                    const std::string& key_system,
55                                    const GURL& security_origin) {
56   DVLOG(1) << "InitializeCDM: key_system = " << key_system;
57
58   DCHECK(!media_keys_);
59   media_keys_ = CreateMediaKeys(cdm_factory, key_system, security_origin);
60   if (!media_keys_)
61     return false;
62
63   is_clear_key_ =
64       media::IsClearKey(key_system) || media::IsExternalClearKey(key_system);
65   return true;
66 }
67
68 // Returns true if |data| is prefixed with |header| and has data after the
69 // |header|.
70 bool HasHeader(const uint8* data, int data_length, const std::string& header) {
71   return static_cast<size_t>(data_length) > header.size() &&
72          std::equal(data, data + header.size(), header.begin());
73 }
74
75 // Removes the first |length| items from |data|.
76 void StripHeader(std::vector<uint8>& data, size_t length) {
77   data.erase(data.begin(), data.begin() + length);
78 }
79
80 bool ProxyDecryptor::GenerateKeyRequest(const std::string& init_data_type,
81                                         const uint8* init_data,
82                                         int init_data_length) {
83   DVLOG(1) << "GenerateKeyRequest()";
84   const char kPrefixedApiPersistentSessionHeader[] = "PERSISTENT|";
85   const char kPrefixedApiLoadSessionHeader[] = "LOAD_SESSION|";
86
87   SessionCreationType session_creation_type = TemporarySession;
88   std::vector<uint8> init_data_vector(init_data, init_data + init_data_length);
89   if (HasHeader(init_data, init_data_length, kPrefixedApiLoadSessionHeader)) {
90     session_creation_type = LoadSession;
91     StripHeader(init_data_vector, strlen(kPrefixedApiLoadSessionHeader));
92   } else if (HasHeader(init_data,
93                        init_data_length,
94                        kPrefixedApiPersistentSessionHeader)) {
95     session_creation_type = PersistentSession;
96     StripHeader(init_data_vector, strlen(kPrefixedApiPersistentSessionHeader));
97   }
98
99   scoped_ptr<media::NewSessionCdmPromise> promise(
100       new media::CdmCallbackPromise<std::string>(
101           base::Bind(&ProxyDecryptor::SetSessionId,
102                      weak_ptr_factory_.GetWeakPtr(),
103                      session_creation_type),
104           base::Bind(&ProxyDecryptor::OnSessionError,
105                      weak_ptr_factory_.GetWeakPtr(),
106                      std::string())));  // No session id until created.
107   uint8* init_data_vector_data =
108       (init_data_vector.size() > 0) ? &init_data_vector[0] : nullptr;
109
110   if (session_creation_type == LoadSession) {
111     media_keys_->LoadSession(
112         std::string(reinterpret_cast<const char*>(init_data_vector_data),
113                     init_data_vector.size()),
114         promise.Pass());
115     return true;
116   }
117
118   media::MediaKeys::SessionType session_type =
119       session_creation_type == PersistentSession
120           ? media::MediaKeys::PERSISTENT_SESSION
121           : media::MediaKeys::TEMPORARY_SESSION;
122
123   media_keys_->CreateSession(init_data_type,
124                              init_data_vector_data,
125                              init_data_vector.size(),
126                              session_type,
127                              promise.Pass());
128   return true;
129 }
130
131 void ProxyDecryptor::AddKey(const uint8* key,
132                             int key_length,
133                             const uint8* init_data,
134                             int init_data_length,
135                             const std::string& web_session_id) {
136   DVLOG(1) << "AddKey()";
137
138   // In the prefixed API, the session parameter provided to addKey() is
139   // optional, so use the single existing session if it exists.
140   // TODO(jrummell): remove when the prefixed API is removed.
141   std::string session_id(web_session_id);
142   if (session_id.empty()) {
143     if (active_sessions_.size() == 1) {
144       base::hash_map<std::string, bool>::iterator it = active_sessions_.begin();
145       session_id = it->first;
146     } else {
147       OnSessionError(std::string(),
148                      media::MediaKeys::NOT_SUPPORTED_ERROR,
149                      0,
150                      "SessionId not specified.");
151       return;
152     }
153   }
154
155   scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
156       base::Bind(&ProxyDecryptor::OnSessionReady,
157                  weak_ptr_factory_.GetWeakPtr(),
158                  web_session_id),
159       base::Bind(&ProxyDecryptor::OnSessionError,
160                  weak_ptr_factory_.GetWeakPtr(),
161                  web_session_id)));
162
163   // EME WD spec only supports a single array passed to the CDM. For
164   // Clear Key using v0.1b, both arrays are used (|init_data| is key_id).
165   // Since the EME WD spec supports the key as a JSON Web Key,
166   // convert the 2 arrays to a JWK and pass it as the single array.
167   if (is_clear_key_) {
168     // Decryptor doesn't support empty key ID (see http://crbug.com/123265).
169     // So ensure a non-empty value is passed.
170     if (!init_data) {
171       static const uint8 kDummyInitData[1] = {0};
172       init_data = kDummyInitData;
173       init_data_length = arraysize(kDummyInitData);
174     }
175
176     std::string jwk =
177         media::GenerateJWKSet(key, key_length, init_data, init_data_length);
178     DCHECK(!jwk.empty());
179     media_keys_->UpdateSession(session_id,
180                                reinterpret_cast<const uint8*>(jwk.data()),
181                                jwk.size(),
182                                promise.Pass());
183     return;
184   }
185
186   media_keys_->UpdateSession(session_id, key, key_length, promise.Pass());
187 }
188
189 void ProxyDecryptor::CancelKeyRequest(const std::string& web_session_id) {
190   DVLOG(1) << "CancelKeyRequest()";
191
192   scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
193       base::Bind(&ProxyDecryptor::OnSessionClosed,
194                  weak_ptr_factory_.GetWeakPtr(),
195                  web_session_id),
196       base::Bind(&ProxyDecryptor::OnSessionError,
197                  weak_ptr_factory_.GetWeakPtr(),
198                  web_session_id)));
199   media_keys_->RemoveSession(web_session_id, promise.Pass());
200 }
201
202 scoped_ptr<media::MediaKeys> ProxyDecryptor::CreateMediaKeys(
203     media::CdmFactory* cdm_factory,
204     const std::string& key_system,
205     const GURL& security_origin) {
206   base::WeakPtr<ProxyDecryptor> weak_this = weak_ptr_factory_.GetWeakPtr();
207   return cdm_factory->Create(
208       key_system,
209       security_origin,
210       base::Bind(&ProxyDecryptor::OnSessionMessage, weak_this),
211       base::Bind(&ProxyDecryptor::OnSessionReady, weak_this),
212       base::Bind(&ProxyDecryptor::OnSessionClosed, weak_this),
213       base::Bind(&ProxyDecryptor::OnSessionError, weak_this),
214       base::Bind(&ProxyDecryptor::OnSessionKeysChange, weak_this),
215       base::Bind(&ProxyDecryptor::OnSessionExpirationUpdate, weak_this));
216 }
217
218 void ProxyDecryptor::OnSessionMessage(const std::string& web_session_id,
219                                       const std::vector<uint8>& message,
220                                       const GURL& destination_url) {
221   // Assumes that OnSessionCreated() has been called before this.
222
223   // For ClearKey, convert the message from JSON into just passing the key
224   // as the message. If unable to extract the key, return the message unchanged.
225   if (is_clear_key_) {
226     std::vector<uint8> key;
227     if (media::ExtractFirstKeyIdFromLicenseRequest(message, &key)) {
228       key_message_cb_.Run(web_session_id, key, destination_url);
229       return;
230     }
231   }
232
233   key_message_cb_.Run(web_session_id, message, destination_url);
234 }
235
236 void ProxyDecryptor::OnSessionKeysChange(const std::string& web_session_id,
237                                          bool has_additional_usable_key) {
238   // EME v0.1b doesn't support this event.
239 }
240
241 void ProxyDecryptor::OnSessionExpirationUpdate(
242     const std::string& web_session_id,
243     const base::Time& new_expiry_time) {
244   // EME v0.1b doesn't support this event.
245 }
246
247 void ProxyDecryptor::OnSessionReady(const std::string& web_session_id) {
248   key_added_cb_.Run(web_session_id);
249 }
250
251 void ProxyDecryptor::OnSessionClosed(const std::string& web_session_id) {
252   base::hash_map<std::string, bool>::iterator it =
253       active_sessions_.find(web_session_id);
254
255   // Latest EME spec separates closing a session ("allows an application to
256   // indicate that it no longer needs the session") and actually closing the
257   // session (done by the CDM at any point "such as in response to a close()
258   // call, when the session is no longer needed, or when system resources are
259   // lost.") Thus the CDM may cause 2 close() events -- one to resolve the
260   // close() promise, and a second to actually close the session. Prefixed EME
261   // only expects 1 close event, so drop the second (and subsequent) events.
262   // However, this means we can't tell if the CDM is generating spurious close()
263   // events.
264   if (it == active_sessions_.end())
265     return;
266
267   if (it->second) {
268     OnSessionError(web_session_id,
269                    media::MediaKeys::NOT_SUPPORTED_ERROR,
270                    kSessionClosedSystemCode,
271                    "Do not close persistent sessions.");
272   }
273   active_sessions_.erase(it);
274 }
275
276 void ProxyDecryptor::OnSessionError(const std::string& web_session_id,
277                                     media::MediaKeys::Exception exception_code,
278                                     uint32 system_code,
279                                     const std::string& error_message) {
280   // Convert |error_name| back to MediaKeys::KeyError if possible. Prefixed
281   // EME has different error message, so all the specific error events will
282   // get lost.
283   media::MediaKeys::KeyError error_code;
284   switch (exception_code) {
285     case media::MediaKeys::CLIENT_ERROR:
286       error_code = media::MediaKeys::kClientError;
287       break;
288     case media::MediaKeys::OUTPUT_ERROR:
289       error_code = media::MediaKeys::kOutputError;
290       break;
291     default:
292       // This will include all other CDM4 errors and any error generated
293       // by CDM5 or later.
294       error_code = media::MediaKeys::kUnknownError;
295       break;
296   }
297   key_error_cb_.Run(web_session_id, error_code, system_code);
298 }
299
300 void ProxyDecryptor::SetSessionId(SessionCreationType session_type,
301                                   const std::string& web_session_id) {
302   // Loaded sessions are considered persistent.
303   bool is_persistent =
304       session_type == PersistentSession || session_type == LoadSession;
305   active_sessions_.insert(std::make_pair(web_session_id, is_persistent));
306
307   // For LoadSession(), generate the SessionReady event.
308   if (session_type == LoadSession)
309     OnSessionReady(web_session_id);
310 }
311
312 }  // namespace content