Throw DeniedByServerException when provisioning detects a revoked device
[ Merge of http://go/wvgerrit/117267 ] The client will now advertise the ability to handle provisioning errors by a minor version updated to the provisioning protocol version. The provisioning service may indicate that the individual device is revoked or all devices with the same make/model have been revoked. If the provisoning service has not been upgraded, the protocol version field in the request will be ignored. The provisioning service/SDK will respond with an HTTP 400 error to a provisioning request from a revoked device. Bug: 174174765 Test: WvCdmRequestLicenseTest.ProvisioningRevocationTest, WV unit/integration tests Change-Id: I5ff61496685f310de6704a90452b8b76b3505cbb
This commit is contained in:
@@ -418,6 +418,7 @@ enum CdmResponseType : int32_t {
|
||||
NO_SRM_VERSION = 363,
|
||||
SESSION_NOT_FOUND_23 = 364,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_9 = 365,
|
||||
CERT_PROVISIONING_RESPONSE_ERROR_10 = 366,
|
||||
// Don't forget to add new values to
|
||||
// * core/test/test_printers.cpp.
|
||||
// * android/include/mapErrors-inl.h
|
||||
|
||||
@@ -118,6 +118,7 @@ using video_widevine::ProvisioningRequest;
|
||||
using video_widevine::ProvisioningResponse;
|
||||
using video_widevine::SignedDrmDeviceCertificate;
|
||||
using video_widevine::SignedProvisioningMessage;
|
||||
using video_widevine::SignedProvisioningMessage_ProvisioningProtocolVersion_VERSION_1_1;
|
||||
|
||||
CdmResponseType CertificateProvisioning::Init(
|
||||
const std::string& service_certificate) {
|
||||
@@ -298,6 +299,8 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest(
|
||||
} else {
|
||||
signed_provisioning_msg.set_oemcrypto_core_message(core_message);
|
||||
}
|
||||
signed_provisioning_msg.set_protocol_version(
|
||||
SignedProvisioningMessage_ProvisioningProtocolVersion_VERSION_1_1);
|
||||
|
||||
std::string serialized_request;
|
||||
signed_provisioning_msg.SerializeToString(&serialized_request);
|
||||
@@ -393,6 +396,22 @@ CdmResponseType CertificateProvisioning::HandleProvisioningResponse(
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_4;
|
||||
}
|
||||
|
||||
if (provisioning_response.has_status()) {
|
||||
if (provisioning_response.status() != ProvisioningResponse::NO_ERROR) {
|
||||
LOGE("Provisioning Response status: %d", provisioning_response.status());
|
||||
}
|
||||
|
||||
switch (provisioning_response.status()) {
|
||||
case ProvisioningResponse::NO_ERROR:
|
||||
break;
|
||||
case ProvisioningResponse::REVOKED_DEVICE_CREDENTIALS:
|
||||
case ProvisioningResponse::REVOKED_DEVICE_SERIES:
|
||||
return DEVICE_REVOKED;
|
||||
default:
|
||||
return CERT_PROVISIONING_RESPONSE_ERROR_10;
|
||||
}
|
||||
}
|
||||
|
||||
CryptoWrappedKey private_key;
|
||||
const CdmResponseType status = crypto_session_->LoadProvisioning(
|
||||
signed_message, core_message, signature, &private_key.key());
|
||||
|
||||
@@ -71,6 +71,10 @@ void PrintTo(const enum CdmResponseType& value, ::std::ostream* os) {
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_9:
|
||||
*os << "CERT_PROVISIONING_RESPONSE_ERROR_9";
|
||||
break;
|
||||
case CERT_PROVISIONING_RESPONSE_ERROR_10:
|
||||
*os << "CERT_PROVISIONING_RESPONSE_ERROR_10";
|
||||
break;
|
||||
break;
|
||||
case CLIENT_ID_AES_ENCRYPT_ERROR:
|
||||
*os << "CLIENT_ID_AES_ENCRYPT_ERROR";
|
||||
break;
|
||||
|
||||
@@ -76,7 +76,15 @@ const std::vector<std::string> kCanDisableAnalogOutput = {
|
||||
using video_widevine::LicenseIdentification;
|
||||
using video_widevine::LicenseRequest_ContentIdentification;
|
||||
using video_widevine::ProvisioningResponse;
|
||||
using video_widevine::ProvisioningResponse_ProvisioningStatus;
|
||||
using video_widevine::ProvisioningResponse_ProvisioningStatus_NO_ERROR;
|
||||
using video_widevine::
|
||||
ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_CREDENTIALS;
|
||||
using video_widevine::
|
||||
ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_SERIES;
|
||||
using video_widevine::SignedProvisioningMessage;
|
||||
using video_widevine::
|
||||
SignedProvisioningMessage_ProvisioningProtocolVersion_VERSION_1_1;
|
||||
|
||||
// TODO(rfrias): refactor to print out the decryption test names
|
||||
struct SubSampleInfo {
|
||||
@@ -2468,6 +2476,113 @@ TEST_F(WvCdmRequestLicenseTest, ProvisioningSpoidTest) {
|
||||
alternate_cdm_id_2_serial_number_1);
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, ProvisioningRevocationTest) {
|
||||
Unprovision();
|
||||
|
||||
CdmResponseType result =
|
||||
decryptor_->OpenSession(config_.key_system(), nullptr,
|
||||
kDefaultCdmIdentifier, nullptr, &session_id_);
|
||||
|
||||
switch (result) {
|
||||
case NO_ERROR:
|
||||
decryptor_->CloseSession(session_id_);
|
||||
return;
|
||||
case NEED_PROVISIONING:
|
||||
break;
|
||||
default:
|
||||
EXPECT_EQ(NO_ERROR, result);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string provisioning_server;
|
||||
std::string cert_authority, provisioning_request;
|
||||
|
||||
result = decryptor_->GetProvisioningRequest(
|
||||
kCertificateWidevine, cert_authority, kDefaultCdmIdentifier,
|
||||
kEmptyServiceCertificate, kLevelDefault, &provisioning_request,
|
||||
&provisioning_server);
|
||||
|
||||
EXPECT_EQ(wvcdm::NO_ERROR, result);
|
||||
if (NO_ERROR != result) return;
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
|
||||
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
|
||||
std::vector<uint8_t> request = Base64SafeDecode(provisioning_request);
|
||||
provisioning_request.assign(request.begin(), request.end());
|
||||
}
|
||||
|
||||
// Verify that provisioning request has protocol version set correctly
|
||||
SignedProvisioningMessage signed_provisioning_msg;
|
||||
EXPECT_TRUE(signed_provisioning_msg.ParseFromString(provisioning_request));
|
||||
EXPECT_EQ(SignedProvisioningMessage_ProvisioningProtocolVersion_VERSION_1_1,
|
||||
signed_provisioning_msg.protocol_version());
|
||||
const bool supports_core_messages =
|
||||
signed_provisioning_msg.has_oemcrypto_core_message() &&
|
||||
!signed_provisioning_msg.oemcrypto_core_message().empty();
|
||||
|
||||
// Create provisioning responses with differing ProvisioningStatus values
|
||||
const std::vector<ProvisioningResponse_ProvisioningStatus>
|
||||
provisioning_status{
|
||||
ProvisioningResponse_ProvisioningStatus_NO_ERROR,
|
||||
ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_CREDENTIALS,
|
||||
ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_SERIES,
|
||||
// Add undefined ProvisioningStatus
|
||||
static_cast<ProvisioningResponse_ProvisioningStatus>(
|
||||
ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_SERIES +
|
||||
10)};
|
||||
|
||||
// Now form provisioning messages for each of the status values and verify
|
||||
// that they have been handled correctly
|
||||
for (ProvisioningResponse_ProvisioningStatus status : provisioning_status) {
|
||||
result = decryptor_->GetProvisioningRequest(
|
||||
kCertificateWidevine, cert_authority, kDefaultCdmIdentifier,
|
||||
kEmptyServiceCertificate, kLevelDefault, &provisioning_request,
|
||||
&provisioning_server);
|
||||
|
||||
EXPECT_EQ(wvcdm::NO_ERROR, result);
|
||||
if (NO_ERROR != result) return;
|
||||
EXPECT_EQ(provisioning_server, kDefaultProvisioningServerUrl);
|
||||
|
||||
ProvisioningResponse provisioning_response;
|
||||
provisioning_response.set_status(status);
|
||||
|
||||
SignedProvisioningMessage signed_response;
|
||||
signed_response.set_signature("fake signature");
|
||||
std::string message;
|
||||
provisioning_response.SerializeToString(&message);
|
||||
signed_response.set_message(message);
|
||||
if (supports_core_messages) {
|
||||
signed_response.set_oemcrypto_core_message("fake oemcrypto core msg");
|
||||
}
|
||||
|
||||
std::string response;
|
||||
signed_response.SerializeToString(&response);
|
||||
|
||||
if (!wvcdm::Properties::provisioning_messages_are_binary()) {
|
||||
std::vector<uint8_t> response_vec(response.begin(), response.end());
|
||||
response = "\"signedResponse\": \"";
|
||||
response.append(wvcdm::Base64SafeEncode(response_vec));
|
||||
response.append("\"");
|
||||
}
|
||||
|
||||
std::string cert, wrapped_key;
|
||||
result = decryptor_->HandleProvisioningResponse(
|
||||
kDefaultCdmIdentifier, response, kLevelDefault, &cert, &wrapped_key);
|
||||
|
||||
switch (status) {
|
||||
case ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_CREDENTIALS:
|
||||
case ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_SERIES:
|
||||
EXPECT_EQ(DEVICE_REVOKED, result) << "Provisioning status: " << status;
|
||||
break;
|
||||
default:
|
||||
// ProvisioningResponse_ProvisioningStatus_NO_ERROR will not return
|
||||
// NO_ERROR because signature, oemcrypto core message are fake
|
||||
EXPECT_NE(DEVICE_REVOKED, result) << "Provisioning status: " << status;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(WvCdmRequestLicenseTest, PropertySetTest) {
|
||||
TestWvCdmClientPropertySet property_set_L1;
|
||||
TestWvCdmClientPropertySet property_set_L3;
|
||||
|
||||
@@ -299,10 +299,11 @@ enum {
|
||||
kRestoreOfflineLicenseError3 = ERROR_DRM_VENDOR_MIN + 314,
|
||||
kNoSrmVersion = ERROR_DRM_VENDOR_MIN + 315,
|
||||
kCertProvisioningResponseError9 = ERROR_DRM_VENDOR_MIN + 316,
|
||||
kCertProvisioningResponseError10 = ERROR_DRM_VENDOR_MIN + 317,
|
||||
|
||||
// This should always follow the last error code.
|
||||
// The offset value should be updated each time a new error code is added.
|
||||
kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 316,
|
||||
kErrorWVDrmMaxErrorUsed = ERROR_DRM_VENDOR_MIN + 317,
|
||||
|
||||
// Used by crypto test mode
|
||||
kErrorTestMode = ERROR_DRM_VENDOR_MAX,
|
||||
|
||||
@@ -105,6 +105,8 @@ static android::status_t mapCdmResponseType(wvcdm::CdmResponseType res) {
|
||||
return kCertProvisioningResponseError8;
|
||||
case wvcdm::CERT_PROVISIONING_RESPONSE_ERROR_9:
|
||||
return kCertProvisioningResponseError9;
|
||||
case wvcdm::CERT_PROVISIONING_RESPONSE_ERROR_10:
|
||||
return kCertProvisioningResponseError10;
|
||||
case wvcdm::CLIENT_IDENTIFICATION_TOKEN_ERROR_1:
|
||||
return kClientIdentificationTokenError1;
|
||||
case wvcdm::CLIENT_ID_AES_ENCRYPT_ERROR:
|
||||
|
||||
@@ -359,6 +359,7 @@ static Status mapCdmResponseType_1_0(wvcdm::CdmResponseType res) {
|
||||
case wvcdm::PROVISIONING_NOT_ALLOWED_FOR_ATSC:
|
||||
case wvcdm::NO_SRM_VERSION:
|
||||
case wvcdm::CERT_PROVISIONING_RESPONSE_ERROR_9:
|
||||
case wvcdm::CERT_PROVISIONING_RESPONSE_ERROR_10:
|
||||
ALOGW("Returns UNKNOWN error for legacy status: %d", res);
|
||||
return Status::ERROR_DRM_UNKNOWN;
|
||||
|
||||
@@ -650,6 +651,11 @@ static S mapCdmResponseType(wvcdm::CdmResponseType res) {
|
||||
case wvcdm::LOAD_PROVISIONING_ERROR:
|
||||
err = ::drm::V1_4::Status::PROVISIONING_PARSE_ERROR;
|
||||
break;
|
||||
/* TODO (b/180579631): Uncomment when DRM Status type
|
||||
* PROVISIONING_REQUEST_REJECTED has been created
|
||||
case wvcdm::CERT_PROVISIONING_RESPONSE_ERROR_10:
|
||||
err = ::drm::V1_4::Status::PROVISIONING_REQUEST_REJECTED;
|
||||
*/
|
||||
case wvcdm::EMPTY_PROVISIONING_CERTIFICATE_1:
|
||||
case wvcdm::EMPTY_PROVISIONING_CERTIFICATE_2:
|
||||
err = ::drm::V1_4::Status::RETRYABLE_PROVISIONING_ERROR;
|
||||
|
||||
Reference in New Issue
Block a user