diff --git a/libwvdrmengine/cdm/core/include/certificate_provisioning.h b/libwvdrmengine/cdm/core/include/certificate_provisioning.h index 392188d6..6159fafe 100644 --- a/libwvdrmengine/cdm/core/include/certificate_provisioning.h +++ b/libwvdrmengine/cdm/core/include/certificate_provisioning.h @@ -68,8 +68,8 @@ class CertificateProvisioning { const std::string& origin, const std::string& spoid, video_widevine::ProvisioningRequest* request); - video_widevine::SignedProvisioningMessage::ProtocolVersion - GetProtocolVersion(); + video_widevine::SignedProvisioningMessage::ProvisioningType + GetProvisioningType(); std::unique_ptr crypto_session_; CdmCertificateType cert_type_; diff --git a/libwvdrmengine/cdm/core/include/wv_cdm_types.h b/libwvdrmengine/cdm/core/include/wv_cdm_types.h index acad7d0f..93c58415 100644 --- a/libwvdrmengine/cdm/core/include/wv_cdm_types.h +++ b/libwvdrmengine/cdm/core/include/wv_cdm_types.h @@ -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 diff --git a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp index 84741bca..6d177b6e 100644 --- a/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp +++ b/libwvdrmengine/cdm/core/src/certificate_provisioning.cpp @@ -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) { @@ -172,8 +173,8 @@ CdmResponseType CertificateProvisioning::SetSpoidParameter( * Return the provisioning protocol version - dictated by OEMCrypto * support for OEM certificates. */ -SignedProvisioningMessage::ProtocolVersion -CertificateProvisioning::GetProtocolVersion() { +SignedProvisioningMessage::ProvisioningType +CertificateProvisioning::GetProvisioningType() { if (crypto_session_->GetPreProvisionTokenType() == kClientTokenOemCert) return SignedProvisioningMessage::PROVISIONING_30; else @@ -291,13 +292,15 @@ CdmResponseType CertificateProvisioning::GetProvisioningRequest( 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()); + signed_provisioning_msg.set_provisioning_type(GetProvisioningType()); if (core_message.empty()) { // OEMCrypto does not support core messages. supports_core_messages_ = false; } 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()); diff --git a/libwvdrmengine/cdm/core/src/license_protocol.proto b/libwvdrmengine/cdm/core/src/license_protocol.proto index b5d0b3cc..cf789287 100644 --- a/libwvdrmengine/cdm/core/src/license_protocol.proto +++ b/libwvdrmengine/cdm/core/src/license_protocol.proto @@ -471,6 +471,21 @@ message SignedMessage { optional bytes oemcrypto_core_message = 9; } +// ---------------------------------------------------------------------------- +// hash_algorithm.proto +// ---------------------------------------------------------------------------- +// Description of section: +// Public protocol buffer definitions for Widevine Hash Algorithm protocol. + +enum HashAlgorithmProto { + // Unspecified hash algorithm: SHA_256 shall be used for ECC based algorithms + // and SHA_1 shall be used otherwise. + HASH_ALGORITHM_UNSPECIFIED = 0; + HASH_ALGORITHM_SHA_1 = 1; + HASH_ALGORITHM_SHA_256 = 2; + HASH_ALGORITHM_SHA_384 = 3; +} + // ---------------------------------------------------------------------------- // certificate_provisioning.proto // ---------------------------------------------------------------------------- @@ -556,6 +571,15 @@ message ProvisioningResponse { // Device CA token component of the keybox. optional bytes device_ca_token = 3; } + enum ProvisioningStatus { + // Indicates a valid provisioning response + NO_ERROR = 0; + // The device credentials have been revoked. Provisioning is not possible. + REVOKED_DEVICE_CREDENTIALS = 1; + // Devices in this series have been revoked. Provisioning is not possible. + REVOKED_DEVICE_SERIES = 2; + } + // AES-128 encrypted device private RSA key. PKCS#1 ASN.1 DER-encoded. // Required. For X.509 certificates, the private RSA key may also include // a prefix as specified by private_key_prefix in the X509CertificateMetadata @@ -575,6 +599,10 @@ message ProvisioningResponse { optional bytes wrapping_key = 5; // Only populated in OTA keybox provisioning response. optional OtaKeybox ota_keybox = 6; + // The provisioning service may return a ProvisioningStatus. Fields other + // than |status| may be empty and should be ignored if the |status| + // is present and not NO_ERROR + optional ProvisioningStatus status = 7; } // Protocol-specific context data used to hold the state of the server in @@ -607,7 +635,21 @@ message ProvisioningContextKeyData { // Serialized ProvisioningRequest or ProvisioningResponse signed with // The message authentication key. message SignedProvisioningMessage { - enum ProtocolVersion { + enum ProvisioningProtocolVersion { + VERSION_UNSPECIFIED = 0; + VERSION_1 = 1; + // Version 1.1 changed error handling. Some errors are returned as a field + // in a response message rather than being handled as errors via the API + // implementation. E.g. embedded in the ProvisioningResponse rather than + // returning a 400 error to the caller. + VERSION_1_1 = 2; + // Version 2 will implement a larger change of the protocol definition + // in protobufs. This will provide a cleaner separation between protocols. + VERSION_2 = 3; + } + + enum ProvisioningType { // This enum was renamed to avoid confusion + PROVISIONING_TYPE_UNSPECIFIED = 0; SERVICE_CERTIFICATE_REQUEST = 1; // Service certificate request. PROVISIONING_20 = 2; // Keybox factory-provisioned devices. PROVISIONING_30 = 3; // OEM certificate factory-provisioned devices. @@ -624,9 +666,9 @@ message SignedProvisioningMessage { // response. optional bytes signature = 2; // Version number of provisioning protocol. - optional ProtocolVersion protocol_version = 3 [default = PROVISIONING_20]; + optional ProvisioningType provisioning_type = 3 [default = PROVISIONING_20]; // Protocol-specific context / state information for multiple-exchange, - // stateful provisioing protocols. Optional. + // stateful provisioning protocols. Optional. optional SignedProvisioningContext signed_provisioning_context = 4; // Remote attestation data to authenticate that the ChromeOS client device // is operating in verified mode. Remote attestation challenge data is @@ -637,6 +679,10 @@ message SignedProvisioningMessage { // This field was introduced in OEMCrypto API v16. The core message format is // documented in the "Widevine Core Message Serialization". optional bytes oemcrypto_core_message = 6; + // Optional field that indicates the hash algorithm used in signature scheme. + optional HashAlgorithmProto hash_algorithm = 7; + // Indicates which version of the protocol is in use. + optional ProvisioningProtocolVersion protocol_version = 8; } // ---------------------------------------------------------------------------- diff --git a/libwvdrmengine/cdm/core/test/fake_provisioning_server.cpp b/libwvdrmengine/cdm/core/test/fake_provisioning_server.cpp index b429ff43..ab0008b3 100644 --- a/libwvdrmengine/cdm/core/test/fake_provisioning_server.cpp +++ b/libwvdrmengine/cdm/core/test/fake_provisioning_server.cpp @@ -263,10 +263,11 @@ bool FakeProvisioningServer::MakeResponse( ? "WIDEVINE_DRM" : "X509"); - video_widevine::SignedProvisioningMessage::ProtocolVersion version = - signed_request.protocol_version(); - LOGD("Request uses protocol version: %d", version); - if (version != video_widevine::SignedProvisioningMessage::PROVISIONING_20) { + const video_widevine::SignedProvisioningMessage::ProvisioningType + provisioning_type = signed_request.provisioning_type(); + LOGD("Request uses provisioning type: %d", provisioning_type); + if (provisioning_type != + video_widevine::SignedProvisioningMessage::PROVISIONING_20) { LOGE("Fake provisioning server only handles Keyboxes"); return false; } @@ -314,7 +315,7 @@ bool FakeProvisioningServer::MakeResponse( // Sign the response. video_widevine::SignedProvisioningMessage signed_response; - signed_response.set_protocol_version(signed_request.protocol_version()); + signed_response.set_provisioning_type(signed_request.provisioning_type()); std::string message; provisioning_response.SerializeToString(&message); signed_response.set_message(message); diff --git a/libwvdrmengine/cdm/core/test/test_printers.cpp b/libwvdrmengine/cdm/core/test/test_printers.cpp index 786b0134..586a828a 100644 --- a/libwvdrmengine/cdm/core/test/test_printers.cpp +++ b/libwvdrmengine/cdm/core/test/test_printers.cpp @@ -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; diff --git a/libwvdrmengine/cdm/test/request_license_test.cpp b/libwvdrmengine/cdm/test/request_license_test.cpp index 787ebf62..d539bad7 100644 --- a/libwvdrmengine/cdm/test/request_license_test.cpp +++ b/libwvdrmengine/cdm/test/request_license_test.cpp @@ -76,7 +76,15 @@ const std::vector 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 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 + provisioning_status{ + ProvisioningResponse_ProvisioningStatus_NO_ERROR, + ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_CREDENTIALS, + ProvisioningResponse_ProvisioningStatus_REVOKED_DEVICE_SERIES, + // Add undefined ProvisioningStatus + static_cast( + 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 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; diff --git a/libwvdrmengine/include/WVErrors.h b/libwvdrmengine/include/WVErrors.h index eda2f4ce..dc6b0dc7 100644 --- a/libwvdrmengine/include/WVErrors.h +++ b/libwvdrmengine/include/WVErrors.h @@ -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, diff --git a/libwvdrmengine/include/mapErrors-inl.h b/libwvdrmengine/include/mapErrors-inl.h index b0ee42ed..5a871d92 100644 --- a/libwvdrmengine/include/mapErrors-inl.h +++ b/libwvdrmengine/include/mapErrors-inl.h @@ -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: diff --git a/libwvdrmengine/include_hidl/mapErrors-inl.h b/libwvdrmengine/include_hidl/mapErrors-inl.h index 6952e126..3d904c48 100644 --- a/libwvdrmengine/include_hidl/mapErrors-inl.h +++ b/libwvdrmengine/include_hidl/mapErrors-inl.h @@ -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;