From be0bb27305cb63eb6b16fbc97846e55b86188028 Mon Sep 17 00:00:00 2001 From: Ramji Chandramouli Date: Thu, 16 May 2019 18:14:58 -0700 Subject: [PATCH] CAS Proxy SDK git checkin as per g3doc. Adds support for specifying service type when creating a service certificate. A recent change to the SDK allows for service certificates (DrmCertificate) to also specify a ServiceType indicating which type of service they are supposed to be used on. This CL adds certificate creation service support for this. ------------- Fix typo in cas proxy SDK. ------------- Migrate C++ proto_library rules to cc_proto_library. We update dependency edges of cc_* rules on proto_library() rules to use a cc_proto_library() intermediary in preparation for eliminating proto_library()'s cc support as per [] More information: [] Tested: TAP --sample for global presubmit queue [] ------------- Migrate cc proto_library to cc_proto_library. Also fixes build break introduced by [] ------------- Remove unnecessary #MOE directives ------------- [Proxy_SDK] Move generateSignature in WvPLSDKEnvironment to signature_util.cc file. ------------- [SDK]Add service_certificate type check in WVPL LSDK and PROXY SDK. ------------- [Proxy_SDK] Add new API to get remote_attestation_cert_serial_number for proxy SDK. ------------- [Proxy_SDK] Add getDrmDeviceId function ------------- [Proxy_SDK] add getrequesttype function for proxy sdk ------------- [SDK] Add videoFeature field to WvPLWidevinePsshData in WvPLWidevine.java and wvpl_type.h. Related to [] ------------- Allow specified devices to request licenses even if these devices are in TEST_ONLY state. This will also override the global setting of TEST_ONLY devices not being allowed to successfully receive licenses from Widevine License SDK. ------------- [Proxy_SDK] Add ParseDCSL function and test case. ------------- [Proxy_SDK] Return non-ok status for service_certificate_request when create proxy sdk session. Add test case in Java test. ------------- [Proxy_SDK] Add video_feature parsing in GetPsshData function. Also check video_feature when geneateModularDrmLicenseRequest. ------------- [SDK]Deprecated message_type() function, use request_type() instead. ------------- Use JDK instead of JRE The concept of a JRE is going away in JDK 11. The jre/ subdirectory in the JDK will no longer exist and the :jre targets will no longer make sense. Currently in JDK 8, the JDK is a superset of the JRE (it contains all of the files in the JRE), so this is a safe change. Tested: TAP found no affected targets [] ------------- Renaming WvPLSDKSession.request_type() API. Added LICENSE_TYPE_UNSPECIFIED enumeration to WvPLLicenseType. ------------- Additional VLOG messaging for licensing with TEST_ONLY devices. ------------- Remove forward declarations of absl names. The style guide bans this, and those names are not for external users to redeclare. External users should include the public headers instead. ------------- Change Kokoro to use latest bazel version ------------- Update the abseil build to the December 18 release. This fixes a problem where the MOE build is failing because there was no definition for node_hash_map. ------------- [CAS_Proxy]Add WvPLCASProxyEnvironmentJNI.cc and com_google_video_widevine_sdk_wvpl_WvPLCASProxyEnvironment.h file to implement JNI layer for WvPLCASProxyEnvironment. ------------- Apply changes to sdk to match device certificate status list updates. Cleans up some of the protos we're using for the new SignedDeviceInfo. Also, adjusts the sdk implementation to reflect the proto and service changes. ------------- [CAS_PROXY]Add WvPLCASProxyEnvironment.java, WvPLCASProxySession.java and WvPLCASProxyTest.java file. ------------- Add API to return the DRM service certificate by provider. ------------- [CAS_PROXY]Implement SetDrmServiceCertificate and SetDeviceCertificateStatusList JNI layer. ------------- Get DeviceInfo from request. ------------- CAS Proxy SDK updated to 1.1.5. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=248640225 --- WORKSPACE | 2 +- common/BUILD | 99 ++++---- common/device_status_list.cc | 201 ++++++++++++----- common/device_status_list.h | 96 ++++++-- common/device_status_list_test.cc | 121 ++++++++-- common/drm_root_certificate_test.cc | 9 +- common/drm_service_certificate.cc | 39 +++- common/drm_service_certificate.h | 11 +- common/drm_service_certificate_test.cc | 61 +++++ common/test_drm_certificates.cc | 140 +++++++++++- common/test_drm_certificates.h | 21 +- example/wvpl_cas_proxy_environment_example.cc | 2 +- license_server_sdk/internal/BUILD | 56 +++-- .../internal/generate_error_response.cc | 35 ++- .../internal/generate_error_response.h | 7 + .../internal/key_control_block.cc | 1 + license_server_sdk/internal/session_impl.cc | 124 ++++++++-- license_server_sdk/internal/session_impl.h | 47 +++- .../internal/session_impl_test.cc | 213 ++++++++++++++++-- .../external/common/wvpl/BUILD | 22 +- .../common/wvpl/wvpl_cas_proxy_environment.cc | 4 +- .../common/wvpl/wvpl_cas_proxy_session.h | 8 +- .../wvpl/wvpl_cas_proxy_session_test.cc | 7 + protos/public/BUILD | 151 +++++++------ protos/public/client_identification.proto | 6 +- protos/public/device_certificate_status.proto | 43 +--- protos/public/drm_certificate.proto | 12 +- protos/public/errors.proto | 7 + protos/public/license_protocol.proto | 25 +- protos/public/license_server_sdk.proto | 3 +- protos/public/license_services.proto | 45 +++- protos/public/media_cas_encryption.proto | 6 +- protos/public/media_cas_license.proto | 31 ++- protos/public/remote_attestation.proto | 4 +- protos/public/sdk_stats.proto | 10 +- protos/public/signed_device_info.proto | 66 ++++++ protos/public/verified_media_pipeline.proto | 4 +- protos/public/widevine_pssh.proto | 13 +- sdk/external/common/wvpl/BUILD | 46 ++-- .../common/wvpl/wvpl_sdk_environment.cc | 129 +++++------ .../common/wvpl/wvpl_sdk_environment.h | 20 +- sdk/external/common/wvpl/wvpl_sdk_session.cc | 56 ++--- sdk/external/common/wvpl/wvpl_sdk_session.h | 33 +-- sdk/external/common/wvpl/wvpl_types.h | 37 ++- 44 files changed, 1496 insertions(+), 577 deletions(-) create mode 100644 protos/public/signed_device_info.proto diff --git a/WORKSPACE b/WORKSPACE index 8a12ee2..aa00e6d 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -10,7 +10,7 @@ git_repository( git_repository( name = "abseil_repo", - commit = "475d64f2de7403a01b1b36c487328ed41d29c20c", #2018-04-10 + commit = "fcb104594b0bb4b8ac306cb2f55ecdad40974683", #2018-12-04 remote = "https://github.com/abseil/abseil-cpp.git", ) diff --git a/common/BUILD b/common/BUILD index a048b23..d0cd246 100644 --- a/common/BUILD +++ b/common/BUILD @@ -73,11 +73,11 @@ cc_library( "@abseil_repo//absl/synchronization", "@abseil_repo//absl/time", "//util/gtl:map_util", - "//protos/public:client_identification_proto", - "//protos/public:drm_certificate_proto", - "//protos/public:errors_proto", - "//protos/public:license_protocol_proto", - "//protos/public:signed_drm_certificate_proto", + "//protos/public:client_identification_cc_proto", + "//protos/public:drm_certificate_cc_proto", + "//protos/public:errors_cc_proto", + "//protos/public:license_protocol_cc_proto", + "//protos/public:signed_drm_certificate_cc_proto", ], ) @@ -99,9 +99,9 @@ cc_test( "@abseil_repo//absl/time", "//common:rsa_key", "//common:rsa_test_keys", - "//protos/public:drm_certificate_proto", - "//protos/public:errors_proto", - "//protos/public:signed_drm_certificate_proto", + "//protos/public:drm_certificate_cc_proto", + "//protos/public:errors_cc_proto", + "//protos/public:signed_drm_certificate_cc_proto", ], ) @@ -111,22 +111,19 @@ cc_library( hdrs = ["device_status_list.h"], deps = [ ":client_cert", - ":crypto_util", - ":drm_root_certificate", ":drm_service_certificate", ":error_space", - ":random_util", ":rsa_key", - ":signing_key_util", ":status", "//base", "@abseil_repo//absl/strings", "@abseil_repo//absl/synchronization", "//util/gtl:map_util", - "//protos/public:client_identification_proto", - "//protos/public:device_certificate_status_proto", - "//protos/public:errors_proto", - "//protos/public:provisioned_device_info_proto", + "//protos/public:client_identification_cc_proto", + "//protos/public:device_certificate_status_cc_proto", + "//protos/public:errors_cc_proto", + "//protos/public:provisioned_device_info_cc_proto", + "//protos/public:signed_device_info_cc_proto", ], ) @@ -137,15 +134,17 @@ cc_test( deps = [ ":client_cert", ":device_status_list", + ":rsa_key", + ":rsa_test_keys", + ":status", "//base", "//testing:gunit_main", "@abseil_repo//absl/strings", - "//common:rsa_key", - "//common:rsa_test_keys", - "//protos/public:client_identification_proto", - "//protos/public:errors_proto", - "//protos/public:provisioned_device_info_proto", - "//protos/public:signed_drm_certificate_proto", + "//protos/public:client_identification_cc_proto", + "//protos/public:device_certificate_status_cc_proto", + "//protos/public:errors_cc_proto", + "//protos/public:provisioned_device_info_cc_proto", + "//protos/public:signed_drm_certificate_cc_proto", ], ) @@ -164,9 +163,9 @@ cc_library( "@abseil_repo//absl/strings", "@abseil_repo//absl/synchronization", "//external:openssl", - "//protos/public:drm_certificate_proto", - "//protos/public:errors_proto", - "//protos/public:signed_drm_certificate_proto", + "//protos/public:drm_certificate_cc_proto", + "//protos/public:errors_cc_proto", + "//protos/public:signed_drm_certificate_cc_proto", ], ) @@ -183,9 +182,9 @@ cc_test( "//base", "//external:protobuf", "//testing:gunit_main", - "//protos/public:drm_certificate_proto", - "//protos/public:errors_proto", - "//protos/public:signed_drm_certificate_proto", + "//protos/public:drm_certificate_cc_proto", + "//protos/public:errors_cc_proto", + "//protos/public:signed_drm_certificate_cc_proto", ], ) @@ -200,8 +199,8 @@ cc_library( ":status", "//base", "@abseil_repo//absl/strings", - "//protos/public:client_identification_proto", - "//protos/public:errors_proto", + "//protos/public:client_identification_cc_proto", + "//protos/public:errors_cc_proto", ], ) @@ -431,7 +430,7 @@ cc_library( deps = [ ":crypto_util", "//base", - "//protos/public:license_protocol_proto", + "//protos/public:license_protocol_cc_proto", ], ) @@ -445,7 +444,7 @@ cc_test( "//testing:gunit", "//testing:gunit_main", "@abseil_repo//absl/strings", - "//protos/public:license_protocol_proto", + "//protos/public:license_protocol_cc_proto", ], ) @@ -509,7 +508,7 @@ cc_library( deps = [ "//util:error_space", "//util:proto_status", - "//protos/public:errors_proto", + "//protos/public:errors_cc_proto", ], ) @@ -527,9 +526,9 @@ cc_library( "//base", "@abseil_repo//absl/strings", "@abseil_repo//absl/synchronization", - "//protos/public:client_identification_proto", - "//protos/public:errors_proto", - "//protos/public:remote_attestation_proto", + "//protos/public:client_identification_cc_proto", + "//protos/public:errors_cc_proto", + "//protos/public:remote_attestation_cc_proto", ], ) @@ -549,10 +548,10 @@ cc_library( "@abseil_repo//absl/strings", "@abseil_repo//absl/synchronization", "//util/gtl:map_util", - "//protos/public:client_identification_proto", - "//protos/public:drm_certificate_proto", - "//protos/public:errors_proto", - "//protos/public:signed_drm_certificate_proto", + "//protos/public:client_identification_cc_proto", + "//protos/public:drm_certificate_cc_proto", + "//protos/public:errors_cc_proto", + "//protos/public:signed_drm_certificate_cc_proto", ], ) @@ -572,11 +571,11 @@ cc_test( "//external:protobuf", "//testing:gunit_main", "@abseil_repo//absl/strings", - "//protos/public:client_identification_proto", - "//protos/public:drm_certificate_proto", - "//protos/public:errors_proto", - "//protos/public:license_server_sdk_proto", - "//protos/public:signed_drm_certificate_proto", + "//protos/public:client_identification_cc_proto", + "//protos/public:drm_certificate_cc_proto", + "//protos/public:errors_cc_proto", + "//protos/public:license_server_sdk_cc_proto", + "//protos/public:signed_drm_certificate_cc_proto", ], ) @@ -589,7 +588,7 @@ cc_library( ":vmp_checker", "//base", "@abseil_repo//absl/strings", - "//protos/public:license_protocol_proto", + "//protos/public:license_protocol_cc_proto", ], ) @@ -646,8 +645,8 @@ cc_library( ":status", ":x509_cert", "//base", - "//protos/public:errors_proto", - "//protos/public:verified_media_pipeline_proto", + "//protos/public:errors_cc_proto", + "//protos/public:verified_media_pipeline_cc_proto", ], ) @@ -661,8 +660,8 @@ cc_test( "//base", "//testing:gunit_main", "@abseil_repo//absl/strings", - "//protos/public:errors_proto", - "//protos/public:verified_media_pipeline_proto", + "//protos/public:errors_cc_proto", + "//protos/public:verified_media_pipeline_cc_proto", ], ) diff --git a/common/device_status_list.cc b/common/device_status_list.cc index da97d3f..6e1f6e1 100644 --- a/common/device_status_list.cc +++ b/common/device_status_list.cc @@ -11,6 +11,7 @@ #include "common/device_status_list.h" #include + #include #include "glog/logging.h" @@ -24,8 +25,13 @@ #include "common/drm_service_certificate.h" #include "common/error_space.h" #include "common/rsa_key.h" +#include "common/status.h" #include "protos/public/client_identification.pb.h" #include "protos/public/errors.pb.h" +#include "protos/public/signed_device_info.pb.h" + +using ::widevine::DeviceCertificateStatusListRequest; +using ::widevine::SignedDeviceInfoRequest; namespace widevine { @@ -54,19 +60,13 @@ DeviceStatusList::~DeviceStatusList() {} Status DeviceStatusList::UpdateStatusList( const std::string& root_certificate_public_key, - const std::string& serialized_certificate_status_list, - uint32_t expiration_period_seconds) { - SignedDeviceCertificateStatusList signed_certificate_status_list; - if (!signed_certificate_status_list.ParseFromString( - serialized_certificate_status_list)) { - return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, - "signed-certificate-status-list-parse-error"); - } - if (!signed_certificate_status_list.has_certificate_status_list()) { + const std::string& serialized_device_certificate_status_list, + const std::string& signature, uint32_t expiration_period_seconds) { + if (serialized_device_certificate_status_list.empty()) { return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, "missing-status-list"); } - if (!signed_certificate_status_list.has_signature()) { + if (signature.empty()) { return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, "missing-status-list-signature"); } @@ -76,17 +76,16 @@ Status DeviceStatusList::UpdateStatusList( return Status(error_space, INVALID_DRM_CERTIFICATE, "invalid-root-public-key"); } - if (!root_key->VerifySignature( - signed_certificate_status_list.certificate_status_list(), - signed_certificate_status_list.signature())) { + if (!root_key->VerifySignature(serialized_device_certificate_status_list, + signature)) { return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, "invalid-status-list-signature"); } DeviceCertificateStatusList certificate_status_list; if (!certificate_status_list.ParseFromString( - signed_certificate_status_list.certificate_status_list())) { + serialized_device_certificate_status_list)) { return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, - "certificate-status-list-parse-error"); + "signed-certificate-status-list-parse-error"); } if (expiration_period_seconds && (GetCurrentTime() > (certificate_status_list.creation_time_seconds() + @@ -162,8 +161,16 @@ Status DeviceStatusList::GetCertStatus(const ClientCert& client_cert, if ((device_cert_status->status() == DeviceCertificateStatus::STATUS_TEST_ONLY) && !allow_test_only_devices_) { - return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED, - "test-only-drm-certificate-not-allowed"); + if (IsTestOnlyDeviceAllowed(client_cert.system_id(), + device_info->manufacturer())) { + LOG(WARNING) << "Allowing TEST_ONLY device: " + << device_info->ShortDebugString(); + } else { + VLOG(2) << "TEST ONLY device with systemId: " << client_cert.system_id() + << " not allowed"; + return Status(error_space, DEVELOPMENT_CERTIFICATE_NOT_ALLOWED, + "test-only-drm-certificate-not-allowed"); + } } if (!client_cert.signed_by_provisioner() && (client_cert.signer_serial_number() != @@ -241,31 +248,83 @@ void DeviceStatusList::AllowRevokedDevices(const std::string& system_id_list) { std::sort(allowed_revoked_devices_.begin(), allowed_revoked_devices_.end()); } +void DeviceStatusList::AllowTestOnlyDevices(const std::string& device_list) { + absl::WriterMutexLock lock(&allowed_test_only_devices_mutex_); + // 'device_list' is expected to be of the format ,..., and + // each 'device' will contain a 'system_id' and 'manufacturer' OR will contain + // only a 'system_id'. + for (absl::string_view device : absl::StrSplit(device_list, ',')) { + // 'device' is expected to be of the format : OR + // of the format : + const std::pair device_split = + absl::StrSplit(device, ':'); + if (device_split.second.empty()) { + allowed_test_only_devices_[std::stoi(std::string(device_split.first))] = "*"; + } else { + allowed_test_only_devices_[std::stoi(std::string(device_split.first))] = + std::string(device_split.second); + } + VLOG(2) + << "Whitelisting TEST_ONLY device: systemId = " + << std::stoi(std::string(device_split.first)) + << allowed_test_only_devices_[std::stoi(std::string(device_split.first))]; + } +} + bool DeviceStatusList::IsRevokedSystemIdAllowed(uint32_t system_id) { auto it = std::binary_search(allowed_revoked_devices_.begin(), allowed_revoked_devices_.end(), system_id); return it; } -Status DeviceStatusList::ExtractFromProvisioningServiceResponse( - const std::string& certificate_provisioning_service_response, - std::string* signed_certificate_status_list, std::string* certificate_status_list) { - Status status = OkStatus(); - size_t signed_list_start = - certificate_provisioning_service_response.find(kSignedList); - if (signed_list_start != std::string::npos) { - size_t signed_list_end = certificate_provisioning_service_response.find( - kSignedListTerminator, signed_list_start); - if (signed_list_end == std::string::npos) { +bool DeviceStatusList::IsTestOnlyDeviceAllowed(uint32_t system_id, + std::string manufacturer) { + absl::ReaderMutexLock lock(&allowed_test_only_devices_mutex_); + std::string* allowed_manufacturer = + gtl::FindOrNull(allowed_test_only_devices_, system_id); + if (allowed_manufacturer == nullptr) { + return false; + } + if (*allowed_manufacturer == "*" || *allowed_manufacturer == manufacturer) { + return true; + } + return false; +} + +Status DeviceStatusList::ExtractFromServiceResponse( + const std::string& service_response, std::string* serialized_certificate_status_list, + std::string* signature) { + // We support two types of payload parsing. First, is the legacy call which + // is a serialized SignedCertificateStatusList proto. The second is the + // newer SignedDeviceInfo. + // TODO(user): Add support for the new response type. + return ExtractLegacyDeviceList(service_response, + serialized_certificate_status_list, signature); +} + +Status DeviceStatusList::ExtractLegacyDeviceList( + const std::string& raw_certificate_provisioning_service_response, + std::string* serialized_certificate_status_list, std::string* signature) { + // Example legacy format. + // "signedList":"" + // where the b64 encoded data is a DeviceCertificateStatusListResponse. + size_t b64_list_response_start = + raw_certificate_provisioning_service_response.find(kSignedList); + std::string serialized_certificate_status_list_response; + if (b64_list_response_start != std::string::npos) { + size_t b64_list_response_end = + raw_certificate_provisioning_service_response.find( + kSignedListTerminator, b64_list_response_start); + if (b64_list_response_end == std::string::npos) { return Status( error_space, error::INVALID_ARGUMENT, "Unable to parse the certificate_provisioning_service_response. " "SignedList not terminated."); } - std::string signed_list( - certificate_provisioning_service_response.begin() + signed_list_start + - kSignedListLen, - certificate_provisioning_service_response.begin() + signed_list_end); + std::string signed_list(raw_certificate_provisioning_service_response.begin() + + b64_list_response_start + kSignedListLen, + raw_certificate_provisioning_service_response.begin() + + b64_list_response_end); // Strip off quotes. signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\"'), @@ -281,9 +340,10 @@ Status DeviceStatusList::ExtractFromProvisioningServiceResponse( // Strip off carriage return (the control-M character) signed_list.erase(std::remove(signed_list.begin(), signed_list.end(), '\r'), signed_list.end()); - if (!absl::WebSafeBase64Unescape(signed_list, - signed_certificate_status_list)) { - if (!absl::Base64Unescape(signed_list, signed_certificate_status_list)) { + if (!absl::WebSafeBase64Unescape( + signed_list, &serialized_certificate_status_list_response)) { + if (!absl::Base64Unescape(signed_list, + &serialized_certificate_status_list_response)) { return Status(error_space, error::INVALID_ARGUMENT, "Base64 decode of signedlist failed."); } @@ -291,36 +351,32 @@ Status DeviceStatusList::ExtractFromProvisioningServiceResponse( } else { // certificate_provisioning_service_response is the signed list and not a // JSON message. - if (!absl::WebSafeBase64Unescape(certificate_provisioning_service_response, - signed_certificate_status_list)) { - if (!absl::Base64Unescape(certificate_provisioning_service_response, - signed_certificate_status_list)) { + if (!absl::WebSafeBase64Unescape( + raw_certificate_provisioning_service_response, + &serialized_certificate_status_list_response)) { + if (!absl::Base64Unescape(raw_certificate_provisioning_service_response, + &serialized_certificate_status_list_response)) { return Status(error_space, error::INVALID_ARGUMENT, "Base64 decode of certList failed."); } } } - SignedDeviceCertificateStatusList signed_status_list; - if (!signed_status_list.ParseFromString(*signed_certificate_status_list)) { - return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, - "signed-certificate-status-list-parse-error"); - } - if (!signed_status_list.has_certificate_status_list()) { - return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, - "missing-status-list"); - } - DeviceCertificateStatusList device_certificate_status_list; - if (!device_certificate_status_list.ParseFromString( - signed_status_list.certificate_status_list())) { - return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, - "certificate-status-list-parse-error"); - } - *certificate_status_list = signed_status_list.certificate_status_list(); - return OkStatus(); + + return ParseSignedDeviceCertificateStatusList( + serialized_certificate_status_list_response, + serialized_certificate_status_list, signature); +} + +Status DeviceStatusList::ExtractSignedDeviceInfo( + const std::string& signed_device_info_response, + std::string* serialized_certificate_status_list, std::string* signature) { + // TODO(user): Implement this. + return Status(error_space, error::UNIMPLEMENTED, + "SignedDeviceInfo not yet supported."); } Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest( - const std::string& version, + const std::string& version, const std::string& serialized_service_certificate, std::string* signed_device_certificate_status_list_request) { if (version.empty()) { return Status(error_space, error::INVALID_ARGUMENT, "SDK version is empty"); @@ -334,9 +390,10 @@ Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest( DeviceCertificateStatusListRequest request; request.set_sdk_version(version); request.set_sdk_time_seconds(DeviceStatusList::Instance()->GetCurrentTime()); + request.set_service_certificate(serialized_service_certificate); std::string device_certificate_status_list_request; request.SerializeToString(&device_certificate_status_list_request); - SignedDeviceCertificateStatusListRequest signed_request; + SignedDeviceInfoRequest signed_request; signed_request.set_device_certificate_status_list_request( device_certificate_status_list_request); const DrmServiceCertificate* sc = @@ -359,4 +416,34 @@ Status DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest( signed_device_certificate_status_list_request); return OkStatus(); } +Status DeviceStatusList::ParseSignedDeviceCertificateStatusList( + const std::string& serialized_signed_device_certificate_status_list, + std::string* serialized_device_certificate_status_list, std::string* signature) { + // Parse the serialized signed device info to extract the certificate + // status list. + SignedDeviceCertificateStatusList signed_device_list; + if (!signed_device_list.ParseFromString( + serialized_signed_device_certificate_status_list)) { + return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "signed-certificate-status-list-parse-error"); + } + if (signed_device_list.certificate_status_list().empty()) { + return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "missing-status-list"); + } + // Parse the DCSL to verify that it's a properly serialized protobuf. + DeviceCertificateStatusList certificate_status_list; + if (!certificate_status_list.ParseFromString( + signed_device_list.certificate_status_list())) { + return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "certificate-status-list-parse-error"); + } + // TODO(user): See if we can refactor this. It seems weird to deserialize + // the proto but then pass around the serialized proto. + *serialized_device_certificate_status_list = + signed_device_list.certificate_status_list(); + *signature = signed_device_list.signature(); + return OkStatus(); +} + } // namespace widevine diff --git a/common/device_status_list.h b/common/device_status_list.h index 13782c7..a6b6b7a 100644 --- a/common/device_status_list.h +++ b/common/device_status_list.h @@ -19,6 +19,7 @@ #include "common/status.h" #include "protos/public/device_certificate_status.pb.h" #include "protos/public/provisioned_device_info.pb.h" +#include "protos/public/signed_device_info.pb.h" namespace widevine { @@ -37,12 +38,14 @@ class DeviceStatusList { DeviceStatusList(); virtual ~DeviceStatusList(); - // Takes |serialized_certificate_status_list| and copies to an internal map of - // device certifcate status list. The internal map is used to verify - // a device was not revoked. Returns true is the list was successfully parsed. - Status UpdateStatusList(const std::string& root_certificate_public_key, - const std::string& serialized_certificate_status_list, - uint32_t expiration_period_seconds); + // Takes |serialized_device_certificate_status_list| and copies to an + // internal map of device certificate status list. The internal map is used + // to verify a device was not revoked. Returns true is the list was + // successfully parsed. + Status UpdateStatusList( + const std::string& root_certificate_public_key, + const std::string& serialized_device_certificate_status_list, + const std::string& signature, uint32_t expiration_period_seconds); void set_allow_unknown_devices(bool flag) { allow_unknown_devices_ = flag; } bool allow_unknown_devices() const { return allow_unknown_devices_; } void set_allow_test_only_devices(bool allow) { @@ -76,18 +79,28 @@ class DeviceStatusList { // a comma separated list of systems Ids to allow even if revoked. virtual void AllowRevokedDevices(const std::string& system_id_list); + // Enable delivery of licenses to TEST_ONLY client devices. |device_list| is + // a comma separated list of devices to allow even if the device state is + // TEST_ONLY. Each device is specified by a colon separated system_id and + // manufacturer. If the manufacturer is not specified, all manufacturers for + // that system_id are allowed. + virtual void AllowTestOnlyDevices(const std::string& device_list); + /** - * Parses signed device certificate status list and certificate status list - * from certificateProvisoningServer response. + * Parses the serialized certificate status list and the signature from the + * service_response. The service_response is the JSON payload that comes + * in the response to a certificate status list request. Both the legacy + * format and the newer SignedDeviceInfo format are supported. * - * @param certificate_provisioning_service_response - * @param signed_certificate_status_list - * @param certificate_status_list + * @param service_response + * @param serialized_certificate_status_list + * @param signature * @return WvPLStatus - Status::OK if success, else error. */ - static Status ExtractFromProvisioningServiceResponse( - const std::string& certificate_provisioning_service_response, - std::string* signed_certificate_status_list, std::string* certificate_status_list); + static Status ExtractFromServiceResponse( + const std::string& service_response, + std::string* serialized_certificate_status_list, std::string* signature); + /** * Constructs signed device certificate status list request string. * @@ -96,13 +109,60 @@ class DeviceStatusList { * @return Status - Status::OK if success, else error. */ static Status GenerateSignedDeviceCertificateStatusListRequest( - const std::string& version, + const std::string& version, const std::string& serialized_service_certificate, std::string* signed_device_certificate_status_list_request); private: + friend class DeviceStatusListTest; + + /** + * Parses the serialized legacy device certificate status list and signature. + * The certificate_provisioning_service_response is the JSON payload that + * comes in the response to a certificate status list request. + * + * @param legacy_certificate_provisioning_service_response + * @param serialized_certificate_status_list + * @param signature + * @return WvPLStatus - Status::OK if success, else error. + */ + static Status ExtractLegacyDeviceList( + const std::string& raw_certificate_provisioning_service_response, + std::string* serialized_certificate_status_list, std::string* signature); + + /** + * Parses the serialized signed device info response. + * The signed_device_info_response is the JSON payload that comes in the + * response to a signed device info request. + * + * @param legacy_certificate_provisioning_service_response + * @param serialized_certificate_status_list + * @param signature + * @return WvPLStatus - Status::OK if success, else error. + */ + static Status ExtractSignedDeviceInfo( + const std::string& signed_device_info_response, + std::string* serialized_certificate_status_list, std::string* signature); + + /** + * Returns a |serialized_device_certificate_status_list| in its output + * parameter by parsing |serialized_signed_device_certificate_status_list| + * returned from Widevine Certificate Provisioning Server. + * + * @param serialized_signed_device_certificate_status_list + * @param serialized_device_certificate_status_list + * + * @return Status - Status::OK if success, else error. + */ + static Status ParseSignedDeviceCertificateStatusList( + const std::string& serialized_signed_device_certificate_status_list, + std::string* serialized_device_certificate_status_list, std::string* signature); + // Returns true if the system ID is allowed to be revoked. // Caller owns |system_id|. They must not be null. bool IsRevokedSystemIdAllowed(uint32_t system_id); + // Returns true if the device, which is identified by system_id and + // manufacturer, is present in |allowed_test_only_devices_|. + bool IsTestOnlyDeviceAllowed(uint32_t system_id, std::string manufacturer); absl::Mutex status_map_lock_; // Key is the system id for the device. @@ -114,6 +174,12 @@ class DeviceStatusList { // Contains the list of system_id values that are allowed to succeed even if // revoked. std::vector allowed_revoked_devices_; + absl::Mutex allowed_test_only_devices_mutex_; + // Contains a map of TEST_ONLY devices that are allowed, even if TEST_ONLY + // devices are prohibited. Contains mappings of 'system_id' to + // 'manufacturer'. If 'manufacturer' value is "*", any 'manufacturer' for + // that 'system_id' is allowed. + std::map allowed_test_only_devices_; DISALLOW_COPY_AND_ASSIGN(DeviceStatusList); }; diff --git a/common/device_status_list_test.cc b/common/device_status_list_test.cc index 73d4683..ebc201b 100644 --- a/common/device_status_list_test.cc +++ b/common/device_status_list_test.cc @@ -9,6 +9,7 @@ #include "common/device_status_list.h" #include + #include #include #include @@ -16,15 +17,26 @@ #include "glog/logging.h" #include "testing/gmock.h" #include "testing/gunit.h" +#include "absl/strings/escaping.h" #include "absl/strings/str_cat.h" #include "common/client_cert.h" #include "common/rsa_key.h" #include "common/rsa_test_keys.h" +#include "common/status.h" #include "protos/public/client_identification.pb.h" +#include "protos/public/device_certificate_status.pb.h" #include "protos/public/errors.pb.h" #include "protos/public/provisioned_device_info.pb.h" #include "protos/public/signed_drm_certificate.pb.h" +namespace { +const char kTestSystemId_1[] = "4121"; +const char kTestManufacturer_1[] = "LG"; +const char kTestSystemId_2[] = "8242"; +const char kTestManufacturer_2[] = "Samsung"; +const char kTestSystemId_3[] = "6556"; +} // namespace + namespace widevine { using ::testing::_; @@ -103,26 +115,50 @@ class DeviceStatusListTest : public ::testing::Test { cert_status_list_.set_creation_time_seconds(kStatusListCreationTime); cert_status_list_.SerializeToString( - signed_cert_status_list_.mutable_certificate_status_list()); + legacy_signed_device_status_list_.mutable_certificate_status_list()); std::unique_ptr root_key( RsaPrivateKey::Create(test_keys_.private_test_key_1_3072_bits())); ASSERT_TRUE(root_key); ASSERT_TRUE(root_key->GenerateSignature( - signed_cert_status_list_.certificate_status_list(), - signed_cert_status_list_.mutable_signature())); - ASSERT_TRUE( - signed_cert_status_list_.SerializeToString(&serialized_status_list_)); + legacy_signed_device_status_list_.certificate_status_list(), + legacy_signed_device_status_list_.mutable_signature())); + ASSERT_TRUE(legacy_signed_device_status_list_.SerializeToString( + &serialized_status_list_)); - ASSERT_EQ(OkStatus(), device_status_list_.UpdateStatusList( - test_keys_.public_test_key_1_3072_bits(), - serialized_status_list_, kDefaultExpirePeriod)); + ASSERT_EQ(OkStatus(), + device_status_list_.UpdateStatusList( + test_keys_.public_test_key_1_3072_bits(), + legacy_signed_device_status_list_.certificate_status_list(), + legacy_signed_device_status_list_.signature(), + kDefaultExpirePeriod)); + std::string serialized_signed_device_info; + ASSERT_TRUE(legacy_signed_device_status_list_.SerializeToString( + &serialized_signed_device_info)); + std::string dcsl; + std::string signature; + ASSERT_EQ(OkStatus(), + DeviceStatusList::ParseSignedDeviceCertificateStatusList( + serialized_signed_device_info, &dcsl, &signature)); + ASSERT_EQ(dcsl, + legacy_signed_device_status_list_.certificate_status_list()); + ASSERT_EQ(dcsl, + legacy_signed_device_status_list_.certificate_status_list()); + } + + int VerifyAllowedTestOnlyDevicesAdded() { + return device_status_list_.allowed_test_only_devices_.size(); + } + + bool VerifyIsTestOnlyDeviceAllowed(uint32_t system_id, std::string manufacturer) { + return device_status_list_.IsTestOnlyDeviceAllowed(system_id, manufacturer); } DeviceStatusList device_status_list_; RsaTestKeys test_keys_; DeviceCertificateStatusList cert_status_list_; - SignedDeviceCertificateStatusList signed_cert_status_list_; + SignedDeviceInfo tmp_signed_device_info_; + SignedDeviceCertificateStatusList legacy_signed_device_status_list_; std::string serialized_status_list_; }; @@ -288,17 +324,19 @@ TEST_F(DeviceStatusListTest, SignerSerialNumberMismatch) { TEST_F(DeviceStatusListTest, InvalidStatusList) { EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST, device_status_list_ - .UpdateStatusList(test_keys_.public_test_key_2_2048_bits(), - serialized_status_list_, 0) + .UpdateStatusList( + test_keys_.public_test_key_2_2048_bits(), + legacy_signed_device_status_list_.certificate_status_list(), + legacy_signed_device_status_list_.signature(), 0) .error_code()); - ++(*signed_cert_status_list_.mutable_certificate_status_list())[4]; - ASSERT_TRUE( - signed_cert_status_list_.SerializeToString(&serialized_status_list_)); + ++(*legacy_signed_device_status_list_.mutable_certificate_status_list())[4]; EXPECT_EQ(INVALID_CERTIFICATE_STATUS_LIST, device_status_list_ - .UpdateStatusList(test_keys_.public_test_key_1_3072_bits(), - serialized_status_list_, 0) + .UpdateStatusList( + test_keys_.public_test_key_1_3072_bits(), + legacy_signed_device_status_list_.certificate_status_list(), + legacy_signed_device_status_list_.signature(), 0) .error_code()); } @@ -313,13 +351,17 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnSet) { .Times(2) .WillOnce(Return(kStatusListCreationTime + 100)) .WillOnce(Return(kStatusListCreationTime + 101)); - EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList( - test_keys_.public_test_key_1_3072_bits(), - serialized_status_list_, 100)); + EXPECT_EQ(OkStatus(), + mock_device_status_list.UpdateStatusList( + test_keys_.public_test_key_1_3072_bits(), + legacy_signed_device_status_list_.certificate_status_list(), + legacy_signed_device_status_list_.signature(), 100)); EXPECT_EQ(EXPIRED_CERTIFICATE_STATUS_LIST, mock_device_status_list - .UpdateStatusList(test_keys_.public_test_key_1_3072_bits(), - serialized_status_list_, 100) + .UpdateStatusList( + test_keys_.public_test_key_1_3072_bits(), + legacy_signed_device_status_list_.certificate_status_list(), + legacy_signed_device_status_list_.signature(), 100) .error_code()); } @@ -330,9 +372,11 @@ TEST_F(DeviceStatusListTest, ExpiredStatusListOnCertCheck) { .WillOnce(Return(kStatusListCreationTime + 100)) .WillOnce(Return(kStatusListCreationTime + 100)) .WillOnce(Return(kStatusListCreationTime + 101)); - EXPECT_EQ(OkStatus(), mock_device_status_list.UpdateStatusList( - test_keys_.public_test_key_1_3072_bits(), - serialized_status_list_, 100)); + EXPECT_EQ(OkStatus(), + mock_device_status_list.UpdateStatusList( + test_keys_.public_test_key_1_3072_bits(), + legacy_signed_device_status_list_.certificate_status_list(), + legacy_signed_device_status_list_.signature(), 100)); ProvisionedDeviceInfo device_info; MockCertificateClientCert valid_client_cert; @@ -373,4 +417,33 @@ TEST_F(DeviceStatusListTest, IsSystemIdActive) { device_status_list_.IsSystemIdActive(kRevokedAllowedDeviceCertSystemId)); } +TEST_F(DeviceStatusListTest, IsTestOnlyDeviceAllowed) { + std::string device_list = + std::string(kTestSystemId_1) + ":" + std::string(kTestManufacturer_1); + device_list += + "," + std::string(kTestSystemId_2) + ":" + std::string(kTestManufacturer_2); + device_list += "," + std::string(kTestSystemId_3) + ":"; + device_status_list_.AllowTestOnlyDevices(device_list); + EXPECT_EQ(3, VerifyAllowedTestOnlyDevicesAdded()); + // Verify that device with system_id = kTestSystemId_1 and + // manufacturer = kTestManufacturer_1 is allowed. + EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_1), + kTestManufacturer_1)); + // Verify that device with system_id = kTestSystemId_2 and + // manufacturer = kTestManufacturer_2 is allowed. + EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_2), + kTestManufacturer_2)); + // Verify that device with system_id = kTestSystemId_3 and + // any manufacturer is allowed. This checks that any manufacturer is + // allowed for this system_id. + EXPECT_TRUE( + VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_3), "Cisco")); + EXPECT_TRUE(VerifyIsTestOnlyDeviceAllowed(std::stoi(kTestSystemId_3), + "ScientificAtlanta")); + uint32_t unknown_system_id = 7890; + // Verify that device with system_id = unknown_system_id and + // manufacturer = "Cisco" is not allowed. + EXPECT_FALSE(VerifyIsTestOnlyDeviceAllowed(unknown_system_id, "Cisco")); +} + } // namespace widevine diff --git a/common/drm_root_certificate_test.cc b/common/drm_root_certificate_test.cc index 24c3509..f56d522 100644 --- a/common/drm_root_certificate_test.cc +++ b/common/drm_root_certificate_test.cc @@ -80,10 +80,11 @@ TEST(DrmRootCertificateTestCertificatesTest, Success) { ->VerifyCertificate(test_certs.test_user_device_certificate(), nullptr, nullptr) .ok()); - EXPECT_TRUE(root_cert - ->VerifyCertificate(test_certs.test_service_certificate(), - nullptr, nullptr) - .ok()); + EXPECT_TRUE( + root_cert + ->VerifyCertificate(test_certs.test_service_certificate_no_type(), + nullptr, nullptr) + .ok()); } class DrmRootCertificateTest : public testing::Test { diff --git a/common/drm_service_certificate.cc b/common/drm_service_certificate.cc index 9229859..92754db 100644 --- a/common/drm_service_certificate.cc +++ b/common/drm_service_certificate.cc @@ -44,8 +44,9 @@ class DrmServiceCertificateMap { void AddCert(std::unique_ptr new_cert); void ClearDefaultDrmServiceCertificate(); const DrmServiceCertificate* GetDefaultCert(); - const DrmServiceCertificate* GetCert(const std::string& serial_number); - + const DrmServiceCertificate* GetCertBySerialNumber( + const std::string& serial_number); + const DrmServiceCertificate* GetCertByProvider(const std::string& provider_id); static DrmServiceCertificateMap* GetInstance(); private: @@ -94,12 +95,30 @@ const DrmServiceCertificate* DrmServiceCertificateMap::GetDefaultCert() { return default_cert_; } -const DrmServiceCertificate* DrmServiceCertificateMap::GetCert( +const DrmServiceCertificate* DrmServiceCertificateMap::GetCertBySerialNumber( const std::string& serial_number) { absl::ReaderMutexLock lock(&mutex_); return map_[serial_number].get(); } +const DrmServiceCertificate* DrmServiceCertificateMap::GetCertByProvider( + const std::string& provider_id) { + absl::ReaderMutexLock lock(&mutex_); + DrmServiceCertificate* provider_drm_cert = nullptr; + for (const auto& drm_cert : map_) { + if (drm_cert.second->provider_id() == provider_id) { + if (provider_drm_cert == nullptr) { + provider_drm_cert = drm_cert.second.get(); + } else if (drm_cert.second->creation_time_seconds() > + provider_drm_cert->creation_time_seconds()) { + // Use the newest cert. + provider_drm_cert = drm_cert.second.get(); + } + } + } + return provider_drm_cert; +} + DrmServiceCertificateMap* DrmServiceCertificateMap::GetInstance() { static auto* const kInstance = new DrmServiceCertificateMap(); return kInstance; @@ -166,9 +185,17 @@ DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie() { return default_cert; } -const DrmServiceCertificate* DrmServiceCertificate::GetDrmServiceCertificate( +const DrmServiceCertificate* +DrmServiceCertificate::GetDrmServiceCertificateBySerialNumber( const std::string& serial_number) { - return DrmServiceCertificateMap::GetInstance()->GetCert(serial_number); + return DrmServiceCertificateMap::GetInstance()->GetCertBySerialNumber( + serial_number); +} + +const DrmServiceCertificate* +DrmServiceCertificate::GetDrmServiceCertificateByProvider( + const std::string& provider) { + return DrmServiceCertificateMap::GetInstance()->GetCertByProvider(provider); } Status DrmServiceCertificate::SetDefaultDrmServiceCertificate( @@ -207,7 +234,7 @@ Status DrmServiceCertificate::DecryptClientIdentification( } std::string privacy_key; std::string provider_id; - const DrmServiceCertificate* cert = GetDrmServiceCertificate( + const DrmServiceCertificate* cert = GetDrmServiceCertificateBySerialNumber( encrypted_client_id.service_certificate_serial_number()); if (!cert) { return Status( diff --git a/common/drm_service_certificate.h b/common/drm_service_certificate.h index fec6055..79e54a3 100644 --- a/common/drm_service_certificate.h +++ b/common/drm_service_certificate.h @@ -69,11 +69,17 @@ class DrmServiceCertificate { // Certificate is set. This method is thread-safe. static const DrmServiceCertificate* GetDefaultDrmServiceCertificateOrDie(); - // Returns the service certificate with the given serial number if found, or + // Returns the service certificate with the given |cert_serial_number|, or // null otherwise. - static const DrmServiceCertificate* GetDrmServiceCertificate( + static const DrmServiceCertificate* GetDrmServiceCertificateBySerialNumber( const std::string& cert_serial_number); + // Returns the service certificate with the given |provider_id|, or + // null otherwise. If multple certificates exist for the provider, the + // newest certificate is returned. + static const DrmServiceCertificate* GetDrmServiceCertificateByProvider( + const std::string& provider_id); + // Decrypts the EncryptedClientIdentification message passed in // |encrypted_client_id| into |client_id| using the private key for the // certificate which was used to encrypt the information. |client_id| must @@ -86,6 +92,7 @@ class DrmServiceCertificate { const std::string& certificate() const { return certificate_; } const std::string& provider_id() const { return provider_id_; } const std::string& serial_number() const { return serial_number_; } + uint32_t creation_time_seconds() const { return creation_time_seconds_; } const RsaPrivateKey* const private_key() const { return private_key_.get(); } const RsaPublicKey* const public_key() const { return public_key_.get(); } diff --git a/common/drm_service_certificate_test.cc b/common/drm_service_certificate_test.cc index b5eef32..d0dd60c 100644 --- a/common/drm_service_certificate_test.cc +++ b/common/drm_service_certificate_test.cc @@ -160,6 +160,9 @@ TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificates) { uint32_t creation_time_seconds1(1234); std::string serial_number2("serial_number2"); uint32_t creation_time_seconds2(1234); + std::string serial_number3("serial_number3"); + std::string provider_id3("service3.com"); + uint32_t creation_time_seconds3(1235); std::string bogus_serial_number("bogus-serial-number2"); EXPECT_EQ(nullptr, DrmServiceCertificate::GetDefaultDrmServiceCertificate()); @@ -172,6 +175,9 @@ TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificates) { EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id1, creation_time_seconds2)); + EXPECT_OK(AddDrmServiceCertificate(serial_number3, provider_id3, + creation_time_seconds3)); + EncryptedClientIdentification encrypted_client_id; EncryptClientIdentification(serial_number1, provider_id1, test_keys_.public_test_key_2_2048_bits(), @@ -199,6 +205,61 @@ TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificates) { .error_code()); } +TEST_F(DrmServiceCertificateTest, MultipleDrmServiceCertificatesLookup) { + std::string serial_number1("serial_number1"); + std::string provider_id1("service1.com"); + uint32_t creation_time_seconds1(1231); + std::string serial_number2("serial_number2"); + std::string provider_id2("service2.com"); + uint32_t creation_time_seconds2(1232); + std::string serial_number3("serial_number3"); + std::string provider_id3("service3.com"); + uint32_t creation_time_seconds3(1234); + std::string serial_number4("serial_number4"); + std::string bogus_serial_number("bogus-serial-number"); + + EXPECT_OK(AddDrmServiceCertificate(serial_number1, provider_id1, + creation_time_seconds1)); + EXPECT_OK(AddDrmServiceCertificate(serial_number2, provider_id2, + creation_time_seconds2)); + EXPECT_OK(AddDrmServiceCertificate(serial_number3, provider_id3, + creation_time_seconds3)); + + EXPECT_EQ(provider_id1, + DrmServiceCertificate::GetDrmServiceCertificateBySerialNumber( + serial_number1) + ->provider_id()); + EXPECT_EQ(provider_id2, + DrmServiceCertificate::GetDrmServiceCertificateBySerialNumber( + serial_number2) + ->provider_id()); + EXPECT_EQ(provider_id3, + DrmServiceCertificate::GetDrmServiceCertificateBySerialNumber( + serial_number3) + ->provider_id()); + + EXPECT_EQ( + serial_number1, + DrmServiceCertificate::GetDrmServiceCertificateByProvider(provider_id1) + ->serial_number()); + EXPECT_EQ( + serial_number2, + DrmServiceCertificate::GetDrmServiceCertificateByProvider(provider_id2) + ->serial_number()); + EXPECT_EQ( + serial_number3, + DrmServiceCertificate::GetDrmServiceCertificateByProvider(provider_id3) + ->serial_number()); + + // Add a second cert for provider 3. + EXPECT_OK(AddDrmServiceCertificate(serial_number4, provider_id3, + creation_time_seconds3 + 60)); + EXPECT_EQ( + serial_number4, + DrmServiceCertificate::GetDrmServiceCertificateByProvider(provider_id3) + ->serial_number()); +} + TEST_F(DrmServiceCertificateTest, MultipleCertsPerService) { std::string serial_number1("serial_number1"); std::string serial_number2("serial_number2"); diff --git a/common/test_drm_certificates.cc b/common/test_drm_certificates.cc index 69faa48..eb51341 100644 --- a/common/test_drm_certificates.cc +++ b/common/test_drm_certificates.cc @@ -250,7 +250,130 @@ const unsigned char kTestUserDrmCertificate[] = { 0x82, 0xc8, 0xff, 0x4d, 0xef, 0x98, 0xcf, 0xb5, 0x6f, 0x6b, 0x55, 0xf8, 0xd4, 0x4a, 0xa2, 0x84, 0x3c, 0xec, 0x1a}; -const unsigned char kTestDrmServiceCertificate[] = { +const unsigned char kTestDrmServiceCertificateLicenseSdk[] = { + 0x0a, 0xbe, 0x02, 0x08, 0x03, 0x12, 0x10, 0x30, 0x30, 0x31, 0x31, 0x32, + 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x18, + 0xb1, 0x97, 0xd3, 0x03, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, + 0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, + 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, + 0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, + 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, + 0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, + 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, + 0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, + 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, + 0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, + 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, + 0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, + 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, + 0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, + 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, + 0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, + 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, + 0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, + 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, + 0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, + 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, + 0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x40, 0x01, 0x12, 0x80, 0x03, + 0x1e, 0xa4, 0x3f, 0x68, 0x74, 0x1c, 0xcd, 0x8c, 0xa3, 0xfe, 0xa7, 0x08, + 0x95, 0x31, 0x32, 0x97, 0x9b, 0x0a, 0x20, 0x55, 0x5a, 0xc3, 0xbb, 0x1c, + 0xf8, 0x04, 0x9c, 0x31, 0x26, 0xc0, 0x93, 0xb3, 0x18, 0x5b, 0x98, 0x23, + 0x8f, 0xc3, 0x7e, 0xdf, 0x59, 0xa5, 0x30, 0xb6, 0x2e, 0x13, 0x14, 0xd6, + 0xfd, 0x24, 0xf1, 0xca, 0x6a, 0x9f, 0x5b, 0xe0, 0x0e, 0x73, 0x43, 0xd9, + 0x7a, 0x2e, 0x05, 0x1c, 0x9e, 0x95, 0xc1, 0x0a, 0xa6, 0x1a, 0xcc, 0x96, + 0x91, 0x03, 0x39, 0xaf, 0xd5, 0xb5, 0xdf, 0x0a, 0xa7, 0x51, 0x1d, 0x6c, + 0x31, 0xa7, 0x24, 0x06, 0x5b, 0x14, 0x25, 0xff, 0x8f, 0x88, 0x83, 0x81, + 0xc3, 0xb5, 0xf8, 0x3e, 0xa8, 0x0e, 0xe7, 0xe4, 0x5b, 0x83, 0xad, 0xc0, + 0xe8, 0x31, 0x2d, 0x22, 0x11, 0x3e, 0xed, 0xd4, 0xde, 0x41, 0x49, 0x08, + 0x37, 0x6d, 0x3e, 0x27, 0x2e, 0x89, 0xc0, 0x9f, 0x9c, 0x75, 0xfa, 0xff, + 0x54, 0x22, 0x6f, 0x3f, 0x0d, 0x38, 0x4f, 0xb1, 0x49, 0xd3, 0xef, 0x37, + 0x80, 0x39, 0x41, 0x1f, 0x03, 0x93, 0xd1, 0x19, 0xb1, 0xba, 0x61, 0xa7, + 0x86, 0xbb, 0x3f, 0xd3, 0xa2, 0x1a, 0xa3, 0x8a, 0xfa, 0xef, 0x14, 0x36, + 0xe1, 0xec, 0xfd, 0x7d, 0xcc, 0xe5, 0x8d, 0x3e, 0x4c, 0x30, 0xa7, 0x9e, + 0x59, 0xe6, 0x27, 0xf2, 0xed, 0x07, 0xc8, 0x18, 0xf2, 0x14, 0x4c, 0x25, + 0x0e, 0x72, 0xdd, 0x75, 0x9e, 0x8b, 0xf9, 0x41, 0xac, 0x5b, 0x26, 0x2d, + 0x56, 0x0d, 0x18, 0x3d, 0xaa, 0x16, 0x48, 0x7c, 0xd1, 0x09, 0x89, 0x68, + 0xac, 0x95, 0x06, 0xce, 0xa8, 0xfa, 0x93, 0x10, 0x60, 0xe3, 0x43, 0x26, + 0x16, 0x0b, 0xbc, 0x44, 0x40, 0x02, 0xf5, 0x72, 0x87, 0x70, 0x08, 0x92, + 0x28, 0xcb, 0xa8, 0x14, 0x41, 0xbf, 0x59, 0xaa, 0x14, 0x0c, 0x10, 0x2f, + 0x1d, 0x89, 0xe4, 0x1e, 0x83, 0x76, 0x48, 0x82, 0x1f, 0x3c, 0x63, 0x36, + 0x6b, 0xaa, 0x33, 0x4f, 0xe0, 0x00, 0x4b, 0x48, 0x71, 0x39, 0x5e, 0x85, + 0xd4, 0x47, 0x7a, 0x31, 0x0a, 0x3b, 0x31, 0x90, 0x28, 0x1c, 0x4e, 0x4e, + 0x3e, 0xb2, 0x02, 0xf0, 0xb9, 0x71, 0xe3, 0xef, 0x22, 0x63, 0xf2, 0x97, + 0x9c, 0xe8, 0x16, 0xfc, 0x06, 0xa4, 0xf8, 0xfa, 0xe6, 0xd2, 0x82, 0x7f, + 0xd5, 0x70, 0x9b, 0xd8, 0xb9, 0xcd, 0xbc, 0xc6, 0x9d, 0xd3, 0x52, 0xf7, + 0x05, 0xa6, 0x25, 0xb8, 0x71, 0x3c, 0x15, 0x29, 0x36, 0xab, 0xb2, 0xa2, + 0xc7, 0xe5, 0xa1, 0x0b, 0x85, 0x4d, 0xa5, 0xb5, 0x4f, 0xda, 0xcd, 0x0a, + 0x8e, 0x8a, 0xbd, 0x2e, 0xf1, 0x2b, 0x0e, 0xdb, 0xf7, 0x9c, 0x9d, 0xcb, + 0x69, 0xe0, 0x37, 0xea, 0xb1, 0x32, 0x71, 0x09, 0x4f, 0x72, 0x42, 0x3c, + 0xdf, 0xe9, 0x5c, 0x40, 0x28, 0x32, 0x82, 0xb2, 0x4e, 0x77, 0xdd, 0x06}; + +const unsigned char kTestDrmServiceCertificateAllTypes[] = { + 0x0a, 0xc2, 0x02, 0x08, 0x03, 0x12, 0x10, 0x30, 0x30, 0x31, 0x31, 0x32, + 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x18, + 0xb1, 0x97, 0xd3, 0x03, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0xa7, 0x00, 0x36, 0x60, 0x65, 0xdc, 0xbd, 0x54, + 0x5a, 0x2a, 0x40, 0xb4, 0xe1, 0x15, 0x94, 0x58, 0x11, 0x4f, 0x94, 0x58, + 0xdd, 0xde, 0xa7, 0x1f, 0x3c, 0x2c, 0xe0, 0x88, 0x09, 0x29, 0x61, 0x57, + 0x67, 0x5e, 0x56, 0x7e, 0xee, 0x27, 0x8f, 0x59, 0x34, 0x9a, 0x2a, 0xaa, + 0x9d, 0xb4, 0x4e, 0xfa, 0xa7, 0x6a, 0xd4, 0xc9, 0x7a, 0x53, 0xc1, 0x4e, + 0x9f, 0xe3, 0x34, 0xf7, 0x3d, 0xb7, 0xc9, 0x10, 0x47, 0x4f, 0x28, 0xda, + 0x3f, 0xce, 0x31, 0x7b, 0xfd, 0x06, 0x10, 0xeb, 0xf7, 0xbe, 0x92, 0xf9, + 0xaf, 0xfb, 0x3e, 0x68, 0xda, 0xee, 0x1a, 0x64, 0x4c, 0xf3, 0x29, 0xf2, + 0x73, 0x9e, 0x39, 0xd8, 0xf6, 0x6f, 0xd8, 0xb2, 0x80, 0x82, 0x71, 0x8e, + 0xb5, 0xa4, 0xf2, 0xc2, 0x3e, 0xcd, 0x0a, 0xca, 0xb6, 0x04, 0xcd, 0x9a, + 0x13, 0x8b, 0x54, 0x73, 0x54, 0x25, 0x54, 0x8c, 0xbe, 0x98, 0x7a, 0x67, + 0xad, 0xda, 0xb3, 0x4e, 0xb3, 0xfa, 0x82, 0xa8, 0x4a, 0x67, 0x98, 0x56, + 0x57, 0x54, 0x71, 0xcd, 0x12, 0x7f, 0xed, 0xa3, 0x01, 0xc0, 0x6a, 0x8b, + 0x24, 0x03, 0x96, 0x88, 0xbe, 0x97, 0x66, 0x2a, 0xbc, 0x53, 0xc9, 0x83, + 0x06, 0x51, 0x5a, 0x88, 0x65, 0x13, 0x18, 0xe4, 0x3a, 0xed, 0x6b, 0xf1, + 0x61, 0x5b, 0x4c, 0xc8, 0x1e, 0xf4, 0xc2, 0xae, 0x08, 0x5e, 0x2d, 0x5f, + 0xf8, 0x12, 0x7f, 0xa2, 0xfc, 0xbb, 0x21, 0x18, 0x30, 0xda, 0xfe, 0x40, + 0xfb, 0x01, 0xca, 0x2e, 0x37, 0x0e, 0xce, 0xdd, 0x76, 0x87, 0x82, 0x46, + 0x0b, 0x3a, 0x77, 0x8f, 0xc0, 0x72, 0x07, 0x2c, 0x7f, 0x9d, 0x1e, 0x86, + 0x5b, 0xed, 0x27, 0x29, 0xdf, 0x03, 0x97, 0x62, 0xef, 0x44, 0xd3, 0x5b, + 0x3d, 0xdb, 0x9c, 0x5e, 0x1b, 0x7b, 0x39, 0xb4, 0x0b, 0x6d, 0x04, 0x6b, + 0xbb, 0xbb, 0x2c, 0x5f, 0xcf, 0xb3, 0x7a, 0x05, 0x02, 0x03, 0x01, 0x00, + 0x01, 0x3a, 0x10, 0x73, 0x6f, 0x6d, 0x65, 0x5f, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x40, 0x01, 0x40, 0x02, 0x40, + 0x03, 0x12, 0x80, 0x03, 0x78, 0x86, 0x5a, 0xfa, 0x0f, 0x0d, 0x39, 0x6c, + 0x19, 0x33, 0x24, 0x2d, 0x20, 0xbd, 0x34, 0xcc, 0x6e, 0xb8, 0x68, 0x36, + 0x8d, 0x75, 0xc6, 0xbe, 0x6f, 0x8b, 0x98, 0x53, 0x4f, 0xed, 0x6e, 0x78, + 0x2e, 0x15, 0xde, 0x82, 0x37, 0xa3, 0xc5, 0x1f, 0x82, 0xb4, 0xec, 0x4b, + 0x5f, 0xb3, 0xd8, 0x99, 0xf4, 0xb1, 0x9a, 0x49, 0x15, 0xd8, 0x80, 0xcc, + 0x7c, 0x16, 0xd9, 0xd8, 0x57, 0x59, 0x36, 0x34, 0xd0, 0x0e, 0xbb, 0x62, + 0x76, 0x96, 0x73, 0x3b, 0xd9, 0xea, 0x61, 0x16, 0x3d, 0xdd, 0x38, 0xef, + 0x25, 0x13, 0xbd, 0xe4, 0xa8, 0x69, 0x17, 0x81, 0x79, 0x2a, 0xe3, 0x29, + 0xac, 0x68, 0xb1, 0x5f, 0x4e, 0x5f, 0xb5, 0x45, 0x71, 0xa6, 0x38, 0x6d, + 0xce, 0x90, 0x7e, 0x0c, 0x5a, 0x7b, 0x6a, 0xce, 0xfa, 0x77, 0xf2, 0x57, + 0xa9, 0x2f, 0x8e, 0xc1, 0x0f, 0x9b, 0x0f, 0xe1, 0xf5, 0x6e, 0x12, 0xf4, + 0xac, 0x43, 0x95, 0x72, 0x73, 0x75, 0x0d, 0x3f, 0xc5, 0xac, 0x82, 0x04, + 0x79, 0xab, 0x01, 0xc1, 0x71, 0x67, 0x3a, 0x0d, 0xf0, 0xa7, 0x74, 0x94, + 0xd8, 0xe3, 0xf4, 0x3d, 0x92, 0x6b, 0x62, 0x63, 0xad, 0x3a, 0x95, 0x1a, + 0x84, 0x00, 0x2d, 0x4b, 0x06, 0x8a, 0x26, 0x65, 0xa9, 0xeb, 0xb4, 0x4f, + 0x7f, 0x66, 0x32, 0x59, 0x55, 0x89, 0x1d, 0x00, 0x5e, 0xed, 0x53, 0x4f, + 0x36, 0xce, 0xc9, 0x9b, 0xfb, 0xa4, 0x04, 0x3a, 0x2f, 0xd9, 0x68, 0x9c, + 0x98, 0xf3, 0xfc, 0xc6, 0x07, 0xc2, 0x00, 0x17, 0x39, 0x27, 0xb4, 0x13, + 0x49, 0x23, 0x4a, 0xf4, 0x96, 0xf1, 0x3e, 0x5d, 0x66, 0x04, 0xe2, 0x93, + 0xf9, 0x2c, 0x08, 0xa7, 0x08, 0x65, 0x39, 0x0f, 0xa9, 0xec, 0x55, 0x24, + 0xab, 0x46, 0x32, 0x48, 0xd0, 0xd2, 0x44, 0x64, 0x29, 0x0d, 0xd5, 0x26, + 0x4d, 0x85, 0xbb, 0x79, 0x7d, 0xa0, 0xf3, 0x83, 0x02, 0xe0, 0x4f, 0xd7, + 0xa0, 0x55, 0x9f, 0x20, 0x4e, 0x58, 0x0b, 0x39, 0xbc, 0x2b, 0xa0, 0x1f, + 0x98, 0x46, 0x3d, 0x96, 0xc0, 0xff, 0x43, 0x78, 0xdc, 0x49, 0x89, 0xf4, + 0x01, 0x83, 0xec, 0x33, 0x4c, 0x8a, 0xe0, 0x6c, 0x92, 0x95, 0xfd, 0x74, + 0x2e, 0x55, 0x86, 0xc8, 0x54, 0x7c, 0x63, 0xce, 0x58, 0xe6, 0xfc, 0xc1, + 0xc3, 0x9d, 0x67, 0x85, 0xe8, 0xa1, 0x4d, 0x11, 0xb2, 0xbe, 0x9f, 0x70, + 0x32, 0x08, 0x67, 0x3a, 0x07, 0xd5, 0xf5, 0xdf, 0xc1, 0x40, 0x8a, 0x60, + 0xc1, 0x1e, 0xa7, 0x64, 0x23, 0x1d, 0x93, 0x3b, 0xfa, 0x64, 0x87, 0x64, + 0xfc, 0x09, 0x1c, 0xad, 0xf8, 0x44, 0x5b, 0x1d, 0x75, 0xde, 0x7c, 0xf2, + 0x9e, 0x9f, 0x17, 0xa0, 0xb4, 0x33, 0xc7, 0x5c, 0x73, 0x19, 0x61, 0x7b, + 0xde, 0xff, 0x0c, 0xf9, 0x66, 0x99, 0xf1, 0xae, 0x42, 0x04, 0x65, 0xf2, + 0x56, 0xdc, 0x1a, 0x8d}; + +const unsigned char kTestDrmServiceCertificateNoType[] = { 0x0a, 0xbc, 0x02, 0x08, 0x03, 0x12, 0x10, 0x30, 0x30, 0x31, 0x31, 0x32, 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x37, 0x18, 0xb1, 0x97, 0xd3, 0x03, 0x22, 0x8e, 0x02, 0x30, 0x82, 0x01, 0x0a, 0x02, @@ -321,8 +444,17 @@ TestDrmCertificates::TestDrmCertificates() test_user_device_certificate_( kTestUserDrmCertificate, kTestUserDrmCertificate + sizeof(kTestUserDrmCertificate)), - test_service_certificate_( - kTestDrmServiceCertificate, - kTestDrmServiceCertificate + sizeof(kTestDrmServiceCertificate)) {} + test_service_certificate_license_sdk_( + kTestDrmServiceCertificateLicenseSdk, + kTestDrmServiceCertificateLicenseSdk + + sizeof(kTestDrmServiceCertificateLicenseSdk)), + test_service_certificate_all_types_( + kTestDrmServiceCertificateAllTypes, + kTestDrmServiceCertificateAllTypes + + sizeof(kTestDrmServiceCertificateAllTypes)), + test_service_certificate_no_type_( + kTestDrmServiceCertificateNoType, + kTestDrmServiceCertificateNoType + + sizeof(kTestDrmServiceCertificateNoType)) {} } // namespace widevine diff --git a/common/test_drm_certificates.h b/common/test_drm_certificates.h index 666d693..61e1bf9 100644 --- a/common/test_drm_certificates.h +++ b/common/test_drm_certificates.h @@ -36,16 +36,29 @@ class TestDrmCertificates { return test_user_device_certificate_; } - // returns a service certificate - const std::string& test_service_certificate() const { - return test_service_certificate_; + // returns a service certificate with license sdk service type + const std::string& test_service_certificate_license_sdk() const { + return test_service_certificate_license_sdk_; + } + + // returns a service certificate with all service types + const std::string& test_service_certificate_all_types() const { + return test_service_certificate_all_types_; + } + + // returns a service certificate prior to a change requiring the service + // type to be specified. + const std::string& test_service_certificate_no_type() const { + return test_service_certificate_no_type_; } private: const std::string test_root_certificate_; const std::string test_intermediate_certificate_; const std::string test_user_device_certificate_; - const std::string test_service_certificate_; + const std::string test_service_certificate_license_sdk_; + const std::string test_service_certificate_all_types_; + const std::string test_service_certificate_no_type_; DISALLOW_COPY_AND_ASSIGN(TestDrmCertificates); }; diff --git a/example/wvpl_cas_proxy_environment_example.cc b/example/wvpl_cas_proxy_environment_example.cc index f8db5b7..f6fe475 100644 --- a/example/wvpl_cas_proxy_environment_example.cc +++ b/example/wvpl_cas_proxy_environment_example.cc @@ -108,7 +108,7 @@ std::string a2b_hex(const std::string& a) { int main(int argc, char** argv) { std::map config_values; - config_values["drm_certficate_type"] = "test"; + config_values["drm_certificate_type"] = "test"; config_values["provider"] = kWidevineTestProvider; widevine_server::wv_pl_sdk::WvPLCASProxyEnvironment environment( config_values); diff --git a/license_server_sdk/internal/BUILD b/license_server_sdk/internal/BUILD index 375ba89..612660f 100644 --- a/license_server_sdk/internal/BUILD +++ b/license_server_sdk/internal/BUILD @@ -36,11 +36,14 @@ cc_library( deps = [ ":sdk", "//base", + "//external:protobuf", "//strings", "@abseil_repo//absl/strings", "@abseil_repo//absl/synchronization", "//util/endian", + "//util/gtl:map_util", "//util/random:global_id", + "//util:error_space", "//common:aes_cbc_util", "//common:client_cert", "//common:crypto_util", @@ -56,12 +59,14 @@ cc_library( "//common:status", "//common:verified_media_pipeline", "//common:vmp_checker", - "//protos/public:client_identification_proto", - "//protos/public:errors_proto", - "//protos/public:license_protocol_proto", - "//protos/public:license_server_sdk_proto", - "//protos/public:provisioned_device_info_proto", - "//protos/public:widevine_pssh_proto", + "//protos/public:oem_key_container_cc_proto", + "//protos/public:client_identification_cc_proto", + "//protos/public:device_certificate_status_cc_proto", + "//protos/public:errors_cc_proto", + "//protos/public:license_protocol_cc_proto", + "//protos/public:license_server_sdk_cc_proto", + "//protos/public:provisioned_device_info_cc_proto", + "//protos/public:widevine_pssh_cc_proto", ], ) @@ -88,11 +93,11 @@ cc_library( "//common:drm_service_certificate", "//common:error_space", "//common:status", - "//protos/public:client_identification_proto", - "//protos/public:errors_proto", - "//protos/public:license_protocol_proto", - "//protos/public:license_server_sdk_proto", - "//protos/public:widevine_pssh_proto", + "//protos/public:client_identification_cc_proto", + "//protos/public:errors_cc_proto", + "//protos/public:license_protocol_cc_proto", + "//protos/public:license_server_sdk_cc_proto", + "//protos/public:widevine_pssh_cc_proto", ], ) @@ -122,16 +127,17 @@ cc_test( "//common:signing_key_util", "//common:test_drm_certificates", "//common:test_utils", - "//protos/public:client_identification_proto", - "//protos/public:device_certificate_status_proto", - "//protos/public:drm_certificate_proto", - "//protos/public:errors_proto", - "//protos/public:license_protocol_proto", - "//protos/public:license_server_sdk_proto", - "//protos/public:provisioned_device_info_proto", - "//protos/public:remote_attestation_proto", - "//protos/public:signed_drm_certificate_proto", - "//protos/public:widevine_pssh_proto", + "//protos/public:client_identification_cc_proto", + "//protos/public:device_certificate_status_cc_proto", + "//protos/public:drm_certificate_cc_proto", + "//protos/public:errors_cc_proto", + "//protos/public:license_protocol_cc_proto", + "//protos/public:license_server_sdk_cc_proto", + "//protos/public:provisioned_device_info_cc_proto", + "//protos/public:remote_attestation_cc_proto", + "//protos/public:signed_device_info_cc_proto", + "//protos/public:signed_drm_certificate_cc_proto", + "//protos/public:widevine_pssh_cc_proto", ], ) @@ -145,8 +151,8 @@ cc_test( "//base", "//testing:gunit_main", "@abseil_repo//absl/strings", - "//protos/public:license_protocol_proto", - "//protos/public:license_server_sdk_proto", + "//protos/public:license_protocol_cc_proto", + "//protos/public:license_server_sdk_cc_proto", ], ) @@ -160,7 +166,7 @@ cc_test( "//testing:gunit_main", "//util/endian", "//common:error_space", - "//protos/public:errors_proto", - "//protos/public:license_server_sdk_proto", + "//protos/public:errors_cc_proto", + "//protos/public:license_server_sdk_cc_proto", ], ) diff --git a/license_server_sdk/internal/generate_error_response.cc b/license_server_sdk/internal/generate_error_response.cc index 5bc5f4e..83bb9cc 100644 --- a/license_server_sdk/internal/generate_error_response.cc +++ b/license_server_sdk/internal/generate_error_response.cc @@ -28,6 +28,7 @@ bool GenerateErrorResponse(const Status& create_session_status, std::string* license_response) { DCHECK(license_response); + const std::string empty_provider = ""; LicenseError error_proto; if (create_session_status.error_space() == error_space) { switch (create_session_status.error_code()) { @@ -45,15 +46,8 @@ bool GenerateErrorResponse(const Status& create_session_status, error_proto.set_error_code(LicenseError::SERVICE_UNAVAILABLE); break; case SERVICE_CERTIFICATE_REQUEST_MESSAGE: { - SignedMessage signed_message; - signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE); - signed_message.set_msg( - DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie() - ->certificate()); - if (!signed_message.SerializeToString(license_response)) { - return false; - } - return true; + return GenerateServiceCertificateResponse(empty_provider, + license_response); } default: break; @@ -84,4 +78,27 @@ bool GenerateErrorResponse(const Status& create_session_status, } return true; } + +bool GenerateServiceCertificateResponse(const std::string& provider, + std::string* license_response) { + DCHECK(license_response); + SignedMessage signed_message; + signed_message.set_type(SignedMessage::SERVICE_CERTIFICATE); + const DrmServiceCertificate* drm_cert; + if (provider.empty()) { + drm_cert = DrmServiceCertificate::GetDefaultDrmServiceCertificate(); + } else { + drm_cert = + DrmServiceCertificate::GetDrmServiceCertificateByProvider(provider); + } + if (drm_cert == nullptr) { + return false; + } + signed_message.set_msg(drm_cert->certificate()); + if (!signed_message.SerializeToString(license_response)) { + return false; + } + return true; +} + } // namespace widevine diff --git a/license_server_sdk/internal/generate_error_response.h b/license_server_sdk/internal/generate_error_response.h index 7931f3f..569ed4f 100644 --- a/license_server_sdk/internal/generate_error_response.h +++ b/license_server_sdk/internal/generate_error_response.h @@ -22,5 +22,12 @@ namespace widevine { // there is an error license to be sent to the client, or false otherwise. // Example usage in the Session::Create comments above. bool GenerateErrorResponse(const Status& status, std::string* license_response); + +// Generates a SignedMessage containing a service certifcate for the specified +// |provider|. Returns false if |provider| does not exist. Returns the default +// service certificate if |provider| is empty. +bool GenerateServiceCertificateResponse(const std::string& provider, + std::string* license_response); } // namespace widevine + #endif // LICENSE_SERVER_SDK_INTERNAL_GENERATE_ERROR_RESPONSE_H_ diff --git a/license_server_sdk/internal/key_control_block.cc b/license_server_sdk/internal/key_control_block.cc index 1faf311..54a85ec 100644 --- a/license_server_sdk/internal/key_control_block.cc +++ b/license_server_sdk/internal/key_control_block.cc @@ -78,6 +78,7 @@ bool Generate( } switch (key_container.type()) { case License::KeyContainer::CONTENT: + case License::KeyContainer::OEM_CONTENT: case License::KeyContainer::ENTITLEMENT: { if (key_container.has_required_protection() && key_container.required_protection().has_cgms_flags()) { diff --git a/license_server_sdk/internal/session_impl.cc b/license_server_sdk/internal/session_impl.cc index d52456b..0ef0dd6 100644 --- a/license_server_sdk/internal/session_impl.cc +++ b/license_server_sdk/internal/session_impl.cc @@ -14,6 +14,7 @@ #include #include #include +#include #include "glog/logging.h" #include "base/timestamp.h" @@ -24,6 +25,7 @@ #include "absl/strings/str_cat.h" #include "absl/synchronization/mutex.h" #include "util/endian/endian.h" +#include "util/gtl/map_util.h" #include "util/random/global_id.h" #include "common/aes_cbc_util.h" #include "common/client_cert.h" @@ -43,9 +45,12 @@ #include "license_server_sdk/internal/key_control_block.h" #include "license_server_sdk/internal/parse_content_id.h" #include "license_server_sdk/internal/session_usage_report.h" +#include "protos/public/device_certificate_status.pb.h" #include "protos/public/errors.pb.h" #include "protos/public/widevine_pssh.pb.h" +using ::widevine::SignedDeviceCertificateStatusList; + namespace widevine { // TODO(user): These constants are also defined in public/session.cc. Fix the @@ -92,13 +97,24 @@ void SessionImpl::SetPreProvisioningKeys( } Status SessionImpl::SetCertificateStatusList( - const DrmRootCertificate* root_cert, const std::string& certificate_status_list, + const DrmRootCertificate* root_cert, + const std::string& serialized_signed_certificate_status_list, uint32_t expiration_period_seconds, bool allow_unknown_devices) { CHECK(root_cert); + // TODO(user): Refactor to enable SignedDeviceInfo response. This + // assumes a SignedDeviceCertificateStatusList request. + SignedDeviceCertificateStatusList signed_certificate_status_list; + if (!signed_certificate_status_list.ParseFromString( + serialized_signed_certificate_status_list)) { + return Status(error_space, INVALID_CERTIFICATE_STATUS_LIST, + "signed-certificate-status-list-parse-error"); + } + Status status = DeviceStatusList::Instance()->UpdateStatusList( - root_cert->public_key(), certificate_status_list, - expiration_period_seconds); + root_cert->public_key(), + signed_certificate_status_list.certificate_status_list(), + signed_certificate_status_list.signature(), expiration_period_seconds); if (!status.ok()) { return status; } @@ -131,6 +147,10 @@ void SessionImpl::AllowDevelopmentClients(bool enable) { VmpChecker::Instance()->set_allow_development_vmp(enable); } +void SessionImpl::AllowTestOnlyDevices(const std::string& device_list) { + DeviceStatusList::Instance()->AllowTestOnlyDevices(device_list); +} + void SessionImpl::AllowRevokedDevices(const std::string& system_id_list) { DeviceStatusList::Instance()->AllowRevokedDevices(system_id_list); } @@ -782,26 +802,40 @@ Status SessionImpl::GenerateSignedLicense( return status; } } - std::string wrapping_key; - if (key_container && !key_container->empty()) { - if (!client_cert_.get()) { - status = Status(error_space, MISSING_CLIENT_CERT, ""); - return status; - } + if (client_cert_) { wrapping_key = DeriveKey(client_cert_->key(), kWrappingKeyLabel, license_request_->protocol_version() < VERSION_2_2 ? signed_message_->msg() : Sha512_Hash(signed_message_->msg()), kWrappingKeySizeBits); + } + std::map oem_content_key_map; + if ((key_container && !key_container->empty()) || + oem_key_container_ != nullptr) { + if (!client_cert_) { + status = Status(error_space, MISSING_CLIENT_CERT, ""); + return status; + } if (wrapping_key.empty()) { status = Status(error_space, MISSING_ENCRYPTION_KEY, ""); return status; } - for (std::list::const_iterator iter = - key_container->begin(); - iter != key_container->end(); iter++) { - *license.add_key() = *iter; + if (oem_key_container_ == nullptr) { + for (std::list::const_iterator iter = + key_container->begin(); + iter != key_container->end(); iter++) { + *license.add_key() = *iter; + } + } else { + for (auto oem_key : *oem_key_container_) { + *license.add_key() = oem_key.key(); + if (oem_key.key().type() == License::KeyContainer::OEM_CONTENT && + !oem_key.wrapping_key().empty()) { + oem_content_key_map.insert( + std::make_pair(oem_key.key().id(), oem_key.wrapping_key())); + } + } } } std::string provider_client_token = @@ -880,12 +914,20 @@ Status SessionImpl::GenerateSignedLicense( // key. Key control blocks for renewals (type = KEY_CONTROL) are not // encrypted. kcb.set_iv(Random16Bytes()); + std::string kcb_encryption_key; // key used to encrypt the KCB. + status = GetOemEncryptionKeyForKCB(oem_content_key_map, license.key(i), + &kcb_encryption_key); + if (!status.ok()) { + LOG(WARNING) << "Could not find KCB Encryption key: Status = " + << status; + return status; + } kcb.set_key_control_block( license_request_->protocol_version() < VERSION_2_2 - ? crypto_util::EncryptAesCbc(license.key(i).key().substr(0, 16), + ? crypto_util::EncryptAesCbc(kcb_encryption_key.substr(0, 16), kcb.iv(), kcb.key_control_block()) : crypto_util::EncryptAesCbcNoPad( - license.key(i).key().substr(0, 16), kcb.iv(), + kcb_encryption_key.substr(0, 16), kcb.iv(), kcb.key_control_block())); } } @@ -905,6 +947,7 @@ Status SessionImpl::GenerateSignedLicense( } } license.mutable_key(i)->set_iv(Random16Bytes()); + // Encrypt specified CONTENT key or ENTITLEMENT or OEM_CONTENT key. license.mutable_key(i)->set_key( license_request_->protocol_version() < VERSION_2_2 ? crypto_util::EncryptAesCbc(wrapping_key, license.key(i).iv(), @@ -916,9 +959,22 @@ Status SessionImpl::GenerateSignedLicense( } } } + if (!oem_content_key_map.empty()) { + oem_content_key_map.clear(); + } if (platform_verification_status_ != PLATFORM_NO_VERIFICATION) { license.set_platform_verification_status(platform_verification_status_); + if (platform_verification_status_ == PLATFORM_HARDWARE_VERIFIED) { + // In the cases where the 'Platform' has already been verified at the + // proxy, it may make more sense to have the 'Remote Attestation' status + // to be sent from the proxy as part of the license request. However, + // since b/65054419 intends to do away with 'Remote Attestation' + // altogether, it is preferred not to add any 'Remote Attestation' status + // to the license request that is sent from the proxy + // (ModularDrmLicenseRequest). + remote_attestation_verified_ = true; + } } // TODO(b/65054419): Deprecate remote_attestation_verified altogether. if (remote_attestation_verified_) { @@ -966,6 +1022,12 @@ bool SessionImpl::GenerateErrorResponse(const Status& status, return widevine::GenerateErrorResponse(status, signed_message_bytes); } +bool SessionImpl::GenerateServiceCertificateResponse( + const std::string& provider, std::string* signed_message_bytes) { + return widevine::GenerateServiceCertificateResponse( + provider, signed_message_bytes); +} + std::string SessionImpl::GetSdkVersionString() { return absl::StrCat(kMajorVersion, ".", kMinorVersion, ".", kRelease); } @@ -1156,7 +1218,7 @@ bool SessionImpl::GetClientInfo(ClientIdentification* client_info) const { } std::string SessionImpl::GetDrmDeviceServiceId() const { - if (client_cert_.get()) { + if (client_cert_) { return client_cert_->service_id(); } return std::string(); @@ -1166,7 +1228,35 @@ Status SessionImpl::GenerateDeviceStatusListRequest( std::string* signed_device_certificate_status_list_request) { std::string version = GetSdkVersionString(); return DeviceStatusList::GenerateSignedDeviceCertificateStatusListRequest( - version, signed_device_certificate_status_list_request); + version, + DrmServiceCertificate::GetDefaultDrmServiceCertificateOrDie() + ->certificate(), + signed_device_certificate_status_list_request); +} + +void SessionImpl::SetKeys(std::list* oem_key_container) { + oem_key_container_ = oem_key_container; +} + +Status SessionImpl::GetOemEncryptionKeyForKCB( + const std::map& oem_keys, const License::KeyContainer& key, + std::string* encryption_key) const { + if (encryption_key == nullptr) { + return Status(error_space, error::INVALID_ARGUMENT, + "encryption key cannot be null"); + } + if (key.type() == License::KeyContainer::OEM_CONTENT) { + auto it = oem_keys.find(key.id()); + if (it != oem_keys.end()) { + *encryption_key = (*it).second; + } else { + return Status(error_space, ENCRYPT_ERROR, + "Encryption key not found for paired OEM_CONTENT key"); + } + } else { + *encryption_key = key.key(); + } + return OkStatus(); } } // namespace widevine diff --git a/license_server_sdk/internal/session_impl.h b/license_server_sdk/internal/session_impl.h index 1d0a01a..b9b53ce 100644 --- a/license_server_sdk/internal/session_impl.h +++ b/license_server_sdk/internal/session_impl.h @@ -21,6 +21,7 @@ #include "protos/public/client_identification.pb.h" #include "protos/public/license_protocol.pb.h" #include "protos/public/license_server_sdk.pb.h" +#include "protos/public/oem_key_container.pb.h" #include "protos/public/provisioned_device_info.pb.h" namespace widevine { @@ -36,7 +37,7 @@ class SessionUsage; const uint32_t kMajorVersion = 4; const uint32_t kMinorVersion = 5; -const uint32_t kRelease = 2; +const uint32_t kRelease = 4; const uint32_t kMasterSigningKeySizeBytes = 16; const uint32_t kSigningKeySizeBytes = 64; @@ -89,12 +90,19 @@ std::string GetProviderClientToken(const SessionInit& session_init, // &init, // &cache, // &signed_license); -// // For renewal, either SessionState may be cached and provided to the call -// // to GenerateSignedLicense. Or if it is not possible to cache the -// // SessionState, then SessionInit must be provided with either -// // |signing_key| or |master_signing_key|, in order to allow the sdk to -// // validate the signature of the renewal request, and to sign the -// // license issued. +// For renewal, either SessionState may be cached and provided to the call +// to GenerateSignedLicense. Or if it is not possible to cache the +// SessionState, then SessionInit must be provided with either +// |signing_key| or |master_signing_key|, in order to allow the sdk to +// validate the signature of the renewal request, and to sign the +// license issued. +// To use the OEM_CONTENT_KEY key type, the API SetKeys( +// std::list* oem_key_container) must be called prior to the +// GenerateSignedLicense(...) API. Each OemKeyContainer contains a +// 'wrapping key', which will used to 'wrap' the KCB when generating a license. +// Note that when this SetKeys(std::list* oem_key_container) is +// called, license generation is only for the keys in this |oem_key_container|. +// The keys passed into the |key_container| parameter are ignored. class SessionImpl { public: @@ -134,6 +142,12 @@ class SessionImpl { // Defaults to false. static void AllowDevelopmentClients(bool enable); + // Enable delivery of licenses to TEST_ONLY client devices. |device_list| is + // a comma separated list of devices to allow even if the device is in a + // TEST_ONLY state. This list wil be used only if + // AllowDevelopmentClient(false) is in use. + static void AllowTestOnlyDevices(const std::string& device_list); + // Enable delivery of licenses to revoked client devices. |system_id_list| is // a comma separated list of systems Ids to allow even if the device is in the // revoked state. @@ -200,6 +214,12 @@ class SessionImpl { static bool GenerateErrorResponse(const Status& status, std::string* signed_message_bytes); + // Generates a SignedMessage containing a service certifcate for the specified + // |provider|. Returns false if |provider| does not exist. Returns the + // default service certificate if |provider| is empty. + static bool GenerateServiceCertificateResponse(const std::string& provider, + std::string* signed_message_bytes); + // DeriveKey uses the NIST 800-108 KDF recommendation, using AES-CMAC PRF. // NIST 800-108: // http://csrc.nist.gov/publications/nistpubs/800-108/sp800-108.pdf @@ -343,6 +363,11 @@ class SessionImpl { // Level 1 devices are verified by default. virtual Status VerifyPlatform(); + // Sets the list of keys that have been wrapped by associated wrapping keys. + // Each OemKeyContainer contains a "License.KeyContainer" and the associated + // wrapping key. + virtual void SetKeys(std::list* oem_key_container); + // Extracts the nonce from |request| and populates |key_control_nonce|. Sets // |key_control_nonce| to true if the nonce is found. static Status LoadKeyControlNonce(const LicenseRequest& request, @@ -361,6 +386,12 @@ class SessionImpl { // Validates the Provider Session Token from |pst_src|. static Status CheckProviderSessionToken(const std::string& pst_src); + // Return true, if |key| is found in |oem_keys| and |encryption_key| is found. + // This |encryption_key| is used to encrypt the KCB. + Status GetOemEncryptionKeyForKCB(const std::map& oem_keys, + const License::KeyContainer& key, + std::string* encryption_key) const; + std::unique_ptr signed_message_; std::unique_ptr license_request_; // Client cert object used for all crypto operations. @@ -374,8 +405,8 @@ class SessionImpl { uint32_t key_control_nonce_; std::unique_ptr provisioned_device_info_; std::string remote_attestation_cert_serial_number_; + std::list* oem_key_container_ = nullptr; - private: DISALLOW_COPY_AND_ASSIGN(SessionImpl); }; diff --git a/license_server_sdk/internal/session_impl_test.cc b/license_server_sdk/internal/session_impl_test.cc index f9ece12..549a4fe 100644 --- a/license_server_sdk/internal/session_impl_test.cc +++ b/license_server_sdk/internal/session_impl_test.cc @@ -49,6 +49,7 @@ #include "protos/public/license_server_sdk.pb.h" #include "protos/public/provisioned_device_info.pb.h" #include "protos/public/remote_attestation.pb.h" +#include "protos/public/signed_device_info.pb.h" #include "protos/public/signed_drm_certificate.pb.h" #include "protos/public/widevine_pssh.pb.h" @@ -56,7 +57,7 @@ using google::protobuf::TextFormat; using testing::Eq; using testing::Return; using widevine::DeviceCertificateStatusListRequest; -using widevine::SignedDeviceCertificateStatusListRequest; +using widevine::SignedDeviceInfoRequest; // TODO(user): Add test case for generateSignedLicense. namespace widevine { namespace { @@ -514,6 +515,10 @@ class SessionTest : public ::testing::Test { license.platform_verification_status()); } + bool remote_attestation_verified(const SessionImpl& session) { + return session.remote_attestation_verified_; + } + void SetupCertificateStatusList( bool allow_unknown_devices, SignedDeviceCertificateStatusList* status_list) { @@ -1375,7 +1380,77 @@ class SessionTest : public ::testing::Test { ASSERT_TRUE(session.get()); } - void TestKeyControlBlocks(const std::string& request_msg) { + void TestOemContentKeysWithNoWrappingKey(const std::string& request_msg) { + SessionImpl* session_ptr = nullptr; + Status status = + SessionImpl::Create(test_root_.get(), request_msg, &session_ptr); + std::unique_ptr session(session_ptr); + ASSERT_EQ(OkStatus(), status); + ASSERT_TRUE(session.get()); + std::list oem_keys; + OemKeyContainer oem_key; + oem_key.mutable_key()->set_key("0123456789abcdef0123456789abcdef"); + oem_key.mutable_key()->set_id("oem_content"); + oem_key.mutable_key()->set_type(License::KeyContainer::OEM_CONTENT); + oem_keys.push_back(oem_key); + // Specify content key via OemKeyContainer. + oem_key.Clear(); + oem_key.mutable_key()->set_id("content"); + oem_key.mutable_key()->set_key("fedcba9876543210"); + oem_key.mutable_key()->set_type(License::KeyContainer::CONTENT); + oem_keys.push_back(oem_key); + session->SetKeys(&oem_keys); + + SessionInit init; + License::Policy policies; + SessionState cache; + std::list keys; + std::string signed_license_bytes; + status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes); + EXPECT_EQ(ENCRYPT_ERROR, status.error_code()); + } + + void TestOemContentKeys(const std::string& request_msg) { + SessionImpl* session_ptr = nullptr; + Status status = + SessionImpl::Create(test_root_.get(), request_msg, &session_ptr); + std::unique_ptr session(session_ptr); + ASSERT_EQ(OkStatus(), status); + ASSERT_TRUE(session.get()); + std::list oem_keys; + License::KeyContainer key; + key.set_id("oem_content"); + const std::string oem_content_key("0123456789abcdef0123456789abcdef"); + key.set_key(oem_content_key); + key.set_type(License::KeyContainer::OEM_CONTENT); + OemKeyContainer oem_key; + oem_key.mutable_key()->CopyFrom(key); + const std::string content_key("0123456789abcdef"); + oem_key.set_wrapping_key(content_key); + oem_keys.push_back(oem_key); + // Specify operator key via OemKeyContainer. + key.set_id("operator"); + const std::string operator_key("fedcba9876543210"); + key.set_key(operator_key); + key.set_type(License::KeyContainer::OPERATOR_SESSION); + oem_key.mutable_key()->CopyFrom(key); + oem_keys.push_back(oem_key); + session->SetKeys(&oem_keys); + SessionInit init; + init.set_purchase_id("Purchases!"); + License::Policy policies; + policies.set_can_persist(true); + SessionState cache; + std::list keys; + std::string signed_license_bytes; + status = session->GenerateSignedLicense(&policies, &keys, &init, &cache, + &signed_license_bytes); + ASSERT_EQ(OkStatus(), status); + } + + void TestKeyControlBlocks(const std::string& request_msg, + bool is_oem_key_container) { SessionImpl* session_ptr = nullptr; Status status = SessionImpl::Create(test_root_.get(), request_msg, &session_ptr); @@ -1403,6 +1478,17 @@ class SessionTest : public ::testing::Test { key.clear_track_label(); keys.push_back(key); + // CONTENT keys added via OemKeyContainer proto. + if (is_oem_key_container) { + std::list oem_keys; + for (std::list::iterator it = keys.begin(); + it != keys.end(); ++it) { + OemKeyContainer oem_key; + *oem_key.mutable_key() = *it; + oem_keys.push_back(oem_key); + } + session->SetKeys(&oem_keys); + } SessionInit init; init.set_purchase_id("Purchases!"); License::Policy policies; @@ -1585,6 +1671,14 @@ class SessionTest : public ::testing::Test { ra->set_salt(salt); } + Status GetOemEncryptionKeyForKCB(const SessionImpl& session_impl, + const std::map& oem_keys, + const License::KeyContainer& key, + std::string* encryption_key) { + return session_impl.GetOemEncryptionKeyForKCB(oem_keys, key, + encryption_key); + } + protected: RsaTestKeys test_keys_; std::unique_ptr dev_root_; @@ -2284,10 +2378,12 @@ TEST_F(SessionTest, NewLicenseWithKeyControlBlock) { client_capabilities.set_client_token(false); client_capabilities.set_session_token(false); client_capabilities.set_anti_rollback_usage_table(false); - TestKeyControlBlocks(GenerateBasicLicenseRequest( - VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, - client_capabilities, kIncludeProviderToken, - LicenseRequest::ContentIdentification::InitData::WEBM)); + TestKeyControlBlocks( + GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM), + false); } TEST_F(SessionTest, NewLicenseWithKeyControlBlockOffline) { @@ -2295,10 +2391,12 @@ TEST_F(SessionTest, NewLicenseWithKeyControlBlockOffline) { client_capabilities.set_client_token(false); client_capabilities.set_session_token(false); client_capabilities.set_anti_rollback_usage_table(false); - TestKeyControlBlocks(GenerateBasicLicenseRequest( - VERSION_2_1, kNoDeprecatedNonce, OFFLINE, kUseCurrentTime, - client_capabilities, kIncludeProviderToken, - LicenseRequest::ContentIdentification::InitData::WEBM)); + TestKeyControlBlocks( + GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, OFFLINE, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM), + false); } TEST_F(SessionTest, NewLicenseWithKeyControlBlockDeprecatedNonce) { @@ -2306,10 +2404,12 @@ TEST_F(SessionTest, NewLicenseWithKeyControlBlockDeprecatedNonce) { client_capabilities.set_client_token(false); client_capabilities.set_session_token(false); client_capabilities.set_anti_rollback_usage_table(false); - TestKeyControlBlocks(GenerateBasicLicenseRequest( - VERSION_2_1, kDeprecatedNonce, STREAMING, kUseCurrentTime, - client_capabilities, kIncludeProviderToken, - LicenseRequest::ContentIdentification::InitData::WEBM)); + TestKeyControlBlocks( + GenerateBasicLicenseRequest( + VERSION_2_1, kDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM), + false); } TEST_F(SessionTest, NewLicenseWithKeyControlBlockAntiRollback) { @@ -2317,8 +2417,26 @@ TEST_F(SessionTest, NewLicenseWithKeyControlBlockAntiRollback) { client_capabilities.set_client_token(false); client_capabilities.set_session_token(false); client_capabilities.set_anti_rollback_usage_table(true); - TestKeyControlBlocks(GenerateBasicLicenseRequest( - VERSION_2_1, kDeprecatedNonce, STREAMING, kUseCurrentTime, + TestKeyControlBlocks( + GenerateBasicLicenseRequest( + VERSION_2_1, kDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM), + false); +} + +TEST_F(SessionTest, NewLicenseWithKeyControlBlockForOemContentKeys) { + ClientIdentification::ClientCapabilities client_capabilities; + TestOemContentKeys(GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, + client_capabilities, kIncludeProviderToken, + LicenseRequest::ContentIdentification::InitData::WEBM)); +} + +TEST_F(SessionTest, NewLicenseWithKeyControlBlockForOemContentKeysError) { + ClientIdentification::ClientCapabilities client_capabilities; + TestOemContentKeysWithNoWrappingKey(GenerateBasicLicenseRequest( + VERSION_2_1, kNoDeprecatedNonce, STREAMING, kUseCurrentTime, client_capabilities, kIncludeProviderToken, LicenseRequest::ContentIdentification::InitData::WEBM)); } @@ -3055,22 +3173,73 @@ TEST_F(SessionTest, BasicRequestResponse_OEMVersion14) { } TEST_F(SessionTest, GenerateDeviceStatusListRequest) { - std::string signed_device_certificate_status_list; + std::string serialized_signed_device_info_request; Status status = session_impl_.GenerateDeviceStatusListRequest( - &signed_device_certificate_status_list); + &serialized_signed_device_info_request); EXPECT_TRUE(status.ok()); - SignedDeviceCertificateStatusListRequest signed_request; - signed_request.ParseFromString(signed_device_certificate_status_list); - std::string device_certificate_status_list = + SignedDeviceInfoRequest signed_request; + signed_request.ParseFromString(serialized_signed_device_info_request); + std::string serialized_device_certificate_status_list_request = signed_request.device_certificate_status_list_request(); DeviceCertificateStatusListRequest request; - request.ParseFromString(device_certificate_status_list); + request.ParseFromString(serialized_device_certificate_status_list_request); EXPECT_EQ(request.sdk_version(), SessionImpl::GetSdkVersionString()); EXPECT_EQ(DeviceStatusList::Instance()->GetCurrentTime(), request.sdk_time_seconds()); } +TEST_F(SessionTest, GetOemEncryptionKeyForKCBForOemContentKeySuccess) { + License::KeyContainer key; + key.set_id("oem_content"); + const std::string oem_content_key("0123456789abcdef0123456789abcdef"); + key.set_key(oem_content_key); + key.set_type(License::KeyContainer::OEM_CONTENT); + OemKeyContainer oem_key; + oem_key.mutable_key()->CopyFrom(key); + const std::string content_key("0123456789abcdef"); + oem_key.set_wrapping_key(content_key); + std::map oem_content_key_map; + // Expect to find the 'wrapping key'. + oem_content_key_map.insert( + std::make_pair(oem_key.key().id(), oem_key.wrapping_key())); + std::string encryption_key; + EXPECT_OK(GetOemEncryptionKeyForKCB(session_impl_, oem_content_key_map, key, + &encryption_key)); + EXPECT_EQ(oem_key.wrapping_key(), encryption_key); +} + +TEST_F(SessionTest, GetOemEncryptionKeyForKCBForOemContentKeyFailure) { + License::KeyContainer key; + key.set_id("oem_content"); + const std::string oem_content_key("0123456789abcdef0123456789abcdef"); + key.set_key(oem_content_key); + key.set_type(License::KeyContainer::OEM_CONTENT); + OemKeyContainer oem_key; + oem_key.mutable_key()->CopyFrom(key); + const std::string content_key("0123456789abcdef"); + oem_key.set_wrapping_key(content_key); + std::map oem_content_key_map; + std::string encryption_key; + Status status = GetOemEncryptionKeyForKCB(session_impl_, oem_content_key_map, + key, &encryption_key); + EXPECT_EQ(ENCRYPT_ERROR, status.error_code()); +} + +TEST_F(SessionTest, GetOemEncryptionKeyForKCBForContentKeySuccess) { + License::KeyContainer key; + key.set_id("content"); + const std::string content_key("0123456789abcdef0123456789abcdef"); + key.set_key(content_key); + key.set_type(License::KeyContainer::CONTENT); + std::map oem_content_key_map; + // Expect to find the 'content key'. + std::string encryption_key; + EXPECT_OK(GetOemEncryptionKeyForKCB(session_impl_, oem_content_key_map, key, + &encryption_key)); + EXPECT_EQ(key.key(), encryption_key); +} + INSTANTIATE_TEST_SUITE_P(SessionVmpTests, SessionVmpTest, ::testing::Values(PLATFORM_NO_VERIFICATION, PLATFORM_UNVERIFIED, diff --git a/media_cas_proxy_sdk/external/common/wvpl/BUILD b/media_cas_proxy_sdk/external/common/wvpl/BUILD index 7e57754..19055d6 100644 --- a/media_cas_proxy_sdk/external/common/wvpl/BUILD +++ b/media_cas_proxy_sdk/external/common/wvpl/BUILD @@ -40,7 +40,7 @@ cc_library( "//base", "@abseil_repo//absl/synchronization", "//sdk/external/common/wvpl:sdk", - "//protos/public:media_cas_license_proto", + "//protos/public:media_cas_license_cc_proto", ], ) @@ -54,16 +54,17 @@ cc_library( ], copts = PUBLIC_COPTS, deps = [ + "//external:protobuf", "@abseil_repo//absl/strings", "@abseil_repo//absl/synchronization", "//common:error_space", "//common:status", "//sdk/external/common/wvpl:wvpl_sdk_session", "//sdk/external/common/wvpl:wvpl_types", - "//protos/public:errors_proto", - "//protos/public:license_protocol_proto", - "//protos/public:media_cas_encryption_proto", - "//protos/public:media_cas_license_proto", + "//protos/public:errors_cc_proto", + "//protos/public:license_protocol_cc_proto", + "//protos/public:media_cas_encryption_cc_proto", + "//protos/public:media_cas_license_cc_proto", ], ) @@ -85,7 +86,7 @@ cc_library( "//common:error_space", "//sdk/external/common/wvpl:wvpl_sdk_environment", "//sdk/external/common/wvpl:wvpl_sdk_session", - "//protos/public:media_cas_license_proto", + "//protos/public:media_cas_license_cc_proto", ], ) @@ -95,6 +96,7 @@ cc_test( deps = [ ":wvpl_cas_proxy_environment", ":wvpl_cas_proxy_session", + "//external:protobuf", "//testing:gunit", "//testing:gunit_main", "@abseil_repo//absl/memory", @@ -103,10 +105,10 @@ cc_test( "//common:drm_root_certificate", "//common:status", "//sdk/external/common/wvpl:wvpl_types", - "//protos/public:errors_proto", - "//protos/public:license_protocol_proto", - "//protos/public:media_cas_encryption_proto", - "//protos/public:media_cas_license_proto", + "//protos/public:errors_cc_proto", + "//protos/public:license_protocol_cc_proto", + "//protos/public:media_cas_encryption_cc_proto", + "//protos/public:media_cas_license_cc_proto", ], ) diff --git a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.cc b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.cc index c50b822..4aabf3e 100644 --- a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.cc +++ b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_environment.cc @@ -36,11 +36,11 @@ WvPLCASProxyEnvironment::WvPLCASProxyEnvironment( } it = config_values.find(kProviderIv); if (it != config_values.end()) { - provider_iv_ = new std::string(absl::HexStringToBytes((*it).second)); + provider_iv_ = absl::HexStringToBytes((*it).second); } it = config_values.find(kProviderKey); if (it != config_values.end()) { - provider_key_ = new std::string(absl::HexStringToBytes((*it).second)); + provider_key_ = absl::HexStringToBytes((*it).second); } } diff --git a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.h b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.h index c938651..f4d6f90 100644 --- a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.h +++ b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session.h @@ -12,11 +12,9 @@ #include #include #include -#include "sdk/external/common/wvpl/wvpl_sdk_session.h" -namespace absl { -class Mutex; -} // namespace absl +#include "absl/synchronization/mutex.h" +#include "sdk/external/common/wvpl/wvpl_sdk_session.h" namespace widevine { class CasDrmLicenseRequest; @@ -27,7 +25,7 @@ namespace wv_pl_sdk { constexpr uint32_t kMajorVersion = 1; constexpr uint32_t kMinorVersion = 1; -constexpr uint32_t kRelease = 3; +constexpr uint32_t kRelease = 5; /** * Models a session for Widevine CAS Proxy functionality for diff --git a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session_test.cc b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session_test.cc index 143f093..0c3ef1d 100644 --- a/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session_test.cc +++ b/media_cas_proxy_sdk/external/common/wvpl/wvpl_cas_proxy_session_test.cc @@ -177,6 +177,13 @@ TEST_F(WvPLCASProxySessionTest, ParseValidLicenseRequest) { EXPECT_GT(names_values.size(), 0); for (const auto name_value : names_values) { } + WvPLDeviceInfo wvpl_device_info; + status = wvpl_cas_proxy_session_->GetDeviceInfo(&wvpl_device_info); + EXPECT_TRUE(status.ok()); + EXPECT_EQ( + "\x8B\x88" + "2\xFBy!\xB3\xE6T\x19\xC6\x11/\x96\xD1\xCE", + wvpl_device_info.drm_certificate_serial_number()); } TEST_F(WvPLCASProxySessionTest, ParseEmptyLicenseRequest) { diff --git a/protos/public/BUILD b/protos/public/BUILD index f40aad1..8d5da15 100644 --- a/protos/public/BUILD +++ b/protos/public/BUILD @@ -29,6 +29,7 @@ proto_library( "license_server_sdk.proto", "provisioned_device_info.proto", "remote_attestation.proto", + "signed_device_info.proto", "signed_drm_certificate.proto", "verified_media_pipeline.proto", "widevine_pssh.proto", @@ -44,228 +45,238 @@ java_proto_library( ) proto_library( - name = "license_services_proto_base", + name = "license_services_proto", srcs = ["license_services.proto"], deps = [ - ":client_identification_proto_base", - ":license_protocol_proto_base", - ":license_server_sdk_proto_base", - ":errors_proto_base", + ":client_identification_proto", + ":license_protocol_proto", + ":license_server_sdk_proto", + ":errors_proto", ], ) cc_proto_library( - name = "license_services_proto", - deps = [":license_services_proto_base"], + name = "license_services_cc_proto", + deps = [":license_services_proto"], ) java_proto_library( name = "license_services_java_proto", - deps = [":license_services_proto_base"], + deps = [":license_services_proto"], ) proto_library( - name = "client_identification_proto_base", + name = "client_identification_proto", srcs = ["client_identification.proto"], ) cc_proto_library( - name = "client_identification_proto", - deps = [":client_identification_proto_base"], + name = "client_identification_cc_proto", + deps = [":client_identification_proto"], ) java_proto_library( name = "client_identification_java_proto", - deps = [":client_identification_proto_base"], + deps = [":client_identification_proto"], ) proto_library( - name = "device_certificate_status_proto_base", + name = "device_certificate_status_proto", srcs = ["device_certificate_status.proto"], - deps = [":provisioned_device_info_proto_base"], + deps = [":provisioned_device_info_proto"], ) cc_proto_library( - name = "device_certificate_status_proto", - deps = [":device_certificate_status_proto_base"], + name = "device_certificate_status_cc_proto", + deps = [":device_certificate_status_proto"], ) java_proto_library( name = "device_certificate_status_java_proto", - deps = [":device_certificate_status_proto_base"], + deps = [":device_certificate_status_proto"], ) proto_library( - name = "sdk_stats_proto_base", + name = "sdk_stats_proto", srcs = ["sdk_stats.proto"], ) cc_proto_library( - name = "sdk_stats_proto", - deps = [":sdk_stats_proto_base"], + name = "sdk_stats_cc_proto", + deps = [":sdk_stats_proto"], ) java_proto_library( name = "sdk_stats_java_proto", - deps = [":sdk_stats_proto_base"], + deps = [":sdk_stats_proto"], ) proto_library( - name = "drm_certificate_proto_base", + name = "drm_certificate_proto", srcs = ["drm_certificate.proto"], ) cc_proto_library( - name = "drm_certificate_proto", - deps = [":drm_certificate_proto_base"], + name = "drm_certificate_cc_proto", + deps = [":drm_certificate_proto"], ) java_proto_library( name = "drm_certificate_java_proto", - deps = [":drm_certificate_proto_base"], + deps = [":drm_certificate_proto"], ) proto_library( - name = "errors_proto_base", + name = "errors_proto", srcs = ["errors.proto"], ) cc_proto_library( - name = "errors_proto", - deps = [":errors_proto_base"], + name = "errors_cc_proto", + deps = [":errors_proto"], ) java_proto_library( name = "errors_java_proto", - deps = [":errors_proto_base"], + deps = [":errors_proto"], ) proto_library( - name = "license_protocol_proto_base", + name = "license_protocol_proto", srcs = ["license_protocol.proto"], deps = [ - ":client_identification_proto_base", - ":remote_attestation_proto_base", + ":client_identification_proto", + ":remote_attestation_proto", ], ) cc_proto_library( - name = "license_protocol_proto", - deps = [":license_protocol_proto_base"], + name = "license_protocol_cc_proto", + deps = [":license_protocol_proto"], ) java_proto_library( name = "license_protocol_java_proto", - deps = [":license_protocol_proto_base"], + deps = [":license_protocol_proto"], ) proto_library( - name = "license_server_sdk_proto_base", + name = "license_server_sdk_proto", srcs = ["license_server_sdk.proto"], - deps = [":license_protocol_proto_base", - ":widevine_pssh_proto_base"], + deps = [":license_protocol_proto", + ":widevine_pssh_proto"], ) cc_proto_library( - name = "license_server_sdk_proto", - deps = [":license_server_sdk_proto_base"], + name = "license_server_sdk_cc_proto", + deps = [":license_server_sdk_proto"], ) java_proto_library( name = "license_server_sdk_java_proto", - deps = [":license_server_sdk_proto_base"], + deps = [":license_server_sdk_proto"], ) proto_library( - name = "provisioned_device_info_proto_base", + name = "provisioned_device_info_proto", srcs = ["provisioned_device_info.proto"], ) cc_proto_library( - name = "provisioned_device_info_proto", - deps = [":provisioned_device_info_proto_base"], + name = "provisioned_device_info_cc_proto", + deps = [":provisioned_device_info_proto"], ) java_proto_library( name = "provisioned_device_info_java_proto", - deps = [":provisioned_device_info_proto_base"], + deps = [":provisioned_device_info_proto"], ) proto_library( - name = "remote_attestation_proto_base", + name = "remote_attestation_proto", srcs = ["remote_attestation.proto"], - deps = [":client_identification_proto_base"], + deps = [":client_identification_proto"], ) cc_proto_library( - name = "remote_attestation_proto", - deps = [":remote_attestation_proto_base"], + name = "remote_attestation_cc_proto", + deps = [":remote_attestation_proto"], ) java_proto_library( name = "remote_attestation_java_proto", - deps = [":remote_attestation_proto_base"], + deps = [":remote_attestation_proto"], ) proto_library( - name = "signed_drm_certificate_proto_base", + name = "signed_device_info_proto", + srcs = ["signed_device_info.proto"], +) + +cc_proto_library( + name = "signed_device_info_cc_proto", + deps = [":signed_device_info_proto"], +) + +proto_library( + name = "signed_drm_certificate_proto", srcs = ["signed_drm_certificate.proto"], ) cc_proto_library( - name = "signed_drm_certificate_proto", - deps = [":signed_drm_certificate_proto_base"], + name = "signed_drm_certificate_cc_proto", + deps = [":signed_drm_certificate_proto"], ) proto_library( - name = "verified_media_pipeline_proto_base", + name = "verified_media_pipeline_proto", srcs = ["verified_media_pipeline.proto"], ) cc_proto_library( - name = "verified_media_pipeline_proto", - deps = [":verified_media_pipeline_proto_base"], + name = "verified_media_pipeline_cc_proto", + deps = [":verified_media_pipeline_proto"], ) proto_library( - name = "widevine_pssh_proto_base", + name = "widevine_pssh_proto", srcs = ["widevine_pssh.proto"], ) cc_proto_library( - name = "widevine_pssh_proto", - deps = [":widevine_pssh_proto_base"], + name = "widevine_pssh_cc_proto", + deps = [":widevine_pssh_proto"], ) java_proto_library( name = "widevine_pssh_java_proto", - deps = [":widevine_pssh_proto_base"], + deps = [":widevine_pssh_proto"], ) proto_library( - name = "media_cas_license_proto_base", + name = "media_cas_license_proto", srcs = ["media_cas_license.proto"], deps = [ - ":license_protocol_proto_base", - ":license_server_sdk_proto_base", - ":errors_proto_base", - ":media_cas_encryption_proto_base", + ":license_protocol_proto", + ":license_server_sdk_proto", + ":errors_proto", + ":media_cas_encryption_proto", ], ) cc_proto_library( - name = "media_cas_license_proto", - deps = [":media_cas_license_proto_base"], + name = "media_cas_license_cc_proto", + deps = [":media_cas_license_proto"], ) proto_library( - name = "media_cas_encryption_proto_base", + name = "media_cas_encryption_proto", srcs = ["media_cas_encryption.proto"], ) cc_proto_library( - name = "media_cas_encryption_proto", - deps = [":media_cas_encryption_proto_base"], + name = "media_cas_encryption_cc_proto", + deps = [":media_cas_encryption_proto"], ) diff --git a/protos/public/client_identification.proto b/protos/public/client_identification.proto index 79c83aa..80d4046 100644 --- a/protos/public/client_identification.proto +++ b/protos/public/client_identification.proto @@ -13,8 +13,8 @@ syntax = "proto2"; package widevine; -option java_package = "com.google.video.widevine.protos"; +option java_package = "com.google.video.widevine.protos"; option java_outer_classname = "ClientIdentificationProtos"; // ClientIdentification message used to authenticate the client device. @@ -110,8 +110,8 @@ message EncryptedClientIdentification { // Serial number for the service certificate for which ClientIdentification is // encrypted. optional bytes service_certificate_serial_number = 2; - // Serialized ClientIdentification message, encrypted with the privacy key using - // AES-128-CBC with PKCS#5 padding. + // Serialized ClientIdentification message, encrypted with the privacy key + // using AES-128-CBC with PKCS#5 padding. optional bytes encrypted_client_id = 3; // Initialization vector needed to decrypt encrypted_client_id. optional bytes encrypted_client_id_iv = 4; diff --git a/protos/public/device_certificate_status.proto b/protos/public/device_certificate_status.proto index 75c9f62..fe18a4e 100644 --- a/protos/public/device_certificate_status.proto +++ b/protos/public/device_certificate_status.proto @@ -14,11 +14,11 @@ syntax = "proto2"; package widevine; +import "protos/public/provisioned_device_info.proto"; + option java_outer_classname = "DeviceCertificateStatusProtos"; option java_package = "com.google.video.widevine.protos"; -import "protos/public/provisioned_device_info.proto"; - // Contains DRM and OEM certificate status and device information for a // specific system ID. // TODO(user): Move this to its own file. @@ -72,42 +72,3 @@ message SignedDeviceCertificateStatusList { // key using RSASSA-PSS. Required. optional bytes signature = 2; } - -// A signed request sent to Widevine Provisioning Server (keysmith) to retrieve -// 'DeviceCertificateStatusList'. -message SignedDeviceCertificateStatusListRequest { - // Serialized DeviceCertificateStatusListRequest. Required. - optional bytes device_certificate_status_list_request = 1; - // Signature of device_certificate_status_list_request. Signed with root - // certificate private key using RSASSA-PSS. Required. - optional bytes signature = 2; -} - -// A request sent to Widevine Provisioning Server (keysmith) to retrieve -// 'DeviceCertificateStatusList'. -message DeviceCertificateStatusListRequest { - // The version of sdk. Required. - optional string sdk_version = 1; - // POSIX time, in seconds, when this request was created. Required. - optional uint64 sdk_time_seconds = 2; -} - -// Contains response from Widevine Provisioning Server with status and -// DeviceCertificateStatusList information. -message DeviceCertificateStatusListResponse { - enum Status { - UNKNOWN = 0; - OK = 1; - SIGNATURE_FAILED = 2; - NOT_AUTHORIZED = 3; - AUTHORIZATION_EXPIRED = 4; - PROVIDER_ID_MISSING = 5; - INTERNAL_ERROR = 6; - } - // Status returned by the Widevine Provisioning Server. Required. - optional Status status = 1; - // String message returned by the Widevine Provisioning Server. - optional string status_message = 2; - // Serialized SignedDeviceCertificateStatusList. Required. - optional bytes signed_device_certificate_status_list = 3; -} diff --git a/protos/public/drm_certificate.proto b/protos/public/drm_certificate.proto index e03a74b..0fb3ce6 100644 --- a/protos/public/drm_certificate.proto +++ b/protos/public/drm_certificate.proto @@ -28,7 +28,11 @@ message DrmCertificate { PROVISIONER = 4; } enum ServiceType { - UNKNOWN = 0; LICENSE_SERVER_SDK = 1; LICENSE_SERVER_PROXY_SDK = 2; + UNKNOWN_SERVICE_TYPE = 0; + LICENSE_SERVER_SDK = 1; + LICENSE_SERVER_PROXY_SDK = 2; + PROVISIONING_SDK = 3; + CAS_PROXY_SDK = 4; } // Type of certificate. Required. optional Type type = 1; @@ -50,6 +54,8 @@ message DrmCertificate { // certificate. Required for service and provisioner certificates. optional string provider_id = 7; // This field is used only when type = SERVICE to specify which SDK uses - // service certificate. - optional ServiceType service_type = 8 [default = UNKNOWN]; + // service certificate. This repeated field is treated as a set. A certificate + // may be used for the specified service SDK if the appropriate ServiceType + // is specified in this field. + repeated ServiceType service_types = 8; } diff --git a/protos/public/errors.proto b/protos/public/errors.proto index ef7c511..60d7751 100644 --- a/protos/public/errors.proto +++ b/protos/public/errors.proto @@ -12,6 +12,7 @@ syntax = "proto2"; package widevine; + option java_package = "com.google.video.widevine.protos"; @@ -245,4 +246,10 @@ enum Errors { // Even Key not specified, CasEncryptionResponse.KeyInfo.KeySlot MISSING_EVEN_KEY = 172; + // VMP verification required for this platform, however VMP data is missing. + VMP_ERROR_PLATFORM_NOT_VERIFIED = 173; + + // VMP verification failed this platform, perhaps was tampered with. + VMP_ERROR_PLATFORM_TAMPERED = 174; + } diff --git a/protos/public/license_protocol.proto b/protos/public/license_protocol.proto index 1f882e1..ba23344 100644 --- a/protos/public/license_protocol.proto +++ b/protos/public/license_protocol.proto @@ -14,11 +14,12 @@ syntax = "proto2"; package widevine; -option java_package = "com.google.video.widevine.protos"; import "protos/public/client_identification.proto"; import "protos/public/remote_attestation.proto"; +option java_package = "com.google.video.widevine.protos"; + // option optimize_for = LITE_RUNTIME; enum LicenseType { STREAMING = 1; @@ -117,11 +118,12 @@ message License { message KeyContainer { enum KeyType { - SIGNING = 1; // Exactly one key of this type must appear. - CONTENT = 2; // Content key. - KEY_CONTROL = 3; // Key control block for license renewals. No key. + SIGNING = 1; // Exactly one key of this type must appear. + CONTENT = 2; // Content key. + KEY_CONTROL = 3; // Key control block for license renewals. No key. OPERATOR_SESSION = 4; // wrapped keys for auxiliary crypto operations. - ENTITLEMENT = 5; // Entitlement keys. + ENTITLEMENT = 5; // Entitlement keys. + OEM_CONTENT = 6; // Partner-specific content key. } // The SecurityLevel enumeration allows the server to communicate the level @@ -269,7 +271,6 @@ message License { [default = PLATFORM_NO_VERIFICATION]; // IDs of the groups for which keys are delivered in this license, if any. repeated bytes group_ids = 11; - } enum ProtocolVersion { @@ -381,6 +382,15 @@ message MetricData { repeated TypeValue metric_data = 2; } +message VersionInfo { + // License SDK version reported by the Widevine License SDK. This field + // is populated automatically by the SDK. + optional string license_sdk_version = 1; + // Version of the service hosting the license SDK. This field is optional. + // It may be provided by the hosting service. + optional string license_service_version = 2; +} + message SignedMessage { enum MessageType { LICENSE_REQUEST = 1; @@ -403,4 +413,7 @@ message SignedMessage { optional RemoteAttestation remote_attestation = 5; repeated MetricData metric_data = 6; + // Version information from the SDK and license service. This information is + // provided in the license response. + optional VersionInfo service_version_info = 7; } diff --git a/protos/public/license_server_sdk.proto b/protos/public/license_server_sdk.proto index b089474..a5f9387 100644 --- a/protos/public/license_server_sdk.proto +++ b/protos/public/license_server_sdk.proto @@ -14,11 +14,12 @@ syntax = "proto2"; package widevine; -option java_package = "com.google.video.widevine.protos"; import "protos/public/license_protocol.proto"; import "protos/public/widevine_pssh.proto"; +option java_package = "com.google.video.widevine.protos"; + // This message is used to pass optional data on initial license issuance. // LINT.IfChange message SessionInit { diff --git a/protos/public/license_services.proto b/protos/public/license_services.proto index 923df54..d54419f 100644 --- a/protos/public/license_services.proto +++ b/protos/public/license_services.proto @@ -11,9 +11,12 @@ syntax = "proto2"; option java_package = "com.google.video.widevine.licensing"; + import "protos/public/client_identification.proto"; import "protos/public/license_protocol.proto"; + import "protos/public/license_server_sdk.proto"; + package widevine; // TODO(user): refactor license_services.proto and sdk_stats.proto. @@ -79,6 +82,11 @@ message ModularDrmLicenseRequest { // be reported. repeated License.KeyContainer.VideoResolutionConstraint video_resolution_constraints = 10; + + // Video feature associated with this key. Common value is HDR. + // If specified and the key_id/key is not specified, this value will be + // used to derive the key_id/key. + optional string video_feature = 11; } // Specifies a list of content keys and policies to be included in a license. @@ -207,6 +215,38 @@ message ModularDrmLicenseRequest { // field by using the ProxySDK(internally, RequestInspector) API. optional PlatformVerificationStatus platform_verification_status = 26 [default = PLATFORM_NO_VERIFICATION]; + + // By default, a license request will fail if VMP status is unverified for + // Chrome. Set this field to 'true' to allow license request to succeed when + // VMP status is unverified for Chrome platforms. + // TODO (b/126434032) Change the default to false once partners are notified + optional bool allow_unverified_platform = 27 [default = true]; + + // By default, a license request will fail if VMP status is tampered for + // Chrome. Set this field to 'true' to allow license request to succeed when + // VMP status is tampered for Chrome platforms. + // TODO (b/126434032) Change the default to false once partners are notified + optional bool allow_tampered_platform = 28 [default = true]; + + // A shortcut for specifying whether to return keys for the video feature only + // or to return all keys or ignore the video feature. + // The VideoFeatureKeySet only applies when video feature is specified in the + // PSSH. + enum VideoFeatureKeySet { + VF_UNSPECIFIED = 0; + // License should not include keys for the video feature, instead only + // include keys not associated with the video feature. + VF_EXCLUDED = 1; + // License should only include keys associated with the video feature + // (e.g., HDR). + VF_ONLY = 2; + // License should include keys for the video feature and also for keys + // not associated with the video feature (e.g., SDR keys). + VF_INCLUDED = 3; + } + // This field is ignored if one or more content_key_specs is specified. + optional VideoFeatureKeySet video_feature_key_set = 29 + [default = VF_EXCLUDED]; } @@ -291,6 +331,7 @@ message ModularDrmLicenseResponse { message Track { optional string type = 1; optional bytes key_id = 2; + optional string video_feature = 3; } // A subset of data from the Widevine PSSH. message Pssh { @@ -310,7 +351,7 @@ message ModularDrmLicenseResponse { optional bool remote_attestation_verified = 8; // Widevine-defined security level. optional uint32 security_level = 9; - // Actual SDK license status as defined in widevine/server/sdk/error.proto. + // Actual SDK license status as defined in widevine/protos/public/errors.proto optional uint32 internal_status = 10; // Usage report sent in a license release. optional SessionUsage session_usage = 11; @@ -342,7 +383,7 @@ message ModularDrmLicenseResponse { optional bool is_live = 24 [default = false]; // Platform verification status optional PlatformVerificationStatus platform_verification_status = 25 - [default = PLATFORM_UNVERIFIED]; + [default = PLATFORM_UNVERIFIED]; // The "provider" field in ModularDrmLicenseRequest. optional string content_owner = 26; // The "requesting_provider" in ModularDrmLicenseRequest. If diff --git a/protos/public/media_cas_encryption.proto b/protos/public/media_cas_encryption.proto index 81d0195..84f8044 100644 --- a/protos/public/media_cas_encryption.proto +++ b/protos/public/media_cas_encryption.proto @@ -10,10 +10,10 @@ syntax = "proto2"; -option java_package = "com.google.video.widevine.mediacasencryption"; - package widevine; +option java_package = "com.google.video.widevine.mediacasencryption"; + message CasEncryptionRequest { optional bytes content_id = 1; optional string provider = 2; @@ -43,7 +43,7 @@ message CasEncryptionResponse { SINGLE = 1; EVEN = 2; ODD = 3; - }; + } optional bytes key_id = 1; optional bytes key = 2; // Optional label used for the key. diff --git a/protos/public/media_cas_license.proto b/protos/public/media_cas_license.proto index 42f2c91..94a0f97 100644 --- a/protos/public/media_cas_license.proto +++ b/protos/public/media_cas_license.proto @@ -14,6 +14,7 @@ syntax = "proto2"; option java_package = "com.google.video.widevine.mediacaslicense"; + import "protos/public/license_protocol.proto"; import "protos/public/license_server_sdk.proto"; import "protos/public/media_cas_encryption.proto"; @@ -70,7 +71,7 @@ message CasDrmLicenseResponse { // TODO(user): Until a CAS license protocol is defined, this field is a // serialized License message defined in license_protocol.proto. optional bytes license = 3; - // Actual SDK license status as defined in widevine/server/sdk/error.proto. + // Actual SDK license status as defined in widevine/protos/public/errors.proto optional uint32 internal_status = 4; // Indicates the type of message in the license response. optional SignedMessage.MessageType message_type = 5; @@ -78,6 +79,8 @@ message CasDrmLicenseResponse { message PsshData { repeated bytes key_id = 1; optional bytes content_id = 2; + // If this is a group key license, this is the group identifier. + optional bytes group_id = 3; } message LicenseMetadata { optional bytes content_id = 1; @@ -88,6 +91,27 @@ message CasDrmLicenseResponse { optional string content_owner = 8; optional string content_provider = 9; optional LicenseMetadata license_metadata = 10; + message DeviceInfo { + // Make as identified from the provisioned device info. If that is not + // available, the device make will be retrieved from the license request. + optional string make = 1; + // Model as identified from the provisioned device info. If that is not + // available, the device model will be retrieved from the license request. + optional string model = 2; + // Widevine-defined device security level. + optional uint32 security_level = 3; + // Globally unique serial number of certificate associated with this + // device. + optional bytes drm_cert_serial_number = 4; + // Platform specifies the OS or device type and perhaps other software + // information for the device receving this license response. + // Example: Android, iOS, Chrome, PC. + optional string platform = 5; + // SystemID of the requesting device. + optional uint32 system_id = 6; + } + // Device information for the device making the CAS license request. + optional DeviceInfo device_info = 11; } message SignedCasDrmRequest { @@ -103,8 +127,3 @@ message SignedCasDrmRequest { optional string user_agent = 5; optional string provider = 6; } - -message SignedCasDrmResponse { - optional bytes response = 1; - optional bytes signature = 2; -} diff --git a/protos/public/remote_attestation.proto b/protos/public/remote_attestation.proto index d8a8551..618706b 100644 --- a/protos/public/remote_attestation.proto +++ b/protos/public/remote_attestation.proto @@ -13,10 +13,11 @@ syntax = "proto2"; package widevine; -option java_package = "com.google.video.widevine.protos"; import "protos/public/client_identification.proto"; +option java_package = "com.google.video.widevine.protos"; + message RemoteAttestation { // Encrypted ClientIdentification message containing the device remote // attestation certificate. Required. @@ -27,4 +28,3 @@ message RemoteAttestation { // Signed remote attestation challenge + salt. Required. optional bytes signature = 3; } - diff --git a/protos/public/sdk_stats.proto b/protos/public/sdk_stats.proto index 904483f..170d265 100644 --- a/protos/public/sdk_stats.proto +++ b/protos/public/sdk_stats.proto @@ -9,14 +9,14 @@ // // Main protocol buffers for Widevine external SDK // licensing statistics. -// Design doc: https://docs.google.com/document/d/1yyt5TxApYbI0N07aH94zwnKYuzYdFcmqZtC3jCyph8k/edit# +// Design doc: +// https://docs.google.com/document/d/1yyt5TxApYbI0N07aH94zwnKYuzYdFcmqZtC3jCyph8k/edit# syntax = "proto2"; package widevine; option java_package = "com.google.video.widevine.protos"; - option java_outer_classname = "LicenseStatsProtos"; @@ -29,14 +29,16 @@ message DeviceLicenseCounterByStatus { } message DeviceLicenseCounterByModel { - // The model of the device sending a license request to the Widevine SDK. Optional. + // The model of the device sending a license request to the Widevine SDK. + // Optional. optional string device_model = 1; // license status specific breakdown of counter data repeated DeviceLicenseCounterByStatus counter_by_status = 2; } message DeviceLicenseCounterByMake { - // The make of the device sending a license request to the Widevine SDK. Optional. + // The make of the device sending a license request to the Widevine SDK. + // Optional. optional string device_make = 1; // device model specific breakdown of counter data. repeated DeviceLicenseCounterByModel counter_by_model = 2; diff --git a/protos/public/signed_device_info.proto b/protos/public/signed_device_info.proto new file mode 100644 index 0000000..d40830b --- /dev/null +++ b/protos/public/signed_device_info.proto @@ -0,0 +1,66 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright 2019 Google LLC. +// +// This software is licensed under the terms defined in the Widevine Master +// License Agreement. For a copy of this agreement, please contact +// widevine-licensing@google.com. +//////////////////////////////////////////////////////////////////////////////// +// +// This is a pared down copy of the file in: +// +// Do not modify this file without keeping the other file in sync. +// Items in the original file but removed here include. +// - comments +// - SignedDeviceInfoService definition +// - Google api annotations. +// +// TODO(user, yawenyu): Figure out how we can avoid having two copies +// of the same file. + +syntax = "proto3"; + +package widevine; + +option csharp_namespace = "Google.Chrome.Widevine.Deviceinfo.V1"; +option java_multiple_files = true; +option java_outer_classname = "DeviceCertificateStatusProtos"; +option java_package = "com.google.chrome.widevine.deviceinfo.v1"; + +// GCWDI == Google Chrome Widevine Device Info +option objc_class_prefix = "GCWDI"; + +// A request sent to Widevine Provisioning Server (keysmith) to retrieve +// 'DeviceCertificateStatusList'. +message DeviceCertificateStatusListRequest { + // The version of sdk. Required. + string sdk_version = 1; + // POSIX time, in seconds, when this request was created. Required. + uint64 sdk_time_seconds = 2; + // The serialized service certificate used to sign the request. Required. + bytes service_certificate = 3; +} + +// A device certificate status resource in the Widevine Signed Device Info +// API. It intended to carry information about DRM and OEM certificate +// status and device information for a specific system ID. The information is +// intended to be shared publicly. + +// A signed request sent to Widevine Provisioning Server (keysmith) to retrieve +// 'DeviceCertificateStatusList'. +message SignedDeviceInfoRequest { + // A serialized DeviceCertificateStatusListRequest. Required. + bytes device_certificate_status_list_request = 1; + + // TODO(user): What's the signature algorithm and the key used? + bytes signature = 2; +} + +// Contains a serialized DeviceCertificateStatusList and the signature. +message SignedDeviceInfo { + // Serialized DeviceCertificateStatusList. Required. + bytes device_certificate_status_list = 1; + + // Signature of device_certificate_status_list_request. Signed with root + // certificate private key using RSASSA-PSS. Required. + bytes signature = 2; +} diff --git a/protos/public/verified_media_pipeline.proto b/protos/public/verified_media_pipeline.proto index b7ce324..bb1e033 100644 --- a/protos/public/verified_media_pipeline.proto +++ b/protos/public/verified_media_pipeline.proto @@ -12,10 +12,10 @@ syntax = "proto2"; -option optimize_for = LITE_RUNTIME; - package vmp; +option optimize_for = LITE_RUNTIME; + message VmpData { message SignedBinaryInfo { // File name of the binary. Required. diff --git a/protos/public/widevine_pssh.proto b/protos/public/widevine_pssh.proto index 01ec1f9..77f45e5 100644 --- a/protos/public/widevine_pssh.proto +++ b/protos/public/widevine_pssh.proto @@ -12,6 +12,7 @@ syntax = "proto2"; package widevine; + option java_package = "com.google.video.widevine.protos"; message WidevinePsshData { @@ -75,8 +76,8 @@ message WidevinePsshData { // Group identifiers for all groups to which the content belongs. This can // be used to deliver licenses to unlock multiple titles / channels. - // Optional, and may only be present in ENTITLEMENT and ENTITLED_KEY PSSHs, and - // not in conjunction with key_ids. + // Optional, and may only be present in ENTITLEMENT and ENTITLED_KEY PSSHs, + // and not in conjunction with key_ids. repeated bytes group_ids = 13; // Copy/copies of the content key used to decrypt the media stream in which @@ -86,11 +87,17 @@ message WidevinePsshData { // PSSHs of type ENTITLED_KEY. repeated EntitledKey entitled_keys = 14; + // Video feature identifier, which is used in conjunction with |content_id| + // to determine the set of keys to be returned in the license. Cannot be + // present in conjunction with |key_ids|. + // Current values are "HDR". + optional string video_feature = 15; + //////////////////////////// Deprecated Fields //////////////////////////// enum Algorithm { UNENCRYPTED = 0; AESCTR = 1; - }; + } optional Algorithm algorithm = 1 [deprecated = true]; optional string provider = 3 [deprecated = true]; optional string track_type = 5 [deprecated = true]; diff --git a/sdk/external/common/wvpl/BUILD b/sdk/external/common/wvpl/BUILD index 0a038f5..9c8d3c3 100644 --- a/sdk/external/common/wvpl/BUILD +++ b/sdk/external/common/wvpl/BUILD @@ -42,20 +42,20 @@ cc_library( "//base", "@abseil_repo//absl/memory", "//common:client_cert", - "//common:drm_root_certificate", "//common:drm_service_certificate", "//common:error_space", "//common:remote_attestation_verifier", "//common:status", "//common:verified_media_pipeline", "//license_server_sdk/internal:sdk", - "//protos/public:client_identification_proto", - "//protos/public:device_certificate_status_proto", - "//protos/public:errors_proto", - "//protos/public:license_protocol_proto", - "//protos/public:license_server_sdk_proto", - "//protos/public:provisioned_device_info_proto", - "//protos/public:widevine_pssh_proto", + "//protos/public:client_identification_cc_proto", + "//protos/public:drm_certificate_cc_proto", + "//protos/public:errors_cc_proto", + "//protos/public:license_protocol_cc_proto", + "//protos/public:license_server_sdk_cc_proto", + "//protos/public:provisioned_device_info_cc_proto", + "//protos/public:signed_drm_certificate_cc_proto", + "//protos/public:widevine_pssh_cc_proto", ], ) @@ -73,18 +73,19 @@ cc_library( "//base", "@abseil_repo//absl/strings", "@abseil_repo//absl/synchronization", - "//common:aes_cbc_util", "//common:device_status_list", "//common:drm_root_certificate", "//common:error_space", "//common:drm_service_certificate", - "//common:sha_util", "//common:status", "//common:vmp_checker", "//license_server_sdk/internal:sdk", - "//protos/public:device_certificate_status_proto", - "//protos/public:errors_proto", - "//protos/public:provisioned_device_info_proto", + "//protos/public:device_certificate_status_cc_proto", + "//protos/public:errors_cc_proto", + "//protos/public:provisioned_device_info_cc_proto", + "//protos/public:drm_certificate_cc_proto", + "//protos/public:signed_device_info_cc_proto", + "//protos/public:signed_drm_certificate_cc_proto", ], ) @@ -102,24 +103,25 @@ cc_library( "@abseil_repo//absl/memory", "@abseil_repo//absl/strings", "@abseil_repo//absl/synchronization", - "//common:aes_cbc_util", "//common:client_cert", "//common:device_status_list", "//common:drm_root_certificate", "//common:drm_service_certificate", "//common:error_space", "//common:remote_attestation_verifier", - "//common:sha_util", "//common:status", "//common:verified_media_pipeline", "//common:vmp_checker", "//license_server_sdk/internal:sdk", - "//protos/public:client_identification_proto", - "//protos/public:device_certificate_status_proto", - "//protos/public:errors_proto", - "//protos/public:license_protocol_proto", - "//protos/public:license_server_sdk_proto", - "//protos/public:provisioned_device_info_proto", - "//protos/public:widevine_pssh_proto", + "//protos/public:client_identification_cc_proto", + "//protos/public:device_certificate_status_cc_proto", + "//protos/public:drm_certificate_cc_proto", + "//protos/public:errors_cc_proto", + "//protos/public:license_protocol_cc_proto", + "//protos/public:license_server_sdk_cc_proto", + "//protos/public:provisioned_device_info_cc_proto", + "//protos/public:signed_device_info_cc_proto", + "//protos/public:signed_drm_certificate_cc_proto", + "//protos/public:widevine_pssh_cc_proto", ], ) diff --git a/sdk/external/common/wvpl/wvpl_sdk_environment.cc b/sdk/external/common/wvpl/wvpl_sdk_environment.cc index 8d4daab..82ea90b 100644 --- a/sdk/external/common/wvpl/wvpl_sdk_environment.cc +++ b/sdk/external/common/wvpl/wvpl_sdk_environment.cc @@ -7,35 +7,37 @@ //////////////////////////////////////////////////////////////////////////////// #include "sdk/external/common/wvpl/wvpl_sdk_environment.h" + #include "glog/logging.h" #include "absl/strings/escaping.h" +#include "absl/strings/str_cat.h" #include "absl/synchronization/mutex.h" -#include "common/aes_cbc_util.h" #include "common/device_status_list.h" #include "common/drm_root_certificate.h" #include "common/drm_service_certificate.h" #include "common/error_space.h" -#include "common/sha_util.h" #include "common/status.h" #include "common/vmp_checker.h" #include "license_server_sdk/internal/generate_error_response.h" #include "protos/public/device_certificate_status.pb.h" +#include "protos/public/drm_certificate.pb.h" #include "protos/public/errors.pb.h" #include "protos/public/provisioned_device_info.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" using widevine::DeviceCertificateStatus; using widevine::DeviceCertificateStatusList; using widevine::DeviceStatusList; +using widevine::DrmCertificate; using widevine::DrmServiceCertificate; using widevine::error_space; using widevine::kCertificateTypeDevelopment; using widevine::kCertificateTypeProduction; using widevine::kCertificateTypeTesting; +using widevine::OkStatus; using widevine::ProvisionedDeviceInfo; -using widevine::SignedDeviceCertificateStatusList; +using widevine::SignedDrmCertificate; using widevine::VmpChecker; -using widevine::crypto_util::EncryptAesCbc; - namespace error = widevine::error; namespace widevine_server { @@ -82,8 +84,10 @@ WvPLSDKEnvironment::~WvPLSDKEnvironment() = default; WvPLStatus WvPLSDKEnvironment::SetDrmServiceCertificate( const std::string& service_certificate, const std::string& service_private_key, const std::string& service_private_key_passphrase) { - CHECK(drm_root_certificate()) << "DRM root certificate not set!"; - WvPLStatus wvpl_status = DrmServiceCertificate::AddDrmServiceCertificate( + DCHECK(drm_root_certificate()); + WvPLStatus wvpl_status = CheckServiceCertificateType(service_certificate); + if (!wvpl_status.ok()) return wvpl_status; + wvpl_status = DrmServiceCertificate::AddDrmServiceCertificate( drm_root_certificate(), service_certificate, service_private_key, service_private_key_passphrase); if (!wvpl_status.ok()) return wvpl_status; @@ -151,80 +155,24 @@ void WvPLSDKEnvironment::AddDeviceInfo( device_info_map.InsertOrUpdate(provisioned_device_info); } -WvPLStatus WvPLSDKEnvironment::GenerateSignature(const std::string& plain_text, - std::string* signature) { - DCHECK(signature); - if (plain_text.empty()) { - return WvPLStatus(error_space, error::INVALID_ARGUMENT, - "Plain_text for signature is empty."); - } - if (signature == nullptr) { - return WvPLStatus(error_space, error::INVALID_ARGUMENT, - "Signature must not be null."); - } - const std::map* config_values = GetConfigValue(); - std::map::const_iterator it = - config_values->find(kDrmCertificateType); - it = config_values->find(kProviderIv); - std::string provider_iv; - std::string provider_key; - if (it != config_values->end()) { - provider_iv = absl::HexStringToBytes((*it).second); - if (provider_iv.empty()) { - return WvPLStatus(error_space, error::NOT_FOUND, "Provider IV is empty."); - } - } - it = config_values->find(kProviderKey); - if (it != config_values->end()) { - provider_key = absl::HexStringToBytes((*it).second); - if (provider_key.empty()) { - return WvPLStatus(error_space, error::NOT_FOUND, - "Provider Key is empty."); - } - } - - std::string hashed_text = widevine::Sha1_Hash(plain_text); - if (hashed_text.empty()) { - return WvPLStatus(error_space, error::INVALID_ARGUMENT, - "Hash for signature is empty."); - } else { - *signature = EncryptAesCbc(provider_key, provider_iv, hashed_text); - if (signature->empty()) { - return WvPLStatus(error_space, error::INVALID_ARGUMENT, - "Generated signature failed"); - } - } - return WvPLStatus(); -} -std::map* WvPLSDKEnvironment::config_values_ = - new std::map(); -std::map* WvPLSDKEnvironment::GetConfigValue() { - return config_values_; -} - -void WvPLSDKEnvironment::SetConfigValue( - const std::map& config_values) { - config_values_->insert(config_values.begin(), config_values.end()); -} - WvPLStatus WvPLSDKEnvironment::SetDeviceCertificateStatusList( const std::string& cert_list) const { WvPLStatus status; - SignedDeviceCertificateStatusList device_certificate_status_list; - std::string decoded_certificate_status_list; - std::string device_certicate_status_list; - status = DeviceStatusList::ExtractFromProvisioningServiceResponse( - cert_list, &decoded_certificate_status_list, - &device_certicate_status_list); + std::string serialized_device_certicate_status_list; + std::string signature; + status = DeviceStatusList::ExtractFromServiceResponse( + cert_list, &serialized_device_certicate_status_list, &signature); if (!status.ok()) return status; DeviceCertificateStatusList certificate_status_list; - if (!certificate_status_list.ParseFromString(device_certicate_status_list)) { + if (!certificate_status_list.ParseFromString( + serialized_device_certicate_status_list)) { return WvPLStatus(error_space, widevine::INVALID_CERTIFICATE_STATUS_LIST, "certificate status list parse error"); } status = DeviceStatusList::Instance()->UpdateStatusList( - drm_root_certificate_->public_key(), decoded_certificate_status_list, + drm_root_certificate_->public_key(), + serialized_device_certicate_status_list, signature, device_certificate_expiration_seconds_); if (!status.ok()) return status; status = WvPLSDKEnvironment::UpdateProvisionedDeviceInfoMap( @@ -232,5 +180,44 @@ WvPLStatus WvPLSDKEnvironment::SetDeviceCertificateStatusList( return status; } +WvPLStatus WvPLSDKEnvironment::CheckServiceCertificateType( + const std::string& service_certificate) { + SignedDrmCertificate signed_drm_certificate; + DrmCertificate drm_certificate; + std::string drm_service_certificate; + if (signed_drm_certificate.ParseFromString(service_certificate)) { + drm_service_certificate = signed_drm_certificate.drm_certificate(); + } + if (!drm_certificate.ParseFromString(drm_service_certificate)) { + return WvPLStatus(error_space, error::INVALID_ARGUMENT, + "Fail to parse service_certificate"); + } + // TODO(user): after Q3-2019, only support UNKNOWN_SERVICE_TYPE for LSDK + // whose version is older than 5.0.Here is the design doc. + // (https://docs.google.com/document/d/1QVdQ6YfpjVqqNpRbikCWU7DdMHQLyUE4Mimzs9u61lc/edit#heading=h.gyz0lrew3paj) + if (drm_certificate.type() == DrmCertificate::SERVICE) { + if (GetExpectedServiceCertificateType() == + DrmCertificate::LICENSE_SERVER_SDK) { + return OkStatus(); + } + for (auto type : drm_certificate.service_types()) { + if (type == GetExpectedServiceCertificateType()) { + return OkStatus(); + } + } + return WvPLStatus( + error_space, error::INVALID_ARGUMENT, + absl::StrCat("Service type in drm certificate is mismatch")); + } + return WvPLStatus(error_space, error::INVALID_ARGUMENT, + absl::StrCat("Type", drm_certificate.type(), + "Service Certificate is wrong type")); +} + +widevine::DrmCertificate::ServiceType +WvPLSDKEnvironment::GetExpectedServiceCertificateType() { + return DrmCertificate::UNKNOWN_SERVICE_TYPE; +} + } // namespace wv_pl_sdk } // namespace widevine_server diff --git a/sdk/external/common/wvpl/wvpl_sdk_environment.h b/sdk/external/common/wvpl/wvpl_sdk_environment.h index 6061181..943345f 100644 --- a/sdk/external/common/wvpl/wvpl_sdk_environment.h +++ b/sdk/external/common/wvpl/wvpl_sdk_environment.h @@ -13,10 +13,12 @@ #include #include "sdk/external/common/wvpl/wvpl_types.h" +#include "protos/public/drm_certificate.pb.h" namespace widevine { class DeviceCertificateStatusList; class DrmRootCertificate; +class DrmCertificate; class ProvisionedDeviceInfo; } // namespace widevine namespace widevine_server { @@ -88,8 +90,6 @@ class WvPLSDKEnvironment { WvPLStatus SetDeviceCertificateStatusList(const std::string& cert_list) const; - static void SetConfigValue(const std::map& config_values); - // Number of seconds until the certificate status list expires after its // creation time. Default value is 604800 seconds. uint32_t device_certificate_expiration_seconds_ = 604800; @@ -99,10 +99,9 @@ class WvPLSDKEnvironment { // name of the provider hosting this service. std::string provider_; // value of the "iv" specified for the provider. - std::string* provider_iv_; + std::string provider_iv_; // value of the "key" specified for the provider. - std::string* provider_key_; - static std::map* config_values_; + std::string provider_key_; // is_service_certificate_loaded_ is not thread safe. bool is_service_certificate_loaded_ = false; // If true, allow devices not in the certificate status list. @@ -111,6 +110,15 @@ class WvPLSDKEnvironment { std::unique_ptr drm_root_certificate_; private: + // Get the expected service type for drm service certificate. + virtual widevine::DrmCertificate::ServiceType + GetExpectedServiceCertificateType(); + + // Check the type of |service_certificate|. Returns "OK" if the cert can be + // used for the current SDK, else an error status. + virtual WvPLStatus CheckServiceCertificateType( + const std::string& service_certificate); + /** * Return provisioned_device_info if the device_info_map_ contains system_id. * @@ -126,8 +134,6 @@ class WvPLSDKEnvironment { static void AddDeviceInfo( const widevine::ProvisionedDeviceInfo& provisioned_device_info); - static std::map* GetConfigValue(); - friend class WvPLSDKSession; friend class WvPLProxySession; friend class WvPLProxySessionTest; diff --git a/sdk/external/common/wvpl/wvpl_sdk_session.cc b/sdk/external/common/wvpl/wvpl_sdk_session.cc index 608883a..72b1235 100644 --- a/sdk/external/common/wvpl/wvpl_sdk_session.cc +++ b/sdk/external/common/wvpl/wvpl_sdk_session.cc @@ -19,10 +19,12 @@ #include "sdk/external/common/wvpl/wvpl_sdk_environment.h" #include "sdk/external/common/wvpl/wvpl_types.h" #include "protos/public/client_identification.pb.h" +#include "protos/public/drm_certificate.pb.h" #include "protos/public/errors.pb.h" #include "protos/public/license_protocol.pb.h" #include "protos/public/license_server_sdk.pb.h" #include "protos/public/provisioned_device_info.pb.h" +#include "protos/public/signed_drm_certificate.pb.h" #include "protos/public/widevine_pssh.pb.h" // TODO(user): Mark getProvisionedDeviceInfo as deprecated, move the @@ -34,7 +36,7 @@ // wvpl_sdk_session_test.cc. // TODO(user): Remove sdk_license_request_ and both proxy and wvpl LSDK set // signed_message_request_from_cdm_ when create session. -// TODO(user): Move all the protected memeber variables to private and use +// TODO(user): Move all the protected member variables to private and use // getter and setter to access it. // TODO(user): (b/119566765) Refactor ParseLicenseRequest and break it into // different classes. @@ -332,8 +334,6 @@ void WvPLSDKSession::CopySessionState( } } -MessageType WvPLSDKSession::message_type() const { return message_type_; } - void WvPLSDKSession::CopyHDCP( HDCP hdcp_value, License::KeyContainer::OutputProtection* output_protection) { @@ -504,7 +504,7 @@ WvPLStatus WvPLSDKSession::ParseLicenseRequest() { case SignedMessage::LICENSE_REQUEST: // TODO(user): Add CAS enum(s) to message_type_ in wvpl_types.h. case SignedMessage::CAS_LICENSE_REQUEST: - message_type_ = LICENSE_REQUEST; + type_.set_message_type(LICENSE_REQUEST); DCHECK(signed_message_request_from_cdm_); sdk_license_request_ = absl::make_unique(); if (!sdk_license_request_->ParseFromString( @@ -516,14 +516,17 @@ WvPLStatus WvPLSDKSession::ParseLicenseRequest() { } break; case SignedMessage::SERVICE_CERTIFICATE_REQUEST: - message_type_ = SERVICE_CERTIFICATE_REQUEST; + type_.set_message_type(SERVICE_CERTIFICATE_REQUEST); + return WvPLStatus(error_space, + widevine::SERVICE_CERTIFICATE_REQUEST_MESSAGE, + "Service Certificate Request message received."); break; default: break; } } // This code is used by proxy and wvpl license server sdk. - if (message_type_ == LICENSE_REQUEST) { + if (type_.message_type() == LICENSE_REQUEST) { // Parse for ClientIdentification in license request. if (sdk_license_request_->has_client_id() && sdk_license_request_->has_encrypted_client_id()) { @@ -556,13 +559,14 @@ WvPLStatus WvPLSDKSession::ParseLicenseRequest() { SetSystemId(KeyboxClientCert::GetSystemId(client_id_->token())); } if (!HasSystemId()) { - ClientCert* client_cert_ptr = nullptr; - status = ClientCert::Create( - drm_root_certificate_, sdk_license_request_->client_id().type(), - sdk_license_request_->client_id().token(), &client_cert_ptr); - std::unique_ptr client_cert(client_cert_ptr); - if (client_cert != nullptr) { - SetSystemId(client_cert->system_id()); + widevine::SignedDrmCertificate signed_drm_certificate; + if (signed_drm_certificate.ParseFromString(client_id_->token())) { + widevine::DrmCertificate drm_certificate; + if (drm_certificate.ParseFromString( + signed_drm_certificate.drm_certificate())) { + SetSystemId(drm_certificate.system_id()); + device_id_ = drm_certificate.serial_number(); + } } } // TODO(user): Consider enforcing missing client id here. @@ -581,7 +585,6 @@ WvPLStatus WvPLSDKSession::ParseLicenseRequest() { LicenseRequestType request_type = static_cast(sdk_license_request_->type()); type_.set_license_request_type(request_type); - type_.set_message_type(message_type_); if (is_offline_license()) { type_.set_license_type(OFFLINE); } @@ -812,6 +815,9 @@ WvPLStatus WvPLSDKSession::GetPsshData( wv_pssh.entitled_keys(idx).entitlement_key_size_bytes()); wvpl_widevine_pssh_data->add_entitled_key(wvpl_entitled_key); } + if (wv_pssh.has_video_feature()) { + wvpl_widevine_pssh_data->set_video_feature(wv_pssh.video_feature()); + } } return status; } else if (content_info.init_data_type() == @@ -850,9 +856,11 @@ WvPLStatus WvPLSDKSession::GetDeviceInfo(WvPLDeviceInfo* device_info) const { ProvisionedDeviceInfo provisioned_device_info; status = LookupDeviceInfo(GetSystemId(), &provisioned_device_info); if (!status.ok()) { - return status; + LOG(WARNING) << "Request from unknown device. SystemId = " + << GetSystemId(); } - device_info->set_system_id(provisioned_device_info.system_id()); + device_info->set_drm_certificate_serial_number(device_id_); + device_info->set_system_id(GetSystemId()); if (provisioned_device_info.has_soc()) { device_info->set_soc(provisioned_device_info.soc()); } @@ -884,7 +892,7 @@ WvPLStatus WvPLSDKSession::GetDeviceInfo(WvPLDeviceInfo* device_info) const { default: break; } - return status; + return OkStatus(); } WvPLStatus WvPLSDKSession::LookupDeviceInfo( @@ -904,20 +912,6 @@ uint32_t WvPLSDKSession::GetSystemId() const { return *system_id_; } -std::string WvPLSDKSession::session_id() const { - DCHECK(sdk_license_request_); - const LicenseRequest::ContentIdentification& content_id = - sdk_license_request_->content_id(); - if (content_id.existing_license().license_id().has_session_id()) - return content_id.existing_license().license_id().session_id(); - else if (content_id.has_widevine_pssh_data()) - return content_id.widevine_pssh_data().request_id(); - else if (content_id.has_webm_key_id()) - return content_id.webm_key_id().request_id(); - else - return std::string(); -} - bool WvPLSDKSession::is_offline_license() const { DCHECK(sdk_license_request_); if (sdk_license_request_ == nullptr) return false; diff --git a/sdk/external/common/wvpl/wvpl_sdk_session.h b/sdk/external/common/wvpl/wvpl_sdk_session.h index 9efe421..a79302b 100644 --- a/sdk/external/common/wvpl/wvpl_sdk_session.h +++ b/sdk/external/common/wvpl/wvpl_sdk_session.h @@ -38,7 +38,7 @@ class WvPLSDKSession { virtual WvPLStatus AddKey(const WvPLKey& key); // Get the WvPLKey. - virtual const std::vector& key() const { return keys_; } + virtual const std::vector& keys() const { return keys_; } // Set the license policy. virtual void set_policy(const WvPLPlaybackPolicy& policy) { @@ -95,18 +95,9 @@ class WvPLSDKSession { */ virtual WvPLStatus GetDeviceInfo(WvPLDeviceInfo* device_info) const; - /** - * Returns the type of the message handled by this session. - * - * @return MessageType enumeration. - * @deprecated use request_type instead. - * @since end of Q1, 2019 - */ - virtual MessageType message_type() const; - virtual PlatformVerificationStatus VerifyPlatform() = 0; - virtual WvPLRequestType request_type() const { return type_; } + virtual WvPLRequestType GetRequestType() const { return type_; } /** * Returns true if the license type is offline, otherwise return false. @@ -115,14 +106,6 @@ class WvPLSDKSession { */ virtual bool is_offline_license() const; - /** - * A session id that remains constant throughout a session. All license - * and heartbeat requests in a session have the same session id. - * - * @return string. - */ - virtual std::string session_id() const; - /** * Returns the license request contains client id or not. * @@ -130,16 +113,6 @@ class WvPLSDKSession { */ virtual bool has_client_id() const { return has_client_id_; } - /** - * Returns true if remote_attestation_cert_serial_number is not empty. - * Otherwise return false. - * - * @return bool. - */ - virtual bool remote_attestation_verified() { - return !remote_attestation_cert_serial_number_.empty(); - } - /** * Returns true if license request has encrypted_client_id. Otherwise return * false. @@ -151,6 +124,7 @@ class WvPLSDKSession { protected: const widevine::DrmRootCertificate* drm_root_certificate_; std::string user_agent_; + std::string device_id_; std::vector keys_; WvPLPlaybackPolicy policy_; WvPLSessionInit session_init_; @@ -158,7 +132,6 @@ class WvPLSDKSession { std::unique_ptr client_id_; bool has_pssh_data_ = false; bool has_client_id_ = false; - MessageType message_type_ = UNKNOWN; PlatformVerificationStatus platform_verification_status_ = PLATFORM_NO_VERIFICATION; std::unique_ptr diff --git a/sdk/external/common/wvpl/wvpl_types.h b/sdk/external/common/wvpl/wvpl_types.h index 084afbb..35b7258 100644 --- a/sdk/external/common/wvpl/wvpl_types.h +++ b/sdk/external/common/wvpl/wvpl_types.h @@ -41,8 +41,27 @@ enum KeyType { ENTITLEMENT = 2 }; +// A shortcut for specifying whether to return keys for the video feature only +// or to return all keys or ignore the video feature. +// The VideoFeatureKeySet only applies when video feature is specified in the +// PSSH. +// LINT.IfChange +enum VideoFeatureKeySet { + VF_UNSPECIFIED = 0, + // License should not include keys for the video feature, instead only + // include keys not associated with the video feature. + VF_EXCLUDED = 1, + // License should only include keys associated with the video feature + // (e.g., HDR). + VF_ONLY = 2, + // License should include keys for the video feature and also for keys + // not associated with the video feature (e.g., SDR keys). + VF_INCLUDED = 3, +}; + // LINT.IfChange enum LicenseType { + LICENSE_TYPE_UNSPECIFIED = 0, STREAMING = 1, OFFLINE = 2, }; @@ -586,10 +605,13 @@ struct WvPLDeviceInfo { void set_test_device(bool test_device) { test_device_ = test_device; } bool test_device() const { return test_device_; } - void set_serial_number(const std::string& serial_number) { - serial_number_ = serial_number; + void set_drm_certificate_serial_number( + const std::string& drm_certificate_serial_number) { + drm_certificate_serial_number_ = drm_certificate_serial_number; + } + const std::string& drm_certificate_serial_number() const { + return drm_certificate_serial_number_; } - const std::string& serial_number() const { return serial_number_; } void set_service_id(const std::string& service_id) { service_id_ = service_id; @@ -611,7 +633,7 @@ struct WvPLDeviceInfo { // True if the certificate corresponds to a test (non production) device. bool test_device_; // 128-bit globally unique serial number of certificate. - std::string serial_number_; + std::string drm_certificate_serial_number_; // Service identifier (web origin) for the provider which owns the // certificate. std::string service_id_; @@ -975,6 +997,11 @@ struct WvPLWidevinePsshData { return entitled_keys_; } + void set_video_feature(const std::string& video_feature) { + video_feature_ = video_feature; + } + const std::string video_feature() const { return video_feature_; } + // Member variables // A list of key identifiers, for entitlement keys or content keys. std::list key_ids_; @@ -982,6 +1009,8 @@ struct WvPLWidevinePsshData { std::string content_id_; // A list of wrapped keys. std::list entitled_keys_; + // Video feature identifier. + std::string video_feature_; }; struct WvPLCasKey {