| Index: media/blink/webcontentdecryptionmodulesession_impl.cc
|
| diff --git a/media/blink/webcontentdecryptionmodulesession_impl.cc b/media/blink/webcontentdecryptionmodulesession_impl.cc
|
| index 5ecba27e9cbabe567e24af5855fcdc9a63b61c47..c80a1d52d550443b7b014b729586dc89a0a8d38c 100644
|
| --- a/media/blink/webcontentdecryptionmodulesession_impl.cc
|
| +++ b/media/blink/webcontentdecryptionmodulesession_impl.cc
|
| @@ -22,6 +22,7 @@
|
| #include "media/blink/webmediaplayer_util.h"
|
| #include "media/cdm/cenc_utils.h"
|
| #include "media/cdm/json_web_key.h"
|
| +#include "media/cdm/key_system_names.h"
|
| #include "third_party/WebKit/public/platform/WebData.h"
|
| #include "third_party/WebKit/public/platform/WebEncryptedMediaKeyInformation.h"
|
| #include "third_party/WebKit/public/platform/WebString.h"
|
| @@ -148,6 +149,67 @@ static bool SanitizeInitData(EmeInitDataType init_data_type,
|
| return false;
|
| }
|
|
|
| +static bool SanitizeSessionId(const blink::WebString& session_id,
|
| + std::string* sanitized_session_id) {
|
| + // The user agent should thoroughly validate the sessionId value before
|
| + // passing it to the CDM. At a minimum, this should include checking that
|
| + // the length and value (e.g. alphanumeric) are reasonable.
|
| + if (!base::IsStringASCII(session_id))
|
| + return false;
|
| +
|
| + sanitized_session_id->assign(base::UTF16ToASCII(session_id));
|
| + if (sanitized_session_id->length() > limits::kMaxSessionIdLength)
|
| + return false;
|
| +
|
| + for (const char c : *sanitized_session_id) {
|
| + if (!IsAsciiAlpha(c) && !IsAsciiDigit(c))
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +static bool SanitizeResponse(const std::string& key_system,
|
| + const uint8* response,
|
| + size_t response_length,
|
| + std::vector<uint8>* sanitized_response) {
|
| + // The user agent should thoroughly validate the response before passing it
|
| + // to the CDM. This may include verifying values are within reasonable limits,
|
| + // stripping irrelevant data or fields, pre-parsing it, sanitizing it,
|
| + // and/or generating a fully sanitized version. The user agent should check
|
| + // that the length and values of fields are reasonable. Unknown fields should
|
| + // be rejected or removed.
|
| + if (response_length > limits::kMaxSessionResponseLength)
|
| + return false;
|
| +
|
| + if (IsClearKey(key_system) || IsExternalClearKey(key_system)) {
|
| + std::string key_string(response, response + response_length);
|
| + KeyIdAndKeyPairs keys;
|
| + MediaKeys::SessionType session_type = MediaKeys::TEMPORARY_SESSION;
|
| + if (!ExtractKeysFromJWKSet(key_string, &keys, &session_type))
|
| + return false;
|
| +
|
| + // Must contain at least one key.
|
| + if (keys.empty())
|
| + return false;
|
| +
|
| + for (const auto key_pair : keys) {
|
| + if (key_pair.first.size() < limits::kMinKeyIdLength ||
|
| + key_pair.first.size() > limits::kMaxKeyIdLength) {
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + std::string sanitized_data = GenerateJWKSet(keys, session_type);
|
| + sanitized_response->assign(sanitized_data.begin(), sanitized_data.end());
|
| + return true;
|
| + }
|
| +
|
| + // TODO(jrummell): Verify responses for Widevine.
|
| + sanitized_response->assign(response, response + response_length);
|
| + return true;
|
| +}
|
| +
|
| WebContentDecryptionModuleSessionImpl::WebContentDecryptionModuleSessionImpl(
|
| const scoped_refptr<CdmSessionAdapter>& adapter)
|
| : adapter_(adapter), is_closed_(false), weak_ptr_factory_(this) {
|
| @@ -240,11 +302,19 @@ void WebContentDecryptionModuleSessionImpl::load(
|
| DCHECK(!session_id.isEmpty());
|
| DCHECK(session_id_.empty());
|
|
|
| + std::string sanitized_session_id;
|
| + if (!SanitizeSessionId(session_id, &sanitized_session_id)) {
|
| + result.completeWithError(
|
| + blink::WebContentDecryptionModuleExceptionInvalidAccessError, 0,
|
| + "Invalid session ID.");
|
| + return;
|
| + }
|
| +
|
| // TODO(jrummell): Now that there are 2 types of persistent sessions, the
|
| // session type should be passed from blink. Type should also be passed in the
|
| // constructor (and removed from initializeNewSession()).
|
| adapter_->LoadSession(
|
| - MediaKeys::PERSISTENT_LICENSE_SESSION, base::UTF16ToASCII(session_id),
|
| + MediaKeys::PERSISTENT_LICENSE_SESSION, sanitized_session_id,
|
| scoped_ptr<NewSessionCdmPromise>(new NewSessionCdmResultPromise(
|
| result, adapter_->GetKeySystemUMAPrefix() + kLoadSessionUMAName,
|
| base::Bind(
|
| @@ -258,8 +328,18 @@ void WebContentDecryptionModuleSessionImpl::update(
|
| blink::WebContentDecryptionModuleResult result) {
|
| DCHECK(response);
|
| DCHECK(!session_id_.empty());
|
| +
|
| + std::vector<uint8> sanitized_response;
|
| + if (!SanitizeResponse(adapter_->GetKeySystem(), response, response_length,
|
| + &sanitized_response)) {
|
| + result.completeWithError(
|
| + blink::WebContentDecryptionModuleExceptionInvalidAccessError, 0,
|
| + "Invalid response.");
|
| + return;
|
| + }
|
| +
|
| adapter_->UpdateSession(
|
| - session_id_, std::vector<uint8>(response, response + response_length),
|
| + session_id_, sanitized_response,
|
| scoped_ptr<SimpleCdmPromise>(new CdmResultPromise<>(
|
| result, adapter_->GetKeySystemUMAPrefix() + kUpdateSessionUMAName)));
|
| }
|
|
|