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(
wvutil::FileSystem* file_system, const std::string& origin,
const std::string& spoid, CdmProvisioningRequest* request,
std::string* default_url);
std::string* default_url, CdmCertificateType cert_type,
const std::string& cert_authority);
CdmResponseType FillEncryptedClientId(
const std::string& client_token,
video_widevine::ProvisioningRequest& provisioning_request,
@@ -93,7 +94,14 @@ class CertificateProvisioning {
video_widevine::ProvisioningRequest& provisioning_request,
const ServiceCertificate& service_certificate);
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(
const std::string& origin, const std::string& spoid,
@@ -120,6 +128,8 @@ class CertificateProvisioning {
std::string provisioning_40_wrapped_private_key_;
// Key type of the generated key pair in provisioning 4.
CryptoWrappedKey::Type provisioning_40_key_type_;
// Store the last provisioning request message
std::string provisioning_request_message_;
CORE_DISALLOW_COPY_AND_ASSIGN(CertificateProvisioning);
};

View File

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

View File

@@ -209,7 +209,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
if (crypto_session_->GetPreProvisionTokenType() ==
kClientTokenBootCertChain) {
return GetProvisioning40RequestInternal(file_system, origin, spoid, request,
default_url);
default_url, cert_type,
cert_authority);
}
// Prepare device provisioning request.
@@ -235,21 +236,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
sizeof(nonce));
provisioning_request.set_nonce(encoded_nonce);
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);
status = CertTypeAssign(provisioning_request, cert_type, cert_authority);
if (status != NO_ERROR) return status;
status = SetSpoidParameter(origin, spoid, &provisioning_request);
if (status != NO_ERROR) return status;
@@ -308,7 +296,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal(
wvutil::FileSystem* file_system, const std::string& origin,
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()) {
LOGE("Crypto session is not open");
return CdmResponseType(PROVISIONING_4_CRYPTO_SESSION_NOT_OPEN);
@@ -391,6 +380,29 @@ CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal(
stored_oem_cert, additional_parameter, provisioning_request,
*service_certificate_);
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;
@@ -413,6 +425,7 @@ CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal(
std::string serialized_message;
provisioning_request.SerializeToString(&serialized_message);
provisioning_request_message_ = serialized_message;
SignedProvisioningMessage signed_provisioning_msg;
signed_provisioning_msg.set_message(serialized_message);
@@ -509,8 +522,11 @@ CertificateProvisioning::FillEncryptedClientIdWithAdditionalParameter(
}
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;
const std::string response_message = signed_response.message();
if (response_message.empty() ||
!provisioning_response.ParseFromString(response_message)) {
return CdmResponseType(PROVISIONING_4_RESPONSE_FAILED_TO_PARSE_MESSAGE);
@@ -538,9 +554,44 @@ CdmResponseType CertificateProvisioning::HandleProvisioning40Response(
LOGE("No private key was generated");
return CdmResponseType(PROVISIONING_4_NO_PRIVATE_KEY);
}
const CryptoWrappedKey private_key(provisioning_40_key_type_,
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();
CloseSession();
wvutil::FileSystem global_file_system;
@@ -619,7 +670,8 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
if (signed_response.provisioning_type() ==
SignedProvisioningMessage::PROVISIONING_40) {
return HandleProvisioning40Response(file_system, signed_response.message());
return HandleProvisioning40Response(file_system, signed_response, cert,
wrapped_key);
}
bool error = false;
@@ -839,4 +891,25 @@ CdmResponseType CertificateProvisioning::CloseSessionOnError(
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

View File

@@ -1030,6 +1030,14 @@ message SignedProvisioningMessage {
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
// the provisioning exchange. ProvisioningRequest or ProvisioningResponse
// 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;
// Indicates which version of the protocol is in use.
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;
}
// ----------------------------------------------------------------------------