OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "webcontentdecryptionmodulesession_impl.h" | 5 #include "webcontentdecryptionmodulesession_impl.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/callback_helpers.h" | 8 #include "base/callback_helpers.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/numerics/safe_conversions.h" | 10 #include "base/numerics/safe_conversions.h" |
11 #include "base/stl_util.h" | 11 #include "base/stl_util.h" |
12 #include "base/strings/string_util.h" | 12 #include "base/strings/string_util.h" |
13 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
14 #include "media/base/cdm_key_information.h" | 14 #include "media/base/cdm_key_information.h" |
15 #include "media/base/cdm_promise.h" | 15 #include "media/base/cdm_promise.h" |
16 #include "media/base/key_systems.h" | 16 #include "media/base/key_systems.h" |
17 #include "media/base/limits.h" | 17 #include "media/base/limits.h" |
18 #include "media/base/media_keys.h" | 18 #include "media/base/media_keys.h" |
19 #include "media/blink/cdm_result_promise.h" | 19 #include "media/blink/cdm_result_promise.h" |
20 #include "media/blink/cdm_session_adapter.h" | 20 #include "media/blink/cdm_session_adapter.h" |
21 #include "media/blink/new_session_cdm_result_promise.h" | 21 #include "media/blink/new_session_cdm_result_promise.h" |
22 #include "media/blink/webmediaplayer_util.h" | 22 #include "media/blink/webmediaplayer_util.h" |
23 #include "media/cdm/cenc_utils.h" | 23 #include "media/cdm/cenc_utils.h" |
24 #include "media/cdm/json_web_key.h" | 24 #include "media/cdm/json_web_key.h" |
25 #include "media/cdm/key_system_names.h" | |
25 #include "third_party/WebKit/public/platform/WebData.h" | 26 #include "third_party/WebKit/public/platform/WebData.h" |
26 #include "third_party/WebKit/public/platform/WebEncryptedMediaKeyInformation.h" | 27 #include "third_party/WebKit/public/platform/WebEncryptedMediaKeyInformation.h" |
27 #include "third_party/WebKit/public/platform/WebString.h" | 28 #include "third_party/WebKit/public/platform/WebString.h" |
28 #include "third_party/WebKit/public/platform/WebURL.h" | 29 #include "third_party/WebKit/public/platform/WebURL.h" |
29 #include "third_party/WebKit/public/platform/WebVector.h" | 30 #include "third_party/WebKit/public/platform/WebVector.h" |
30 | 31 |
31 namespace media { | 32 namespace media { |
32 | 33 |
33 const char kCloseSessionUMAName[] = "CloseSession"; | 34 const char kCloseSessionUMAName[] = "CloseSession"; |
34 const char kGenerateRequestUMAName[] = "GenerateRequest"; | 35 const char kGenerateRequestUMAName[] = "GenerateRequest"; |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
141 | 142 |
142 case EmeInitDataType::UNKNOWN: | 143 case EmeInitDataType::UNKNOWN: |
143 break; | 144 break; |
144 } | 145 } |
145 | 146 |
146 NOTREACHED(); | 147 NOTREACHED(); |
147 error_message->assign("Initialization data type is not supported."); | 148 error_message->assign("Initialization data type is not supported."); |
148 return false; | 149 return false; |
149 } | 150 } |
150 | 151 |
152 static bool SanitizeSessionId(const blink::WebString& session_id, | |
153 std::string* sanitized_session_id) { | |
154 // The user agent should thoroughly validate the sessionId value before | |
155 // passing it to the CDM. At a minimum, this should include checking that | |
156 // the length and value (e.g. alphanumeric) are reasonable. | |
157 if (!base::IsStringASCII(session_id)) | |
158 return false; | |
159 | |
160 sanitized_session_id->assign(base::UTF16ToASCII(session_id)); | |
161 if (sanitized_session_id->length() > limits::kMaxSessionIdLength) | |
162 return false; | |
163 | |
164 for (const char c : *sanitized_session_id) { | |
165 if (!IsAsciiAlpha(c) && !IsAsciiDigit(c)) | |
166 return false; | |
167 } | |
168 | |
169 return true; | |
170 } | |
171 | |
172 static bool SanitizeResponse(const std::string& key_system, | |
173 const uint8* response, | |
174 size_t response_length, | |
175 std::vector<uint8>* sanitized_response) { | |
176 // The user agent should thoroughly validate the response before passing it | |
177 // to the CDM. This may include verifying values are within reasonable limits, | |
178 // stripping irrelevant data or fields, pre-parsing it, sanitizing it, | |
179 // and/or generating a fully sanitized version. The user agent should check | |
180 // that the length and values of fields are reasonable. Unknown fields should | |
181 // be rejected or removed. | |
182 if (response_length > limits::kMaxSessionResponseLength) | |
183 return false; | |
184 | |
185 if (IsClearKey(key_system) || IsExternalClearKey(key_system)) { | |
186 std::string key_string(response, response + response_length); | |
187 KeyIdAndKeyPairs keys; | |
188 MediaKeys::SessionType session_type = MediaKeys::TEMPORARY_SESSION; | |
189 if (!ExtractKeysFromJWKSet(key_string, &keys, &session_type)) | |
190 return false; | |
191 | |
192 // Must contain at least one key. | |
193 if (keys.empty()) | |
194 return false; | |
195 | |
196 for (const auto key_pair : keys) { | |
197 if (key_pair.first.size() < limits::kMinKeyIdLength || | |
198 key_pair.first.size() > limits::kMaxKeyIdLength) { | |
199 return false; | |
200 } | |
201 } | |
202 | |
203 std::string sanitized_data = GenerateJWKSet(keys, session_type); | |
204 sanitized_response->assign(sanitized_data.begin(), sanitized_data.end()); | |
205 return true; | |
206 } | |
207 | |
208 // TODO(jrummell): Verify responses for Widevine. | |
209 sanitized_response->assign(response, response + response_length); | |
210 return true; | |
211 } | |
212 | |
151 WebContentDecryptionModuleSessionImpl::WebContentDecryptionModuleSessionImpl( | 213 WebContentDecryptionModuleSessionImpl::WebContentDecryptionModuleSessionImpl( |
152 const scoped_refptr<CdmSessionAdapter>& adapter) | 214 const scoped_refptr<CdmSessionAdapter>& adapter) |
153 : adapter_(adapter), is_closed_(false), weak_ptr_factory_(this) { | 215 : adapter_(adapter), is_closed_(false), weak_ptr_factory_(this) { |
154 } | 216 } |
155 | 217 |
156 WebContentDecryptionModuleSessionImpl:: | 218 WebContentDecryptionModuleSessionImpl:: |
157 ~WebContentDecryptionModuleSessionImpl() { | 219 ~WebContentDecryptionModuleSessionImpl() { |
158 if (!session_id_.empty()) | 220 if (!session_id_.empty()) |
159 adapter_->UnregisterSession(session_id_); | 221 adapter_->UnregisterSession(session_id_); |
160 } | 222 } |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
233 &WebContentDecryptionModuleSessionImpl::OnSessionInitialized, | 295 &WebContentDecryptionModuleSessionImpl::OnSessionInitialized, |
234 base::Unretained(this))))); | 296 base::Unretained(this))))); |
235 } | 297 } |
236 | 298 |
237 void WebContentDecryptionModuleSessionImpl::load( | 299 void WebContentDecryptionModuleSessionImpl::load( |
238 const blink::WebString& session_id, | 300 const blink::WebString& session_id, |
239 blink::WebContentDecryptionModuleResult result) { | 301 blink::WebContentDecryptionModuleResult result) { |
240 DCHECK(!session_id.isEmpty()); | 302 DCHECK(!session_id.isEmpty()); |
241 DCHECK(session_id_.empty()); | 303 DCHECK(session_id_.empty()); |
242 | 304 |
305 std::string sanitized_session_id; | |
306 if (!SanitizeSessionId(session_id, &sanitized_session_id)) { | |
307 result.completeWithError( | |
308 blink::WebContentDecryptionModuleExceptionInvalidAccessError, 0, | |
309 "Invalid session Id."); | |
sandersd (OOO until July 31)
2015/05/09 01:06:57
ID
jrummell
2015/05/09 01:11:45
Done.
| |
310 return; | |
311 } | |
312 | |
243 // TODO(jrummell): Now that there are 2 types of persistent sessions, the | 313 // TODO(jrummell): Now that there are 2 types of persistent sessions, the |
244 // session type should be passed from blink. Type should also be passed in the | 314 // session type should be passed from blink. Type should also be passed in the |
245 // constructor (and removed from initializeNewSession()). | 315 // constructor (and removed from initializeNewSession()). |
246 adapter_->LoadSession( | 316 adapter_->LoadSession( |
247 MediaKeys::PERSISTENT_LICENSE_SESSION, base::UTF16ToASCII(session_id), | 317 MediaKeys::PERSISTENT_LICENSE_SESSION, sanitized_session_id, |
248 scoped_ptr<NewSessionCdmPromise>(new NewSessionCdmResultPromise( | 318 scoped_ptr<NewSessionCdmPromise>(new NewSessionCdmResultPromise( |
249 result, adapter_->GetKeySystemUMAPrefix() + kLoadSessionUMAName, | 319 result, adapter_->GetKeySystemUMAPrefix() + kLoadSessionUMAName, |
250 base::Bind( | 320 base::Bind( |
251 &WebContentDecryptionModuleSessionImpl::OnSessionInitialized, | 321 &WebContentDecryptionModuleSessionImpl::OnSessionInitialized, |
252 base::Unretained(this))))); | 322 base::Unretained(this))))); |
253 } | 323 } |
254 | 324 |
255 void WebContentDecryptionModuleSessionImpl::update( | 325 void WebContentDecryptionModuleSessionImpl::update( |
256 const uint8* response, | 326 const uint8* response, |
257 size_t response_length, | 327 size_t response_length, |
258 blink::WebContentDecryptionModuleResult result) { | 328 blink::WebContentDecryptionModuleResult result) { |
259 DCHECK(response); | 329 DCHECK(response); |
260 DCHECK(!session_id_.empty()); | 330 DCHECK(!session_id_.empty()); |
331 | |
332 std::vector<uint8> sanitized_response; | |
333 if (!SanitizeResponse(adapter_->GetKeySystem(), response, response_length, | |
334 &sanitized_response)) { | |
335 result.completeWithError( | |
336 blink::WebContentDecryptionModuleExceptionInvalidAccessError, 0, | |
337 "Invalid response."); | |
338 return; | |
339 } | |
340 | |
261 adapter_->UpdateSession( | 341 adapter_->UpdateSession( |
262 session_id_, std::vector<uint8>(response, response + response_length), | 342 session_id_, sanitized_response, |
263 scoped_ptr<SimpleCdmPromise>(new CdmResultPromise<>( | 343 scoped_ptr<SimpleCdmPromise>(new CdmResultPromise<>( |
264 result, adapter_->GetKeySystemUMAPrefix() + kUpdateSessionUMAName))); | 344 result, adapter_->GetKeySystemUMAPrefix() + kUpdateSessionUMAName))); |
265 } | 345 } |
266 | 346 |
267 void WebContentDecryptionModuleSessionImpl::close( | 347 void WebContentDecryptionModuleSessionImpl::close( |
268 blink::WebContentDecryptionModuleResult result) { | 348 blink::WebContentDecryptionModuleResult result) { |
269 DCHECK(!session_id_.empty()); | 349 DCHECK(!session_id_.empty()); |
270 adapter_->CloseSession( | 350 adapter_->CloseSession( |
271 session_id_, | 351 session_id_, |
272 scoped_ptr<SimpleCdmPromise>(new CdmResultPromise<>( | 352 scoped_ptr<SimpleCdmPromise>(new CdmResultPromise<>( |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
328 return blink::WebContentDecryptionModuleResult::SessionNotFound; | 408 return blink::WebContentDecryptionModuleResult::SessionNotFound; |
329 | 409 |
330 DCHECK(session_id_.empty()) << "Session ID may not be changed once set."; | 410 DCHECK(session_id_.empty()) << "Session ID may not be changed once set."; |
331 session_id_ = session_id; | 411 session_id_ = session_id; |
332 return adapter_->RegisterSession(session_id_, weak_ptr_factory_.GetWeakPtr()) | 412 return adapter_->RegisterSession(session_id_, weak_ptr_factory_.GetWeakPtr()) |
333 ? blink::WebContentDecryptionModuleResult::NewSession | 413 ? blink::WebContentDecryptionModuleResult::NewSession |
334 : blink::WebContentDecryptionModuleResult::SessionAlreadyExists; | 414 : blink::WebContentDecryptionModuleResult::SessionAlreadyExists; |
335 } | 415 } |
336 | 416 |
337 } // namespace media | 417 } // namespace media |
OLD | NEW |