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 {