Source release v3.1.0

This commit is contained in:
Gene Morgan
2016-07-19 18:43:15 -07:00
parent 7a7f78d654
commit 643b91b616
108 changed files with 16537 additions and 7174 deletions

View File

@@ -38,14 +38,14 @@ struct HostType {
Cdm::IStorage* storage;
Cdm::IClock* clock;
Cdm::ITimer* timer;
CdmEngine* provisioning_engine;
FileSystem* file_system;
bool initialized;
HostType()
: storage(NULL),
clock(NULL),
timer(NULL),
provisioning_engine(NULL),
file_system(NULL),
initialized(false) {}
} host;
@@ -109,8 +109,7 @@ class PropertySet : public CdmClientPropertySet {
class CdmImpl : public Cdm,
public WvCdmEventListener {
public:
CdmImpl(IEventListener* listener,
bool privacy_mode);
CdmImpl(IEventListener* listener, IStorage* storage, bool privacy_mode);
virtual ~CdmImpl();
@@ -135,6 +134,13 @@ class CdmImpl : public Cdm,
virtual Status getKeyStatuses(const std::string& session_id,
KeyStatusMap* key_statuses) OVERRIDE;
virtual Status getKeyAllowedUsages(
const std::string& session_id, const std::string& key_id,
KeyAllowedUsageFlags* usage_flags) OVERRIDE;
virtual Status getKeyAllowedUsages(
const std::string& key_id, KeyAllowedUsageFlags* usage_flags) OVERRIDE;
virtual Status setAppParameter(const std::string& key,
const std::string& value) OVERRIDE;
@@ -152,6 +158,25 @@ class CdmImpl : public Cdm,
virtual Status decrypt(const InputBuffer& input,
const OutputBuffer& output) OVERRIDE;
virtual Status genericEncrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
GenericEncryptionAlgorithmType algorithm,
std::string* out_buffer) OVERRIDE;
virtual Status genericDecrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
GenericEncryptionAlgorithmType algorithm,
std::string* out_buffer) OVERRIDE;
virtual Status genericSign(
const std::string& session_id, const std::string& message,
const std::string& key_id, GenericSigningAlgorithmType algorithm,
std::string* signature) OVERRIDE;
virtual Status genericVerify(
const std::string& session_id, const std::string& message,
const std::string& key_id, GenericSigningAlgorithmType algorithm,
const std::string& signature) OVERRIDE;
// ITimerClient:
virtual void onTimerExpired(void* context) OVERRIDE;
@@ -166,9 +191,17 @@ class CdmImpl : public Cdm,
int64_t new_expiry_time_seconds) OVERRIDE;
private:
KeyAllowedUsageFlags KeyAllowedFlags(const CdmKeyAllowedUsage& usages);
bool SendProvisioningRequest(const std::string& session_id);
CdmEncryptionAlgorithm ConvertEncryptionAlgorithm(
GenericEncryptionAlgorithmType algorithm);
CdmSigningAlgorithm ConvertSigningAlgorithm(
GenericSigningAlgorithmType algorithm);
IEventListener* listener_;
bool policy_timer_enabled_;
FileSystem file_system_;
CdmEngine cdm_engine_;
PropertySet property_set_;
CdmAppParameterMap app_parameters_;
@@ -184,13 +217,33 @@ class CdmImpl : public Cdm,
type((SessionType)-1),
expiration(0) {}
};
struct UnprovisionedSessionMetadata {
SessionType type;
std::string init_data;
InitDataType init_data_type;
bool has_init_data;
bool is_load;
UnprovisionedSessionMetadata()
: type((SessionType)-1),
init_data_type((InitDataType)-1),
has_init_data(false),
is_load(false) {}
};
typedef std::map<std::string, UnprovisionedSessionMetadata> UnprovisionedMap;
std::map<std::string, SessionMetadata> sessions_;
UnprovisionedMap unprovisioned_sessions_;
bool provision_request_sent_;
};
CdmImpl::CdmImpl(IEventListener* listener,
bool privacy_mode)
CdmImpl::CdmImpl(IEventListener* listener, IStorage* storage, bool privacy_mode)
: listener_(listener),
policy_timer_enabled_(false) {
policy_timer_enabled_(false),
file_system_("", storage),
cdm_engine_(&file_system_),
provision_request_sent_(false) {
assert(NULL != listener_);
property_set_.set_use_privacy_mode(privacy_mode);
}
@@ -220,7 +273,7 @@ Cdm::Status CdmImpl::setServerCertificate(const std::string& certificate) {
Cdm::Status CdmImpl::createSession(SessionType session_type,
std::string* session_id) {
if (!session_id) {
if (NULL == session_id) {
LOGE("Missing session ID pointer.");
return kTypeError;
}
@@ -238,17 +291,17 @@ Cdm::Status CdmImpl::createSession(SessionType session_type,
return kNotSupported;
}
std::string empty_origin;
CdmResponseType result = cdm_engine_.OpenSession(
"com.widevine.alpha", &property_set_, empty_origin, this,
NULL, session_id);
"com.widevine.alpha", &property_set_, this, session_id);
switch (result) {
case NO_ERROR:
sessions_[*session_id].type = session_type;
return kSuccess;
case NEED_PROVISIONING:
LOGE("A device certificate is needed.");
return kNeedsDeviceCertificate;
// The session does not exist in |cdm_engine_| yet, will be added in
// generateRequest.
unprovisioned_sessions_[*session_id].type = session_type;
return kSuccess;
default:
LOGE("Unexpected error %d", result);
return kUnexpectedError;
@@ -259,8 +312,43 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
InitDataType init_data_type,
const std::string& init_data) {
if (!cdm_engine_.IsOpenSession(session_id)) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
UnprovisionedMap::iterator session_iter =
unprovisioned_sessions_.find(session_id);
if (session_iter == unprovisioned_sessions_.end()) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (cdm_engine_.IsProvisioned(kSecurityLevelL1)) {
// We are provisioned now, create the session and generate the request
// like normal.
CdmResponseType result = cdm_engine_.OpenSession(
"com.widevine.alpha", &property_set_, session_id, this);
if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
sessions_[session_id].type = unprovisioned_sessions_[session_id].type;
unprovisioned_sessions_.erase(session_iter);
} else {
// We are not provisioned yet, save the init data and send a provisioning
// request if needed.
if (unprovisioned_sessions_[session_id].has_init_data) {
LOGE("Request already generated: %s", session_id.c_str());
return kInvalidState;
}
unprovisioned_sessions_[session_id].has_init_data = true;
unprovisioned_sessions_[session_id].init_data_type = init_data_type;
unprovisioned_sessions_[session_id].init_data = init_data;
if (provision_request_sent_)
return kDeferred;
if (!SendProvisioningRequest(session_id))
return kUnexpectedError;
return kSuccess;
}
}
if (sessions_[session_id].callable) {
@@ -296,6 +384,9 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
case kWebM:
init_data_type_name = WEBM_INIT_DATA_FORMAT;
break;
case kHls:
init_data_type_name = HLS_INIT_DATA_FORMAT;
break;
default:
LOGE("Invalid init data type: %d", init_data_type);
return kTypeError;
@@ -315,14 +406,11 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
return kNotSupported;
}
std::string key_request;
CdmKeyRequestType key_request_type;
std::string ignored_server_url;
CdmKeyRequest key_request;
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
session_id, session_id, init_data_obj,
license_type, app_parameters_, &key_request, &key_request_type,
&ignored_server_url, NULL);
license_type, app_parameters_, &key_request);
if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
@@ -330,7 +418,9 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
}
sessions_[session_id].callable = true;
assert(key_request_type == kKeyRequestTypeInitial);
assert(key_request.type == kKeyRequestTypeInitial);
MessageType message_type;
if (property_set_.use_privacy_mode() &&
property_set_.service_certificate().empty()) {
// We can deduce that this is a server cert request, even though CdmEgine
@@ -339,10 +429,12 @@ Cdm::Status CdmImpl::generateRequest(const std::string& session_id,
// The EME editor has clarified that this was a misinterpretation of the
// spec, and that this should also be kLicenseRequest.
LOGI("A server certificate request has been generated.");
message_type = kLicenseRequest;
} else {
LOGI("A license request has been generated.");
message_type = kLicenseRequest;
}
listener_->onMessage(session_id, kLicenseRequest, key_request);
listener_->onMessage(session_id, message_type, key_request.message);
return kSuccess;
}
@@ -357,23 +449,28 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
return kQuotaExceeded;
}
std::string empty_origin;
std::string ignored_session_id_output;
CdmResponseType result = cdm_engine_.OpenSession(
"com.widevine.alpha", &property_set_, empty_origin, this,
&session_id, &ignored_session_id_output);
"com.widevine.alpha", &property_set_, session_id, this);
switch (result) {
case NO_ERROR:
break;
case NEED_PROVISIONING:
LOGE("A device certificate is needed.");
return kNeedsDeviceCertificate;
unprovisioned_sessions_[session_id].is_load = true;
if (provision_request_sent_)
return kDeferred;
// Send a provisioning request right away. Then we will load the session
// again in |update|.
if (!SendProvisioningRequest(session_id))
return kUnexpectedError;
return kSuccess;
default:
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
DeviceFiles f;
DeviceFiles f(&file_system_);
if (!f.Init(kSecurityLevelUnknown)) {
LOGE("Unexpected error, failed to init DeviceFiles");
return kUnexpectedError;
@@ -381,8 +478,8 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
if (!f.LicenseExists(session_id)) {
// This might be a usage record session which needs to be loaded.
CdmKeyMessage release_message;
result = cdm_engine_.LoadUsageSession(session_id, &release_message);
CdmKeyMessage ignored_release_message;
result = cdm_engine_.LoadUsageSession(session_id, &ignored_release_message);
if (result == LOAD_USAGE_INFO_MISSING) {
LOGE("Unable to load license: %s", session_id.c_str());
cdm_engine_.CloseSession(session_id);
@@ -393,10 +490,6 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
return kUnexpectedError;
}
LOGI("A usage record release has been generated.");
MessageType message_type = kLicenseRelease;
listener_->onMessage(session_id, message_type, release_message);
sessions_[session_id].type = kPersistentUsageRecord;
sessions_[session_id].callable = true;
return kSuccess;
@@ -405,24 +498,7 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
result = cdm_engine_.RestoreKey(session_id, session_id);
if (result == GET_RELEASED_LICENSE_ERROR) {
// This was partially removed already.
// The EME spec states that we should send a release message right away.
InitializationData empty_initialization_data;
CdmKeyMessage key_request;
std::string ignored_server_url;
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
session_id, session_id, empty_initialization_data,
kLicenseTypeRelease, app_parameters_, &key_request, NULL,
&ignored_server_url, NULL);
if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
cdm_engine_.CloseSession(session_id);
return kUnexpectedError;
}
LOGI("A license release has been generated.");
MessageType message_type = kLicenseRelease;
listener_->onMessage(session_id, message_type, key_request);
// The EME spec states that we should be able to load it, but not use it.
} else if (result != KEY_ADDED) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
@@ -435,6 +511,64 @@ Cdm::Status CdmImpl::load(const std::string& session_id) {
Cdm::Status CdmImpl::update(const std::string& session_id,
const std::string& response) {
if (provision_request_sent_) {
std::string ignored_cert;
std::string ignored_wrapped_key;
provision_request_sent_ = false;
CdmResponseType result = cdm_engine_.HandleProvisioningResponse(
response, &ignored_cert, &ignored_wrapped_key);
if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
// We are now provisioned, we need to recreate the unprovisioned sessions.
Cdm::Status ret = kSuccess;
for (UnprovisionedMap::iterator it = unprovisioned_sessions_.begin();
it != unprovisioned_sessions_.end();) {
if (it->second.is_load) {
Cdm::Status load_status = load(it->first);
if (it->first != session_id)
listener_->onDeferredComplete(it->first, load_status);
else if (load_status != kSuccess)
ret = load_status;
unprovisioned_sessions_.erase(it++);
continue;
}
result = cdm_engine_.OpenSession("com.widevine.alpha", &property_set_,
it->first, this);
if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
if (it->first == session_id)
ret = kUnexpectedError;
else
listener_->onDeferredComplete(it->first, kUnexpectedError);
// We failed to create the session. Keep it in the unprovisioned
// sessions map so that another call to generateRequest will try to
// create the session again.
++it;
continue;
}
sessions_[it->first].type = it->second.type;
// Call generate request again for the session.
if (it->second.has_init_data) {
Cdm::Status generate_status = generateRequest(
it->first, it->second.init_data_type, it->second.init_data);
if (it->first != session_id)
listener_->onDeferredComplete(it->first, generate_status);
else if (generate_status != kSuccess)
// Still erase the item because the session exists in |cdm_engine_|.
ret = generate_status;
}
unprovisioned_sessions_.erase(it++);
}
return ret;
}
if (!cdm_engine_.IsOpenSession(session_id)) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
@@ -467,14 +601,11 @@ Cdm::Status CdmImpl::update(const std::string& session_id,
// The underlying session in CdmEngine has stored a copy of the original
// init data, so we can use an empty one this time.
InitializationData empty_init_data;
std::string key_request;
CdmKeyRequestType key_request_type;
std::string ignored_server_url;
CdmKeyRequest key_request;
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
session_id, session_id, empty_init_data, kLicenseTypeDeferred,
app_parameters_, &key_request, &key_request_type,
&ignored_server_url, NULL);
app_parameters_, &key_request);
if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
@@ -482,9 +613,9 @@ Cdm::Status CdmImpl::update(const std::string& session_id,
}
LOGI("A deferred license request has been generated.");
assert(key_request_type == kKeyRequestTypeInitial);
assert(key_request.type == kKeyRequestTypeInitial);
MessageType message_type = kLicenseRequest;
listener_->onMessage(session_id, message_type, key_request);
listener_->onMessage(session_id, message_type, key_request.message);
return kSuccess;
} else if (result == OFFLINE_LICENSE_PROHIBITED) {
LOGE("A temporary session cannot be used for a persistent license.");
@@ -518,6 +649,10 @@ Cdm::Status CdmImpl::getExpiration(const std::string& session_id,
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (NULL == expiration) {
LOGE("Missing pointer to expiration result.");
return kTypeError;
}
*expiration = sessions_[session_id].expiration;
return kSuccess;
@@ -529,11 +664,69 @@ Cdm::Status CdmImpl::getKeyStatuses(const std::string& session_id,
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (NULL == key_statuses) {
LOGE("Missing pointer to KeyStatusMap result.");
return kTypeError;
}
*key_statuses = sessions_[session_id].key_statuses;
return kSuccess;
}
Cdm::Status CdmImpl::getKeyAllowedUsages(const std::string& session_id,
const std::string& key_id,
KeyAllowedUsageFlags* usage_flags) {
if (!cdm_engine_.IsOpenSession(session_id)) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (NULL == usage_flags) {
LOGE("Missing pointer to KeyAllowedUsageFlags result.");
return kTypeError;
}
CdmKeyAllowedUsage usage_for_key;
CdmResponseType result = cdm_engine_.QueryKeyAllowedUsage(session_id,
key_id,
&usage_for_key);
if (result != NO_ERROR) {
if (result == KEY_NOT_FOUND_1) {
return kNoKey;
} else {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
}
*usage_flags = KeyAllowedFlags(usage_for_key);
return kSuccess;
}
Cdm::Status CdmImpl::getKeyAllowedUsages(const std::string& key_id,
KeyAllowedUsageFlags* usage_flags) {
if (NULL == usage_flags) {
LOGE("Missing pointer to KeyAllowedUsageFlags result.");
return kTypeError;
}
CdmKeyAllowedUsage usage_for_key;
CdmResponseType result = cdm_engine_.QueryKeyAllowedUsage(key_id,
&usage_for_key);
if (result != NO_ERROR) {
if (result == KEY_NOT_FOUND_1 || result == KEY_NOT_FOUND_2) {
return kNoKey;
} else if (result == KEY_CONFLICT_1) {
return kTypeError;
} else {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
}
*usage_flags = KeyAllowedFlags(usage_for_key);
return kSuccess;
}
Cdm::Status CdmImpl::setAppParameter(const std::string& key,
const std::string& value) {
if (key.empty()) {
@@ -602,8 +795,7 @@ Cdm::Status CdmImpl::remove(const std::string& session_id) {
}
InitializationData empty_initialization_data;
CdmKeyMessage key_request;
std::string ignored_server_url;
CdmKeyRequest key_request;
// Mark all keys as released ahead of generating the release request.
// When released, cdm_engine_ will mark all keys as expired, which we will
@@ -615,8 +807,7 @@ Cdm::Status CdmImpl::remove(const std::string& session_id) {
CdmResponseType result = cdm_engine_.GenerateKeyRequest(
session_id, session_id, empty_initialization_data,
kLicenseTypeRelease, app_parameters_, &key_request, NULL,
&ignored_server_url, NULL);
kLicenseTypeRelease, app_parameters_, &key_request);
if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
cdm_engine_.CloseSession(session_id);
@@ -625,13 +816,14 @@ Cdm::Status CdmImpl::remove(const std::string& session_id) {
LOGI("A license release has been generated.");
MessageType message_type = kLicenseRelease;
listener_->onMessage(session_id, message_type, key_request);
listener_->onMessage(session_id, message_type, key_request.message);
return kSuccess;
}
Cdm::Status CdmImpl::decrypt(const InputBuffer& input,
const OutputBuffer& output) {
if (input.is_encrypted && input.iv_length != 16) {
const bool is_encrypted = (input.encryption_scheme != kClear);
if (is_encrypted && input.iv_length != 16) {
LOGE("The IV must be 16 bytes long.");
return kTypeError;
}
@@ -646,8 +838,13 @@ Cdm::Status CdmImpl::decrypt(const InputBuffer& input,
std::vector<uint8_t> iv(input.iv, input.iv + input.iv_length);
CdmDecryptionParameters parameters;
parameters.is_encrypted = input.is_encrypted;
parameters.is_encrypted = is_encrypted;
parameters.is_secure = output.is_secure;
if (input.encryption_scheme == kAesCtr) {
parameters.cipher_mode = kCipherModeCtr;
} else if (input.encryption_scheme == kAesCbc) {
parameters.cipher_mode = kCipherModeCbc;
}
parameters.key_id = &key_id;
parameters.encrypt_buffer = input.data;
parameters.encrypt_length = input.data_length;
@@ -660,6 +857,8 @@ Cdm::Status CdmImpl::decrypt(const InputBuffer& input,
(input.first_subsample ? OEMCrypto_FirstSubsample : 0) |
(input.last_subsample ? OEMCrypto_LastSubsample : 0);
parameters.is_video = input.is_video;
parameters.pattern_descriptor.encrypt_blocks = input.pattern.encrypted_blocks;
parameters.pattern_descriptor.skip_blocks = input.pattern.clear_blocks;
CdmSessionId empty_session_id;
CdmResponseType result = cdm_engine_.Decrypt(empty_session_id, parameters);
@@ -675,6 +874,118 @@ Cdm::Status CdmImpl::decrypt(const InputBuffer& input,
return kDecryptError;
}
Cdm::Status CdmImpl::genericEncrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
GenericEncryptionAlgorithmType algorithm, std::string* out_buffer) {
CdmEncryptionAlgorithm cdm_algorithm = ConvertEncryptionAlgorithm(algorithm);
if (cdm_algorithm == wvcdm::kEncryptionAlgorithmUnknown) {
LOGE("Unrecognized encryption algorithm: %d.", cdm_algorithm);
return kNotSupported;
}
CdmResponseType result = cdm_engine_.GenericEncrypt(
session_id, in_buffer, key_id, iv, cdm_algorithm, out_buffer);
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SESSION_NOT_FOUND_13) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (result == KEY_NOT_FOUND_3 || result == KEY_ERROR_1) {
LOGE("Key Error: %s", session_id.c_str());
return kNoKey;
}
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
Cdm::Status CdmImpl::genericDecrypt(
const std::string& session_id, const std::string& in_buffer,
const std::string& key_id, const std::string& iv,
GenericEncryptionAlgorithmType algorithm, std::string* out_buffer) {
CdmEncryptionAlgorithm cdm_algorithm = ConvertEncryptionAlgorithm(algorithm);
if (cdm_algorithm == wvcdm::kEncryptionAlgorithmUnknown) {
LOGE("Unrecognized encryption algorithm: %d.", cdm_algorithm);
return kNotSupported;
}
CdmResponseType result = cdm_engine_.GenericDecrypt(
session_id, in_buffer, key_id, iv, cdm_algorithm, out_buffer);
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SESSION_NOT_FOUND_14) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (result == KEY_NOT_FOUND_4 || result == KEY_ERROR_2) {
LOGE("Key Error: %s", session_id.c_str());
return kNoKey;
}
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
Cdm::Status CdmImpl::genericSign(
const std::string& session_id, const std::string& message,
const std::string& key_id, GenericSigningAlgorithmType algorithm,
std::string* signature) {
CdmSigningAlgorithm cdm_algorithm = ConvertSigningAlgorithm(algorithm);
if (cdm_algorithm == wvcdm::kSigningAlgorithmUnknown) {
LOGE("Unrecognized signing algorithm: %d.", cdm_algorithm);
return kNotSupported;
}
CdmResponseType result = cdm_engine_.GenericSign(
session_id, message, key_id, cdm_algorithm, signature);
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SESSION_NOT_FOUND_15) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (result == KEY_NOT_FOUND_5 || result == KEY_ERROR_3) {
LOGE("Key Error: %s", session_id.c_str());
return kNoKey;
}
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
Cdm::Status CdmImpl::genericVerify(
const std::string& session_id, const std::string& message,
const std::string& key_id, GenericSigningAlgorithmType algorithm,
const std::string& signature) {
CdmSigningAlgorithm cdm_algorithm = ConvertSigningAlgorithm(algorithm);
if (cdm_algorithm == wvcdm::kSigningAlgorithmUnknown) {
LOGE("Unrecognized signing algorithm: %d.", cdm_algorithm);
return kNotSupported;
}
CdmResponseType result = cdm_engine_.GenericVerify(
session_id, message, key_id, cdm_algorithm, signature);
if (result == NO_ERROR) {
return kSuccess;
}
if (result == SESSION_NOT_FOUND_16) {
LOGE("No such session: %s", session_id.c_str());
return kSessionNotFound;
}
if (result == KEY_NOT_FOUND_6 || result == KEY_ERROR_4) {
LOGE("Key Error: %s", session_id.c_str());
return kNoKey;
}
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
void CdmImpl::onTimerExpired(void* context) {
if (context == kPolicyTimerContext) {
if (policy_timer_enabled_) {
@@ -687,10 +998,9 @@ void CdmImpl::onTimerExpired(void* context) {
}
void CdmImpl::OnSessionRenewalNeeded(const CdmSessionId& session_id) {
CdmKeyMessage message;
std::string server_url;
CdmKeyRequest key_request;
CdmResponseType result =
cdm_engine_.GenerateRenewalRequest(session_id, &message, &server_url);
cdm_engine_.GenerateRenewalRequest(session_id, &key_request);
if (result != KEY_MESSAGE) {
LOGE("Unexpected error %d", result);
return;
@@ -698,12 +1008,7 @@ void CdmImpl::OnSessionRenewalNeeded(const CdmSessionId& session_id) {
LOGI("A license renewal has been generated.");
MessageType message_type = kLicenseRenewal;
// Post the server_url before providing the message.
// For systems that still require the server URL,
// the listener will add the URL to its renewal request.
listener_->onMessageUrl(session_id, server_url);
listener_->onMessage(session_id, message_type, message);
listener_->onMessage(session_id, message_type, key_request.message);
}
void CdmImpl::OnSessionKeysChange(const CdmSessionId& session_id,
@@ -756,6 +1061,55 @@ void CdmImpl::OnExpirationUpdate(const CdmSessionId& session_id,
}
}
Cdm::KeyAllowedUsageFlags CdmImpl::KeyAllowedFlags(
const CdmKeyAllowedUsage& usages) {
KeyAllowedUsageFlags flags = kAllowNone;
flags |= (usages.decrypt_to_clear_buffer) ? kAllowDecryptToClearBuffer : 0;
flags |= (usages.decrypt_to_secure_buffer) ? kAllowDecryptToSecureBuffer : 0;
flags |= (usages.generic_encrypt) ? kAllowGenericEncrypt : 0;
flags |= (usages.generic_decrypt) ? kAllowGenericDecrypt : 0;
flags |= (usages.generic_sign) ? kAllowGenericSign : 0;
flags |= (usages.generic_verify) ? kAllowGenericSignatureVerify : 0;
return flags;
}
bool CdmImpl::SendProvisioningRequest(const std::string& session_id) {
// Generate a provisioning request.
std::string empty_authority;
std::string ignored_base_url;
std::string signed_request;
CdmResponseType result = cdm_engine_.GetProvisioningRequest(
kCertificateWidevine, empty_authority, &signed_request,
&ignored_base_url);
if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return false;
}
listener_->onDirectIndividualizationRequest(session_id, signed_request);
provision_request_sent_ = true;
return true;
}
CdmEncryptionAlgorithm CdmImpl::ConvertEncryptionAlgorithm(
GenericEncryptionAlgorithmType algorithm) {
if (algorithm == Cdm::kEncryptionAlgorithmAesCbc128) {
return wvcdm::kEncryptionAlgorithmAesCbc128;
} else {
LOGW("Unknown encryption algorithm: %d", algorithm);
return wvcdm::kEncryptionAlgorithmUnknown;
}
}
CdmSigningAlgorithm CdmImpl::ConvertSigningAlgorithm(
GenericSigningAlgorithmType algorithm) {
if (algorithm == Cdm::kSigningAlgorithmHmacSha256) {
return wvcdm::kSigningAlgorithmHmacSha256;
} else {
LOGW("Unknown signing algorithm: %d", algorithm);
return wvcdm::kSigningAlgorithmUnknown;
}
}
bool VerifyL1() {
CryptoSession cs;
@@ -772,7 +1126,6 @@ Cdm::Status Cdm::initialize(
IStorage* storage,
IClock* clock,
ITimer* timer,
DeviceCertificateRequest* device_certificate_request,
LogLevel verbosity) {
// If you want to direct-render on L3, CryptoSession will pass that request
// along to OEMCrypto. But if you want to use an opaque handle on L3,
@@ -813,11 +1166,6 @@ Cdm::Status Cdm::initialize(
return kTypeError;
}
if (!device_certificate_request) {
LOGE("Device certificate request pointer is required!");
return kTypeError;
}
// Our enum values match those in core/include/log.h
g_cutoff = static_cast<LogPriority>(verbosity);
@@ -827,33 +1175,6 @@ Cdm::Status Cdm::initialize(
host.storage = storage;
host.clock = clock;
host.timer = timer;
device_certificate_request->needed = false;
if (!host.provisioning_engine) {
host.provisioning_engine = new CdmEngine();
}
bool has_cert = host.provisioning_engine->IsProvisioned(
kSecurityLevelL1, "" /* origin */);
if (!has_cert) {
device_certificate_request->needed = true;
std::string empty_authority;
std::string empty_origin;
std::string base_url;
std::string signed_request;
CdmResponseType result = host.provisioning_engine->GetProvisioningRequest(
kCertificateWidevine, empty_authority, empty_origin,
&signed_request, &base_url);
if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
device_certificate_request->url = base_url;
device_certificate_request->url.append("&signedRequest=");
device_certificate_request->url.append(signed_request);
}
host.initialized = true;
return kSuccess;
}
@@ -865,6 +1186,7 @@ const char* Cdm::version() {
// static
Cdm* Cdm::create(IEventListener* listener,
IStorage* storage,
bool privacy_mode) {
if (!host.initialized) {
LOGE("Not initialized!");
@@ -874,28 +1196,10 @@ Cdm* Cdm::create(IEventListener* listener,
LOGE("No listener!");
return NULL;
}
return new CdmImpl(listener, privacy_mode);
}
if (!storage)
storage = host.storage;
Cdm::Status Cdm::DeviceCertificateRequest::acceptReply(
const std::string& reply) {
if (!host.provisioning_engine) {
LOGE("Provisioning reply received while not in a provisioning state!");
return kTypeError;
}
std::string empty_origin;
std::string ignored_cert;
std::string ignored_wrapped_key;
CdmResponseType result =
host.provisioning_engine->HandleProvisioningResponse(
empty_origin, reply, &ignored_cert, &ignored_wrapped_key);
if (result != NO_ERROR) {
LOGE("Unexpected error %d", result);
return kUnexpectedError;
}
return kSuccess;
return new CdmImpl(listener, storage, privacy_mode);
}
} // namespace widevine
@@ -911,35 +1215,22 @@ int64_t Clock::GetCurrentTime() {
class File::Impl {
public:
Cdm::IStorage* storage;
std::string name;
bool read_only;
bool truncate;
};
File::File() : impl_(NULL) {}
File::File(Impl* impl) : impl_(impl) {}
File::~File() {
Close();
}
bool File::Open(const std::string& file_path, int flags) {
if (!(flags & kCreate) && !host.storage->exists(file_path)) {
return false;
}
impl_ = new Impl;
impl_->name = file_path;
impl_->read_only = (flags & kReadOnly);
impl_->truncate = (flags & kTruncate);
return true;
}
File::~File() {}
ssize_t File::Read(char* buffer, size_t bytes) {
if (!impl_) {
return -1;
}
std::string data;
if (!host.storage->read(impl_->name, &data)) {
if (!impl_->storage->read(impl_->name, &data)) {
return -1;
}
@@ -957,7 +1248,7 @@ ssize_t File::Write(const char* buffer, size_t bytes) {
return -1;
}
std::string data(buffer, bytes);
if (!host.storage->write(impl_->name, data)) {
if (!impl_->storage->write(impl_->name, data)) {
return -1;
}
return bytes;
@@ -968,45 +1259,61 @@ void File::Close() {
delete impl_;
}
impl_ = NULL;
delete this;
}
bool File::Exists(const std::string& file_path) {
// An empty path is the "base directory" for CE CDM's file storage.
// Therefore, it should always be seen as existing.
// If it ever does not exist, CdmEngine detects this as a "factory reset"
// and wipes out all usage table data.
return file_path.empty() || host.storage->exists(file_path);
class FileSystem::Impl {
public:
widevine::Cdm::IStorage* storage;
};
FileSystem::FileSystem() : FileSystem("", NULL) {}
FileSystem::FileSystem(const std::string& origin, void* extra_data)
: impl_(new Impl), origin_(origin) {
if (extra_data)
impl_->storage = (widevine::Cdm::IStorage*)extra_data;
else
impl_->storage = host.storage;
}
bool File::Remove(const std::string& file_path) {
return host.storage->remove(file_path);
FileSystem::~FileSystem() {
delete impl_;
impl_ = NULL;
}
bool File::Copy(const std::string& old_path, const std::string& new_path) {
std::string data;
bool read_ok = host.storage->read(old_path, &data);
if (!read_ok) return false;
return host.storage->write(new_path, data);
File* FileSystem::Open(const std::string& file_path, int flags) {
if (!impl_ || (!(flags & kCreate) && !impl_->storage->exists(file_path))) {
return NULL;
}
File::Impl* file_impl = new File::Impl;
file_impl->storage = impl_->storage;
file_impl->name = file_path;
file_impl->read_only = (flags & kReadOnly);
file_impl->truncate = (flags & kTruncate);
return new File(file_impl);
}
bool File::List(const std::string& path, std::vector<std::string>* files) {
return false;
bool FileSystem::Exists(const std::string& file_path) {
if (!impl_)
return false;
return file_path.empty() || impl_->storage->exists(file_path);
}
bool File::CreateDirectory(const std::string dir_path) {
return true;
bool FileSystem::Remove(const std::string& file_path) {
if (!impl_)
return false;
return impl_->storage->remove(file_path);
}
bool File::IsDirectory(const std::string& dir_path) {
return false;
}
ssize_t FileSystem::FileSize(const std::string& file_path) {
if (!impl_)
return -1;
bool File::IsRegularFile(const std::string& file_path) {
return host.storage->exists(file_path);
}
ssize_t File::FileSize(const std::string& file_path) {
return host.storage->size(file_path);
return impl_->storage->size(file_path);
}
} // namespace wvcdm