Chromium Code Reviews| 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 |