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/strings/string_util.h"
13 #include "media/base/cdm_callback_promise.h"
14 #include "media/base/cdm_factory.h"
15 #include "media/base/cdm_key_information.h"
16 #include "media/base/key_systems.h"
17 #include "media/base/media_permission.h"
18 #include "media/cdm/json_web_key.h"
19 #include "media/cdm/key_system_names.h"
23 // Special system code to signal a closed persistent session in a SessionError()
24 // call. This is needed because there is no SessionClosed() call in the prefixed
26 const int kSessionClosedSystemCode
= 29127;
28 ProxyDecryptor::ProxyDecryptor(MediaPermission
* media_permission
,
29 const KeyAddedCB
& key_added_cb
,
30 const KeyErrorCB
& key_error_cb
,
31 const KeyMessageCB
& key_message_cb
)
32 : key_added_cb_(key_added_cb
),
33 key_error_cb_(key_error_cb
),
34 key_message_cb_(key_message_cb
),
36 weak_ptr_factory_(this) {
37 DCHECK(media_permission
);
38 DCHECK(!key_added_cb_
.is_null());
39 DCHECK(!key_error_cb_
.is_null());
40 DCHECK(!key_message_cb_
.is_null());
43 ProxyDecryptor::~ProxyDecryptor() {
44 // Destroy the decryptor explicitly before destroying the plugin.
48 CdmContext
* ProxyDecryptor::GetCdmContext() {
49 return media_keys_
? media_keys_
->GetCdmContext() : nullptr;
52 bool ProxyDecryptor::InitializeCDM(CdmFactory
* cdm_factory
,
53 const std::string
& key_system
,
54 const GURL
& security_origin
) {
55 DVLOG(1) << "InitializeCDM: key_system = " << key_system
;
58 media_keys_
= CreateMediaKeys(cdm_factory
, key_system
, security_origin
);
63 IsClearKey(key_system
) || IsExternalClearKey(key_system
);
67 // Returns true if |data| is prefixed with |header| and has data after the
69 bool HasHeader(const uint8
* data
, int data_length
, const std::string
& header
) {
70 return static_cast<size_t>(data_length
) > header
.size() &&
71 std::equal(data
, data
+ header
.size(), header
.begin());
74 // Removes the first |length| items from |data|.
75 void StripHeader(std::vector
<uint8
>& data
, size_t length
) {
76 data
.erase(data
.begin(), data
.begin() + length
);
79 bool ProxyDecryptor::GenerateKeyRequest(const std::string
& init_data_type
,
80 const uint8
* init_data
,
81 int init_data_length
) {
82 DVLOG(1) << "GenerateKeyRequest()";
83 const char kPrefixedApiPersistentSessionHeader
[] = "PERSISTENT|";
84 const char kPrefixedApiLoadSessionHeader
[] = "LOAD_SESSION|";
86 SessionCreationType session_creation_type
= TemporarySession
;
87 std::vector
<uint8
> init_data_vector(init_data
, init_data
+ init_data_length
);
88 if (HasHeader(init_data
, init_data_length
, kPrefixedApiLoadSessionHeader
)) {
89 session_creation_type
= LoadSession
;
90 StripHeader(init_data_vector
, strlen(kPrefixedApiLoadSessionHeader
));
91 } else if (HasHeader(init_data
,
93 kPrefixedApiPersistentSessionHeader
)) {
94 session_creation_type
= PersistentSession
;
95 StripHeader(init_data_vector
, strlen(kPrefixedApiPersistentSessionHeader
));
98 scoped_ptr
<NewSessionCdmPromise
> promise(
99 new CdmCallbackPromise
<std::string
>(
100 base::Bind(&ProxyDecryptor::SetSessionId
,
101 weak_ptr_factory_
.GetWeakPtr(),
102 session_creation_type
),
103 base::Bind(&ProxyDecryptor::OnSessionError
,
104 weak_ptr_factory_
.GetWeakPtr(),
105 std::string()))); // No session id until created.
106 uint8
* init_data_vector_data
=
107 (init_data_vector
.size() > 0) ? &init_data_vector
[0] : nullptr;
109 if (session_creation_type
== LoadSession
) {
110 media_keys_
->LoadSession(
111 MediaKeys::PERSISTENT_LICENSE_SESSION
,
112 std::string(reinterpret_cast<const char*>(init_data_vector_data
),
113 init_data_vector
.size()),
118 MediaKeys::SessionType session_type
=
119 session_creation_type
== PersistentSession
120 ? MediaKeys::PERSISTENT_LICENSE_SESSION
121 : MediaKeys::TEMPORARY_SESSION
;
123 media_keys_
->CreateSessionAndGenerateRequest(
124 session_type
, init_data_type
, init_data_vector_data
,
125 init_data_vector
.size(), promise
.Pass());
129 void ProxyDecryptor::AddKey(const uint8
* key
,
131 const uint8
* init_data
,
132 int init_data_length
,
133 const std::string
& session_id
) {
134 DVLOG(1) << "AddKey()";
136 // In the prefixed API, the session parameter provided to addKey() is
137 // optional, so use the single existing session if it exists.
138 std::string
new_session_id(session_id
);
139 if (new_session_id
.empty()) {
140 if (active_sessions_
.size() == 1) {
141 base::hash_map
<std::string
, bool>::iterator it
= active_sessions_
.begin();
142 new_session_id
= it
->first
;
144 OnSessionError(std::string(),
145 MediaKeys::NOT_SUPPORTED_ERROR
,
147 "SessionId not specified.");
152 scoped_ptr
<SimpleCdmPromise
> promise(new CdmCallbackPromise
<>(
153 base::Bind(&ProxyDecryptor::GenerateKeyAdded
,
154 weak_ptr_factory_
.GetWeakPtr(), session_id
),
155 base::Bind(&ProxyDecryptor::OnSessionError
,
156 weak_ptr_factory_
.GetWeakPtr(), session_id
)));
158 // EME WD spec only supports a single array passed to the CDM. For
159 // Clear Key using v0.1b, both arrays are used (|init_data| is key_id).
160 // Since the EME WD spec supports the key as a JSON Web Key,
161 // convert the 2 arrays to a JWK and pass it as the single array.
163 // Decryptor doesn't support empty key ID (see http://crbug.com/123265).
164 // So ensure a non-empty value is passed.
166 static const uint8 kDummyInitData
[1] = {0};
167 init_data
= kDummyInitData
;
168 init_data_length
= arraysize(kDummyInitData
);
172 GenerateJWKSet(key
, key_length
, init_data
, init_data_length
);
173 DCHECK(!jwk
.empty());
174 media_keys_
->UpdateSession(new_session_id
,
175 reinterpret_cast<const uint8
*>(jwk
.data()),
176 jwk
.size(), promise
.Pass());
180 media_keys_
->UpdateSession(new_session_id
, key
, key_length
, promise
.Pass());
183 void ProxyDecryptor::CancelKeyRequest(const std::string
& session_id
) {
184 DVLOG(1) << "CancelKeyRequest()";
186 scoped_ptr
<SimpleCdmPromise
> promise(new CdmCallbackPromise
<>(
187 base::Bind(&ProxyDecryptor::OnSessionClosed
,
188 weak_ptr_factory_
.GetWeakPtr(), session_id
),
189 base::Bind(&ProxyDecryptor::OnSessionError
,
190 weak_ptr_factory_
.GetWeakPtr(), session_id
)));
191 media_keys_
->RemoveSession(session_id
, promise
.Pass());
194 scoped_ptr
<MediaKeys
> ProxyDecryptor::CreateMediaKeys(
195 CdmFactory
* cdm_factory
,
196 const std::string
& key_system
,
197 const GURL
& security_origin
) {
198 base::WeakPtr
<ProxyDecryptor
> weak_this
= weak_ptr_factory_
.GetWeakPtr();
199 return cdm_factory
->Create(
202 base::Bind(&ProxyDecryptor::OnSessionMessage
, weak_this
),
203 base::Bind(&ProxyDecryptor::OnSessionClosed
, weak_this
),
204 base::Bind(&ProxyDecryptor::OnSessionError
, weak_this
),
205 base::Bind(&ProxyDecryptor::OnSessionKeysChange
, weak_this
),
206 base::Bind(&ProxyDecryptor::OnSessionExpirationUpdate
, weak_this
));
209 void ProxyDecryptor::OnSessionMessage(const std::string
& session_id
,
210 MediaKeys::MessageType message_type
,
211 const std::vector
<uint8
>& message
,
212 const GURL
& legacy_destination_url
) {
213 // Assumes that OnSessionCreated() has been called before this.
215 // For ClearKey, convert the message from JSON into just passing the key
216 // as the message. If unable to extract the key, return the message unchanged.
218 std::vector
<uint8
> key
;
219 if (ExtractFirstKeyIdFromLicenseRequest(message
, &key
)) {
220 key_message_cb_
.Run(session_id
, key
, legacy_destination_url
);
225 key_message_cb_
.Run(session_id
, message
, legacy_destination_url
);
228 void ProxyDecryptor::OnSessionKeysChange(const std::string
& session_id
,
229 bool has_additional_usable_key
,
230 CdmKeysInfo keys_info
) {
231 // EME v0.1b doesn't support this event.
234 void ProxyDecryptor::OnSessionExpirationUpdate(
235 const std::string
& session_id
,
236 const base::Time
& new_expiry_time
) {
237 // EME v0.1b doesn't support this event.
240 void ProxyDecryptor::GenerateKeyAdded(const std::string
& session_id
) {
241 // EME WD doesn't support this event, but it is needed for EME v0.1b.
242 key_added_cb_
.Run(session_id
);
245 void ProxyDecryptor::OnSessionClosed(const std::string
& session_id
) {
246 base::hash_map
<std::string
, bool>::iterator it
=
247 active_sessions_
.find(session_id
);
249 // Latest EME spec separates closing a session ("allows an application to
250 // indicate that it no longer needs the session") and actually closing the
251 // session (done by the CDM at any point "such as in response to a close()
252 // call, when the session is no longer needed, or when system resources are
253 // lost.") Thus the CDM may cause 2 close() events -- one to resolve the
254 // close() promise, and a second to actually close the session. Prefixed EME
255 // only expects 1 close event, so drop the second (and subsequent) events.
256 // However, this means we can't tell if the CDM is generating spurious close()
258 if (it
== active_sessions_
.end())
262 OnSessionError(session_id
, MediaKeys::NOT_SUPPORTED_ERROR
,
263 kSessionClosedSystemCode
,
264 "Do not close persistent sessions.");
266 active_sessions_
.erase(it
);
269 void ProxyDecryptor::OnSessionError(const std::string
& session_id
,
270 MediaKeys::Exception exception_code
,
272 const std::string
& error_message
) {
273 // Convert |error_name| back to MediaKeys::KeyError if possible. Prefixed
274 // EME has different error message, so all the specific error events will
276 MediaKeys::KeyError error_code
;
277 switch (exception_code
) {
278 case MediaKeys::CLIENT_ERROR
:
279 error_code
= MediaKeys::kClientError
;
281 case MediaKeys::OUTPUT_ERROR
:
282 error_code
= MediaKeys::kOutputError
;
285 // This will include all other CDM4 errors and any error generated
287 error_code
= MediaKeys::kUnknownError
;
290 key_error_cb_
.Run(session_id
, error_code
, system_code
);
293 void ProxyDecryptor::SetSessionId(SessionCreationType session_type
,
294 const std::string
& session_id
) {
295 // Loaded sessions are considered persistent.
297 session_type
== PersistentSession
|| session_type
== LoadSession
;
298 active_sessions_
.insert(std::make_pair(session_id
, is_persistent
));
300 // For LoadSession(), generate the KeyAdded event.
301 if (session_type
== LoadSession
)
302 GenerateKeyAdded(session_id
);