Add Cdm support for Cast provision 4.0 flow

[ Merge of http://go/wvgerrit/178135 ]

Bug: 259455235
Test: CorePIGTest.CastReceiverProvisioning*
Test: com.google.android.wvts
Change-Id: I8d546a73a64a71a4d61225d9c6d14d893decce22
(cherry picked from commit 494da3dddf0f47e516e6fc1a73e19b091e6c2abd)
This commit is contained in:
Kyle Zhang
2023-07-07 20:28:37 +00:00
parent b0c3e69962
commit 14f7594f5e
4 changed files with 128 additions and 23 deletions

View File

@@ -82,7 +82,8 @@ class CertificateProvisioning {
CdmResponseType GetProvisioning40RequestInternal( CdmResponseType GetProvisioning40RequestInternal(
wvutil::FileSystem* file_system, const std::string& origin, wvutil::FileSystem* file_system, const std::string& origin,
const std::string& spoid, CdmProvisioningRequest* request, const std::string& spoid, CdmProvisioningRequest* request,
std::string* default_url); std::string* default_url, CdmCertificateType cert_type,
const std::string& cert_authority);
CdmResponseType FillEncryptedClientId( CdmResponseType FillEncryptedClientId(
const std::string& client_token, const std::string& client_token,
video_widevine::ProvisioningRequest& provisioning_request, video_widevine::ProvisioningRequest& provisioning_request,
@@ -93,7 +94,14 @@ class CertificateProvisioning {
video_widevine::ProvisioningRequest& provisioning_request, video_widevine::ProvisioningRequest& provisioning_request,
const ServiceCertificate& service_certificate); const ServiceCertificate& service_certificate);
CdmResponseType HandleProvisioning40Response( CdmResponseType HandleProvisioning40Response(
wvutil::FileSystem* file_system, const std::string& response_message); wvutil::FileSystem* file_system,
const video_widevine::SignedProvisioningMessage& signed_message,
std::string* cert, std::string* wrapped_key);
// Assign the cert type for provisioning request
// Required by Cast cert provisioning flow
CdmResponseType CertTypeAssign(
video_widevine::ProvisioningRequest& provisioning_request,
CdmCertificateType cert_type, const std::string& cert_authority);
CdmResponseType SetSpoidParameter( CdmResponseType SetSpoidParameter(
const std::string& origin, const std::string& spoid, const std::string& origin, const std::string& spoid,
@@ -120,6 +128,8 @@ class CertificateProvisioning {
std::string provisioning_40_wrapped_private_key_; std::string provisioning_40_wrapped_private_key_;
// Key type of the generated key pair in provisioning 4. // Key type of the generated key pair in provisioning 4.
CryptoWrappedKey::Type provisioning_40_key_type_; CryptoWrappedKey::Type provisioning_40_key_type_;
// Store the last provisioning request message
std::string provisioning_request_message_;
CORE_DISALLOW_COPY_AND_ASSIGN(CertificateProvisioning); CORE_DISALLOW_COPY_AND_ASSIGN(CertificateProvisioning);
}; };

View File

@@ -1310,8 +1310,7 @@ CdmResponseType CdmSession::LoadCastPrivateKey(
CdmResponseType CdmSession::GenerateRsaSignature(const std::string& message, CdmResponseType CdmSession::GenerateRsaSignature(const std::string& message,
std::string* signature, std::string* signature,
RSA_Padding_Scheme scheme) { RSA_Padding_Scheme scheme) {
return crypto_session_->GenerateRsaSignature(message, signature, return crypto_session_->GenerateRsaSignature(message, signature, scheme);
scheme);
} }
// For testing only - takes ownership of pointers // For testing only - takes ownership of pointers

View File

@@ -209,7 +209,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
if (crypto_session_->GetPreProvisionTokenType() == if (crypto_session_->GetPreProvisionTokenType() ==
kClientTokenBootCertChain) { kClientTokenBootCertChain) {
return GetProvisioning40RequestInternal(file_system, origin, spoid, request, return GetProvisioning40RequestInternal(file_system, origin, spoid, request,
default_url); default_url, cert_type,
cert_authority);
} }
// Prepare device provisioning request. // Prepare device provisioning request.
@@ -235,21 +236,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
sizeof(nonce)); sizeof(nonce));
provisioning_request.set_nonce(encoded_nonce); provisioning_request.set_nonce(encoded_nonce);
ProvisioningOptions* options = provisioning_request.mutable_options(); status = CertTypeAssign(provisioning_request, cert_type, cert_authority);
switch (cert_type) { if (status != NO_ERROR) return status;
case kCertificateWidevine:
options->set_certificate_type(ProvisioningOptions::WIDEVINE_DRM);
break;
case kCertificateX509:
options->set_certificate_type(ProvisioningOptions::X509);
break;
default:
LOGE("Unknown certificate type: %d", static_cast<int>(cert_type));
return CdmResponseType(CERT_PROVISIONING_INVALID_CERT_TYPE);
}
cert_type_ = cert_type;
options->set_certificate_authority(cert_authority);
status = SetSpoidParameter(origin, spoid, &provisioning_request); status = SetSpoidParameter(origin, spoid, &provisioning_request);
if (status != NO_ERROR) return status; if (status != NO_ERROR) return status;
@@ -308,7 +296,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal( CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal(
wvutil::FileSystem* file_system, const std::string& origin, wvutil::FileSystem* file_system, const std::string& origin,
const std::string& spoid, CdmProvisioningRequest* request, const std::string& spoid, CdmProvisioningRequest* request,
std::string* default_url) { std::string* default_url, CdmCertificateType cert_type,
const std::string& cert_authority) {
if (!crypto_session_->IsOpen()) { if (!crypto_session_->IsOpen()) {
LOGE("Crypto session is not open"); LOGE("Crypto session is not open");
return CdmResponseType(PROVISIONING_4_CRYPTO_SESSION_NOT_OPEN); return CdmResponseType(PROVISIONING_4_CRYPTO_SESSION_NOT_OPEN);
@@ -391,6 +380,29 @@ CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal(
stored_oem_cert, additional_parameter, provisioning_request, stored_oem_cert, additional_parameter, provisioning_request,
*service_certificate_); *service_certificate_);
if (status != NO_ERROR) return status; if (status != NO_ERROR) return status;
// If cert type is X509, provisioning Cast cert.
if (cert_type == kCertificateX509) {
uint32_t nonce;
status = crypto_session_->GenerateNonce(&nonce);
if (status != NO_ERROR) {
LOGE("Failed to generate a nonce: status = %d",
static_cast<int>(status));
return status == NONCE_GENERATION_ERROR
? CdmResponseType(CERT_PROVISIONING_NONCE_GENERATION_ERROR)
: status;
}
// The provisioning server does not convert the nonce to uint32_t, it just
// passes the binary data to the response message.
const std::string encoded_nonce(reinterpret_cast<char*>(&nonce),
sizeof(nonce));
provisioning_request.set_nonce(encoded_nonce);
status = CertTypeAssign(provisioning_request, cert_type, cert_authority);
if (status != NO_ERROR) return status;
}
} }
std::string public_key; std::string public_key;
@@ -413,6 +425,7 @@ CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal(
std::string serialized_message; std::string serialized_message;
provisioning_request.SerializeToString(&serialized_message); provisioning_request.SerializeToString(&serialized_message);
provisioning_request_message_ = serialized_message;
SignedProvisioningMessage signed_provisioning_msg; SignedProvisioningMessage signed_provisioning_msg;
signed_provisioning_msg.set_message(serialized_message); signed_provisioning_msg.set_message(serialized_message);
@@ -509,8 +522,11 @@ CertificateProvisioning::FillEncryptedClientIdWithAdditionalParameter(
} }
CdmResponseType CertificateProvisioning::HandleProvisioning40Response( CdmResponseType CertificateProvisioning::HandleProvisioning40Response(
wvutil::FileSystem* file_system, const std::string& response_message) { wvutil::FileSystem* file_system,
const SignedProvisioningMessage& signed_response, std::string* cert,
std::string* wrapped_key) {
ProvisioningResponse provisioning_response; ProvisioningResponse provisioning_response;
const std::string response_message = signed_response.message();
if (response_message.empty() || if (response_message.empty() ||
!provisioning_response.ParseFromString(response_message)) { !provisioning_response.ParseFromString(response_message)) {
return CdmResponseType(PROVISIONING_4_RESPONSE_FAILED_TO_PARSE_MESSAGE); return CdmResponseType(PROVISIONING_4_RESPONSE_FAILED_TO_PARSE_MESSAGE);
@@ -538,9 +554,44 @@ CdmResponseType CertificateProvisioning::HandleProvisioning40Response(
LOGE("No private key was generated"); LOGE("No private key was generated");
return CdmResponseType(PROVISIONING_4_NO_PRIVATE_KEY); return CdmResponseType(PROVISIONING_4_NO_PRIVATE_KEY);
} }
const CryptoWrappedKey private_key(provisioning_40_key_type_, const CryptoWrappedKey private_key(provisioning_40_key_type_,
provisioning_40_wrapped_private_key_); provisioning_40_wrapped_private_key_);
if (cert_type_ == kCertificateX509) {
// Load csr private key to decrypt session key
auto status = crypto_session_->LoadCertificatePrivateKey(private_key);
if (status != NO_ERROR) {
LOGE("Failed to load x509 certificate.");
return status;
}
status = crypto_session_->GenerateDerivedKeys(
provisioning_request_message_, signed_response.session_key());
if (status != NO_ERROR) {
LOGE("Failed to generate derived keys.");
return status;
}
// Get wrapped private key for cast cert
CryptoWrappedKey cast_cert_private_key;
const std::string signature = signed_response.signature();
const std::string core_message = signed_response.oemcrypto_core_message();
status = crypto_session_->LoadProvisioning(response_message, core_message,
signature,
&cast_cert_private_key.key());
if (status != NO_ERROR) {
LOGE("Failed to generate wrapped key for cast cert.");
return status;
}
CloseSession();
*cert = device_certificate;
*wrapped_key = cast_cert_private_key.key();
return CdmResponseType(NO_ERROR);
}
const CdmSecurityLevel security_level = crypto_session_->GetSecurityLevel(); const CdmSecurityLevel security_level = crypto_session_->GetSecurityLevel();
CloseSession(); CloseSession();
wvutil::FileSystem global_file_system; wvutil::FileSystem global_file_system;
@@ -619,7 +670,8 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
if (signed_response.provisioning_type() == if (signed_response.provisioning_type() ==
SignedProvisioningMessage::PROVISIONING_40) { SignedProvisioningMessage::PROVISIONING_40) {
return HandleProvisioning40Response(file_system, signed_response.message()); return HandleProvisioning40Response(file_system, signed_response, cert,
wrapped_key);
} }
bool error = false; bool error = false;
@@ -839,4 +891,25 @@ CdmResponseType CertificateProvisioning::CloseSessionOnError(
return status; return status;
} }
CdmResponseType CertificateProvisioning::CertTypeAssign(
video_widevine::ProvisioningRequest& provisioning_request,
CdmCertificateType cert_type, const std::string& cert_authority) {
ProvisioningOptions* options = provisioning_request.mutable_options();
switch (cert_type) {
case kCertificateWidevine:
options->set_certificate_type(ProvisioningOptions::WIDEVINE_DRM);
break;
case kCertificateX509:
options->set_certificate_type(ProvisioningOptions::X509);
break;
default:
LOGE("Unknown certificate type: %d", static_cast<int>(cert_type));
return CdmResponseType(CERT_PROVISIONING_INVALID_CERT_TYPE);
}
cert_type_ = cert_type;
options->set_certificate_authority(cert_authority);
return CdmResponseType(NO_ERROR);
}
} // namespace wvcdm } // namespace wvcdm

View File

@@ -1030,6 +1030,14 @@ message SignedProvisioningMessage {
INTEL_SIGMA_210 = 210; // Intel Sigma 2.1.0 protocol. INTEL_SIGMA_210 = 210; // Intel Sigma 2.1.0 protocol.
} }
// Used by provisioning 4.0 to deliver cast certificates in which the server
// delivers a new rsa private key that must be encrypted and signed.
enum SessionKeyType {
UNDEFINED = 0;
WRAPPED_AES_KEY = 1;
EPHEMERAL_ECC_PUBLIC_KEY = 2;
}
// Serialized protobuf message for the corresponding protocol and stage of // Serialized protobuf message for the corresponding protocol and stage of
// the provisioning exchange. ProvisioningRequest or ProvisioningResponse // the provisioning exchange. ProvisioningRequest or ProvisioningResponse
// in the case of Provisioning 2.0, 3.0, 4.0 and ARCPP_PROVISIONING. Required. // in the case of Provisioning 2.0, 3.0, 4.0 and ARCPP_PROVISIONING. Required.
@@ -1056,6 +1064,21 @@ message SignedProvisioningMessage {
optional HashAlgorithmProto hash_algorithm = 7; optional HashAlgorithmProto hash_algorithm = 7;
// Indicates which version of the protocol is in use. // Indicates which version of the protocol is in use.
optional ProvisioningProtocolVersion protocol_version = 8; optional ProvisioningProtocolVersion protocol_version = 8;
// If populated, the contents of this field will be signaled by the
// |session_key_type| type. If the |session_key_type| is WRAPPED_AES_KEY the
// key is the bytes of an encrypted AES key. If the |session_key_type| is
// EPHEMERAL_ECC_PUBLIC_KEY the field contains the bytes of an RFC5208 ASN1
// serialized ECC public key.
// This field is only required to be set in a success response to
// Provisioning 4.0 X509 (cast) certificate request.
optional bytes session_key = 9;
// Optional field that contains the algorithm type used to generate the
// session_key and signature in a ProvisioningResponse message. This value is
// populated in a success response to a request for a X509 (cast) certificate.
// The value used depends on the key type of the PublicKeyToCertify contained
// in Provisioning 4.0 ProvisioningMessage.
// This value must be populated if session_key is populated.
optional SessionKeyType session_key_type = 10;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------