Provisioning 3.0: Changes to Provisioning and Service Certs.
[ Merge of http://go/wvgerrit/23360 ] Service Certificates are used in two places, provisioning and licensing. The service certificate code depended on a session_id to get and set the service certificate properties, but the session_id was not available in the provisioning path. This patch pulls out the property lookup by session_id dependency, and passes the CdmImpl's property_set into the provisioning code, so the service certificate can be read and written there. Bug: 62972441 Test: WV unit/integration tests. This introduces three test failures * WvCdmRequestLicenseTest.PrivacyModeWithServiceCertificateTest * Cdm/WvCdmStreamingLicenseRenewalTest.WithClientId/4 * Cdm/WvCdmOfflineLicenseReleaseTest.WithClientId/3 Change-Id: I6e9d4e23a9e7e81a63a994db8ec0b443893449a6
This commit is contained in:
@@ -42,11 +42,6 @@ class UsagePropertySet : public CdmClientPropertySet {
|
||||
virtual bool use_privacy_mode() const { return false; }
|
||||
virtual const std::string& service_certificate() const { return empty_; }
|
||||
virtual void set_service_certificate(const std::string&) {}
|
||||
virtual const std::string& device_provisioning_service_certificate() const {
|
||||
return empty_;
|
||||
}
|
||||
virtual void set_device_provisioning_service_certificate(const std::string&) {
|
||||
}
|
||||
virtual bool is_session_sharing_enabled() const { return false; }
|
||||
virtual uint32_t session_sharing_id() const { return 0; }
|
||||
virtual void set_session_sharing_id(uint32_t /* id */) {}
|
||||
@@ -102,27 +97,28 @@ CdmEngine::~CdmEngine() {
|
||||
M_RECORD(&metrics_, cdm_engine_life_span_, life_span_.AsMs());
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
CdmClientPropertySet* property_set,
|
||||
const CdmSessionId& forced_session_id,
|
||||
WvCdmEventListener* event_listener) {
|
||||
CdmResponseType CdmEngine::SetServiceCertificate(
|
||||
const std::string& certificate) {
|
||||
return service_certificate_.Init(certificate);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenSession(
|
||||
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
|
||||
const CdmSessionId& forced_session_id, WvCdmEventListener* event_listener) {
|
||||
return OpenSession(key_system, property_set, event_listener,
|
||||
&forced_session_id, NULL);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener,
|
||||
CdmSessionId* session_id) {
|
||||
return OpenSession(key_system, property_set, event_listener, NULL,
|
||||
session_id);
|
||||
CdmResponseType CdmEngine::OpenSession(
|
||||
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener, CdmSessionId* session_id) {
|
||||
return OpenSession(key_system, property_set, event_listener, NULL, session_id);
|
||||
}
|
||||
|
||||
CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener,
|
||||
const CdmSessionId* forced_session_id,
|
||||
CdmSessionId* session_id) {
|
||||
CdmResponseType CdmEngine::OpenSession(
|
||||
const CdmKeySystem& key_system, CdmClientPropertySet* property_set,
|
||||
WvCdmEventListener* event_listener, const CdmSessionId* forced_session_id,
|
||||
CdmSessionId* session_id) {
|
||||
LOGI("CdmEngine::OpenSession");
|
||||
|
||||
if (!ValidateKeySystem(key_system)) {
|
||||
@@ -145,8 +141,8 @@ CdmResponseType CdmEngine::OpenSession(const CdmKeySystem& key_system,
|
||||
|
||||
scoped_ptr<CdmSession> new_session(new CdmSession(file_system_,
|
||||
metrics_.AddSession()));
|
||||
CdmResponseType sts = new_session->Init(property_set, forced_session_id,
|
||||
event_listener);
|
||||
CdmResponseType sts = new_session->Init(&service_certificate_, property_set,
|
||||
forced_session_id, event_listener);
|
||||
if (sts != NO_ERROR) {
|
||||
if (sts == NEED_PROVISIONING) {
|
||||
cert_provisioning_requested_security_level_ =
|
||||
@@ -547,6 +543,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
} else if (query_token == QUERY_KEY_DEVICE_ID) {
|
||||
// TODO(gmorgan): this should not be performed before device is provisioned
|
||||
// (except if keybox provisioned).
|
||||
std::string deviceId;
|
||||
bool got_id = crypto_session.GetExternalDeviceUniqueId(&deviceId);
|
||||
metrics_.GetCryptoMetrics()->crypto_session_get_device_unique_id_
|
||||
@@ -558,6 +556,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
|
||||
*query_response = deviceId;
|
||||
} else if (query_token == QUERY_KEY_SYSTEM_ID) {
|
||||
// TODO(gmorgan): this should not be performed before device is provisioned
|
||||
// (except if keybox provisioned).
|
||||
uint32_t system_id;
|
||||
bool got_id = crypto_session.GetSystemId(&system_id);
|
||||
if (!got_id) {
|
||||
@@ -569,6 +569,8 @@ CdmResponseType CdmEngine::QueryStatus(SecurityLevel security_level,
|
||||
system_id_stream << system_id;
|
||||
*query_response = system_id_stream.str();
|
||||
} else if (query_token == QUERY_KEY_PROVISIONING_ID) {
|
||||
// TODO(gmorgan): this should not be performed before device is provisioned
|
||||
// (except if keybox provisioned).
|
||||
std::string provisioning_id;
|
||||
if (!crypto_session.GetProvisioningId(&provisioning_id)) {
|
||||
LOGW("CdmEngine::QueryStatus: GetProvisioningId failed");
|
||||
@@ -798,7 +800,8 @@ CdmResponseType CdmEngine::GetProvisioningRequest(
|
||||
|
||||
if (NULL == cert_provisioning_.get()) {
|
||||
cert_provisioning_.reset(
|
||||
new CertificateProvisioning(metrics_.GetCryptoMetrics()));
|
||||
new CertificateProvisioning(metrics_.GetCryptoMetrics(),
|
||||
&service_certificate_));
|
||||
}
|
||||
CdmResponseType ret = cert_provisioning_->GetProvisioningRequest(
|
||||
cert_provisioning_requested_security_level_, cert_type, cert_authority,
|
||||
@@ -824,14 +827,14 @@ CdmResponseType CdmEngine::HandleProvisioningResponse(
|
||||
cert_provisioning_.reset(NULL);
|
||||
return EMPTY_PROVISIONING_RESPONSE;
|
||||
}
|
||||
if (NULL == cert) {
|
||||
if (cert == NULL) {
|
||||
LOGE(
|
||||
"CdmEngine::HandleProvisioningResponse: invalid certificate "
|
||||
"destination");
|
||||
cert_provisioning_.reset(NULL);
|
||||
return INVALID_PROVISIONING_PARAMETERS_1;
|
||||
}
|
||||
if (NULL == wrapped_key) {
|
||||
if (wrapped_key == NULL) {
|
||||
LOGE("CdmEngine::HandleProvisioningResponse: invalid wrapped key "
|
||||
"destination");
|
||||
cert_provisioning_.reset(NULL);
|
||||
|
||||
@@ -65,12 +65,13 @@ CdmSession::~CdmSession() {
|
||||
|
||||
CdmResponseType CdmSession::Init(
|
||||
CdmClientPropertySet* cdm_client_property_set) {
|
||||
return Init(cdm_client_property_set, NULL, NULL);
|
||||
return Init(NULL, cdm_client_property_set, NULL, NULL);
|
||||
}
|
||||
|
||||
CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
|
||||
const CdmSessionId* forced_session_id,
|
||||
WvCdmEventListener* event_listener) {
|
||||
CdmResponseType CdmSession::Init(
|
||||
ServiceCertificate* service_certificate,
|
||||
CdmClientPropertySet* cdm_client_property_set,
|
||||
const CdmSessionId* forced_session_id, WvCdmEventListener* event_listener) {
|
||||
if (initialized_) {
|
||||
LOGE("CdmSession::Init: Failed due to previous initialization");
|
||||
return SESSION_INIT_ERROR_2;
|
||||
@@ -112,11 +113,13 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
|
||||
// Otherwise, try to fetch device certificate. If not successful and
|
||||
// provisioning is supported, return NEED_PROVISIONING. Otherwise, return
|
||||
// an error.
|
||||
// client_token and client_token_type are determined here; they are needed
|
||||
// to initialize the license parser.
|
||||
std::string client_token;
|
||||
CdmClientTokenType client_token_type =
|
||||
crypto_session_->GetPreProvisionTokenType();
|
||||
if ((client_token_type == kClientTokenKeybox) &&
|
||||
(!Properties::use_certificates_as_identification())) {
|
||||
!Properties::use_certificates_as_identification()) {
|
||||
// Keybox is client token.
|
||||
LOGW("CdmSession::Init: Properties::use_certificates_as_identification() "
|
||||
"is not set - using Keybox for license requests (not recommended).");
|
||||
@@ -147,6 +150,9 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
|
||||
client_token_type = kClientTokenDrmCert;
|
||||
}
|
||||
|
||||
// Session is provisioned with certificate needed to construct
|
||||
// license request (or with keybox).
|
||||
|
||||
if (forced_session_id) {
|
||||
key_set_id_ = *forced_session_id;
|
||||
} else {
|
||||
@@ -172,8 +178,9 @@ CdmResponseType CdmSession::Init(CdmClientPropertySet* cdm_client_property_set,
|
||||
policy_engine_.reset(new PolicyEngine(
|
||||
session_id_, event_listener, crypto_session_.get()));
|
||||
|
||||
if (!license_parser_->Init(client_token, client_token_type,
|
||||
crypto_session_.get(), policy_engine_.get()))
|
||||
if (!license_parser_->Init(
|
||||
service_certificate, client_token, client_token_type,
|
||||
crypto_session_.get(), policy_engine_.get()))
|
||||
return LICENSE_PARSER_INIT_ERROR;
|
||||
|
||||
license_received_ = false;
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "license_protocol.pb.h"
|
||||
#include "log.h"
|
||||
#include "properties.h"
|
||||
#include "service_certificate.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
@@ -86,16 +87,17 @@ bool CertificateProvisioning::SetSpoidParameter(
|
||||
"passed to method.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!spoid.empty()) {
|
||||
// Use the SPOID that has been pre-provided
|
||||
request->set_spoid(spoid);
|
||||
} else if (Properties::UseProviderIdInProvisioningRequest() &&
|
||||
false /* TODO(gmorgan): use provider ID. */) {
|
||||
// Use the provider ID from the service certificate
|
||||
|
||||
// TODO(gmorgan): use provider ID.
|
||||
// request->set_provider_id(???);
|
||||
} else if (Properties::UseProviderIdInProvisioningRequest()) {
|
||||
if (service_certificate_->HasProviderId()) {
|
||||
request->set_provider_id(service_certificate_->provider_id());
|
||||
} else {
|
||||
LOGE("CertificateProvisioning::SetSpoidParameter: Failure getting "
|
||||
"provider ID");
|
||||
return false;
|
||||
}
|
||||
} else if (origin != EMPTY_ORIGIN) {
|
||||
// Legacy behavior - Concatenate Unique ID with Origin
|
||||
std::string device_unique_id;
|
||||
@@ -130,8 +132,8 @@ SignedProvisioningMessage::ProtocolVersion
|
||||
CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
SecurityLevel requested_security_level, CdmCertificateType cert_type,
|
||||
const std::string& cert_authority, const std::string& origin,
|
||||
const std::string& spoid,
|
||||
CdmProvisioningRequest* request, std::string* default_url) {
|
||||
const std::string& spoid, CdmProvisioningRequest* request,
|
||||
std::string* default_url) {
|
||||
if (!default_url) {
|
||||
LOGE("GetProvisioningRequest: pointer for returning URL is NULL");
|
||||
return CERT_PROVISIONING_REQUEST_ERROR_1;
|
||||
@@ -139,13 +141,13 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
|
||||
default_url->assign(kProvisioningServerUrl);
|
||||
|
||||
CdmResponseType sts = crypto_session_.Open(requested_security_level);
|
||||
if (NO_ERROR != sts) {
|
||||
CdmResponseType status = crypto_session_.Open(requested_security_level);
|
||||
if (NO_ERROR != status) {
|
||||
LOGE("GetProvisioningRequest: fails to create a crypto session");
|
||||
return sts;
|
||||
return status;
|
||||
}
|
||||
|
||||
// Prepares device provisioning request.
|
||||
// Prepare device provisioning request.
|
||||
ProvisioningRequest provisioning_request;
|
||||
std::string token;
|
||||
ClientIdentification* client_id = provisioning_request.mutable_client_id();
|
||||
|
||||
@@ -292,6 +292,8 @@ bool CryptoSession::GetInternalDeviceUniqueId(std::string* device_id) {
|
||||
if (pre_provision_token_type_ == kClientTokenOemCert) {
|
||||
return GetTokenFromOemCert(device_id);
|
||||
} else {
|
||||
// Device's authentication root is a keybox.
|
||||
// Or not. If no keybox, let the OEMCrypto call fail.
|
||||
std::vector<uint8_t> id;
|
||||
size_t id_length = 32;
|
||||
id.resize(id_length);
|
||||
@@ -348,6 +350,8 @@ bool CryptoSession::GetApiVersion(uint32_t* version) {
|
||||
}
|
||||
|
||||
bool CryptoSession::GetSystemId(uint32_t* system_id) {
|
||||
// TODO(gmorgan): if Prov 3.0, get system ID from DRM Device Cert. If
|
||||
// DRM Device Cert is not available (e.g., not yet provisioned), return false.
|
||||
if (!system_id) {
|
||||
LOGE("CryptoSession::GetSystemId: No buffer passed to method.");
|
||||
return false;
|
||||
@@ -496,6 +500,7 @@ bool CryptoSession::ExtractSystemIdFromOemCert(const std::string& oem_cert,
|
||||
|
||||
|
||||
bool CryptoSession::GetProvisioningId(std::string* provisioning_id) {
|
||||
// TODO(gmorgan): if Prov 3.0, return false (no "provisioning ID" available).
|
||||
if (!provisioning_id) {
|
||||
LOGE("CryptoSession::GetProvisioningId : No buffer passed to method.");
|
||||
return false;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "policy_engine.h"
|
||||
#include "privacy_crypto.h"
|
||||
#include "properties.h"
|
||||
#include "service_certificate.h"
|
||||
#include "string_conversions.h"
|
||||
#include "wv_cdm_constants.h"
|
||||
|
||||
@@ -145,8 +146,9 @@ CdmLicense::CdmLicense(const CdmSessionId& session_id, Clock* clock)
|
||||
CdmLicense::~CdmLicense() {}
|
||||
|
||||
bool CdmLicense::Init(
|
||||
const std::string& client_token, CdmClientTokenType client_token_type,
|
||||
CryptoSession* session, PolicyEngine* policy_engine) {
|
||||
ServiceCertificate* service_certificate, const std::string& client_token,
|
||||
CdmClientTokenType client_token_type, CryptoSession* session,
|
||||
PolicyEngine* policy_engine) {
|
||||
if (clock_.get() == NULL) {
|
||||
LOGE("CdmLicense::Init: clock parameter not provided");
|
||||
return false;
|
||||
@@ -168,19 +170,11 @@ bool CdmLicense::Init(
|
||||
return false;
|
||||
}
|
||||
|
||||
service_certificate_ = service_certificate;
|
||||
client_token_ = client_token;
|
||||
client_token_type_ = client_token_type;
|
||||
crypto_session_ = session;
|
||||
policy_engine_ = policy_engine;
|
||||
service_certificate_.reset(new ServiceCertificate());
|
||||
if (service_certificate_.get() == NULL) {
|
||||
LOGE("CdmLicense::Init: creation of service_certificate failed");
|
||||
return false;
|
||||
}
|
||||
if (!service_certificate_->Init(session_id_, crypto_session_)) {
|
||||
LOGE("CdmLicense::Init: init of service_certificate failed");
|
||||
return false;
|
||||
}
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
@@ -217,14 +211,16 @@ CdmResponseType CdmLicense::PrepareKeyRequest(
|
||||
return INVALID_PARAMETERS_LIC_7;
|
||||
}
|
||||
|
||||
if (service_certificate_->IsRequired() &&
|
||||
!service_certificate_->IsAvailable()) {
|
||||
// If privacy mode and no service certificate, initiate a
|
||||
// service certificate request.
|
||||
if (Properties::UsePrivacyMode(session_id_) &&
|
||||
!service_certificate_->HasCertificate()) {
|
||||
stored_init_data_.reset(new InitializationData(init_data));
|
||||
*server_url = server_url_;
|
||||
if (service_certificate_->PrepareServiceCertificateRequest(signed_request))
|
||||
if (service_certificate_->PrepareRequest(signed_request)) {
|
||||
return KEY_MESSAGE;
|
||||
else
|
||||
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
|
||||
}
|
||||
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
|
||||
}
|
||||
|
||||
std::string request_id;
|
||||
@@ -309,14 +305,13 @@ CdmResponseType CdmLicense::PrepareKeyUpdateRequest(
|
||||
}
|
||||
|
||||
if (renew_with_client_id_) {
|
||||
if (service_certificate_->IsRequired() &&
|
||||
!service_certificate_->IsAvailable()) {
|
||||
if (Properties::UsePrivacyMode(session_id_) &&
|
||||
!service_certificate_->HasCertificate()) {
|
||||
*server_url = server_url_;
|
||||
if (service_certificate_->
|
||||
PrepareServiceCertificateRequest(signed_request))
|
||||
if (service_certificate_->PrepareRequest(signed_request)) {
|
||||
return KEY_MESSAGE;
|
||||
else
|
||||
return LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR;
|
||||
}
|
||||
return LICENSE_RENEWAL_SERVICE_CERTIFICATE_GENERATION_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -439,8 +434,8 @@ CdmResponseType CdmLicense::HandleKeyResponse(
|
||||
case SignedMessage::LICENSE:
|
||||
break;
|
||||
case SignedMessage::SERVICE_CERTIFICATE: {
|
||||
CdmResponseType status =
|
||||
service_certificate_->VerifyAndSet(signed_response.msg());
|
||||
CdmResponseType status;
|
||||
status = service_certificate_->HandleResponse(signed_response.msg());
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
@@ -558,8 +553,8 @@ CdmResponseType CdmLicense::HandleKeyUpdateResponse(
|
||||
case SignedMessage::LICENSE:
|
||||
break;
|
||||
case SignedMessage::SERVICE_CERTIFICATE: {
|
||||
CdmResponseType status =
|
||||
service_certificate_->VerifyAndSet(signed_response.msg());
|
||||
CdmResponseType status;
|
||||
status = service_certificate_->HandleResponse(signed_response.msg());
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
@@ -1027,16 +1022,15 @@ CdmResponseType CdmLicense::PrepareClientId(
|
||||
if (crypto_session_->GetSrmVersion(&srm_version))
|
||||
client_capabilities->set_srm_version(srm_version);
|
||||
|
||||
if (service_certificate_->IsRequired()) {
|
||||
if (!service_certificate_->IsAvailable()) {
|
||||
if (Properties::UsePrivacyMode(session_id_)) {
|
||||
if (!service_certificate_->HasCertificate()) {
|
||||
LOGE("CdmLicense::PrepareClientId: Service Certificate not staged");
|
||||
return LICENSE_REQUEST_SERVICE_CERTIFICATE_GENERATION_ERROR;
|
||||
}
|
||||
|
||||
EncryptedClientIdentification* encrypted_client_id =
|
||||
license_request->mutable_encrypted_client_id();
|
||||
CdmResponseType status;
|
||||
status = service_certificate_->EncryptClientId(client_id,
|
||||
status = service_certificate_->EncryptClientId(crypto_session_, client_id,
|
||||
encrypted_client_id);
|
||||
if (NO_ERROR == status) {
|
||||
license_request->clear_client_id();
|
||||
|
||||
@@ -62,7 +62,7 @@ bool Properties::GetServiceCertificate(const CdmSessionId& session_id,
|
||||
std::string* service_certificate) {
|
||||
const CdmClientPropertySet* property_set =
|
||||
GetCdmClientPropertySet(session_id);
|
||||
if (NULL == property_set) {
|
||||
if (property_set == NULL) {
|
||||
return false;
|
||||
}
|
||||
*service_certificate = property_set->service_certificate();
|
||||
@@ -73,35 +73,13 @@ bool Properties::SetServiceCertificate(const CdmSessionId& session_id,
|
||||
const std::string& service_certificate) {
|
||||
CdmClientPropertySet* property_set =
|
||||
GetCdmClientPropertySet(session_id);
|
||||
if (NULL == property_set) {
|
||||
if (property_set == NULL) {
|
||||
return false;
|
||||
}
|
||||
property_set->set_service_certificate(service_certificate);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Properties::GetDeviceProvisioningServiceCertificate(
|
||||
const CdmSessionId& session_id, std::string* service_certificate) {
|
||||
const CdmClientPropertySet* property_set =
|
||||
GetCdmClientPropertySet(session_id);
|
||||
if (NULL == property_set) {
|
||||
return false;
|
||||
}
|
||||
*service_certificate = property_set->device_provisioning_service_certificate();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Properties::SetDeviceProvisioningServiceCertificate(
|
||||
const CdmSessionId& session_id, const std::string& service_certificate) {
|
||||
CdmClientPropertySet* property_set =
|
||||
GetCdmClientPropertySet(session_id);
|
||||
if (NULL == property_set) {
|
||||
return false;
|
||||
}
|
||||
property_set->set_device_provisioning_service_certificate(service_certificate);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Properties::UsePrivacyMode(const CdmSessionId& session_id) {
|
||||
const CdmClientPropertySet* property_set =
|
||||
GetCdmClientPropertySet(session_id);
|
||||
|
||||
@@ -57,148 +57,26 @@ using video_widevine::EncryptedClientIdentification;
|
||||
using video_widevine::SignedDrmDeviceCertificate;
|
||||
using video_widevine::SignedMessage;
|
||||
|
||||
ServiceCertificate::ServiceCertificate()
|
||||
: crypto_session_(NULL),
|
||||
valid_(false),
|
||||
initialized_(false) {
|
||||
void ServiceCertificate::Clear() {
|
||||
fetch_in_progress_ = false;
|
||||
certificate_.clear();
|
||||
provider_id_.clear();
|
||||
}
|
||||
|
||||
ServiceCertificate::~ServiceCertificate() {}
|
||||
|
||||
bool ServiceCertificate::Init(const CdmSessionId& session_id,
|
||||
CryptoSession* session) {
|
||||
if (session_id.empty()) {
|
||||
LOGE("ServiceCertificate::Init: empty session id provided");
|
||||
return false;
|
||||
}
|
||||
if (session == NULL) {
|
||||
LOGE("ServiceCertificate::Init: crypto session not provided");
|
||||
return false;
|
||||
}
|
||||
session_id_.assign(session_id);
|
||||
crypto_session_ = session;
|
||||
privacy_mode_enabled_ = Properties::UsePrivacyMode(session_id_);
|
||||
SetupServiceCertificate();
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ServiceCertificate::IsRequired() {
|
||||
return privacy_mode_enabled_;
|
||||
}
|
||||
|
||||
bool ServiceCertificate::IsAvailable() {
|
||||
return valid_;
|
||||
}
|
||||
|
||||
CdmResponseType ServiceCertificate::VerifyAndSet(
|
||||
const std::string& signed_service_certificate) {
|
||||
CdmResponseType status;
|
||||
bool has_provider_id;
|
||||
status = VerifyAndExtractFromSignedCertificate(signed_service_certificate,
|
||||
&certificate_,
|
||||
&has_provider_id,
|
||||
NULL);
|
||||
if (status != NO_ERROR) {
|
||||
LOGE("ServiceCertificate::VerifyAndSet: verify and extract failed with "
|
||||
"status %d", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
Properties::SetServiceCertificate(session_id_, signed_service_certificate);
|
||||
valid_ = true;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool ServiceCertificate::PrepareServiceCertificateRequest(
|
||||
CdmKeyMessage* signed_request) {
|
||||
if (!initialized_) {
|
||||
LOGE("ServiceCertificate::PrepareServiceCertificateRequest: "
|
||||
"not initialized");
|
||||
return false;
|
||||
}
|
||||
if (!signed_request) {
|
||||
LOGE("ServiceCertificate::PrepareServiceCertificateRequest: "
|
||||
"no signed request provided");
|
||||
return false;
|
||||
}
|
||||
SignedMessage signed_message;
|
||||
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
|
||||
signed_message.SerializeToString(signed_request);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType ServiceCertificate::VerifyAndExtractFromSignedCertificate(
|
||||
const std::string& signed_certificate, std::string* certificate,
|
||||
bool* /* has_provider_id */, std::string* /* provider_id */) {
|
||||
SignedDrmDeviceCertificate signed_service_certificate;
|
||||
if (!signed_service_certificate.ParseFromString(signed_certificate)) {
|
||||
LOGE(
|
||||
"ServiceCertificate::VerifyAndExtractFromSignedCertificate: "
|
||||
"unable to parse signed service certificate");
|
||||
return DEVICE_CERTIFICATE_ERROR_1;
|
||||
}
|
||||
|
||||
RsaPublicKey root_ca_key;
|
||||
std::string ca_public_key(
|
||||
reinterpret_cast<const char*>(&kServiceCertificateCAPublicKey[0]),
|
||||
sizeof(kServiceCertificateCAPublicKey));
|
||||
if (!root_ca_key.Init(ca_public_key)) {
|
||||
LOGE(
|
||||
"ServiceCertificate::VerifyAndExtractFromSignedCertificate: public key "
|
||||
"initialization failed");
|
||||
return DEVICE_CERTIFICATE_ERROR_2;
|
||||
}
|
||||
|
||||
if (!root_ca_key.VerifySignature(
|
||||
signed_service_certificate.drm_certificate(),
|
||||
signed_service_certificate.signature())) {
|
||||
LOGE(
|
||||
"ServiceCertificate::VerifyAndExtractFromSignedCertificate: service "
|
||||
"certificate verification failed");
|
||||
return DEVICE_CERTIFICATE_ERROR_3;
|
||||
}
|
||||
|
||||
DrmDeviceCertificate service_certificate;
|
||||
if (!service_certificate.ParseFromString(
|
||||
signed_service_certificate.drm_certificate())) {
|
||||
LOGE(
|
||||
"ServiceCertificate::VerifyAndExtractFromSignedCertificate: unable to "
|
||||
"parse retrieved service certificate");
|
||||
return DEVICE_CERTIFICATE_ERROR_4;
|
||||
}
|
||||
|
||||
if (service_certificate.type() !=
|
||||
video_widevine::DrmDeviceCertificate_CertificateType_SERVICE) {
|
||||
LOGE(
|
||||
"ServiceCertificate::VerifyAndExtractFromSignedCertificate: "
|
||||
"certificate not of type service, %d", service_certificate.type());
|
||||
return INVALID_DEVICE_CERTIFICATE_TYPE;
|
||||
}
|
||||
|
||||
#if 0 // TODO(gmorgan): service cert has no provider_id
|
||||
*has_provider_id = service_certificate.has_provider_id();
|
||||
if (*has_provider_id && provider_id != NULL) {
|
||||
*provider_id = service_certificate.provider_id();
|
||||
} else {
|
||||
return DEVICE_CERTIFICATE_ERROR_5;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (certificate != NULL &&
|
||||
!signed_service_certificate.drm_certificate().empty()) {
|
||||
*certificate = signed_service_certificate.drm_certificate();
|
||||
}
|
||||
return NO_ERROR;
|
||||
CdmResponseType ServiceCertificate::Init(const std::string& raw_certificate) {
|
||||
return VerifyAndExtract(raw_certificate);
|
||||
}
|
||||
|
||||
CdmResponseType ServiceCertificate::EncryptClientId(
|
||||
const ClientIdentification* clear_client_id,
|
||||
CryptoSession* crypto_session, const ClientIdentification* clear_client_id,
|
||||
EncryptedClientIdentification* encrypted_client_id) {
|
||||
DrmDeviceCertificate service_certificate;
|
||||
|
||||
if (certificate_.empty()) {
|
||||
LOGE("ServiceCertificate::EncryptClientId: "
|
||||
"service certificate is not properly initialized");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
if (!service_certificate.ParseFromString(certificate_)) {
|
||||
LOGE("ServiceCertificate::EncryptClientId: unable to parse retrieved "
|
||||
"service certificate");
|
||||
@@ -215,14 +93,14 @@ CdmResponseType ServiceCertificate::EncryptClientId(
|
||||
encrypted_client_id->set_service_certificate_serial_number(
|
||||
service_certificate.serial_number());
|
||||
|
||||
std::string iv(KEY_IV_SIZE, 0); // TODO(gmorgan) randomize
|
||||
std::string iv(KEY_IV_SIZE, 0);
|
||||
std::string key(KEY_SIZE, 0);
|
||||
|
||||
if (!crypto_session_->GetRandom(key.size(),
|
||||
reinterpret_cast<uint8_t*>(&key[0])))
|
||||
if (!crypto_session->GetRandom(key.size(),
|
||||
reinterpret_cast<uint8_t*>(&key[0])))
|
||||
return CLIENT_ID_GENERATE_RANDOM_ERROR;
|
||||
if (!crypto_session_->GetRandom(iv.size(),
|
||||
reinterpret_cast<uint8_t*>(&iv[0])))
|
||||
if (!crypto_session->GetRandom(iv.size(),
|
||||
reinterpret_cast<uint8_t*>(&iv[0])))
|
||||
return CLIENT_ID_GENERATE_RANDOM_ERROR;
|
||||
std::string id, enc_id, enc_key;
|
||||
clear_client_id->SerializeToString(&id);
|
||||
@@ -242,38 +120,97 @@ CdmResponseType ServiceCertificate::EncryptClientId(
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
bool ServiceCertificate::SetupServiceCertificate() {
|
||||
std::string signed_certificate;
|
||||
|
||||
valid_ = false;
|
||||
certificate_.clear();
|
||||
|
||||
if (!Properties::GetServiceCertificate(session_id_, &signed_certificate)) {
|
||||
LOGV("ServiceCertificate::SetupServiceCertificate: no signed service "
|
||||
"certificate set");
|
||||
return false;
|
||||
}
|
||||
if (signed_certificate.empty()) {
|
||||
LOGV("ServiceCertificate::SetupServiceCertificate: service certificate "
|
||||
"empty");
|
||||
bool ServiceCertificate::PrepareRequest(CdmKeyMessage* signed_request) {
|
||||
if (!signed_request) {
|
||||
LOGE("ServiceCertificate::PrepareRequest: no signed request provided");
|
||||
return false;
|
||||
}
|
||||
SignedMessage signed_message;
|
||||
signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE_REQUEST);
|
||||
signed_message.SerializeToString(signed_request);
|
||||
|
||||
std::string extracted_certificate;
|
||||
std::string extracted_provider_id;
|
||||
bool has_provider_id;
|
||||
if (NO_ERROR != VerifyAndExtractFromSignedCertificate(
|
||||
signed_certificate, &extracted_certificate,
|
||||
&has_provider_id, &extracted_provider_id)) {
|
||||
return false;
|
||||
}
|
||||
has_provider_id_ = has_provider_id;
|
||||
if (has_provider_id_) {
|
||||
provider_id_ = extracted_provider_id;
|
||||
}
|
||||
certificate_ = extracted_certificate;
|
||||
valid_ = true;
|
||||
fetch_in_progress_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
CdmResponseType ServiceCertificate::HandleResponse(
|
||||
const std::string& signed_response) {
|
||||
if (!fetch_in_progress_) {
|
||||
LOGE("ServiceCertificate::HandleResponse: unexpected service "
|
||||
"certificate response.");
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
fetch_in_progress_ = false;
|
||||
CdmResponseType status = VerifyAndExtract(signed_response);
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType ServiceCertificate::VerifyAndExtract(
|
||||
const std::string& raw_certificate) {
|
||||
if (raw_certificate.empty()) {
|
||||
Clear();
|
||||
return NO_ERROR;
|
||||
}
|
||||
// Deserialize and parse raw certificate.
|
||||
SignedDrmDeviceCertificate signed_service_certificate;
|
||||
if (!signed_service_certificate.ParseFromString(raw_certificate)) {
|
||||
LOGE(
|
||||
"ServiceCertificate::VerifyAndExtract: unable to parse signed "
|
||||
"service certificate");
|
||||
return DEVICE_CERTIFICATE_ERROR_1;
|
||||
}
|
||||
|
||||
// Set up root key (for verifying signature).
|
||||
RsaPublicKey root_ca_key;
|
||||
std::string ca_public_key(
|
||||
reinterpret_cast<const char*>(&kServiceCertificateCAPublicKey[0]),
|
||||
sizeof(kServiceCertificateCAPublicKey));
|
||||
if (!root_ca_key.Init(ca_public_key)) {
|
||||
LOGE(
|
||||
"ServiceCertificate::VerifyAndExtract: public key initialization "
|
||||
"failed");
|
||||
return DEVICE_CERTIFICATE_ERROR_2;
|
||||
}
|
||||
|
||||
// Verify the signature.
|
||||
if (!root_ca_key.VerifySignature(
|
||||
signed_service_certificate.drm_certificate(),
|
||||
signed_service_certificate.signature())) {
|
||||
LOGE(
|
||||
"ServiceCertificate::VerifyAndExtract: service certificate "
|
||||
"verification failed");
|
||||
return DEVICE_CERTIFICATE_ERROR_3;
|
||||
}
|
||||
|
||||
// Deserialize and parse actual certificate.
|
||||
DrmDeviceCertificate service_certificate;
|
||||
if (!service_certificate.ParseFromString(
|
||||
signed_service_certificate.drm_certificate())) {
|
||||
LOGE(
|
||||
"ServiceCertificate::VerifyAndExtract: unable to parse retrieved "
|
||||
"service certificate");
|
||||
return DEVICE_CERTIFICATE_ERROR_4;
|
||||
}
|
||||
|
||||
// Verify, extract needed fields.
|
||||
if (service_certificate.type() !=
|
||||
video_widevine::DrmDeviceCertificate_CertificateType_SERVICE) {
|
||||
LOGE(
|
||||
"ServiceCertificate::VerifyAndExtract: certificate not of type "
|
||||
"service, %d", service_certificate.type());
|
||||
return INVALID_DEVICE_CERTIFICATE_TYPE;
|
||||
}
|
||||
if (service_certificate.has_provider_id()) {
|
||||
provider_id_.assign(service_certificate.provider_id());
|
||||
} else {
|
||||
provider_id_.clear();
|
||||
}
|
||||
certificate_.assign(signed_service_certificate.drm_certificate());
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
} // namespace wvcdm
|
||||
|
||||
Reference in New Issue
Block a user