// Copyright 2013 Google Inc. All Rights Reserved. #include "certificate_provisioning.h" #include "client_identification.h" #include "device_files.h" #include "file_store.h" #include "license_protocol.pb.h" #include "log.h" #include "properties.h" #include "service_certificate.h" #include "string_conversions.h" #include "wv_cdm_constants.h" namespace { // URL for Google Provisioning Server. // The provisioning server supplies the certificate that is needed // to communicate with the License Server. const std::string kProvisioningServerUrl = "https://www.googleapis.com/" "certificateprovisioning/v1/devicecertificates/create" "?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE"; // NOTE: Provider ID = staging.google.com const std::string kCpProductionServiceCertificate = wvcdm::a2bs_hex( "0ABF020803121028703454C008F63618ADE7443DB6C4C8188BE7F9900522" "8E023082010A0282010100B52112B8D05D023FCC5D95E2C251C1C649B417" "7CD8D2BEEF355BB06743DE661E3D2ABC3182B79946D55FDC08DFE9540781" "5E9A6274B322A2C7F5E067BB5F0AC07A89D45AEA94B2516F075B66EF811D" "0D26E1B9A6B894F2B9857962AA171C4F66630D3E4C602718897F5E1EF9B6" "AAF5AD4DBA2A7E14176DF134A1D3185B5A218AC05A4C41F081EFFF80A3A0" "40C50B09BBC740EEDCD8F14D675A91980F92CA7DDC646A06ADAD5101F74A" "0E498CC01F00532BAC217850BD905E90923656B7DFEFEF42486767F33EF6" "283D4F4254AB72589390BEE55808F1D668080D45D893C2BCA2F74D60A0C0" "D0A0993CEF01604703334C3638139486BC9DAF24FD67A07F9AD943020301" "00013A1273746167696E672E676F6F676C652E636F6D128003983E303526" "75F40BA715FC249BDAE5D4AC7249A2666521E43655739529721FF880E0AA" "EFC5E27BC980DAEADABF3FC386D084A02C82537848CC753FF497B011A7DA" "97788A00E2AA6B84CD7D71C07A48EBF61602CCA5A3F32030A7295C30DA91" "5B91DC18B9BC9593B8DE8BB50F0DEDC12938B8E9E039CDDE18FA82E81BB0" "32630FE955D85A566CE154300BF6D4C1BD126966356B287D657B18CE63D0" "EFD45FC5269E97EAB11CB563E55643B26FF49F109C2101AFCAF35B832F28" "8F0D9D45960E259E85FB5D24DBD2CF82764C5DD9BF727EFBE9C861F86932" "1F6ADE18905F4D92F9A6DA6536DB8475871D168E870BB2303CF70C6E9784" "C93D2DE845AD8262BE7E0D4E2E4A0759CEF82D109D2592C72429F8C01742" "BAE2B3DECADBC33C3E5F4BAF5E16ECB74EADBAFCB7C6705F7A9E3B6F3940" "383F9C5116D202A20C9229EE969C2519718303B50D0130C3352E06B014D8" "38540F8A0C227C0011E0F5B38E4E298ED2CB301EB4564965F55C5D79757A" "250A4EB9C84AB3E6539F6B6FDF56899EA29914"); /* * Provisioning response is a base64-encoded protobuf, optionally within a * JSON wrapper. If the JSON wrapper is present, extract the embedded response * message. Then perform the base64 decode and return the result. * * If an error occurs during the parse or the decode, return an empty string. */ void ExtractAndDecodeSignedMessage(const std::string& provisioning_response, std::string* result) { const std::string json_start_substr("\"signedResponse\": \""); const std::string json_end_substr("\""); std::string message_string; size_t start = provisioning_response.find(json_start_substr); if (start == provisioning_response.npos) { // Message is not properly wrapped - reject it. LOGE("ExtractAndDecodeSignedMessage: cannot locate start substring"); result->clear(); return; } else { // Appears to be JSON-wrapped protobuf - find end of protobuf portion. size_t end = provisioning_response.find(json_end_substr, start + json_start_substr.length()); if (end == provisioning_response.npos) { LOGE("ExtractAndDecodeSignedMessage: cannot locate end substring"); result->clear(); return; } size_t b64_string_size = end - start - json_start_substr.length(); message_string.assign(provisioning_response, start + json_start_substr.length(), b64_string_size); } if (message_string.empty()) { LOGE("ExtractAndDecodeSignedMessage: CdmProvisioningResponse is empty"); result->clear(); return; } // Decode the base64-encoded message. const std::vector decoded_message = wvcdm::Base64SafeDecode(message_string); result->assign(decoded_message.begin(), decoded_message.end()); } } // namespace namespace wvcdm { // Protobuf generated classes. using video_widevine::ClientIdentification_ClientCapabilities; using video_widevine::ClientIdentification_NameValue; using video_widevine::EncryptedClientIdentification; using video_widevine::ProvisioningOptions; using video_widevine::ProvisioningRequest; using video_widevine::ProvisioningResponse; using video_widevine::SignedProvisioningMessage; CdmResponseType CertificateProvisioning::Init( const std::string& service_certificate) { std::string certificate = service_certificate.empty() ? kCpProductionServiceCertificate : service_certificate; return service_certificate_->Init(certificate); } /* * Fill in the appropriate SPOID (Stable Per-Origin IDentifier) option. * One of spoid, provider_id or stable_id will be passed to the provisioning * server for determining a unique per origin ID for the device. * It is also valid (though deprecated) to leave the settings unset. */ bool CertificateProvisioning::SetSpoidParameter( const std::string& origin, const std::string& spoid, ProvisioningRequest* request) { if (!request) { LOGE("CertificateProvisioning::SetSpoidParameter: No request buffer " "passed to method."); return false; } if (!spoid.empty()) { // Use the SPOID that has been pre-provided request->set_spoid(spoid); } else if (Properties::UseProviderIdInProvisioningRequest()) { if (!service_certificate_->provider_id().empty()) { request->set_provider_id(service_certificate_->provider_id()); } else { LOGE("CertificateProvisioning::SetSpoidParameter: Failure getting " "provider ID"); return false; } } else if (origin != EMPTY_ORIGIN) { // Legacy behavior - Concatenate Unique ID with Origin std::string device_unique_id; if (!crypto_session_.GetInternalDeviceUniqueId(&device_unique_id)) { LOGE("CertificateProvisioning::SetSpoidParameter: Failure getting " "device unique ID"); return false; } request->set_stable_id(device_unique_id + origin); } // No else clause, by design. It is valid to do nothing. return true; } /* * Return the provisioning protocol version - dictated by OEMCrypto * support for OEM certificates. */ SignedProvisioningMessage::ProtocolVersion CertificateProvisioning::GetProtocolVersion() { if (crypto_session_.GetPreProvisionTokenType() == kClientTokenOemCert) return SignedProvisioningMessage::VERSION_3; else return SignedProvisioningMessage::VERSION_2; } /* * Compose a device provisioning request and output *request in a * JSON-compatible format (web-safe base64). * Also return *default_url of the provisioning server. * * Returns NO_ERROR for success and CERT_PROVISIONING_REQUEST_ERROR_? if fails. */ CdmResponseType CertificateProvisioning::GetProvisioningRequest( SecurityLevel requested_security_level, CdmCertificateType cert_type, const std::string& cert_authority, const std::string& origin, const std::string& spoid, CdmProvisioningRequest* request, std::string* default_url) { if (!default_url) { LOGE("GetProvisioningRequest: pointer for returning URL is NULL"); return CERT_PROVISIONING_REQUEST_ERROR_1; } default_url->assign(kProvisioningServerUrl); CdmResponseType status = crypto_session_.Open(requested_security_level); if (NO_ERROR != status) { LOGE("GetProvisioningRequest: fails to create a crypto session"); return status; } // Prepare device provisioning request. ProvisioningRequest provisioning_request; wvcdm::ClientIdentification id; status = id.Init(&crypto_session_); if (status != NO_ERROR) return status; video_widevine::ClientIdentification* client_id = provisioning_request.mutable_client_id(); CdmAppParameterMap app_parameter; status = id.Prepare(app_parameter, client_id); if (status != NO_ERROR) return status; if (!service_certificate_->has_certificate()) { LOGE("CertificateProvisioning::GetProvisioningRequest: Service Certificate " "not staged"); return CERT_PROVISIONING_EMPTY_SERVICE_CERTIFICATE; } // TODO(rfrias): Uncomment when b/69427217 is addressed /* EncryptedClientIdentification* encrypted_client_id = provisioning_request->mutable_encrypted_client_id(); CdmResponseType status = service_certificate_->EncryptClientId(&crypto_session_, client_id, encrypted_client_id); provisioning_request->clear_client_id(); */ uint32_t nonce; if (!crypto_session_.GenerateNonce(&nonce)) { LOGE("GetProvisioningRequest: fails to generate a nonce"); return CERT_PROVISIONING_NONCE_GENERATION_ERROR; } // The provisioning server does not convert the nonce to uint32_t, it just // passes the binary data to the response message. std::string the_nonce(reinterpret_cast(&nonce), sizeof(nonce)); provisioning_request.set_nonce(the_nonce); ProvisioningOptions* options = provisioning_request.mutable_options(); switch (cert_type) { case kCertificateWidevine: options->set_certificate_type( video_widevine::ProvisioningOptions_CertificateType_WIDEVINE_DRM); break; case kCertificateX509: options->set_certificate_type( video_widevine::ProvisioningOptions_CertificateType_X509); break; default: LOGE("GetProvisioningRequest: unknown certificate type %ld", cert_type); return CERT_PROVISIONING_INVALID_CERT_TYPE; } cert_type_ = cert_type; options->set_certificate_authority(cert_authority); if (!SetSpoidParameter(origin, spoid, &provisioning_request)) { return CERT_PROVISIONING_GET_KEYBOX_ERROR_2; } std::string serialized_message; provisioning_request.SerializeToString(&serialized_message); // Derives signing and encryption keys and constructs signature. std::string request_signature; if (!crypto_session_.PrepareRequest(serialized_message, true, &request_signature)) { LOGE("GetProvisioningRequest: fails to prepare request"); return CERT_PROVISIONING_REQUEST_ERROR_3; } if (request_signature.empty()) { LOGE("GetProvisioningRequest: request signature is empty"); return CERT_PROVISIONING_REQUEST_ERROR_4; } SignedProvisioningMessage signed_provisioning_msg; signed_provisioning_msg.set_message(serialized_message); signed_provisioning_msg.set_signature(request_signature); signed_provisioning_msg.set_protocol_version(GetProtocolVersion()); std::string serialized_request; signed_provisioning_msg.SerializeToString(&serialized_request); if (!wvcdm::Properties::provisioning_messages_are_binary()) { // Return request as web-safe base64 string std::vector request_vector(serialized_request.begin(), serialized_request.end()); request->assign(Base64SafeEncodeNoPad(request_vector)); } else { request->swap(serialized_request); } return NO_ERROR; } /* * The response message consists of a device certificate and the device RSA key. * The device RSA key is stored in the T.E.E. The device certificate is stored * in the device. * * Returns NO_ERROR for success and CERT_PROVISIONING_RESPONSE_ERROR_? if fails. */ CdmResponseType CertificateProvisioning::HandleProvisioningResponse( FileSystem* file_system, const CdmProvisioningResponse& response_message, std::string* cert, std::string* wrapped_key) { if (response_message.empty()) { LOGE("HandleProvisioningResponse: response message is empty."); return CERT_PROVISIONING_RESPONSE_ERROR_1; } std::string response; if (wvcdm::Properties::provisioning_messages_are_binary()) { response.assign(response_message); } else { // The response is base64 encoded in a JSON wrapper. // Extract it and decode it. On error return an empty string. ExtractAndDecodeSignedMessage(response_message, &response); if (response.empty()) { LOGE("HandleProvisioningResponse: response message is " "an invalid JSON/base64 string."); return CERT_PROVISIONING_RESPONSE_ERROR_1; } } // Authenticates provisioning response using D1s (server key derived from // the provisioing request's input). Validate provisioning response and // stores private device RSA key and certificate. SignedProvisioningMessage signed_response; if (!signed_response.ParseFromString(response)) { LOGE("HandleProvisioningResponse: fails to parse signed response"); return CERT_PROVISIONING_RESPONSE_ERROR_2; } bool error = false; if (!signed_response.has_signature()) { LOGE("HandleProvisioningResponse: signature not found"); error = true; } if (!signed_response.has_message()) { LOGE("HandleProvisioningResponse: message not found"); error = true; } if (error) return CERT_PROVISIONING_RESPONSE_ERROR_3; const std::string& signed_message = signed_response.message(); const std::string& signature = signed_response.signature(); ProvisioningResponse provisioning_response; if (!provisioning_response.ParseFromString(signed_message)) { LOGE("HandleProvisioningResponse: Fails to parse signed message"); return CERT_PROVISIONING_RESPONSE_ERROR_4; } if (!provisioning_response.has_device_rsa_key()) { LOGE("HandleProvisioningResponse: key not found"); return CERT_PROVISIONING_RESPONSE_ERROR_5; } // If Provisioning 3.0 (OEM Cert provisioned), verify that the // message is properly signed. if (crypto_session_.GetPreProvisionTokenType() == kClientTokenOemCert) { if (service_certificate_->VerifySignedMessage(signed_message, signature) != NO_ERROR) { LOGE("HandleProvisioningResponse: message not properly signed"); return CERT_PROVISIONING_RESPONSE_ERROR_6; } } const std::string& new_private_key = provisioning_response.device_rsa_key(); const std::string& nonce = provisioning_response.nonce(); const std::string& iv = provisioning_response.device_rsa_key_iv(); const std::string& wrapping_key = (provisioning_response.has_wrapping_key()) ? provisioning_response.wrapping_key() : std::string(); std::string wrapped_private_key; if (!crypto_session_.RewrapCertificate(signed_message, signature, nonce, new_private_key, iv, wrapping_key, &wrapped_private_key)) { LOGE("HandleProvisioningResponse: RewrapCertificate fails"); return CERT_PROVISIONING_RESPONSE_ERROR_6; } crypto_session_.Close(); if (cert_type_ == kCertificateX509) { *cert = provisioning_response.device_certificate(); *wrapped_key = wrapped_private_key; return NO_ERROR; } // This is the entire certificate (SignedDrmDeviceCertificate). // This will be stored to the device as the final step in the device // provisioning process. const std::string& device_certificate = provisioning_response.device_certificate(); DeviceFiles handle(file_system); if (!handle.Init(crypto_session_.GetSecurityLevel())) { LOGE("HandleProvisioningResponse: failed to init DeviceFiles"); return CERT_PROVISIONING_RESPONSE_ERROR_7; } if (!handle.StoreCertificate(device_certificate, wrapped_private_key)) { LOGE("HandleProvisioningResponse: failed to save provisioning certificate"); return CERT_PROVISIONING_RESPONSE_ERROR_8; } return NO_ERROR; } } // namespace wvcdm