Merge latest oemcrypto-v17 change
No-Typo-Check: Not related to this change. Bug: 161477208 Change-Id: I99e4780f6855b7045aa0cd5a49c13d2d0d51ed64
This commit is contained in:
committed by
Fred Gylys-Colwell
parent
c924960962
commit
642965c678
@@ -28,7 +28,7 @@ const std::string kProvisioningServerUrl =
|
||||
"?key=AIzaSyB-5OLKTx2iU5mko18DfdwK5611JIjbUhE";
|
||||
|
||||
// NOTE: Provider ID = widevine.com
|
||||
const std::string kCpProductionServiceCertificate = wvcdm::a2bs_hex(
|
||||
const std::string kCpProductionServiceCertificate = wvutil::a2bs_hex(
|
||||
"0ab9020803121051434fe2a44c763bcc2c826a2d6ef9a718f7d793d005228e02"
|
||||
"3082010a02820101009e27088659dbd9126bc6ed594caf652b0eaab82abb9862"
|
||||
"ada1ee6d2cb5247e94b28973fef5a3e11b57d0b0872c930f351b5694354a8c77"
|
||||
@@ -51,6 +51,30 @@ const std::string kCpProductionServiceCertificate = wvcdm::a2bs_hex(
|
||||
"26e0c050f3fd3ebe68cef9903ef6405b25fc6e31f93559fcff05657662b3653a"
|
||||
"8598ed5751b38694419242a875d9e00d5a5832933024b934859ec8be78adccbb"
|
||||
"1ec7127ae9afeef9c5cd2e15bd3048e8ce652f7d8c5d595a0323238c598a28");
|
||||
|
||||
// Retrieves |stored_oem_cert| from |file_handle|, and load the OEM private key
|
||||
// to |crypto_session|. Returns true if all operations are successful.
|
||||
bool RetrieveOemCertificateAndLoadPrivateKey(CryptoSession& crypto_session,
|
||||
DeviceFiles& file_handle,
|
||||
std::string& stored_oem_cert) {
|
||||
stored_oem_cert.clear();
|
||||
CryptoWrappedKey wrapped_private_key;
|
||||
if (file_handle.RetrieveOemCertificate(&stored_oem_cert,
|
||||
&wrapped_private_key) !=
|
||||
DeviceFiles::kCertificateValid) {
|
||||
LOGE("An invalid stored OEM certificated is retrieved");
|
||||
stored_oem_cert.clear();
|
||||
return false;
|
||||
}
|
||||
if (crypto_session.LoadOemCertificatePrivateKey(wrapped_private_key) !=
|
||||
NO_ERROR) {
|
||||
LOGE("Can not load the OEM private key");
|
||||
stored_oem_cert.clear();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
// Protobuf generated classes.
|
||||
using video_widevine::ClientIdentification_ClientCapabilities;
|
||||
@@ -60,6 +84,7 @@ using video_widevine::EncryptedClientIdentification;
|
||||
using video_widevine::ProvisioningOptions;
|
||||
using video_widevine::ProvisioningRequest;
|
||||
using video_widevine::ProvisioningResponse;
|
||||
using video_widevine::PublicKeyToCertify;
|
||||
using video_widevine::SignedDrmCertificate;
|
||||
using video_widevine::SignedProvisioningMessage;
|
||||
using video_widevine::
|
||||
@@ -130,10 +155,14 @@ CdmResponseType CertificateProvisioning::SetSpoidParameter(
|
||||
*/
|
||||
SignedProvisioningMessage::ProvisioningType
|
||||
CertificateProvisioning::GetProvisioningType() {
|
||||
if (crypto_session_->GetPreProvisionTokenType() == kClientTokenOemCert)
|
||||
return SignedProvisioningMessage::PROVISIONING_30;
|
||||
else
|
||||
return SignedProvisioningMessage::PROVISIONING_20;
|
||||
switch (crypto_session_->GetPreProvisionTokenType()) {
|
||||
case kClientTokenBootCertChain:
|
||||
return SignedProvisioningMessage::PROVISIONING_40;
|
||||
case kClientTokenOemCert:
|
||||
return SignedProvisioningMessage::PROVISIONING_30;
|
||||
default:
|
||||
return SignedProvisioningMessage::PROVISIONING_20;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -144,20 +173,20 @@ CertificateProvisioning::GetProvisioningType() {
|
||||
* 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) {
|
||||
wvutil::FileSystem* file_system, 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) {
|
||||
return CloseSessionOnError(GetProvisioningRequestInternal(
|
||||
requested_security_level, cert_type, cert_authority, origin, spoid,
|
||||
request, default_url));
|
||||
file_system, requested_security_level, cert_type, cert_authority, origin,
|
||||
spoid, request, default_url));
|
||||
}
|
||||
|
||||
CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
|
||||
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) {
|
||||
wvutil::FileSystem* file_system, 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 (!request || !default_url) {
|
||||
LOGE("Output parameter |%s| is not provided",
|
||||
request ? "default_url" : "request");
|
||||
@@ -174,30 +203,17 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
|
||||
return status;
|
||||
}
|
||||
|
||||
if (crypto_session_->GetPreProvisionTokenType() ==
|
||||
kClientTokenBootCertChain) {
|
||||
return GetProvisioning40RequestInternal(file_system, request);
|
||||
}
|
||||
|
||||
// Prepare device provisioning request.
|
||||
ProvisioningRequest provisioning_request;
|
||||
|
||||
wvcdm::ClientIdentification id;
|
||||
status = id.InitForProvisioning(crypto_session_.get());
|
||||
status = FillEncryptedClientId(/*client_token=*/"", provisioning_request);
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
video_widevine::ClientIdentification client_id;
|
||||
|
||||
CdmAppParameterMap app_parameter;
|
||||
status = id.Prepare(app_parameter, kEmptyString, &client_id);
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
if (!service_certificate_->has_certificate()) {
|
||||
LOGE("Service certificate not staged");
|
||||
return CERT_PROVISIONING_EMPTY_SERVICE_CERTIFICATE;
|
||||
}
|
||||
|
||||
// Encrypt client identification
|
||||
EncryptedClientIdentification* encrypted_client_id =
|
||||
provisioning_request.mutable_encrypted_client_id();
|
||||
status = service_certificate_->EncryptClientId(
|
||||
crypto_session_.get(), &client_id, encrypted_client_id);
|
||||
|
||||
uint32_t nonce;
|
||||
status = crypto_session_->GenerateNonce(&nonce);
|
||||
|
||||
@@ -272,13 +288,184 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
|
||||
|
||||
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
|
||||
// Return request as web-safe base64 string
|
||||
*request = Base64SafeEncodeNoPad(serialized_request);
|
||||
*request = wvutil::Base64SafeEncodeNoPad(serialized_request);
|
||||
} else {
|
||||
*request = std::move(serialized_request);
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CertificateProvisioning::GetProvisioning40RequestInternal(
|
||||
wvutil::FileSystem* file_system, CdmProvisioningRequest* request) {
|
||||
if (!crypto_session_->IsOpen()) {
|
||||
LOGE("Crypto session is not open");
|
||||
return PROVISIONING_4_CRYPTO_SESSION_NOT_OPEN;
|
||||
}
|
||||
|
||||
if (file_system == nullptr) {
|
||||
LOGE("file_system is nullptr but is required in provisioning 4");
|
||||
return PROVISIONING_4_FILE_SYSTEM_IS_NULL;
|
||||
}
|
||||
const CdmSecurityLevel security_level = crypto_session_->GetSecurityLevel();
|
||||
DeviceFiles file_handle(file_system);
|
||||
if (!file_handle.Init(security_level)) {
|
||||
LOGE("Failed to initialize DeviceFiles");
|
||||
return PROVISIONING_4_FAILED_TO_INITIALIZE_DEVICE_FILES;
|
||||
}
|
||||
|
||||
ProvisioningRequest provisioning_request;
|
||||
// Determine the current stage by checking if OEM cert exists.
|
||||
std::string stored_oem_cert;
|
||||
if (file_handle.HasOemCertificate()) {
|
||||
// This is second stage requesting for DRM cert. We try to use the stored
|
||||
// OEM cert. In case of error, we just fall back to the first stage
|
||||
// provisioning (request for an OEM cert).
|
||||
if (!RetrieveOemCertificateAndLoadPrivateKey(*crypto_session_, file_handle,
|
||||
stored_oem_cert)) {
|
||||
stored_oem_cert.clear();
|
||||
LOGD("Deleting the stored OEM certificate due to unsuccessful read");
|
||||
if (!file_handle.RemoveOemCertificate()) {
|
||||
// This should not happen.
|
||||
LOGE("Failed to delete the OEM certificate certificate");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
provisioning_40_key_type_ = CryptoWrappedKey::kUninitialized;
|
||||
status = crypto_session_->GenerateCertificateKeyPair(
|
||||
&public_key, &public_key_signature, &provisioning_40_wrapped_private_key_,
|
||||
&provisioning_40_key_type_);
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
PublicKeyToCertify* key_to_certify =
|
||||
provisioning_request.mutable_certificate_public_key();
|
||||
key_to_certify->set_public_key(public_key);
|
||||
key_to_certify->set_signature(public_key_signature);
|
||||
key_to_certify->set_key_type(provisioning_40_key_type_ ==
|
||||
CryptoWrappedKey::kRsa
|
||||
? PublicKeyToCertify::RSA
|
||||
: PublicKeyToCertify::ECC);
|
||||
|
||||
// In provisioning 4, the message is not signed.
|
||||
SignedProvisioningMessage signed_provisioning_msg;
|
||||
provisioning_request.SerializeToString(
|
||||
signed_provisioning_msg.mutable_message());
|
||||
signed_provisioning_msg.set_provisioning_type(GetProvisioningType());
|
||||
signed_provisioning_msg.set_protocol_version(
|
||||
SignedProvisioningMessage_ProvisioningProtocolVersion_VERSION_1_1);
|
||||
|
||||
std::string serialized_request;
|
||||
signed_provisioning_msg.SerializeToString(&serialized_request);
|
||||
|
||||
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
|
||||
// Return request as web-safe base64 string
|
||||
*request = wvutil::Base64SafeEncodeNoPad(serialized_request);
|
||||
} else {
|
||||
*request = std::move(serialized_request);
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
CdmResponseType CertificateProvisioning::FillEncryptedClientId(
|
||||
const std::string& client_token,
|
||||
ProvisioningRequest& provisioning_request) {
|
||||
if (!crypto_session_->IsOpen()) {
|
||||
return UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
wvcdm::ClientIdentification id;
|
||||
CdmResponseType status =
|
||||
id.InitForProvisioningRequest(client_token, crypto_session_.get());
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
video_widevine::ClientIdentification client_id;
|
||||
CdmAppParameterMap app_parameter;
|
||||
status = id.Prepare(app_parameter, kEmptyString, &client_id);
|
||||
if (status != NO_ERROR) return status;
|
||||
|
||||
if (!service_certificate_->has_certificate()) {
|
||||
LOGE("Service certificate not staged");
|
||||
return CERT_PROVISIONING_EMPTY_SERVICE_CERTIFICATE;
|
||||
}
|
||||
|
||||
// Encrypt client identification
|
||||
return service_certificate_->EncryptClientId(
|
||||
crypto_session_.get(), &client_id,
|
||||
provisioning_request.mutable_encrypted_client_id());
|
||||
}
|
||||
|
||||
CdmResponseType CertificateProvisioning::HandleProvisioning40Response(
|
||||
wvutil::FileSystem* file_system, const std::string& response_message) {
|
||||
ProvisioningResponse provisioning_response;
|
||||
if (response_message.empty() ||
|
||||
!provisioning_response.ParseFromString(response_message)) {
|
||||
return PROVISIONING_4_RESPONSE_FAILED_TO_PARSE_MESSAGE;
|
||||
}
|
||||
if (provisioning_response.has_status() &&
|
||||
provisioning_response.status() != ProvisioningResponse::NO_ERROR) {
|
||||
LOGE("Provisioning Response status: %d", provisioning_response.status());
|
||||
switch (provisioning_response.status()) {
|
||||
case ProvisioningResponse::REVOKED_DEVICE_CREDENTIALS:
|
||||
case ProvisioningResponse::REVOKED_DEVICE_SERIES:
|
||||
return DEVICE_REVOKED;
|
||||
default:
|
||||
return PROVISIONING_4_RESPONSE_HAS_ERROR_STATUS;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& device_certificate =
|
||||
provisioning_response.device_certificate();
|
||||
if (device_certificate.empty()) {
|
||||
LOGE("Provisioning response has no certificate");
|
||||
return PROVISIONING_4_RESPONSE_HAS_NO_CERTIFICATE;
|
||||
}
|
||||
|
||||
if (provisioning_40_wrapped_private_key_.empty()) {
|
||||
LOGE("No private key was generated");
|
||||
return PROVISIONING_4_NO_PRIVATE_KEY;
|
||||
}
|
||||
const CryptoWrappedKey private_key(provisioning_40_key_type_,
|
||||
provisioning_40_wrapped_private_key_);
|
||||
|
||||
const CdmSecurityLevel security_level = crypto_session_->GetSecurityLevel();
|
||||
CloseSession();
|
||||
DeviceFiles file_handle(file_system);
|
||||
if (!file_handle.Init(security_level)) {
|
||||
LOGE("Failed to initialize DeviceFiles");
|
||||
return PROVISIONING_4_FAILED_TO_INITIALIZE_DEVICE_FILES_2;
|
||||
}
|
||||
|
||||
// Check the stage of the provisioning by checking if an OEM cert is already
|
||||
// stored in the file system.
|
||||
if (!file_handle.HasOemCertificate()) {
|
||||
// No OEM cert already stored => the response is expected to be an OEM cert.
|
||||
if (!file_handle.StoreOemCertificate(device_certificate, private_key)) {
|
||||
LOGE("Failed to store provisioning 4 OEM certificate");
|
||||
return PROVISIONING_4_FAILED_TO_STORE_OEM_CERTIFICATE;
|
||||
}
|
||||
} else {
|
||||
// The response is assumed to be an DRM cert.
|
||||
if (!file_handle.StoreCertificate(device_certificate, private_key)) {
|
||||
LOGE("Failed to store provisioning 4 DRM certificate");
|
||||
return PROVISIONING_4_FAILED_TO_STORE_DRM_CERTIFICATE;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -287,8 +474,9 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequestInternal(
|
||||
* 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) {
|
||||
wvutil::FileSystem* file_system,
|
||||
const CdmProvisioningResponse& response_message, std::string* cert,
|
||||
std::string* wrapped_key) {
|
||||
if (response_message.empty()) {
|
||||
LOGE("Provisioning response message is empty");
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_1;
|
||||
@@ -317,6 +505,11 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_2;
|
||||
}
|
||||
|
||||
if (signed_response.provisioning_type() ==
|
||||
SignedProvisioningMessage::PROVISIONING_40) {
|
||||
return HandleProvisioning40Response(file_system, signed_response.message());
|
||||
}
|
||||
|
||||
bool error = false;
|
||||
if (!signed_response.has_signature()) {
|
||||
LOGE("Signed response does not have signature");
|
||||
@@ -491,7 +684,7 @@ bool CertificateProvisioning::ExtractAndDecodeSignedMessage(
|
||||
|
||||
// Decode the base64-encoded message.
|
||||
const std::vector<uint8_t> decoded_message =
|
||||
wvcdm::Base64SafeDecode(message_string);
|
||||
wvutil::Base64SafeDecode(message_string);
|
||||
result->assign(decoded_message.begin(), decoded_message.end());
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user