First-stage Provisioning 4.0 client ID encryption

(This is a merge of http://go/wvgerrit/150131 to the Android repo.)

This patch changes the code path in the CDM so that the first-stage
provisioning request for Provisioning 4.0 is always encrypted with the
Widevine service certificate instead of the client-set service
certificate, reflecting that the first-stage provisioning is always
handled by Widevine.

This patch also makes several methods on the ServiceCertificate class
const. This has no impact on their behavior.

Bug: 221443151
Test: prov40 tests
Change-Id: Ide4c3927afadcd45ae7fb629b99e2f55cc29d56e
This commit is contained in:
John "Juce" Bruce
2022-04-20 14:10:20 -07:00
parent 47ebc28f6a
commit c823a85e7d
4 changed files with 34 additions and 23 deletions

View File

@@ -86,7 +86,8 @@ class CertificateProvisioning {
std::string* default_url);
CdmResponseType FillEncryptedClientId(
const std::string& client_token,
video_widevine::ProvisioningRequest& provisioning_request);
video_widevine::ProvisioningRequest& provisioning_request,
const ServiceCertificate& service_certificate);
CdmResponseType HandleProvisioning40Response(
wvutil::FileSystem* file_system, const std::string& response_message);

View File

@@ -38,8 +38,8 @@ class ServiceCertificate {
const std::string& provider_id() const { return provider_id_; }
// Verify the signature for a message.
virtual CdmResponseType VerifySignedMessage(const std::string& message,
const std::string& signature);
virtual CdmResponseType VerifySignedMessage(
const std::string& message, const std::string& signature) const;
// Encrypt the ClientIdentification message for a provisioning or
// licensing request. Encryption is performed using the current
@@ -50,7 +50,7 @@ class ServiceCertificate {
virtual CdmResponseType EncryptClientId(
CryptoSession* crypto_session,
const video_widevine::ClientIdentification* clear_client_id,
video_widevine::EncryptedClientIdentification* encrypted_client_id);
video_widevine::EncryptedClientIdentification* encrypted_client_id) const;
// Helper methods
static bool GetRequest(CdmKeyMessage* request);
@@ -63,7 +63,7 @@ class ServiceCertificate {
// string to contain the decrypted data on return, and may not be null.
// returns NO_ERROR if successful or an appropriate error code otherwise.
virtual CdmResponseType EncryptRsaOaep(const std::string& plaintext,
std::string* ciphertext);
std::string* ciphertext) const;
// Track whether object holds valid certificate
bool has_certificate_;

View File

@@ -218,7 +218,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
// Prepare device provisioning request.
ProvisioningRequest provisioning_request;
status = FillEncryptedClientId(/*client_token=*/"", provisioning_request);
status = FillEncryptedClientId(/*client_token=*/"", provisioning_request,
*service_certificate_);
if (status != NO_ERROR) return status;
uint32_t nonce;
@@ -339,24 +340,33 @@ CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal(
}
}
CdmResponseType status = NO_ERROR;
if (stored_oem_cert.empty()) {
// This is the first stage provisioning.
default_url->assign(kProvisioningServerUrl +
kProv40FirstStageServerUrlSuffix);
// First-stage provisioning always uses the WV production service cert for
// encryption.
ServiceCertificate wv_service_cert;
status = wv_service_cert.Init(kCpProductionServiceCertificate);
if (status != NO_ERROR) return status;
// Since |stored_oem_cert| is empty, the client identification token will be
// retrieved from OEMCrypto, which is the BCC in this case.
status = FillEncryptedClientId(stored_oem_cert, provisioning_request,
wv_service_cert);
if (status != NO_ERROR) return status;
} else {
// This is the second stage provisioning.
default_url->assign(kProvisioningServerUrl);
// Since |stored_oem_cert| is non-empty, it will be used as the client
// identification token.
status = FillEncryptedClientId(stored_oem_cert, provisioning_request,
*service_certificate_);
if (status != NO_ERROR) return status;
}
// If this is the first stage, |stored_oem_cert| remains empty. In this case,
// the client identification token will be retrieved from OEMCrypto, which is
// the BCC in this case.
// If this is the second stage, |stored_oem_cert| is non-empty and will be
// used as the client identification token.
CdmResponseType status =
FillEncryptedClientId(stored_oem_cert, provisioning_request);
if (status != NO_ERROR) return status;
std::string public_key;
std::string public_key_signature;
provisioning_40_wrapped_private_key_.clear();
@@ -396,8 +406,8 @@ CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal(
}
CdmResponseType CertificateProvisioning::FillEncryptedClientId(
const std::string& client_token,
ProvisioningRequest& provisioning_request) {
const std::string& client_token, ProvisioningRequest& provisioning_request,
const ServiceCertificate& service_certificate) {
if (!crypto_session_->IsOpen()) {
return UNKNOWN_ERROR;
}
@@ -412,13 +422,13 @@ CdmResponseType CertificateProvisioning::FillEncryptedClientId(
status = id.Prepare(app_parameter, kEmptyString, &client_id);
if (status != NO_ERROR) return status;
if (!service_certificate_->has_certificate()) {
if (!service_certificate.has_certificate()) {
LOGE("Service certificate not staged");
return CERT_PROVISIONING_EMPTY_SERVICE_CERTIFICATE;
}
// Encrypt client identification
return service_certificate_->EncryptClientId(
return service_certificate.EncryptClientId(
crypto_session_.get(), &client_id,
provisioning_request.mutable_encrypted_client_id());
}

View File

@@ -206,7 +206,7 @@ CdmResponseType ServiceCertificate::Init(const std::string& certificate) {
}
CdmResponseType ServiceCertificate::VerifySignedMessage(
const std::string& message, const std::string& signature) {
const std::string& message, const std::string& signature) const {
if (!public_key_) {
LOGE("Service certificate not set");
return DEVICE_CERTIFICATE_ERROR_4;
@@ -218,8 +218,8 @@ CdmResponseType ServiceCertificate::VerifySignedMessage(
return NO_ERROR;
}
CdmResponseType ServiceCertificate::EncryptRsaOaep(const std::string& plaintext,
std::string* ciphertext) {
CdmResponseType ServiceCertificate::EncryptRsaOaep(
const std::string& plaintext, std::string* ciphertext) const {
if (!public_key_) {
LOGE("Service certificate not set");
return DEVICE_CERTIFICATE_ERROR_4;
@@ -233,7 +233,7 @@ CdmResponseType ServiceCertificate::EncryptRsaOaep(const std::string& plaintext,
CdmResponseType ServiceCertificate::EncryptClientId(
CryptoSession* crypto_session, const ClientIdentification* clear_client_id,
EncryptedClientIdentification* encrypted_client_id) {
EncryptedClientIdentification* encrypted_client_id) const {
encrypted_client_id->set_provider_id(provider_id_);
encrypted_client_id->set_service_certificate_serial_number(serial_number_);