diff --git a/libwvdrmengine/cdm/core/include/certificate_provisioning.h b/libwvdrmengine/cdm/core/include/certificate_provisioning.h index 05751485..34918fb4 100644 --- a/libwvdrmengine/cdm/core/include/certificate_provisioning.h +++ b/libwvdrmengine/cdm/core/include/certificate_provisioning.h @@ -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); }; diff --git a/libwvdrmengine/cdm/core/src/cdm_session.cpp b/libwvdrmengine/cdm/core/src/cdm_session.cpp index 6c10ad31..af6828ae 100644 --- a/libwvdrmengine/cdm/core/src/cdm_session.cpp +++ b/libwvdrmengine/cdm/core/src/cdm_session.cpp @@ -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 diff --git a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp index 9526d8de..488b3d75 100644 --- a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp +++ b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp @@ -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(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(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(&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(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 diff --git a/libwvdrmengine/cdm/core/src/license_protocol.proto b/libwvdrmengine/cdm/core/src/license_protocol.proto index 25ede138..9366687d 100644 --- a/libwvdrmengine/cdm/core/src/license_protocol.proto +++ b/libwvdrmengine/cdm/core/src/license_protocol.proto @@ -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; } // ----------------------------------------------------------------------------