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 media_permission_(media_permission
),
47 use_hw_secure_codecs_(use_hw_secure_codecs
),
48 key_added_cb_(key_added_cb
),
49 key_error_cb_(key_error_cb
),
50 key_message_cb_(key_message_cb
),
52 weak_ptr_factory_(this) {
53 DCHECK(media_permission
);
54 DCHECK(!key_added_cb_
.is_null());
55 DCHECK(!key_error_cb_
.is_null());
56 DCHECK(!key_message_cb_
.is_null());
59 ProxyDecryptor::~ProxyDecryptor() {
60 // Destroy the decryptor explicitly before destroying the plugin.
64 void ProxyDecryptor::CreateCdm(CdmFactory
* cdm_factory
,
65 const std::string
& key_system
,
66 const GURL
& security_origin
,
67 const CdmContextReadyCB
& cdm_context_ready_cb
) {
68 DVLOG(1) << __FUNCTION__
<< ": key_system = " << key_system
;
69 DCHECK(!is_creating_cdm_
);
72 // TODO(sandersd): Trigger permissions check here and use it to determine
73 // distinctive identifier support, instead of always requiring the
74 // permission. http://crbug.com/455271
76 cdm_config
.allow_distinctive_identifier
= true;
77 cdm_config
.allow_persistent_state
= true;
78 cdm_config
.use_hw_secure_codecs
= use_hw_secure_codecs_
;
80 is_creating_cdm_
= true;
82 base::WeakPtr
<ProxyDecryptor
> weak_this
= weak_ptr_factory_
.GetWeakPtr();
84 key_system
, security_origin
, cdm_config
,
85 base::Bind(&ProxyDecryptor::OnSessionMessage
, weak_this
),
86 base::Bind(&ProxyDecryptor::OnSessionClosed
, weak_this
),
87 base::Bind(&ProxyDecryptor::OnLegacySessionError
, weak_this
),
88 base::Bind(&ProxyDecryptor::OnSessionKeysChange
, weak_this
),
89 base::Bind(&ProxyDecryptor::OnSessionExpirationUpdate
, weak_this
),
90 base::Bind(&ProxyDecryptor::OnCdmCreated
, weak_this
, key_system
,
91 security_origin
, cdm_context_ready_cb
));
94 void ProxyDecryptor::OnCdmCreated(const std::string
& key_system
,
95 const GURL
& security_origin
,
96 const CdmContextReadyCB
& cdm_context_ready_cb
,
97 scoped_ptr
<MediaKeys
> cdm
,
98 const std::string
& /* error_message */) {
99 is_creating_cdm_
= false;
102 cdm_context_ready_cb
.Run(nullptr);
104 key_system_
= key_system
;
105 security_origin_
= security_origin
;
106 is_clear_key_
= IsClearKey(key_system
) || IsExternalClearKey(key_system
);
107 media_keys_
= cdm
.Pass();
109 cdm_context_ready_cb
.Run(media_keys_
->GetCdmContext());
112 for (const auto& request
: pending_requests_
)
113 GenerateKeyRequestInternal(request
->init_data_type
, request
->init_data
);
115 pending_requests_
.clear();
118 void ProxyDecryptor::GenerateKeyRequest(EmeInitDataType init_data_type
,
119 const uint8
* init_data
,
120 int init_data_length
) {
121 std::vector
<uint8
> init_data_vector(init_data
, init_data
+ init_data_length
);
123 if (is_creating_cdm_
) {
124 pending_requests_
.push_back(
125 new PendingGenerateKeyRequestData(init_data_type
, init_data_vector
));
129 GenerateKeyRequestInternal(init_data_type
, init_data_vector
);
132 // Returns true if |data| is prefixed with |header| and has data after the
134 bool HasHeader(const std::vector
<uint8
>& data
, const std::string
& header
) {
135 return data
.size() > header
.size() &&
136 std::equal(header
.begin(), header
.end(), data
.begin());
139 // Removes the first |length| items from |data|.
140 void StripHeader(std::vector
<uint8
>& data
, size_t length
) {
141 data
.erase(data
.begin(), data
.begin() + length
);
144 void ProxyDecryptor::GenerateKeyRequestInternal(
145 EmeInitDataType init_data_type
,
146 const std::vector
<uint8
>& init_data
) {
147 DVLOG(1) << __FUNCTION__
;
148 DCHECK(!is_creating_cdm_
);
151 OnLegacySessionError(std::string(), MediaKeys::NOT_SUPPORTED_ERROR
, 0,
152 "CDM creation failed.");
156 const char kPrefixedApiPersistentSessionHeader
[] = "PERSISTENT|";
157 const char kPrefixedApiLoadSessionHeader
[] = "LOAD_SESSION|";
159 SessionCreationType session_creation_type
= TemporarySession
;
160 std::vector
<uint8
> stripped_init_data
= init_data
;
161 if (HasHeader(init_data
, kPrefixedApiLoadSessionHeader
)) {
162 session_creation_type
= LoadSession
;
163 StripHeader(stripped_init_data
, strlen(kPrefixedApiLoadSessionHeader
));
164 } else if (HasHeader(init_data
, kPrefixedApiPersistentSessionHeader
)) {
165 session_creation_type
= PersistentSession
;
166 StripHeader(stripped_init_data
,
167 strlen(kPrefixedApiPersistentSessionHeader
));
170 scoped_ptr
<NewSessionCdmPromise
> promise(new CdmCallbackPromise
<std::string
>(
171 base::Bind(&ProxyDecryptor::SetSessionId
, weak_ptr_factory_
.GetWeakPtr(),
172 session_creation_type
),
173 base::Bind(&ProxyDecryptor::OnLegacySessionError
,
174 weak_ptr_factory_
.GetWeakPtr(),
175 std::string()))); // No session id until created.
177 if (session_creation_type
== LoadSession
) {
178 media_keys_
->LoadSession(
179 MediaKeys::PERSISTENT_LICENSE_SESSION
,
181 reinterpret_cast<const char*>(vector_as_array(&stripped_init_data
)),
182 stripped_init_data
.size()),
187 MediaKeys::SessionType session_type
=
188 session_creation_type
== PersistentSession
189 ? MediaKeys::PERSISTENT_LICENSE_SESSION
190 : MediaKeys::TEMPORARY_SESSION
;
192 // No permission required when AesDecryptor is used or when the key system is
193 // external clear key.
194 DCHECK(!key_system_
.empty());
195 if (CanUseAesDecryptor(key_system_
) || IsExternalClearKey(key_system_
)) {
196 OnPermissionStatus(session_type
, init_data_type
, stripped_init_data
,
197 promise
.Pass(), true /* granted */);
201 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
202 media_permission_
->RequestPermission(
203 MediaPermission::PROTECTED_MEDIA_IDENTIFIER
, security_origin_
,
204 base::Bind(&ProxyDecryptor::OnPermissionStatus
,
205 weak_ptr_factory_
.GetWeakPtr(), session_type
, init_data_type
,
206 stripped_init_data
, base::Passed(&promise
)));
208 OnPermissionStatus(session_type
, init_data_type
, stripped_init_data
,
209 promise
.Pass(), true /* granted */);
213 void ProxyDecryptor::OnPermissionStatus(
214 MediaKeys::SessionType session_type
,
215 EmeInitDataType init_data_type
,
216 const std::vector
<uint8
>& init_data
,
217 scoped_ptr
<NewSessionCdmPromise
> promise
,
219 // ProxyDecryptor is only used by Prefixed EME, where RequestPermission() is
220 // only for triggering the permission UI. Later CheckPermission() will be
221 // called (e.g. in PlatformVerificationFlow on ChromeOS; in BrowserCdmManager
222 // on Android) and the permission status will be evaluated then.
223 DVLOG_IF(1, !granted
) << "Permission request rejected.";
225 media_keys_
->CreateSessionAndGenerateRequest(session_type
, init_data_type
,
226 init_data
, promise
.Pass());
229 void ProxyDecryptor::AddKey(const uint8
* key
,
231 const uint8
* init_data
,
232 int init_data_length
,
233 const std::string
& session_id
) {
234 DVLOG(1) << "AddKey()";
237 OnLegacySessionError(std::string(), MediaKeys::INVALID_STATE_ERROR
, 0,
238 "CDM is not available.");
242 // In the prefixed API, the session parameter provided to addKey() is
243 // optional, so use the single existing session if it exists.
244 std::string
new_session_id(session_id
);
245 if (new_session_id
.empty()) {
246 if (active_sessions_
.size() == 1) {
247 base::hash_map
<std::string
, bool>::iterator it
= active_sessions_
.begin();
248 new_session_id
= it
->first
;
250 OnLegacySessionError(std::string(), MediaKeys::NOT_SUPPORTED_ERROR
, 0,
251 "SessionId not specified.");
256 scoped_ptr
<SimpleCdmPromise
> promise(new CdmCallbackPromise
<>(
257 base::Bind(&ProxyDecryptor::GenerateKeyAdded
,
258 weak_ptr_factory_
.GetWeakPtr(), session_id
),
259 base::Bind(&ProxyDecryptor::OnLegacySessionError
,
260 weak_ptr_factory_
.GetWeakPtr(), session_id
)));
262 // EME WD spec only supports a single array passed to the CDM. For
263 // Clear Key using v0.1b, both arrays are used (|init_data| is key_id).
264 // Since the EME WD spec supports the key as a JSON Web Key,
265 // convert the 2 arrays to a JWK and pass it as the single array.
267 // Decryptor doesn't support empty key ID (see http://crbug.com/123265).
268 // So ensure a non-empty value is passed.
270 static const uint8 kDummyInitData
[1] = {0};
271 init_data
= kDummyInitData
;
272 init_data_length
= arraysize(kDummyInitData
);
276 GenerateJWKSet(key
, key_length
, init_data
, init_data_length
);
277 DCHECK(!jwk
.empty());
278 media_keys_
->UpdateSession(new_session_id
,
279 std::vector
<uint8_t>(jwk
.begin(), jwk
.end()),
284 media_keys_
->UpdateSession(new_session_id
,
285 std::vector
<uint8_t>(key
, key
+ key_length
),
289 void ProxyDecryptor::CancelKeyRequest(const std::string
& session_id
) {
290 DVLOG(1) << "CancelKeyRequest()";
293 OnLegacySessionError(std::string(), MediaKeys::INVALID_STATE_ERROR
, 0,
294 "CDM is not available.");
298 scoped_ptr
<SimpleCdmPromise
> promise(new CdmCallbackPromise
<>(
299 base::Bind(&ProxyDecryptor::OnSessionClosed
,
300 weak_ptr_factory_
.GetWeakPtr(), session_id
),
301 base::Bind(&ProxyDecryptor::OnLegacySessionError
,
302 weak_ptr_factory_
.GetWeakPtr(), session_id
)));
303 media_keys_
->RemoveSession(session_id
, promise
.Pass());
306 void ProxyDecryptor::OnSessionMessage(const std::string
& session_id
,
307 MediaKeys::MessageType message_type
,
308 const std::vector
<uint8
>& message
,
309 const GURL
& legacy_destination_url
) {
310 // Assumes that OnSessionCreated() has been called before this.
312 // For ClearKey, convert the message from JSON into just passing the key
313 // as the message. If unable to extract the key, return the message unchanged.
315 std::vector
<uint8
> key
;
316 if (ExtractFirstKeyIdFromLicenseRequest(message
, &key
)) {
317 key_message_cb_
.Run(session_id
, key
, legacy_destination_url
);
322 key_message_cb_
.Run(session_id
, message
, legacy_destination_url
);
325 void ProxyDecryptor::OnSessionKeysChange(const std::string
& session_id
,
326 bool has_additional_usable_key
,
327 CdmKeysInfo keys_info
) {
328 // EME v0.1b doesn't support this event.
331 void ProxyDecryptor::OnSessionExpirationUpdate(
332 const std::string
& session_id
,
333 const base::Time
& new_expiry_time
) {
334 // EME v0.1b doesn't support this event.
337 void ProxyDecryptor::GenerateKeyAdded(const std::string
& session_id
) {
338 // EME WD doesn't support this event, but it is needed for EME v0.1b.
339 key_added_cb_
.Run(session_id
);
342 void ProxyDecryptor::OnSessionClosed(const std::string
& session_id
) {
343 base::hash_map
<std::string
, bool>::iterator it
=
344 active_sessions_
.find(session_id
);
346 // Latest EME spec separates closing a session ("allows an application to
347 // indicate that it no longer needs the session") and actually closing the
348 // session (done by the CDM at any point "such as in response to a close()
349 // call, when the session is no longer needed, or when system resources are
350 // lost.") Thus the CDM may cause 2 close() events -- one to resolve the
351 // close() promise, and a second to actually close the session. Prefixed EME
352 // only expects 1 close event, so drop the second (and subsequent) events.
353 // However, this means we can't tell if the CDM is generating spurious close()
355 if (it
== active_sessions_
.end())
359 OnLegacySessionError(session_id
, MediaKeys::NOT_SUPPORTED_ERROR
,
360 kSessionClosedSystemCode
,
361 "Do not close persistent sessions.");
363 active_sessions_
.erase(it
);
366 void ProxyDecryptor::OnLegacySessionError(const std::string
& session_id
,
367 MediaKeys::Exception exception_code
,
369 const std::string
& error_message
) {
370 // Convert |error_name| back to MediaKeys::KeyError if possible. Prefixed
371 // EME has different error message, so all the specific error events will
373 MediaKeys::KeyError error_code
;
374 switch (exception_code
) {
375 case MediaKeys::CLIENT_ERROR
:
376 error_code
= MediaKeys::kClientError
;
378 case MediaKeys::OUTPUT_ERROR
:
379 error_code
= MediaKeys::kOutputError
;
382 // This will include all other CDM4 errors and any error generated
384 error_code
= MediaKeys::kUnknownError
;
387 key_error_cb_
.Run(session_id
, error_code
, system_code
);
390 void ProxyDecryptor::SetSessionId(SessionCreationType session_type
,
391 const std::string
& session_id
) {
392 // Loaded sessions are considered persistent.
394 session_type
== PersistentSession
|| session_type
== LoadSession
;
395 active_sessions_
.insert(std::make_pair(session_id
, is_persistent
));
397 // For LoadSession(), generate the KeyAdded event.
398 if (session_type
== LoadSession
)
399 GenerateKeyAdded(session_id
);