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:
@@ -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