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.
5 #include "media/cdm/proxy_decryptor.h"
10 #include "base/callback_helpers.h"
11 #include "base/logging.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_util.h"
14 #include "media/base/cdm_callback_promise.h"
15 #include "media/base/cdm_config.h"
16 #include "media/base/cdm_factory.h"
17 #include "media/base/cdm_key_information.h"
18 #include "media/base/key_systems.h"
19 #include "media/base/media_permission.h"
20 #include "media/cdm/json_web_key.h"
21 #include "media/cdm/key_system_names.h"
25 // Special system code to signal a closed persistent session in a SessionError()
26 // call. This is needed because there is no SessionClosed() call in the prefixed
28 const int kSessionClosedSystemCode
= 29127;
30 ProxyDecryptor::PendingGenerateKeyRequestData::PendingGenerateKeyRequestData(
31 EmeInitDataType init_data_type
,
32 const std::vector
<uint8
>& init_data
)
33 : init_data_type(init_data_type
), init_data(init_data
) {
36 ProxyDecryptor::PendingGenerateKeyRequestData::
37 ~PendingGenerateKeyRequestData() {
40 ProxyDecryptor::ProxyDecryptor(MediaPermission
* media_permission
,
41 bool use_hw_secure_codecs
,
42 const KeyAddedCB
& key_added_cb
,
43 const KeyErrorCB
& key_error_cb
,
44 const KeyMessageCB
& key_message_cb
)
45 : is_creating_cdm_(false),
46 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
47 media_permission_(media_permission
),
49 use_hw_secure_codecs_(use_hw_secure_codecs
),
50 key_added_cb_(key_added_cb
),
51 key_error_cb_(key_error_cb
),
52 key_message_cb_(key_message_cb
),
54 weak_ptr_factory_(this) {
55 DCHECK(media_permission
);
56 DCHECK(!key_added_cb_
.is_null());
57 DCHECK(!key_error_cb_
.is_null());
58 DCHECK(!key_message_cb_
.is_null());
61 ProxyDecryptor::~ProxyDecryptor() {
62 // Destroy the decryptor explicitly before destroying the plugin.
66 void ProxyDecryptor::CreateCdm(CdmFactory
* cdm_factory
,
67 const std::string
& key_system
,
68 const GURL
& security_origin
,
69 const CdmContextReadyCB
& cdm_context_ready_cb
) {
70 DVLOG(1) << __FUNCTION__
<< ": key_system = " << key_system
;
71 DCHECK(!is_creating_cdm_
);
74 // TODO(sandersd): Trigger permissions check here and use it to determine
75 // distinctive identifier support, instead of always requiring the
76 // permission. http://crbug.com/455271
78 cdm_config
.allow_distinctive_identifier
= true;
79 cdm_config
.allow_persistent_state
= true;
80 cdm_config
.use_hw_secure_codecs
= use_hw_secure_codecs_
;
82 is_creating_cdm_
= true;
84 base::WeakPtr
<ProxyDecryptor
> weak_this
= weak_ptr_factory_
.GetWeakPtr();
86 key_system
, security_origin
, cdm_config
,
87 base::Bind(&ProxyDecryptor::OnSessionMessage
, weak_this
),
88 base::Bind(&ProxyDecryptor::OnSessionClosed
, weak_this
),
89 base::Bind(&ProxyDecryptor::OnLegacySessionError
, weak_this
),
90 base::Bind(&ProxyDecryptor::OnSessionKeysChange
, weak_this
),
91 base::Bind(&ProxyDecryptor::OnSessionExpirationUpdate
, weak_this
),
92 base::Bind(&ProxyDecryptor::OnCdmCreated
, weak_this
, key_system
,
93 security_origin
, cdm_context_ready_cb
));
96 void ProxyDecryptor::OnCdmCreated(const std::string
& key_system
,
97 const GURL
& security_origin
,
98 const CdmContextReadyCB
& cdm_context_ready_cb
,
99 scoped_ptr
<MediaKeys
> cdm
,
100 const std::string
& /* error_message */) {
101 is_creating_cdm_
= false;
104 cdm_context_ready_cb
.Run(nullptr);
106 key_system_
= key_system
;
107 security_origin_
= security_origin
;
108 is_clear_key_
= IsClearKey(key_system
) || IsExternalClearKey(key_system
);
109 media_keys_
= cdm
.Pass();
111 cdm_context_ready_cb
.Run(media_keys_
->GetCdmContext());
114 for (const auto& request
: pending_requests_
)
115 GenerateKeyRequestInternal(request
->init_data_type
, request
->init_data
);
117 pending_requests_
.clear();
120 void ProxyDecryptor::GenerateKeyRequest(EmeInitDataType init_data_type
,
121 const uint8
* init_data
,
122 int init_data_length
) {
123 std::vector
<uint8
> init_data_vector(init_data
, init_data
+ init_data_length
);
125 if (is_creating_cdm_
) {
126 pending_requests_
.push_back(
127 new PendingGenerateKeyRequestData(init_data_type
, init_data_vector
));
131 GenerateKeyRequestInternal(init_data_type
, init_data_vector
);
134 // Returns true if |data| is prefixed with |header| and has data after the
136 static bool HasHeader(const std::vector
<uint8
>& data
,
137 const std::string
& header
) {
138 return data
.size() > header
.size() &&
139 std::equal(header
.begin(), header
.end(), data
.begin());
142 // Removes the first |length| items from |data|.
143 static void StripHeader(std::vector
<uint8
>& data
, size_t length
) {
144 data
.erase(data
.begin(), data
.begin() + length
);
147 void ProxyDecryptor::GenerateKeyRequestInternal(
148 EmeInitDataType init_data_type
,
149 const std::vector
<uint8
>& init_data
) {
150 DVLOG(1) << __FUNCTION__
;
151 DCHECK(!is_creating_cdm_
);
154 OnLegacySessionError(std::string(), MediaKeys::NOT_SUPPORTED_ERROR
, 0,
155 "CDM creation failed.");
159 const char kPrefixedApiPersistentSessionHeader
[] = "PERSISTENT|";
160 const char kPrefixedApiLoadSessionHeader
[] = "LOAD_SESSION|";
162 SessionCreationType session_creation_type
= TemporarySession
;
163 std::vector
<uint8
> stripped_init_data
= init_data
;
164 if (HasHeader(init_data
, kPrefixedApiLoadSessionHeader
)) {
165 session_creation_type
= LoadSession
;
166 StripHeader(stripped_init_data
, strlen(kPrefixedApiLoadSessionHeader
));
167 } else if (HasHeader(init_data
, kPrefixedApiPersistentSessionHeader
)) {
168 session_creation_type
= PersistentSession
;
169 StripHeader(stripped_init_data
,
170 strlen(kPrefixedApiPersistentSessionHeader
));
173 scoped_ptr
<NewSessionCdmPromise
> promise(new CdmCallbackPromise
<std::string
>(
174 base::Bind(&ProxyDecryptor::SetSessionId
, weak_ptr_factory_
.GetWeakPtr(),
175 session_creation_type
),
176 base::Bind(&ProxyDecryptor::OnLegacySessionError
,
177 weak_ptr_factory_
.GetWeakPtr(),
178 std::string()))); // No session id until created.
180 if (session_creation_type
== LoadSession
) {
181 media_keys_
->LoadSession(
182 MediaKeys::PERSISTENT_LICENSE_SESSION
,
184 reinterpret_cast<const char*>(vector_as_array(&stripped_init_data
)),
185 stripped_init_data
.size()),
190 MediaKeys::SessionType session_type
=
191 session_creation_type
== PersistentSession
192 ? MediaKeys::PERSISTENT_LICENSE_SESSION
193 : MediaKeys::TEMPORARY_SESSION
;
195 // No permission required when AesDecryptor is used or when the key system is
196 // external clear key.
197 DCHECK(!key_system_
.empty());
198 if (CanUseAesDecryptor(key_system_
) || IsExternalClearKey(key_system_
)) {
199 OnPermissionStatus(session_type
, init_data_type
, stripped_init_data
,
200 promise
.Pass(), true /* granted */);
204 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
205 media_permission_
->RequestPermission(
206 MediaPermission::PROTECTED_MEDIA_IDENTIFIER
, security_origin_
,
207 base::Bind(&ProxyDecryptor::OnPermissionStatus
,
208 weak_ptr_factory_
.GetWeakPtr(), session_type
, init_data_type
,
209 stripped_init_data
, base::Passed(&promise
)));
211 OnPermissionStatus(session_type
, init_data_type
, stripped_init_data
,
212 promise
.Pass(), true /* granted */);
216 void ProxyDecryptor::OnPermissionStatus(
217 MediaKeys::SessionType session_type
,
218 EmeInitDataType init_data_type
,
219 const std::vector
<uint8
>& init_data
,
220 scoped_ptr
<NewSessionCdmPromise
> promise
,
222 // ProxyDecryptor is only used by Prefixed EME, where RequestPermission() is
223 // only for triggering the permission UI. Later CheckPermission() will be
224 // called (e.g. in PlatformVerificationFlow on ChromeOS; in BrowserCdmManager
225 // on Android) and the permission status will be evaluated then.
226 DVLOG_IF(1, !granted
) << "Permission request rejected.";
228 media_keys_
->CreateSessionAndGenerateRequest(session_type
, init_data_type
,
229 init_data
, promise
.Pass());
232 void ProxyDecryptor::AddKey(const uint8
* key
,
234 const uint8
* init_data
,
235 int init_data_length
,
236 const std::string
& session_id
) {
237 DVLOG(1) << "AddKey()";
240 OnLegacySessionError(std::string(), MediaKeys::INVALID_STATE_ERROR
, 0,
241 "CDM is not available.");
245 // In the prefixed API, the session parameter provided to addKey() is
246 // optional, so use the single existing session if it exists.
247 std::string
new_session_id(session_id
);
248 if (new_session_id
.empty()) {
249 if (active_sessions_
.size() == 1) {
250 base::hash_map
<std::string
, bool>::iterator it
= active_sessions_
.begin();
251 new_session_id
= it
->first
;
253 OnLegacySessionError(std::string(), MediaKeys::NOT_SUPPORTED_ERROR
, 0,
254 "SessionId not specified.");
259 scoped_ptr
<SimpleCdmPromise
> promise(new CdmCallbackPromise
<>(
260 base::Bind(&ProxyDecryptor::GenerateKeyAdded
,
261 weak_ptr_factory_
.GetWeakPtr(), session_id
),
262 base::Bind(&ProxyDecryptor::OnLegacySessionError
,
263 weak_ptr_factory_
.GetWeakPtr(), session_id
)));
265 // EME WD spec only supports a single array passed to the CDM. For
266 // Clear Key using v0.1b, both arrays are used (|init_data| is key_id).
267 // Since the EME WD spec supports the key as a JSON Web Key,
268 // convert the 2 arrays to a JWK and pass it as the single array.
270 // Decryptor doesn't support empty key ID (see http://crbug.com/123265).
271 // So ensure a non-empty value is passed.
273 static const uint8 kDummyInitData
[1] = {0};
274 init_data
= kDummyInitData
;
275 init_data_length
= arraysize(kDummyInitData
);
279 GenerateJWKSet(key
, key_length
, init_data
, init_data_length
);
280 DCHECK(!jwk
.empty());
281 media_keys_
->UpdateSession(new_session_id
,
282 std::vector
<uint8_t>(jwk
.begin(), jwk
.end()),
287 media_keys_
->UpdateSession(new_session_id
,
288 std::vector
<uint8_t>(key
, key
+ key_length
),
292 void ProxyDecryptor::CancelKeyRequest(const std::string
& session_id
) {
293 DVLOG(1) << "CancelKeyRequest()";
296 OnLegacySessionError(std::string(), MediaKeys::INVALID_STATE_ERROR
, 0,
297 "CDM is not available.");
301 scoped_ptr
<SimpleCdmPromise
> promise(new CdmCallbackPromise
<>(
302 base::Bind(&ProxyDecryptor::OnSessionClosed
,
303 weak_ptr_factory_
.GetWeakPtr(), session_id
),
304 base::Bind(&ProxyDecryptor::OnLegacySessionError
,
305 weak_ptr_factory_
.GetWeakPtr(), session_id
)));
306 media_keys_
->RemoveSession(session_id
, promise
.Pass());
309 void ProxyDecryptor::OnSessionMessage(const std::string
& session_id
,
310 MediaKeys::MessageType message_type
,
311 const std::vector
<uint8
>& message
,
312 const GURL
& legacy_destination_url
) {
313 // Assumes that OnSessionCreated() has been called before this.
315 // For ClearKey, convert the message from JSON into just passing the key
316 // as the message. If unable to extract the key, return the message unchanged.
318 std::vector
<uint8
> key
;
319 if (ExtractFirstKeyIdFromLicenseRequest(message
, &key
)) {
320 key_message_cb_
.Run(session_id
, key
, legacy_destination_url
);
325 key_message_cb_
.Run(session_id
, message
, legacy_destination_url
);
328 void ProxyDecryptor::OnSessionKeysChange(const std::string
& session_id
,
329 bool has_additional_usable_key
,
330 CdmKeysInfo keys_info
) {
331 // EME v0.1b doesn't support this event.
334 void ProxyDecryptor::OnSessionExpirationUpdate(
335 const std::string
& session_id
,
336 const base::Time
& new_expiry_time
) {
337 // EME v0.1b doesn't support this event.
340 void ProxyDecryptor::GenerateKeyAdded(const std::string
& session_id
) {
341 // EME WD doesn't support this event, but it is needed for EME v0.1b.
342 key_added_cb_
.Run(session_id
);
345 void ProxyDecryptor::OnSessionClosed(const std::string
& session_id
) {
346 base::hash_map
<std::string
, bool>::iterator it
=
347 active_sessions_
.find(session_id
);
349 // Latest EME spec separates closing a session ("allows an application to
350 // indicate that it no longer needs the session") and actually closing the
351 // session (done by the CDM at any point "such as in response to a close()
352 // call, when the session is no longer needed, or when system resources are
353 // lost.") Thus the CDM may cause 2 close() events -- one to resolve the
354 // close() promise, and a second to actually close the session. Prefixed EME
355 // only expects 1 close event, so drop the second (and subsequent) events.
356 // However, this means we can't tell if the CDM is generating spurious close()
358 if (it
== active_sessions_
.end())
362 OnLegacySessionError(session_id
, MediaKeys::NOT_SUPPORTED_ERROR
,
363 kSessionClosedSystemCode
,
364 "Do not close persistent sessions.");
366 active_sessions_
.erase(it
);
369 void ProxyDecryptor::OnLegacySessionError(const std::string
& session_id
,
370 MediaKeys::Exception exception_code
,
372 const std::string
& error_message
) {
373 // Convert |error_name| back to MediaKeys::KeyError if possible. Prefixed
374 // EME has different error message, so all the specific error events will
376 MediaKeys::KeyError error_code
;
377 switch (exception_code
) {
378 case MediaKeys::CLIENT_ERROR
:
379 error_code
= MediaKeys::kClientError
;
381 case MediaKeys::OUTPUT_ERROR
:
382 error_code
= MediaKeys::kOutputError
;
385 // This will include all other CDM4 errors and any error generated
387 error_code
= MediaKeys::kUnknownError
;
390 key_error_cb_
.Run(session_id
, error_code
, system_code
);
393 void ProxyDecryptor::SetSessionId(SessionCreationType session_type
,
394 const std::string
& session_id
) {
395 // LoadSession() returns empty |session_id| if the session is not found, so
396 // convert this into an error.
397 if (session_type
== LoadSession
&& session_id
.empty()) {
398 OnLegacySessionError(session_id
, MediaKeys::INVALID_ACCESS_ERROR
, 0,
399 "Incorrect session id specified for LoadSession().");
403 // Loaded sessions are considered persistent.
405 session_type
== PersistentSession
|| session_type
== LoadSession
;
406 active_sessions_
.insert(std::make_pair(session_id
, is_persistent
));
408 // For LoadSession(), generate the KeyAdded event.
409 if (session_type
== LoadSession
)
410 GenerateKeyAdded(session_id
);