Widevine SDK Release Branch: beta-19.10.1

This commit is contained in:
Buildbot
2025-05-29 12:21:32 -07:00
parent b31fb00c1a
commit 8349b5bc0f
219 changed files with 101122 additions and 0 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,56 @@
########################################
## 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.
########################################
# Published Devices protocol buffer definitions for the
# Widevine Published Devices One Platform API service.
package(default_visibility = ["//visibility:public"])
load("@io_grpc_grpc_java//:java_grpc_library.bzl", "java_grpc_library")
proto_library(
name = "published_devices_proto",
srcs = ["published_devices.proto"],
deps = [
":device_security_profiles_proto",
"@com_github_googleapis_googleapis//google/api:annotations_proto",
"@com_github_googleapis_googleapis//google/api:field_behavior_proto",
"@com_google_protobuf//:timestamp_proto",
],
)
proto_library(
name = "device_security_profiles_proto",
srcs = ["device_security_profiles.proto"],
deps = [
"@com_github_googleapis_googleapis//google/api:field_behavior_proto",
],
)
cc_proto_library(
name = "published_devices_cc_proto",
deps = [":published_devices_proto"],
)
java_proto_library(
name = "published_devices_java_proto",
deps = [":published_devices_proto"],
)
java_grpc_library(
name = "published_devices_grpc",
srcs = [":published_devices_proto"],
deps = [
":published_devices_java_proto",
],
)
java_proto_library(
name = "device_security_profiles_java_proto",
deps = [":device_security_profiles_proto"],
)

View File

@@ -0,0 +1,108 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2020 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.
////////////////////////////////////////////////////////////////////////////////
syntax = "proto3";
package google.chrome.widevine.contentpartners.v1beta1;
import "google/api/field_behavior.proto";
option java_multiple_files = true;
option java_outer_classname = "DeviceSecurityProfilesProto";
option java_package = "com.google.chrome.widevine.contentpartners.v1beta1";
message DeviceSecurityProfileCriteria {
string content_provider = 1 [
(google.api.field_behavior) = REQUIRED
];
repeated string content_owners = 2 [(google.api.field_behavior) = OPTIONAL];
}
message ListDeviceSecurityProfilesRequest {
DeviceSecurityProfileCriteria device_security_profile_criteria = 1 [
(google.api.field_behavior) = REQUIRED
];
}
message SignedDeviceSecurityProfiles {
bytes device_security_profiles = 1 [(google.api.field_behavior) = REQUIRED];
bytes signature = 2 [(google.api.field_behavior) = REQUIRED];
HashAlgorithm hash_algorithm = 3 [(google.api.field_behavior) = OPTIONAL];
}
message ListDeviceSecurityProfilesResponse {
SignedDeviceSecurityProfiles signed_device_security_profiles = 1
[(google.api.field_behavior) = REQUIRED];
}
enum HashAlgorithm {
HASH_ALGORITHM_UNSPECIFIED = 0;
HASH_ALGORITHM_SHA_1 = 1;
HASH_ALGORITHM_SHA_256 = 2;
HASH_ALGORITHM_SHA_384 = 3;
}

View File

@@ -0,0 +1,188 @@
////////////////////////////////////////////////////////////////////////////////
//// 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.
////////////////////////////////////////////////////////////////////////////////
//
//
syntax = "proto3";
package google.chrome.widevine.contentpartners.v1beta1;
import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
import "google/chrome/widevine/contentpartners/v1beta1/device_security_profiles.proto";
import "google/protobuf/timestamp.proto";
option csharp_namespace = "Google.Chrome.Widevine.ContentPartners.V1Beta1";
option java_multiple_files = true;
option java_outer_classname = "PublishedDevicesProtos";
option java_package = "com.google.chrome.widevine.contentpartners.v1beta1";
option objc_class_prefix = "GCWPD";
service PublishedDevicesService {
rpc GetPublishedDevices(PublishedDevicesRequest) returns (PublishedDevices) {
option (google.api.http) = {
post: "/v1beta1/publishedDevices:getSignedBatch"
body: "*"
};
}
rpc RetrieveIndividualDeviceRevocationList(
RetrieveIndividualDeviceRevocationListRequest)
returns (RetrieveIndividualDeviceRevocationListResponse) {
option (google.api.http) = {
post: "/v1beta1/individualDeviceRevocationList:retrieve"
body: "*"
};
}
rpc ListDeviceSecurityProfiles(ListDeviceSecurityProfilesRequest)
returns (ListDeviceSecurityProfilesResponse) {
option (google.api.http) = {
get: "/v1beta1/deviceSecurityProfiles:listDeviceSecurityProfiles"
};
}
}
message SdkClientInformation {
string sdk_version = 1;
uint64 sdk_time_seconds = 2;
bytes service_certificate = 3;
string provider = 4;
}
message PublishedDevicesRequest {
bytes sdk_client_information = 1;
bytes signature = 2;
HashAlgorithm hash_algorithm = 3 [(google.api.field_behavior) = OPTIONAL];
DcslSnapshotType dcsl_snapshot_type = 4
[(google.api.field_behavior) = OPTIONAL];
google.protobuf.Timestamp last_dcsl_epoch_time = 5
[(google.api.field_behavior) = OPTIONAL];
bool include_impact_analysis = 6 [(google.api.field_behavior) = OPTIONAL];
}
message PublishedDevices {
bytes published_devices = 1;
bytes signature = 2;
HashAlgorithm hash_algorithm = 3 [(google.api.field_behavior) = OPTIONAL];
DcslSnapshotType dcsl_snapshot_type = 4
[(google.api.field_behavior) = OPTIONAL];
}
message RetrieveIndividualDeviceRevocationListRequest {
string provider = 1 [
(google.api.field_behavior) = REQUIRED
];
}
message RetrieveIndividualDeviceRevocationListResponse {
bytes global_individual_device_revocation_list = 1
[(google.api.field_behavior) = REQUIRED];
bytes provider_individual_device_revocation_list = 2
[(google.api.field_behavior) = OPTIONAL];
}
enum DcslSnapshotType {
DCSL_SNAPSHOT_TYPE_UNSPECIFIED = 0;
DCSL_ALPHA = 1;
DCSL_BETA = 2;
DCSL_RELEASED = 3;
}

View File

@@ -0,0 +1,31 @@
########################################
## Copyright 2023 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.
########################################
package(default_visibility = ["//visibility:public"])
load("@io_grpc_grpc_java//:java_grpc_library.bzl", "java_grpc_library")
proto_library(
name = "fraud_detection_and_reporting_service_proto",
srcs = ["fraud_detection_and_reporting_service.proto"],
deps = [
"@com_github_googleapis_googleapis//google/api:annotations_proto",
"@com_github_googleapis_googleapis//google/api:field_behavior_proto",
],
)
java_proto_library(
name = "fraud_detection_and_reporting_service_java_proto",
deps = [":fraud_detection_and_reporting_service_proto"],
)
java_grpc_library(
name = "fraud_detection_and_reporting_grpc",
srcs = [":fraud_detection_and_reporting_service_proto"],
deps = [":fraud_detection_and_reporting_service_java_proto"],
)

View File

@@ -0,0 +1,201 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2022 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.
////////////////////////////////////////////////////////////////////////////////
syntax = "proto3";
package google.chrome.widevine.frauddetection.v1eap1;
import "google/api/annotations.proto";
import "google/api/field_behavior.proto";
option java_multiple_files = true;
option java_outer_classname = "FraudDetectionAndReportingServiceProto";
option java_package = "com.google.chrome.widevine.frauddetection.v1eap1";
service FraudDetectionAndReportingService {
rpc CalculateFraudLevel(CalculateFraudLevelRequest)
returns (CalculateFraudLevelResponse) {
option (google.api.http) = {
post: "/v1eap1/fraudDetection:calculateFraudLevel"
body: "*"
};
}
rpc ReportFraudLevel(ReportFraudLevelRequest)
returns (ReportFraudLevelResponse) {
option (google.api.http) = {
post: "/v1eap1/fraudReporting:reportFraudLevel"
body: "*"
};
}
}
message LicenseRequestInfo {
message RequestMetadata {
string client_ip_address = 1 [
(google.api.field_behavior) = OPTIONAL
];
string user_agent = 2;
string provider = 3;
}
bytes license_request = 1 [
(google.api.field_behavior) = REQUIRED
];
bytes clear_client_id = 2 [
(google.api.field_behavior) = OPTIONAL
];
RequestMetadata request_metadata = 3;
}
message FraudLevel {
enum FraudScore {
FRAUD_SCORE_UNSPECIFIED = 0;
FRAUD_SCORE_NONE = 1;
FRAUD_SCORE_LOW = 2;
FRAUD_SCORE_MEDIUM = 3;
FRAUD_SCORE_HIGH = 4;
FRAUD_SCORE_CRITICAL = 5;
}
enum ConfidenceLevel {
CONFIDENCE_LEVEL_UNSPECIFIED = 0;
CONFIDENCE_LEVEL_NONE = 1;
CONFIDENCE_LEVEL_LOW = 2;
CONFIDENCE_LEVEL_MEDIUM = 3;
CONFIDENCE_LEVEL_HIGH = 4;
CONFIDENCE_LEVEL_VERIFIED = 5;
}
FraudScore fraud_score = 1;
ConfidenceLevel confidence_level = 2;
}
message CalculateFraudLevelRequest {
enum Option {
OPTION_UNSPECIFIED = 0;
OPTION_NO_RESULTS = 1;
OPTION_LOW_LATENCY = 2;
OPTION_HIGH_LATENCY = 3;
}
Option request_option = 1 [
(google.api.field_behavior) = REQUIRED
];
LicenseRequestInfo license_request_info = 2 [
(google.api.field_behavior) = REQUIRED
];
bool validate_only = 3 [(google.api.field_behavior) = OPTIONAL];
}
message CalculateFraudLevelResponse {
FraudLevel fraud_level = 1;
LicenseRequestInfo license_request_info = 2;
}
message ReportFraudLevelRequest {
FraudLevel fraud_level = 1 [
(google.api.field_behavior) = REQUIRED
];
LicenseRequestInfo license_request_info = 2 [
(google.api.field_behavior) = REQUIRED
];
string reason = 3 [(google.api.field_behavior) = OPTIONAL];
}
message ReportFraudLevelResponse {
FraudLevel fraud_level = 1;
LicenseRequestInfo license_request_info = 2;
}

View File

@@ -0,0 +1,32 @@
########################################
## Copyright 2024 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.
########################################
package(default_visibility = ["//visibility:public"])
COMMON_DATA = [
"cts_resources/cert_staging_google_com.der",
"cts_resources/dev_cert_status_list.dat",
"cts_resources/private_key_staging_google_com.der",
"cts_resources/dcsl-google.com_blockbuster-1154-5f30025abf26.json",
]
filegroup(
name = "cts_resources",
srcs = COMMON_DATA,
)
cc_library(
name = "cts_resource_config",
srcs = ["cts_resource_config.cc"],
hdrs = ["cts_resource_config.h"],
data = COMMON_DATA,
deps = [
"//base",
"//devtools/build/runtime:get_runfiles_dir",
],
)

View File

@@ -0,0 +1,106 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2024 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.
////////////////////////////////////////////////////////////////////////////////
#include "testing/cts/common/cts_resource_config.h"
#include <fstream>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <utility>
#include "devtools/build/runtime/get_runfiles_dir.h"
namespace video_widevine {
namespace cts_resource_config {
using devtools_build::GetDataDependencyFilepath;
std::map<std::string, std::string> SetupDefaultConfigs() {
std::map<std::string, std::string> config_values;
config_values.insert(std::make_pair("allow_unknown_device", "1"));
config_values.insert(std::make_pair("device_certificate_expiration", "0"));
config_values.insert(std::make_pair("provider", "widevine_test"));
config_values.insert(
std::make_pair("provider_iv", "b9c3c175d69b9905e50715d196326acc"));
config_values.insert(std::make_pair(
"provider_key",
"93338e5289145f5f9990d9c9612bcb2bb79e6a684acc6d0ad34bdb7a475621da"));
return config_values;
}
std::string GetServiceCertificate() {
// use the jts server cert
const std::string resource_path =
"google3/testing/cts/common/"
"cts_resources/"
"cert_staging_google_com.der";
// update dependency path for borg
std::string resource_path_full = GetDataDependencyFilepath(resource_path);
std::ifstream input_file(resource_path_full);
if (!input_file.is_open()) {
std::cerr << "Error opening file!\n";
std::cerr << resource_path << "\n";
return "";
}
std::stringstream buffer;
buffer << input_file.rdbuf();
std::string service_certificate = buffer.str();
input_file.close();
return service_certificate;
}
std::string GetServiceCertificatePrivateKey() {
// use the jts server cert
const std::string resource_path =
"google3/testing/cts/common/"
"cts_resources/"
"private_key_staging_google_com.der";
// update dependency path for borg
std::string resource_path_full = GetDataDependencyFilepath(resource_path);
std::ifstream input_file(resource_path_full);
if (!input_file.is_open()) {
std::cerr << "Error opening file!\n";
std::cerr << resource_path << "\n";
return "";
}
std::stringstream buffer;
buffer << input_file.rdbuf();
std::string service_certificate_private_key = buffer.str();
input_file.close();
return service_certificate_private_key;
}
std::string GetDCSLRawData() {
const std::string dcsl_path =
"google3/testing/cts/common/"
"cts_resources/"
"dev_cert_status_list.dat";
// update dependency path for borg
std::string dcsl_path_full = GetDataDependencyFilepath(dcsl_path);
std::ifstream input_file(dcsl_path_full);
if (!input_file.is_open()) {
std::cerr << "Error opening file!\n";
std::cerr << dcsl_path << "\n";
return "";
}
std::stringstream buffer;
buffer << input_file.rdbuf();
std::string dcsl_str = buffer.str();
input_file.close();
return dcsl_str;
}
std::string GetPrivateKeyPassphrase() {
return "mViUNMa3Lag7KJyEl5aphWn4/dkOOI3H/"
"D8LSJ0g3t8GLj3lOHGTTjn1Y2XKPqU7M3NlxAw5mFZl";
}
} // namespace cts_resource_config
} // namespace video_widevine

View File

@@ -0,0 +1,26 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2024 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.
////////////////////////////////////////////////////////////////////////////////
#ifndef VIDEO_WIDEVINE_EXPORT_TESTING_CTS_COMMON_CTS_RESOURCE_CONFIG_H_
#define VIDEO_WIDEVINE_EXPORT_TESTING_CTS_COMMON_CTS_RESOURCE_CONFIG_H_
#include <map>
#include <string>
namespace video_widevine {
namespace cts_resource_config {
std::map<std::string, std::string> SetupDefaultConfigs();
std::string GetDCSLRawData();
std::string GetServiceCertificate();
std::string GetServiceCertificatePrivateKey();
std::string GetPrivateKeyPassphrase();
} // namespace cts_resource_config
} // namespace video_widevine
#endif // VIDEO_WIDEVINE_EXPORT_TESTING_CTS_COMMON_CTS_RESOURCE_CONFIG_H_

22
centos/tools/src/testing/cts/env/BUILD vendored Normal file
View File

@@ -0,0 +1,22 @@
########################################
## Copyright 2024 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.
########################################
package(default_visibility = ["//visibility:public"])
cc_library(
name = "wvpl_env",
hdrs = ["wvpl_env.h"],
deps = [
"@abseil_repo//absl/log",
"@abseil_repo//absl/strings",
"//sdk/external/cpp/wvpl/common:wvpl_types",
"//sdk/external/cpp/wvpl/license_server_sdk:wvpl_environment",
"//testing/cts/common:cts_resource_config",
"//testing/cts/published_devices:published_devices_provider",
],
)

View File

@@ -0,0 +1,116 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2024 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.
////////////////////////////////////////////////////////////////////////////////
#ifndef VIDEO_WIDEVINE_EXPORT_TESTING_CTS_ENV_WVPL_ENV_H_
#define VIDEO_WIDEVINE_EXPORT_TESTING_CTS_ENV_WVPL_ENV_H_
#include <cstdlib>
#include <string>
#include "absl/log/log.h"
#include "absl/strings/escaping.h"
#include "sdk/external/cpp/wvpl/common/wvpl_types.h"
#include "sdk/external/cpp/wvpl/license_server_sdk/wvpl_environment.h"
#include "testing/cts/common/cts_resource_config.h"
#include "testing/cts/published_devices/published_devices_provider.h"
namespace video_widevine {
namespace cts {
namespace wvpl_env {
using video_widevine::published_devices_provider::GetDCSL;
using video_widevine_server::wv_pl_sdk::WvPLEnvironment;
// Singleton class to get an instance of WvPLEnvironment from wvpl_environment.h
class WvplEnv {
public:
// Gets the instance of WvplEnv singleton which stores a WvPLEnvironment.
static WvplEnv* GetInstance() {
static WvplEnv* s = new WvplEnv();
return s;
}
// Gets the WvPLEnvironment instance stored inside this singleton.
WvPLEnvironment* get_env() { return &wvpl_env_instance_; }
// Returns true if env initialization failed for any reason.
bool env_setup_failed() { return env_init_failed_; }
private:
WvplEnv()
: wvpl_env_instance_(
video_widevine::cts_resource_config::SetupDefaultConfigs()) {
env_init_failed_ = false;
video_widevine_server::wv_pl_sdk::WvPLStatus status =
wvpl_env_instance_.Initialize();
if (status.ok()) {
} else {
env_init_failed_ = true;
return;
}
std::string service_cert =
video_widevine::cts_resource_config::GetServiceCertificate();
std::string service_cert_private_key =
video_widevine::cts_resource_config::GetServiceCertificatePrivateKey();
status = wvpl_env_instance_.SetDrmServiceCertificate(
service_cert, service_cert_private_key,
video_widevine::cts_resource_config::GetPrivateKeyPassphrase());
if (status.ok()) {
} else {
env_init_failed_ = true;
return;
}
std::string use_dcsl_from_file = "";
if (getenv("USE_DCSL_FROM_FILE") != nullptr) {
use_dcsl_from_file = getenv("USE_DCSL_FROM_FILE");
}
if (use_dcsl_from_file == "True") {
std::string dcsl_str_encoded =
video_widevine::cts_resource_config::GetDCSLRawData();
std::string dcsl_decoded;
bool result =
absl::WebSafeBase64Unescape(dcsl_str_encoded, &dcsl_decoded);
if (!result) {
}
status = wvpl_env_instance_.SetDeviceCertificateStatusList(dcsl_decoded);
if (status.ok()) {
} else {
env_init_failed_ = true;
return;
}
} else {
// Fetch the device certificate status list.
absl::StatusOr<std::string> dcslStatus = GetDCSL(wvpl_env_instance_);
if (dcslStatus.ok()) {
} else {
env_init_failed_ = true;
return;
}
status =
wvpl_env_instance_.SetDeviceCertificateStatusList(dcslStatus.value());
if (status.ok()) {
} else {
env_init_failed_ = true;
return;
}
}
}
~WvplEnv() = default;
WvplEnv(const WvplEnv&) = delete;
WvplEnv& operator=(const WvplEnv&) = delete;
WvPLEnvironment wvpl_env_instance_;
bool env_init_failed_;
};
} // namespace wvpl_env
} // namespace cts
} // namespace video_widevine
#endif // VIDEO_WIDEVINE_EXPORT_TESTING_CTS_ENV_WVPL_ENV_H_

View File

@@ -0,0 +1,38 @@
########################################
## Copyright 2024 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.
########################################
package(default_visibility = ["//visibility:public"])
cc_library(
name = "published_devices_provider",
srcs = ["published_devices_provider.cc"],
hdrs = ["published_devices_provider.h"],
deps = [
"//base",
"//chubby/lib/public:lockservice",
"//google/chrome/widevine/contentpartners/v1beta1:published_devices_cc_grpc",
"//google/chrome/widevine/contentpartners/v1beta1:published_devices_cc_proto",
"//net/eventmanager",
"//net/grpc:grpc++",
"//net/http:httpserverrequest",
"//net/http2/server/lib/public:server",
"//net/rpc",
"@abseil_repo//absl/functional:bind_front",
"@abseil_repo//absl/log",
"@abseil_repo//absl/log:check",
"@abseil_repo//absl/status",
"@abseil_repo//absl/status:statusor",
"//third_party/grpc",
"//third_party/grpc:grpc++",
"//thread",
"//util:error_space",
"//sdk/external/cpp/wvpl/common:wvpl_types",
"//sdk/external/cpp/wvpl/license_server_sdk:wvpl_environment",
],
)

View File

@@ -0,0 +1,94 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2024 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.
////////////////////////////////////////////////////////////////////////////////
#include "testing/cts/published_devices/published_devices_provider.h"
#include <wrapper_internal_exception_macros.h>
#include <memory>
#include <ostream>
#include <string>
#include "google/chrome/widevine/contentpartners/v1beta1/published_devices.grpc.pb.h"
#include "google/chrome/widevine/contentpartners/v1beta1/published_devices.pb.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "third_party/grpc/include/grpcpp/channel.h"
#include "third_party/grpc/include/grpcpp/client_context.h"
#include "third_party/grpc/include/grpcpp/create_channel.h"
#include "third_party/grpc/include/grpcpp/security/credentials.h"
#include "third_party/grpc/include/grpcpp/support/status.h"
#include "sdk/external/cpp/wvpl/common/wvpl_types.h"
#include "sdk/external/cpp/wvpl/license_server_sdk/wvpl_environment.h"
namespace video_widevine {
namespace published_devices_provider {
using google::chrome::widevine::contentpartners::v1beta1::PublishedDevices;
using google::chrome::widevine::contentpartners::v1beta1::
PublishedDevicesRequest;
using google::chrome::widevine::contentpartners::v1beta1::
PublishedDevicesService;
using video_widevine_server::wv_pl_sdk::WvPLEnvironment;
using video_widevine_server::wv_pl_sdk::WvPLStatus;
const char apiServicePath[] = "widevine.googleapis.com";
absl::StatusOr<PublishedDevices> GetPublishedDevices(
const WvPLEnvironment& env) {
std::shared_ptr<grpc::Channel> channel = nullptr;
PublishedDevices devicesResponse;
try {
// Create grpc channel.
std::shared_ptr<grpc::Channel> channel =
grpc::CreateChannel(apiServicePath, grpc::GoogleDefaultCredentials());
// Create a stub.
std::unique_ptr<PublishedDevicesService::Stub> stub(
(PublishedDevicesService::NewStub(channel)));
PublishedDevicesRequest publishedDevicesRequest;
std::string publishedDevicesRequestString;
WvPLStatus status =
env.GenerateDeviceStatusListRequest(&publishedDevicesRequestString);
if (!status.ok()) {
return absl::FailedPreconditionError("unable to generate dcsl request: " +
status.error_message());
}
if (!publishedDevicesRequest.ParseFromString(
publishedDevicesRequestString)) {
return absl::FailedPreconditionError(
"unable to parse published devices request");
}
// Send gRPC request.
grpc::ClientContext context;
grpc::Status grpcStatus = stub->GetPublishedDevices(
&context, publishedDevicesRequest, &devicesResponse);
if (grpcStatus.ok()) {
} else {
return absl::FailedPreconditionError("grpc getPublishedDevices failed: " +
grpcStatus.error_message());
}
} catch (const std::exception& exc) {
return absl::FailedPreconditionError("failed to retrieve signd list");
}
return devicesResponse;
}
absl::StatusOr<std::string> GetDCSL(const WvPLEnvironment& env) {
absl::StatusOr<PublishedDevices> pdResponse = GetPublishedDevices(env);
if (!pdResponse.ok()) {
return pdResponse.status();
}
return pdResponse.value().SerializeAsString();
}
} // namespace published_devices_provider
} // namespace video_widevine

View File

@@ -0,0 +1,32 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2024 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.
////////////////////////////////////////////////////////////////////////////////
#ifndef VIDEO_WIDEVINE_EXPORT_TESTING_CTS_PUBLISHED_DEVICES_PUBLISHED_DEVICES_PROVIDER_H_
#define VIDEO_WIDEVINE_EXPORT_TESTING_CTS_PUBLISHED_DEVICES_PUBLISHED_DEVICES_PROVIDER_H_
#include <string>
#include "google/chrome/widevine/contentpartners/v1beta1/published_devices.pb.h"
#include "absl/status/statusor.h"
#include "sdk/external/cpp/wvpl/license_server_sdk/wvpl_environment.h"
namespace video_widevine {
namespace published_devices_provider {
absl::StatusOr<
google::chrome::widevine::contentpartners::v1beta1::PublishedDevices>
GetPublishedDevices(
const video_widevine_server::wv_pl_sdk::WvPLEnvironment& env);
absl::StatusOr<std::string> GetDCSL(
const video_widevine_server::wv_pl_sdk::WvPLEnvironment& env);
} // namespace published_devices_provider
} // namespace video_widevine
#endif // VIDEO_WIDEVINE_EXPORT_TESTING_CTS_PUBLISHED_DEVICES_PUBLISHED_DEVICES_PROVIDER_H_

View File

@@ -0,0 +1,28 @@
########################################
## Copyright 2024 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.
########################################
package(default_visibility = ["//visibility:public"])
cc_library(
name = "wvpl_sdk",
srcs = ["wvpl_sdk.cc"],
hdrs = ["wvpl_sdk.h"],
deps = [
"//google/chrome/widevine/licensedata/v1:content_key_spec_cc_proto",
"@abseil_repo//absl/log",
"@abseil_repo//absl/status",
"@abseil_repo//absl/status:statusor",
"//sdk/external/cpp/wvpl/common:wvpl_types",
"//sdk/external/cpp/wvpl/license_server_sdk:wvpl_environment",
"//sdk/external/cpp/wvpl/license_server_sdk:wvpl_session",
"//testing/cts/env:wvpl_env",
"//protos/public:license_protocol_cc_proto",
"//protos/public:license_protos_cc_proto",
"//protos/public:license_services_cc_proto",
],
)

View File

@@ -0,0 +1,153 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2024 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.
////////////////////////////////////////////////////////////////////////////////
#include "testing/cts/sdk/wvpl_sdk.h"
#include <string>
#include <vector>
#include "google/chrome/widevine/licensedata/v1/content_key_spec.pb.h"
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "sdk/external/cpp/wvpl/common/wvpl_types.h"
#include "sdk/external/cpp/wvpl/license_server_sdk/wvpl_environment.h"
#include "sdk/external/cpp/wvpl/license_server_sdk/wvpl_session.h"
#include "testing/cts/env/wvpl_env.h"
#include "protos/public/license_protocol.pb.h"
#include "protos/public/license_protos.pb.h"
#include "protos/public/license_services.pb.h"
namespace video_widevine {
namespace wvpl_sdk {
using video_widevine_server::wv_pl_sdk::WvPLEnvironment;
using video_widevine_server::wv_pl_sdk::WvPLKey;
using video_widevine_server::wv_pl_sdk::WvPLPlaybackPolicy;
using video_widevine_server::wv_pl_sdk::WvPLRequestType;
using video_widevine_server::wv_pl_sdk::WvPLSession;
using video_widevine_server::wv_pl_sdk::WvPLStatus;
using video_widevine_server::wv_pl_sdk::WvPLWidevinePsshData;
video_widevine_server::wv_pl_sdk::TrackType get_track_type(
std::string track_type) {
if (track_type == "SD") {
return video_widevine_server::wv_pl_sdk::VIDEO_SD;
} else if (track_type == "HD") {
return video_widevine_server::wv_pl_sdk::VIDEO_HD;
} else if (track_type == "UHD1") {
return video_widevine_server::wv_pl_sdk::VIDEO_UHD1;
} else if (track_type == "UHD2") {
return video_widevine_server::wv_pl_sdk::VIDEO_UHD2;
} else if (track_type == "AUDIO") {
return video_widevine_server::wv_pl_sdk::AUDIO;
} else {
return video_widevine_server::wv_pl_sdk::TRACK_TYPE_UNSPECIFIED;
}
}
bool add_keys_to_session(WvPLSession& session, std::vector<WvPLKey> wvpl_keys) {
for (const WvPLKey& wvpl_key : wvpl_keys) {
WvPLStatus status = session.AddKey(wvpl_key);
if (!status.ok()) {
return false;
}
}
return true;
}
std::vector<WvPLKey> create_wvpl_keys_from_content_key_specs(
ModularDrmLicenseRequest drmRequest, WvPLWidevinePsshData _wv_pssh_data,
bool is_external_license_request) {
std::vector<WvPLKey> wvpl_keys;
for (int i = 0; i < drmRequest.content_key_specs_size(); i++) {
WvPLKey wvpl_key;
const auto& content_key_spec = drmRequest.content_key_specs(i);
wvpl_key.set_key_id(content_key_spec.key_id());
wvpl_key.set_key_bytes(content_key_spec.key());
wvpl_key.set_key_type(
static_cast<video_widevine_server::wv_pl_sdk::KeyType>(
content_key_spec.key_type()));
wvpl_key.set_track_type(get_track_type(content_key_spec.track_type()));
wvpl_keys.push_back(wvpl_key);
}
return wvpl_keys;
}
// TODO: b/323916039 - Setup handler for /drmRequest
absl::StatusOr<std::string> HandleDrmLicenseRequest(std::string requestString) {
ModularDrmLicenseRequest drmRequest;
if (!drmRequest.ParseFromString(requestString)) {
return "";
}
std::string license_request_payload = drmRequest.payload();
std::string response = "";
WvPLSession* session;
auto wvpl_env_instance =
video_widevine::cts::wvpl_env::WvplEnv::GetInstance();
WvPLEnvironment* wvpl_env = wvpl_env_instance->get_env();
WvPLStatus status =
wvpl_env->CreateSession(license_request_payload, &session);
if (!status.ok()) {
return absl::InternalError("Failed to create session: " +
status.error_message());
} else {
}
WvPLRequestType request_type = session->GetRequestType();
video_widevine::LicenseRequest license_request = session->request();
video_widevine::LicenseRequest::RequestType license_request_type =
license_request.type();
video_widevine_server::wv_pl_sdk::MessageType message_type =
request_type.message_type();
bool should_add_keys = false;
bool external_license_request = false;
switch (message_type) {
case SignedMessage::LICENSE_REQUEST:
if (license_request_type == video_widevine::LicenseRequest::NEW) {
should_add_keys = true;
}
break;
case SignedMessage::EXTERNAL_LICENSE_REQUEST:
should_add_keys = true;
external_license_request = true;
break;
case video_widevine_server::wv_pl_sdk::UNKNOWN:
case video_widevine_server::wv_pl_sdk::SERVICE_CERTIFICATE_REQUEST:
break;
}
WvPLWidevinePsshData wv_pssh_data;
status = session->GetPsshData(&wv_pssh_data);
if (!status.ok()) {
return absl::InternalError("Failed to get pssh data: " +
status.error_message());
}
if (should_add_keys) {
std::vector<WvPLKey> wvpl_keys = create_wvpl_keys_from_content_key_specs(
drmRequest, wv_pssh_data, external_license_request);
bool keys_added = add_keys_to_session(*session, wvpl_keys);
if (!keys_added) {
return absl::InternalError("Failed to add keys");
}
}
License::Policy license_policy = drmRequest.policy_overrides();
WvPLPlaybackPolicy wvpl_playback_policy;
wvpl_playback_policy.set_license_duration_seconds(
license_policy.license_duration_seconds());
session->set_policy(wvpl_playback_policy);
status = session->GenerateLicense(&response);
if (!status.ok()) {
// TODO: b/329230518 - Sometimes error message is empty, needs investigation
return absl::InternalError("Failed to generate license: " +
status.error_message());
} else {
}
return response;
}
} // namespace wvpl_sdk
} // namespace video_widevine

View File

@@ -0,0 +1,23 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2024 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.
////////////////////////////////////////////////////////////////////////////////
#ifndef VIDEO_WIDEVINE_EXPORT_TESTING_CTS_SDK_WVPL_SDK_H_
#define VIDEO_WIDEVINE_EXPORT_TESTING_CTS_SDK_WVPL_SDK_H_
#include <string>
#include "absl/status/statusor.h"
namespace video_widevine {
namespace wvpl_sdk {
absl::StatusOr<std::string> HandleDrmLicenseRequest(std::string requestString);
}
} // namespace video_widevine
#endif // VIDEO_WIDEVINE_EXPORT_TESTING_CTS_SDK_WVPL_SDK_H_

View File

@@ -0,0 +1,64 @@
########################################
## Copyright 2024 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.
########################################
# Binary to run cts wvpl server
load("//tools/build_defs/pkg:google.bzl", "pkg_binary_with_runfiles")
cc_binary(
name = "test_server",
srcs = ["test_server.cc"],
deps = [
"//base",
"//chubby/lib/public:lockservice",
"//net/eventmanager",
"//net/eventmanager:eventmanager_utils",
"//net/grpc:grpc++",
"//net/http:httpserverrequest",
"//net/http2/server/lib/public:server",
"//net/rpc",
"@abseil_repo//absl/functional:bind_front",
"@abseil_repo//absl/log",
"@abseil_repo//absl/log:check",
"//third_party/grpc",
"//third_party/grpc:grpc++",
"//thread",
"//util:error_space",
"//sdk/external/cpp/wvpl/common:wvpl_types",
"//sdk/external/cpp/wvpl/license_server_sdk:wvpl_environment",
"//testing/cts/env:wvpl_env",
"//testing/cts/sdk:wvpl_sdk",
"//webutil/http",
],
)
pkg_binary_with_runfiles(
name = "test_server_with_runfiles",
binary = ":test_server",
)
genmpm(
# Name of the rule, as usual.
name = "test_server_temporal_mpm",
# List of files to include in the package.
srcs = [":test_server"],
# Temporal MPM option. This allows us to use this target with `blaze_label`
# in the .borg file, as explained below.
temporal = True,
)
genmpm(
name = "test_server_mpm",
# MPM package name.
package_name = "testing/cts/server/test_server",
# List of files to include in the package.
deps = [
":test_server_with_runfiles",
],
)

View File

@@ -0,0 +1,79 @@
# CTS server setup
The CTS server setup directory contains the cc_binary needed to build and run
the CTS server. Instructions on how to build this binary locally are below.
[TOC]
## Get cts resources
Resource files are the ones from jts_resources, currently only using the ones
needed for server startup. To get the resource files run the following from this
directory (cts/server):\
`mkdir cts_resources`\
`gsutil -m cp -r gs://blockbuster/jts_resources/* ./cts_resources`
only resources being used currently are:
* cert_staging_google_com.der
* private_key_staging_google_com.der
## Setup service account locally
Important:
**do not upload the service account json file,**
**it is only to be used locally.**
Download the service account json from the jts google cloud server.
Copy file locally to following location:
`testing/cts/published_devices/service_acc_cred.json`\
JSON cred file must be this exact name/path as this is specified in the `BUILD`
file for
the server.
Run the following command to setup google default creds:\
`export GOOGLE_APPLICATION_CREDENTIALS=testing/cts/common/cts_resources/dcsl-google.com_blockbuster-1154-5f30025abf26.json`
## Run server
Running the server: \
`blaze run //testing/cts/server:test_server -- --logtostderr
--port=10000`
Another option to run server if logs are very long: \
`blaze run //testing/cts/server:test_server >
cts_error_log.txt -- --port=10000`
## Run server on borg local
build your web server and mpm package:\
```
blaze mpm -c opt testing/cts/server:test_server_mpm
```
check versions, latest one is at bottom\
```
mpm packageinfo --show_version_map testing/cts/server/test_server
```
run following with the latest package version (or whichever you want)\
```
PACKAGE_VERSION_ID=1-1ae53a0e_03cacc13_1f05d096_3052174c_28367faa
```
set live label\
```
mpm setlabel testing/cts/server/test_server --label=live --version=$PACKAGE_VERSION_ID
```
start up borg\
```
borgcfg production/borg/widevine/templates/dev/cts_wvpl.borg --borguser=nihardamar --user=nihardamar up
```
shut down borg job\
```
borgcfg production/borg/widevine/templates/dev/cts_wvpl.borg --borguser=nihardamar --user=nihardamar down
```
Url to look up borg job\
http://sigma

View File

@@ -0,0 +1,82 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2024 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.
////////////////////////////////////////////////////////////////////////////////
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include "base/init_google.h"
#include "net/eventmanager/eventmanager.h"
#include "net/eventmanager/eventmanager_utils.h"
#include "net/http/httpserverrequest.h"
#include "net/http2/server/lib/public/httpserver2.h"
#include "absl/flags/flag.h"
#include "absl/functional/bind_front.h"
#include "absl/log/check.h"
#include "absl/log/log.h"
#include "testing/cts/env/wvpl_env.h"
#include "testing/cts/sdk/wvpl_sdk.h"
#include "webutil/http/httpresponse.h"
using net_http2::HTTPServer2;
using video_widevine::wvpl_sdk::HandleDrmLicenseRequest;
ABSL_FLAG(int32_t, port, 10000, "Port for incoming HTTP requests");
ABSL_FLAG(int32_t, worker_threads, 10, "Number of worker threads");
void SDKHandler(HTTPServerRequest* req) {
req->output()->WriteString("Hello world");
req->Reply();
}
// TODO: b/323916039 - Setup handlers to response to endpoints for SDK
void drmRequestHandler(HTTPServerRequest* req) {
std::string requestString = req->input()->ToString();
absl::StatusOr<std::string> response = HandleDrmLicenseRequest(requestString);
if (!response.ok()) {
req->output()->WriteString(response.status().ToString());
req->ReplyWithStatus(HTTPResponse::RC_BAD_REQUEST);
return;
}
req->output()->WriteString(response.value());
req->Reply();
}
int main(int argc, char** argv) {
InitGoogle(argv[0], &argc, &argv, true);
auto wvpl_env = video_widevine::cts::wvpl_env::WvplEnv::GetInstance();
if (wvpl_env->env_setup_failed()) {
return 1;
} else {
}
// Configure the options for your server
auto options = absl::make_unique<HTTPServer2::EventModeOptions>();
options->SetVersion("Example HTTPServer2 (non-blocking)");
options->SetServerType("test_server");
options->AddPort(absl::GetFlag(FLAGS_port)); // Create the HTTPServer2
eventmanager::GenericEventManagerOptions eventOptions;
eventOptions.num_threads_hint = absl::GetFlag(FLAGS_worker_threads);
auto eventManager = eventmanager::NewEventManager(eventOptions);
eventmanager::EventManagerInterface* eventManagerPtr = eventManager.get();
std::unique_ptr<HTTPServer2> server =
HTTPServer2::CreateEventDrivenModeServer(eventManagerPtr,
std::move(options))
.value();
server->RegisterHandler("/", absl::bind_front(SDKHandler));
server->RegisterHandler("/drmRequest", absl::bind_front(drmRequestHandler));
CHECK_OK(server->StartAcceptingRequests());
server->WaitForTermination();
return 0;
}

View File

@@ -0,0 +1,44 @@
########################################
## 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.
########################################
# Desciption:
# JTS http clients.
load("@rules_java//java:defs.bzl", "java_library")
package(
default_visibility = ["//visibility:public"],
)
java_library(
name = "httpclient",
srcs = glob(
["*.java"],
exclude = [
"CdmHttpClient.java",
],
),
deps = [
"@google_api//:com_google_api_client_google_api_client",
"@google_guice//:com_google_inject_guice",
"@google_http_client//:com_google_http_client_google_http_client",
"@google_http_client_gson//:com_google_http_client_google_http_client_gson",
"@google_oauth2//:com_google_oauth_client_google_oauth_client",
"@jackson_core//:com_fasterxml_jackson_core_jackson_core",
"@json//:org_json_json",
],
)
java_library(
name = "cdm_http_client",
srcs = ["CdmHttpClient.java"],
deps = [
"//java/com/google/chubby/svelte:bns_resolver",
"//java/com/google/io/base",
"//java/com/google/net/httpclient",
],
)

View File

@@ -0,0 +1,62 @@
////////////////////////////////////////////////////////////////////////////////
//// 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.
////////////////////////////////////////////////////////////////////////////////
package com.google.video.widevine.jts.httpclient;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* JTS Cloud Credentials.
*
* Provides Oauth2 authentication, and access/refresh tokens.
*/
public class Credentials {
private static final List<String> SCOPES = Arrays.asList(
"https://www.googleapis.com/auth/cloud-platform",
"https://www.googleapis.com/auth/widevine/frontend");
private static final int MIN_ACCESS_TOKEN_REFRESH_SEC = 1200;
private static final Logger logger = Logger.getLogger(Credentials.class.getName());
private String credentialsJsonFile = null;
private GoogleCredential googleCredential = null;
private String accessToken = null;
/**
* Credentials Constructor.
*
* @param credentialsJsonFile The full path to a GCP service account json credentials file.
*/
public Credentials(String credentialsJsonFile) throws IOException {
this.credentialsJsonFile = credentialsJsonFile;
activateGoogleCredentials();
}
private void activateGoogleCredentials() throws IOException {
googleCredential = GoogleCredential
.fromStream(new FileInputStream(credentialsJsonFile))
.createScoped(SCOPES);
googleCredential.refreshToken();
}
/** Provides an Oauth2 Access Token that can be used to make GCP service calls.*/
public String getAccessToken() throws IOException {
if (accessToken == null
|| googleCredential.getExpiresInSeconds() <= MIN_ACCESS_TOKEN_REFRESH_SEC) {
googleCredential.refreshToken();
accessToken = googleCredential.getAccessToken();
logger.log(Level.INFO, "Getting new access token.");
}
return accessToken;
}
}

View File

@@ -0,0 +1,27 @@
########################################
## Copyright 2018 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.
########################################
# Desciption:
# JTS interfaces.
load("@rules_java//java:defs.bzl", "java_library")
package(
default_visibility = ["//visibility:public"],
)
java_library(
name = "interfaces",
srcs = glob(["*.java"]),
deps = [
"//google/chrome/widevine/contentpartners/v1beta1:device_security_profiles_java_proto",
"//google/chrome/widevine/contentpartners/v1beta1:published_devices_java_proto",
"@com_google_protobuf//:protobuf_java",
"@apache_httpcore//:org_apache_httpcomponents_httpcore",
"//protos/public:license_protocol_java_proto",
],
)

View File

@@ -0,0 +1,24 @@
////////////////////////////////////////////////////////////////////////////////
//// 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.
////////////////////////////////////////////////////////////////////////////////
package com.google.video.widevine.jts.interfaces;
import com.google.chrome.widevine.contentpartners.v1beta1.PublishedDevices;
/**
* DeviceCertificate defines APIs for getting PublishedDevices and Published Devices list.
*/
public interface DeviceCertificate {
/**
* Get the latest {@code PublishedDevices} containing Published Devices list data.
*
* @return {@code PublishedDevices} containing Published Devices list data.
*/
public PublishedDevices getPublishedDevices() throws Exception;
}

View File

@@ -0,0 +1,22 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2020 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.
////////////////////////////////////////////////////////////////////////////////
package com.google.video.widevine.jts.interfaces;
import com.google.chrome.widevine.contentpartners.v1beta1.SignedDeviceSecurityProfiles;
/** DeviceSecurityProfile defines APIs for getting Signed Device Security Profile list. */
public interface DeviceSecurityProfile {
/**
* Get the latest {@code SignedDeviceSecurityProfiles} containing Device Security Profile list
* data.
*
* @return {@code SignedDeviceSecurityProfiles} containing Device Security Profile list data.
*/
public SignedDeviceSecurityProfiles getSignedDeviceSecurityProfiles() throws Exception;
}

View File

@@ -0,0 +1,21 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2023 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.
////////////////////////////////////////////////////////////////////////////////
package com.google.video.widevine.jts.interfaces;
/**
* IndividualDeviceRevocationList defines APIs for getting Individual Device Revocation List (IDRL).
*/
public interface IndividualDeviceRevocationList {
/**
* Get the latest Individual Device Revocation List data.
*
* @return Individual Device Revocation List data.
*/
public byte[] getIndividualDeviceRevocationList() throws Exception;
}

View File

@@ -0,0 +1,54 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2018 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.
////////////////////////////////////////////////////////////////////////////////
package com.google.video.widevine.jts.interfaces;
import com.google.protobuf.ByteString;
import com.google.video.widevine.protos.LicenseProtocol.License.KeyContainer;
import java.util.List;
/** An interface to be implemented by a Key provider. */
public interface KeyProviderInterface {
/**
* Gets a list of KeyContainers for the specified content Id.
*
* @param contentId The content Id that uniquely identifies the video.
* Cannot be used in conjunction with keyIds.
* See http://google3/protos/public/widevine_pssh.proto
* @return A List of KeyContainer protos if the content is found, else null.
*/
public List<KeyContainer> getKeys(String contentId);
/**
* Gets a list of KeyContainers for the specified key Ids.
* @param keyIds The key Ids to apply to a video's license.
* Cannot be used in conjunction with contentId.
* See http://google3/protos/public/widevine_pssh.proto
* @return A List of KeyContainer protos if the key IDs are found, else null.
*/
public List<KeyContainer> getKeys(List<ByteString> keyIds);
/**
* Gets a list of KeyContainers for the specified List of DSP names.
* @param dspNames The DSP names to fetch associated KeyContainers.
* @return A List of KeyContainer protos if the DSPs are found, else returns empty List
* if no DSPs found.
*/
public List<KeyContainer> getKeysFromDsp(List<String> dspNames);
/**
* Gets a profile name that is associated with the keyId and contentOwner.
*
* @param keyId The key Ids to apply to a video's license.
* @param contentOwner The content owner of the key.
* @return A profile name if the keyId and contentOwner are found, else returns empty string.
*/
public String getRequiredDspProfileForKeyId(String keyId, String contentOwner);
/** Returns the owner of the DSP. */
public String getOwnerForDsp();
}

View File

@@ -0,0 +1,56 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2018 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.
////////////////////////////////////////////////////////////////////////////////
package com.google.video.widevine.jts.interfaces;
import org.apache.http.HttpException;
/**
* LicenseInterface defines the API that a license-based SDK must implement.
*
* <p>The {@link LicenseRequestAction} receives all incoming license requests and dispatches them to
* the appropriate licensing service SDK implementation for processing. This interface defines the
* API that each SDK implementation must provide in order for requests to be properly handled.
*/
public interface LicenseInterface {
/**
* Handles an incoming license request and returns the license response.
*
* @param requestBody The incoming body of the license request.
* @param policyId Optional. A custom policy ID to use if specified, else null.
* @param renewalPolicyId Optional. A custom renewal policy ID to use if specified, else null.
* @param requestId A unique request id used to track, or collect stats on, a specific request, or
* series of requests.
* @param useDsp Set {@code true} to process license using Device Security Profiles (DSP).
* @param useFilterKey Set {@code true} to using filterKey() to insert keys in license request.
* @return The license response to be returned to the client as a byte[].
*/
public byte[] handleLicenseRequest(
byte[] requestBody,
String policyId,
String renewalPolicyId,
String requestId,
boolean useDsp,
boolean useFilterKey,
StringBuilder sessionUsageBuilder)
throws HttpException;
/** Handles returning status info about a running SDK. */
public byte[] getSdkStatus();
/**
* Handles an incoming DrmLicenseRequest and returns the license response.
*
* @param requestBody The incoming body of the ModularDrmLicenseRequest or CasDrmLicenseRequest.
* @param useDsp Set {@code true} to process license using Device Security Profiles (DSP).
* @param useFilterKey Set {@code true} to using filterKey() to insert keys in license request.
* @return The license response to be returned to the client as a byte[].
*/
public byte[] handleDrmLicenseRequest(byte[] requestBody, boolean useDsp, boolean useFilterKey)
throws HttpException;
}

View File

@@ -0,0 +1,48 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2018 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.
////////////////////////////////////////////////////////////////////////////////
package com.google.video.widevine.jts.interfaces;
import com.google.video.widevine.protos.LicenseProtocol.License.Policy;
// TODO(march): Move back to /j/c/g/v/w/testing/sdk/interfaces once b/79438055 is fixed.
/** An interface to be implemented by a Policy provider. */
public interface PolicyProviderInterface {
/**
* Gets a Policy for the specified policy ID.
*
* @param policyId The ID of the policy to retrieve.
* @return A Policy proto if the policy is found, else null.
*/
public Policy getPolicy(String policyId);
/**
* Gets a policy id for the specified content id.
*
* @param contentId The id of the content to retrieve.
* @return The policy id if the content is found and has a policy, else null.
*/
public String getPolicyIdForContentId(String contentId);
/**
* Gets a Provider Session Token (PST) for the specified content id.
*
* @param contentId The id of the content to retrieve.
* @return The PST token if the content is found and has uses one.
*/
public String getProviderSessionToken(String contentId);
/**
* Sets a Policy for the specified policy ID.
*
* @param policyId The ID of the policy to set.
* @param policy A proto defining the policy details.
* @return A boolean indicating whether the policy was successfully set.
*/
public Boolean setPolicy(String policyId, Policy policy);
}

View File

@@ -0,0 +1,72 @@
########################################
## Copyright 2018 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.
########################################
# Description:
# JTS interfaces.
load("@rules_java//java:defs.bzl", "java_library")
package(
default_visibility = ["//visibility:public"],
)
java_library(
name = "providers",
srcs = glob(["*.java"]),
deps = [
"//google/chrome/widevine/contentpartners/v1beta1:device_security_profiles_java_proto",
"//google/chrome/widevine/contentpartners/v1beta1:published_devices_grpc",
"//google/chrome/widevine/contentpartners/v1beta1:published_devices_java_proto",
"//google/chrome/widevine/frauddetection/v1eap1:fraud_detection_and_reporting_grpc",
"//google/chrome/widevine/frauddetection/v1eap1:fraud_detection_and_reporting_service_java_proto",
"//protos/public:device_certificate_status_java_proto",
"//protos/public:license_protocol_java_proto",
"//protos/public:sdk_license_data_config_java_proto",
"//protos/public:widevine_pssh_java_proto",
":libwidevine_license_wvpl_sdk_lib.jar",
"//testing/java/com/google/video/widevine/jts/common",
"//testing/java/com/google/video/widevine/jts/httpclient",
"//testing/java/com/google/video/widevine/jts/interfaces",
"@com_google_protobuf//:protobuf_java",
"@com_google_protobuf//:timestamp_proto",
"@google_guava//:com_google_guava_guava",
"@google_guice//:com_google_inject_guice",
"@io_grpc_grpc_java//api",
"@io_grpc_grpc_java//netty",
"@io_grpc_grpc_java//stub",
"@json//:org_json_json",
"@maven//:com_google_api_grpc_proto_google_common_protos",
"@maven//:com_google_code_findbugs_jsr305",
"@maven//:com_google_code_gson_gson",
],
)
java_library(
name = "providers_tools",
srcs = [
"PublishedDevicesProvider.java",
],
deps = [
"//google/chrome/widevine/contentpartners/v1beta1:device_security_profiles_java_proto",
"//google/chrome/widevine/contentpartners/v1beta1:published_devices_grpc",
"//google/chrome/widevine/contentpartners/v1beta1:published_devices_java_proto",
"//google/chrome/widevine/frauddetection/v1eap1:fraud_detection_and_reporting_grpc",
"//google/chrome/widevine/frauddetection/v1eap1:fraud_detection_and_reporting_service_java_proto",
"//protos/public:device_certificate_status_java_proto",
":libwidevine_license_wvpl_sdk_lib.jar",
"//testing/java/com/google/video/widevine/jts/httpclient",
"//testing/java/com/google/video/widevine/jts/interfaces",
"@com_google_protobuf//:protobuf_java",
"@com_google_protobuf//:timestamp_proto",
"@google_guava//:com_google_guava_guava",
"@google_guice//:com_google_inject_guice",
"@io_grpc_grpc_java//api",
"@io_grpc_grpc_java//netty",
"@io_grpc_grpc_java//stub",
"@maven//:com_google_code_findbugs_jsr305",
],
)

View File

@@ -0,0 +1,339 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2020 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.
////////////////////////////////////////////////////////////////////////////////
package com.google.video.widevine.jts.providers;
import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.chrome.widevine.contentpartners.v1beta1.DcslSnapshotType;
import com.google.chrome.widevine.contentpartners.v1beta1.DeviceSecurityProfileCriteria;
import com.google.chrome.widevine.contentpartners.v1beta1.ListDeviceSecurityProfilesRequest;
import com.google.chrome.widevine.contentpartners.v1beta1.PublishedDevices;
import com.google.chrome.widevine.contentpartners.v1beta1.PublishedDevicesRequest;
import com.google.chrome.widevine.contentpartners.v1beta1.PublishedDevicesServiceGrpc;
import com.google.chrome.widevine.contentpartners.v1beta1.RetrieveIndividualDeviceRevocationListRequest;
import com.google.chrome.widevine.contentpartners.v1beta1.RetrieveIndividualDeviceRevocationListResponse;
import com.google.chrome.widevine.contentpartners.v1beta1.SignedDeviceSecurityProfiles;
import com.google.chrome.widevine.frauddetection.v1eap1.FraudDetectionAndReportingServiceGrpc;
import com.google.chrome.widevine.frauddetection.v1eap1.FraudLevel;
import com.google.chrome.widevine.frauddetection.v1eap1.LicenseRequestInfo;
import com.google.chrome.widevine.frauddetection.v1eap1.LicenseRequestInfo.RequestMetadata;
import com.google.chrome.widevine.frauddetection.v1eap1.ReportFraudLevelRequest;
import com.google.chrome.widevine.frauddetection.v1eap1.ReportFraudLevelResponse;
import com.google.common.base.Ascii;
import com.google.inject.Inject;
import com.google.protobuf.ByteString;
import com.google.protobuf.ExtensionRegistry;
import com.google.protobuf.TextFormat;
import com.google.protobuf.Timestamp;
import com.google.video.widevine.jts.httpclient.Credentials;
import com.google.video.widevine.jts.interfaces.DeviceCertificate;
import com.google.video.widevine.jts.interfaces.DeviceSecurityProfile;
import com.google.video.widevine.jts.interfaces.IndividualDeviceRevocationList;
import com.google.video.widevine.protos.DeviceCertificateStatusProtos.DeviceCertificateStatusList;
import com.google.video.widevine.sdk.wvpl.WvPLBaseEnvironment;
import com.google.video.widevine.sdk.wvpl.WvPLStatusException;
import io.grpc.ManagedChannel;
import io.grpc.Metadata;
import io.grpc.netty.NettyChannelBuilder;
import io.grpc.stub.MetadataUtils;
import java.io.IOException;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
/**
* Provides the latest {@code PublishedDevices} or {@code SignedDeviceSecurityProfiles} data from
* the Widevine Published Devices Service.
*
* <p>This implementation uses the Widevine Published Devices API, and support a gRPC method for
* retrieving PublishedDevices with an embedded Published Devices list, or Device Security Profiles
* list.
*/
public class PublishedDevicesProvider
implements DeviceCertificate, DeviceSecurityProfile, IndividualDeviceRevocationList {
private static final Logger logger = Logger.getLogger(PublishedDevicesProvider.class.getName());
private WvPLBaseEnvironment<?> environment = null;
private String apiServicePath = null;
private Credentials credentials = null;
private String provider = null;
private boolean includeImpactAnalysis = false;
private Integer lastDcslEpochTime = null;
private String dcslType = null;
private static final String FRAUDULENT_API_SERVICE_PATH = "widevinefrauddetection.googleapis.com";
/**
* PublishedDevicesProvider constructor.
*
* @param environment A WvPLBaseEnvironment object initialized with a Service Certificate.
* @param serviceAccountPath Path to a GCP Service Account json file, used in OAUTH.
* @param apiServicePath Path to a Widevine Published Devices API service.
* @param provider Provider name, necessary for creating ListDSP request. It could be null if it
* is not used by dsps.
* @param includeImpactAnalysis Includes impact analysis in published devices response based on
* flag value. It can be false if caller doesn't care about impact analysis data from
* published devices.
* @param lastDcslEpochTime Timestamp that helps to calculate the DCSL impact analysis from a DCSL
* snapshot at last_dcsl_epoch_time to the latest DCSL snapshot. It can be set to null if
* caller doesn't care about impact analysis data from published devices.
* @throws IOException upon failure creating OAUTH credentials.
*/
@Inject
public PublishedDevicesProvider(
WvPLBaseEnvironment<?> environment,
String serviceAccountPath,
String apiServicePath,
@Nullable String provider,
boolean includeImpactAnalysis,
Integer lastDcslEpochTime,
String dcslType)
throws IOException {
this.environment = environment;
this.apiServicePath = apiServicePath;
credentials = new Credentials(serviceAccountPath);
this.provider = provider;
this.includeImpactAnalysis = includeImpactAnalysis;
this.lastDcslEpochTime = lastDcslEpochTime;
this.dcslType = dcslType;
}
/**
* Get the latest {@code PublishedDevices} from the Widevine Published Devices Service.
*
* @return The latest {@code PublishedDevices} data.
* @throws InterruptedException upon RPC failure.
* @throws WvPLStatusException upon WvPLBaseEnvironment errors.
*/
@Override
public PublishedDevices getPublishedDevices() throws InterruptedException, WvPLStatusException {
ManagedChannel channel = null;
PublishedDevices devicesResponse = null;
logger.log(Level.INFO, "Getting PublishedDevices...");
try {
channel = createRpcChannel(apiServicePath);
PublishedDevicesServiceGrpc.PublishedDevicesServiceBlockingStub blockingStub =
createBlockingStub(channel);
PublishedDevicesRequest publishedDevicesRequest =
PublishedDevicesRequest.parseFrom(
environment.generateDeviceStatusListRequest(), ExtensionRegistry.getEmptyRegistry());
PublishedDevicesRequest.Builder updatedPublishedDevicesRequestBuilder =
publishedDevicesRequest.toBuilder().setIncludeImpactAnalysis(includeImpactAnalysis);
if (lastDcslEpochTime != null) {
updatedPublishedDevicesRequestBuilder.setLastDcslEpochTime(
Timestamp.newBuilder().setSeconds(lastDcslEpochTime).build());
}
updatedPublishedDevicesRequestBuilder.setDcslSnapshotType(getDcslSnapshotType(dcslType));
devicesResponse =
blockingStub.getPublishedDevices(updatedPublishedDevicesRequestBuilder.build());
String truncatedLogString = TextFormat.printer().printToString(devicesResponse);
int tenLines = 800;
if (truncatedLogString.length() > tenLines) {
truncatedLogString = truncatedLogString.substring(0, tenLines) + "...";
}
logger.log(
Level.INFO,
"GRPC Call to PublishedDevicesService.GetPublishedDevices returned:\n"
+ truncatedLogString);
DeviceCertificateStatusList dcsl =
DeviceCertificateStatusList.parseFrom(
devicesResponse.getPublishedDevices(), ExtensionRegistry.getEmptyRegistry());
logger.log(
Level.INFO,
"Impact analysis count in response from PublishedDevicesService.GetPublishedDevices is:\n"
+ dcsl.getImpactAnalysisReport().getImpactAnalysisCount());
logger.log(
Level.INFO,
"DCSL type in response from PublishedDevicesService.GetPublishedDevices is:\n"
+ devicesResponse.getDcslSnapshotType());
logger.log(
Level.INFO,
"DCSL snapshot creation time in response from"
+ " PublishedDevicesService.GetPublishedDevices is: "
+ dcsl.getCreationTimeSeconds()
+ " in UTC time is :\n"
+ Instant.ofEpochSecond(dcsl.getCreationTimeSeconds())
.atZone(ZoneOffset.UTC)
.toLocalDateTime()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
} catch (IOException e) {
logger.log(Level.INFO, "IOException encountered trying to retrieve the signed list: " + e);
} finally {
closeRpcChannel(channel);
}
return devicesResponse;
}
@Override
public SignedDeviceSecurityProfiles getSignedDeviceSecurityProfiles()
throws InterruptedException, WvPLStatusException {
// Generate list dsp request.
ListDeviceSecurityProfilesRequest listDspRequest =
ListDeviceSecurityProfilesRequest.newBuilder()
.setDeviceSecurityProfileCriteria(
DeviceSecurityProfileCriteria.newBuilder()
.setContentProvider(this.provider)
.build())
.build();
ManagedChannel channel = null;
SignedDeviceSecurityProfiles signedMessageInResponse = null;
logger.log(Level.INFO, "Getting DeviceSecurityProfilesList...");
try {
channel = createRpcChannel(apiServicePath);
PublishedDevicesServiceGrpc.PublishedDevicesServiceBlockingStub blockingStub =
createBlockingStub(channel);
signedMessageInResponse =
blockingStub.listDeviceSecurityProfiles(listDspRequest).getSignedDeviceSecurityProfiles();
logger.log(
Level.INFO,
"GRPC Call to PublishedDevicesService.ListDeviceSecurityProfiles returned:\n"
+ TextFormat.printer().printToString(signedMessageInResponse));
} catch (IOException e) {
logger.log(
Level.INFO, "IOException encountered trying to retrieve the signed dsp list: " + e);
} finally {
closeRpcChannel(channel);
}
return signedMessageInResponse;
}
@Override
public byte[] getIndividualDeviceRevocationList() throws InterruptedException {
ManagedChannel channel = null;
RetrieveIndividualDeviceRevocationListResponse response = null;
logger.log(Level.INFO, "Getting IndividualDeviceRevocationList...");
try {
channel = createRpcChannel(apiServicePath);
PublishedDevicesServiceGrpc.PublishedDevicesServiceBlockingStub blockingStub =
createBlockingStub(channel);
RetrieveIndividualDeviceRevocationListRequest request =
RetrieveIndividualDeviceRevocationListRequest.newBuilder()
.setProvider(this.provider)
.build();
response = blockingStub.retrieveIndividualDeviceRevocationList(request);
logger.log(
Level.INFO,
"GRPC Call to PublishedDevicesService.retrieveIndividualDeviceRevocationList returned:\n"
+ TextFormat.printer().printToString(response));
} catch (IOException e) {
logger.log(Level.INFO, "IOException encountered trying to retrieve the IDRL: " + e);
} finally {
closeRpcChannel(channel);
}
return response.toByteArray();
}
public byte[] reportFraudulentDevice(
String licenseRequest,
@Nullable String clearClientId,
@Nullable String userAgent,
@Nullable String reason,
FraudLevel.FraudScore fraudScore)
throws InterruptedException, IOException {
ReportFraudLevelRequest request =
createReportFraudLevelRequest(
this.provider, licenseRequest, clearClientId, userAgent, reason, fraudScore);
ManagedChannel channel = createRpcChannel(FRAUDULENT_API_SERVICE_PATH);
try {
return reportFraudulentDeviceWithChannel(channel, request);
} finally {
closeRpcChannel(channel);
}
}
// Construct the ReportFraudLevelRequest.
static ReportFraudLevelRequest createReportFraudLevelRequest(
String provider,
String licenseRequest,
@Nullable String clearClientId,
@Nullable String userAgent,
@Nullable String reason,
FraudLevel.FraudScore fraudScore) {
RequestMetadata.Builder requestMetadata = RequestMetadata.newBuilder().setProvider(provider);
if (userAgent != null) {
requestMetadata.setUserAgent(userAgent);
}
LicenseRequestInfo.Builder licenseRequestInfo =
LicenseRequestInfo.newBuilder()
.setRequestMetadata(requestMetadata.build())
.setLicenseRequest(ByteString.copyFrom(Base64.getDecoder().decode(licenseRequest)));
if (clearClientId != null) {
licenseRequestInfo.setClearClientId(
ByteString.copyFrom(Base64.getDecoder().decode(clearClientId)));
}
ReportFraudLevelRequest.Builder request =
ReportFraudLevelRequest.newBuilder()
.setFraudLevel(FraudLevel.newBuilder().setFraudScore(fraudScore))
.setLicenseRequestInfo(licenseRequestInfo.build());
if (reason != null) {
request.setReason(reason);
}
return request.build();
}
private byte[] reportFraudulentDeviceWithChannel(
ManagedChannel channel, ReportFraudLevelRequest request) throws IOException {
logger.log(Level.INFO, "Reporting fraudulent device...");
FraudDetectionAndReportingServiceGrpc.FraudDetectionAndReportingServiceBlockingStub
blockingStub =
FraudDetectionAndReportingServiceGrpc.newBlockingStub(channel)
.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(createMetadata()));
ReportFraudLevelResponse response = blockingStub.reportFraudLevel(request);
logger.log(
Level.INFO,
"GRPC Call to report fraud returned:\n" + TextFormat.printer().printToString(response));
return response.toByteArray();
}
private static ManagedChannel createRpcChannel(String host) {
return NettyChannelBuilder.forTarget(host).build();
}
private static void closeRpcChannel(ManagedChannel channel) throws InterruptedException {
if (channel != null) {
channel.shutdown();
channel.awaitTermination(1, SECONDS);
}
}
private Metadata createMetadata() throws IOException {
Metadata metadata = new Metadata();
String token = "Bearer " + credentials.getAccessToken();
metadata.put(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER), token);
return metadata;
}
private PublishedDevicesServiceGrpc.PublishedDevicesServiceBlockingStub createBlockingStub(
ManagedChannel channel) throws IOException {
Metadata metadata = createMetadata();
return PublishedDevicesServiceGrpc.newBlockingStub(channel)
.withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata));
}
private DcslSnapshotType getDcslSnapshotType(String dcslType) throws IOException {
if (dcslType == null) {
return DcslSnapshotType.DCSL_SNAPSHOT_TYPE_UNSPECIFIED;
} else if (Ascii.equalsIgnoreCase(dcslType, "alpha")) {
return DcslSnapshotType.DCSL_ALPHA;
} else if (Ascii.equalsIgnoreCase(dcslType, "beta")) {
return DcslSnapshotType.DCSL_BETA;
} else if (Ascii.equalsIgnoreCase(dcslType, "released")) {
return DcslSnapshotType.DCSL_RELEASED;
}
throw new IOException(
"Invalid value set for dcslType flag. Valid values are Alpha, Beta and Released.");
}
}

View File

@@ -0,0 +1,101 @@
########################################
## 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.
########################################
# Description:
# JTS tools.
load("@rules_java//java:defs.bzl", "java_binary", "java_library")
package(default_visibility = ["//visibility:public"])
java_library(
name = "published_devices_cli_lib",
srcs = [
"PublishedDevicesCli.java",
],
deps = [
"//google/chrome/widevine/contentpartners/v1beta1:device_security_profiles_java_proto",
"//google/chrome/widevine/contentpartners/v1beta1:published_devices_grpc",
"//google/chrome/widevine/contentpartners/v1beta1:published_devices_java_proto",
"//google/chrome/widevine/frauddetection/v1eap1:fraud_detection_and_reporting_service_java_proto",
"//testing/java/com/google/video/widevine/jts/providers:libwidevine_license_wvpl_sdk_lib.jar",
"//testing/java/com/google/video/widevine/jts/providers:providers_tools",
"@google_guava//:com_google_guava_guava",
"@jcommander//:com_beust_jcommander",
],
)
java_library(
name = "published_devices_delta_lib",
srcs = [
"PublishedDevicesDeltaWrapper.java",
"PublishedDevicesWrapper.java",
],
deps = [
"//google/chrome/widevine/contentpartners/v1beta1:published_devices_java_proto",
"//protos/public:device_certificate_status_java_proto",
"//protos/public:device_certificate_status_proto",
"//protos/public:published_devices_delta_java_proto",
"//protos/public:published_devices_delta_proto",
"@com_google_protobuf//:protobuf_java",
"@google_guava//:com_google_guava_guava",
],
)
java_library(
name = "device_security_profiles_lib",
srcs = [
"DeviceSecurityProfilesDeltaWrapper.java",
"DeviceSecurityProfilesWrapper.java",
],
deps = [
"//google/chrome/widevine/contentpartners/v1beta1:device_security_profiles_java_proto",
"//protos/public:device_security_profile_list_java_proto",
"//protos/public:device_security_profiles_delta_java_proto",
"//protos/public:security_profile_java_proto",
"@com_google_protobuf//:protobuf_java",
"@google_guava//:com_google_guava_guava",
],
)
java_binary(
name = "published_devices_cli",
main_class = "com.google.video.widevine.jts.tools.PublishedDevicesCli",
runtime_deps = [
":published_devices_cli_lib",
],
)
java_binary(
name = "published_devices_delta",
main_class = "com.google.video.widevine.jts.tools.PublishedDevicesMain",
runtime_deps = [
":published_devices_delta_main_lib",
],
)
java_library(
name = "published_devices_delta_main_lib",
srcs = [
"PublishedDevicesMain.java",
],
deps = [
":device_security_profiles_lib",
":published_devices_delta_lib",
"//protos/public:device_certificate_status_java_proto",
"//protos/public:device_certificate_status_proto",
"//protos/public:device_security_profiles_delta_java_proto",
"//protos/public:device_security_profiles_delta_proto",
"//protos/public:published_devices_delta_java_proto",
"//protos/public:published_devices_delta_proto",
"//protos/public:security_profile_java_proto",
"@com_google_protobuf//:protobuf_java",
"@google_guava//:com_google_guava_guava",
"@jcommander//:com_beust_jcommander",
"@json//:org_json_json",
],
)

View File

@@ -0,0 +1,265 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2020 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.
////////////////////////////////////////////////////////////////////////////////
package com.google.video.widevine.jts.tools;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.chrome.widevine.contentpartners.v1beta1.SignedDeviceSecurityProfiles;
import com.google.protobuf.ExtensionRegistry;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.video.widevine.protos.DeviceSecurityProfileListProtos.DeviceSecurityProfileList;
import com.google.video.widevine.protos.DeviceSecurityProfilesDeltaProtos.DeviceSecurityProfilesDelta;
import com.google.video.widevine.protos.DeviceSecurityProfilesDeltaProtos.DeviceSecurityProfilesDelta.DspDelta;
import com.google.video.widevine.protos.DeviceSecurityProfilesDeltaProtos.DeviceSecurityProfilesDelta.Header;
import com.google.video.widevine.protos.DeviceSecurityProfilesDeltaProtos.DeviceSecurityProfilesDelta.Modified;
import com.google.video.widevine.protos.SecurityProfileProtos.SecurityProfile;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** Device Security Profiles Delta Wrapper class to find the diff of two DSPs list. */
final class DeviceSecurityProfilesDeltaWrapper {
private DeviceSecurityProfileList originalDspList = null;
private DeviceSecurityProfileList newDspList = null;
private DeviceSecurityProfilesDelta dspDelta = null;
private final List<SecurityProfile> dspAddedList = new ArrayList<>();
private final List<SecurityProfile> dspRemovedList = new ArrayList<>();
private final List<Modified> dspModifiedList = new ArrayList<>();
/**
* DeviceSecurityProfilesDeltaWrapper constructor. The input is two {@link
* SignedDeviceSecurityProfiles} proto messages.
*
* @param signedDspList1 first {@link SignedDeviceSecurityProfiles} proto message.
* @param signedDspList2 second {@link SignedDeviceSecurityProfiles} proto message.
* @throws InvalidProtocolBufferException if failed to parse DeviceSecurityProfileList from {@link
* SignedDeviceSecurityProfiles}.
*/
public DeviceSecurityProfilesDeltaWrapper(
SignedDeviceSecurityProfiles signedDspList1, SignedDeviceSecurityProfiles signedDspList2)
throws InvalidProtocolBufferException {
checkNotNull(signedDspList1, "'signedDspList1' must not be null");
checkNotNull(signedDspList2, "'signedDspList2' must not be null");
parseDeviceSecurityProfilesList(signedDspList1, signedDspList2);
this.dspDelta = createDeviceSecurityProfilesDelta(originalDspList, newDspList);
createDeviceSecurityProfilesDeltaInfo(this.dspDelta);
}
/**
* DeviceSecurityProfilesDeltaWrapper constructor. The input is two byte arrays of serialized
* {@link SignedDeviceSecurityProfiles} proto.
*
* @param signedDspListInBytes1 first serialized {@link SignedDeviceSecurityProfiles} proto in
* bytes.
* @param signedDspListInBytes2 second serialized {@link SignedDeviceSecurityProfiles} proto in
* bytes.
* @throws InvalidProtocolBufferException if failed to parse {@link SignedDeviceSecurityProfiles}
* or if failed to parse {@link DeviceSecurityProfileList}.
*/
public DeviceSecurityProfilesDeltaWrapper(
byte[] signedDspListInBytes1, byte[] signedDspListInBytes2)
throws InvalidProtocolBufferException {
checkNotNull(signedDspListInBytes1, "'signedDspListInBytes1' must not be null");
checkNotNull(signedDspListInBytes2, "'signedDspListInBytes2' must not be null");
SignedDeviceSecurityProfiles signedDspListProto1 =
SignedDeviceSecurityProfiles.parseFrom(
signedDspListInBytes1, ExtensionRegistry.getEmptyRegistry());
SignedDeviceSecurityProfiles signedDspListProto2 =
SignedDeviceSecurityProfiles.parseFrom(
signedDspListInBytes2, ExtensionRegistry.getEmptyRegistry());
parseDeviceSecurityProfilesList(signedDspListProto1, signedDspListProto2);
this.dspDelta = createDeviceSecurityProfilesDelta(originalDspList, newDspList);
createDeviceSecurityProfilesDeltaInfo(this.dspDelta);
}
/** Returns {@link DeviceSecurityProfilesDelta} of two dsp lists. */
public DeviceSecurityProfilesDelta getDelta() {
return dspDelta;
}
/**
* Returns {@link DspDelta} for a specific content owner. If no matched result, return empty
* DspDelta proto.
*/
public DspDelta getDeviceSecurityProfilesDelta(String owner) {
for (DspDelta dspDeltaEntry : dspDelta.getDspDeltaList()) {
if (dspDeltaEntry.getOwner().equals(owner)) {
return dspDeltaEntry;
}
}
return DspDelta.getDefaultInstance();
}
/** Returns a list of added DSPs from DeviceSecurityProfilesDelta for all content owners. */
public List<SecurityProfile> getAddedDeviceSecurityProfiles() {
return dspAddedList;
}
/** Returns a list of removed DSPs from DeviceSecurityProfilesDelta for all content owners. */
public List<SecurityProfile> getRemovedDeviceSecurityProfiles() {
return dspRemovedList;
}
/** Returns a list of modified DSPs from DeviceSecurityProfilesDelta for all content owners. */
public List<Modified> getModifiedDeviceSecurityProfiles() {
return dspModifiedList;
}
/** Parse {@link DeviceSecurityProfileList} from {@link SignedDeviceSecurityProfiles}. */
private void parseDeviceSecurityProfilesList(
SignedDeviceSecurityProfiles signedDspList1, SignedDeviceSecurityProfiles signedDspList2)
throws InvalidProtocolBufferException {
originalDspList =
DeviceSecurityProfileList.parseFrom(
signedDspList1.getDeviceSecurityProfiles(), ExtensionRegistry.getEmptyRegistry());
newDspList =
DeviceSecurityProfileList.parseFrom(
signedDspList2.getDeviceSecurityProfiles(), ExtensionRegistry.getEmptyRegistry());
// Swap the DeviceSecurityProfileList if originalDspList is newer than signedDspList2.
if (originalDspList.getCreationTimeSeconds() > newDspList.getCreationTimeSeconds()) {
DeviceSecurityProfileList tempList = newDspList;
newDspList = originalDspList;
originalDspList = tempList;
}
}
/** Creates a {@link DeviceSecurityProfilesDelta} of originalDspList and newDspList. */
private static DeviceSecurityProfilesDelta createDeviceSecurityProfilesDelta(
DeviceSecurityProfileList originalDspList, DeviceSecurityProfileList newDspList) {
Header header =
Header.newBuilder()
.setPrevCreationTimeSeconds(originalDspList.getCreationTimeSeconds())
.setNewCreationTimeSeconds(newDspList.getCreationTimeSeconds())
.build();
DeviceSecurityProfilesDelta.Builder deltaBuilder =
DeviceSecurityProfilesDelta.newBuilder().setHeader(header);
// Separately add all original dsps and new dsps into two maps. Key is content owner name,
// value is a list of dsps created by this content owner.
Map<String, List<SecurityProfile>> originalDspMap = initializeDspMap(originalDspList);
Map<String, List<SecurityProfile>> newDspMap = initializeDspMap(newDspList);
// Create a map to store DspDelta.Builder. Key is content owner name, value is dspDelta for each
// owner.
Map<String, DspDelta.Builder> dspDeltaBuilderMap = calculateDspDelta(originalDspMap, newDspMap);
// Export dspDeltaBuilderMap and return DeviceSecurityProfilesDelta proto.
for (String owner : dspDeltaBuilderMap.keySet()) {
DspDelta.Builder dspDeltaBuilder = dspDeltaBuilderMap.get(owner).setOwner(owner);
deltaBuilder.addDspDelta(dspDeltaBuilder.build());
}
return deltaBuilder.build();
}
/**
* Parses {@link DeviceSecurityProfilesDelta} to obtain AddedList, RemovedList and ModifiedList
* for all content owners from DeviceSecurityProfilesList.
*/
private void createDeviceSecurityProfilesDeltaInfo(DeviceSecurityProfilesDelta dspDelta) {
for (DspDelta dspDeltaEntry : dspDelta.getDspDeltaList()) {
dspAddedList.addAll(dspDeltaEntry.getAddedList());
dspRemovedList.addAll(dspDeltaEntry.getRemovedList());
dspModifiedList.addAll(dspDeltaEntry.getModifiedList());
}
}
/**
* Creates dsp map storing dsp list.
*
* @param dspList dsp list in DeviceSecurityProfileList proto.
* @return a map which stores dsp list. Key is content owner name, value is a list of dsps for
* that owner.
*/
private static Map<String, List<SecurityProfile>> initializeDspMap(
DeviceSecurityProfileList dspList) {
Map<String, List<SecurityProfile>> dspMap = new LinkedHashMap<>();
for (SecurityProfile dsp : dspList.getDeviceSecurityProfilesList()) {
dspMap.putIfAbsent(dsp.getOwner(), new ArrayList<>());
dspMap.get(dsp.getOwner()).add(dsp);
}
return dspMap;
}
/**
* Calculates dsp delta based on the original dsp and new dsp map.
*
* @param originalDspMap original dsp map.
* @param newDspMap new dsp map.
* @return a dsp delta map. Key is content owner name, value is the dsp delta builder belongs to
* that owner.
*/
private static Map<String, DspDelta.Builder> calculateDspDelta(
Map<String, List<SecurityProfile>> originalDspMap,
Map<String, List<SecurityProfile>> newDspMap) {
Map<String, DspDelta.Builder> dspDeltaBuilderMap = new LinkedHashMap<>();
for (String owner : newDspMap.keySet()) {
// Record already-processed original dsp set.
Set<SecurityProfile> processedOriginalDspSet = new HashSet<>();
// If current owner is not shown in the original dsp map, add all new dsps to Add field for
// this content owner and then continue checking the next owner from new dsp map.
if (!originalDspMap.containsKey(owner)) {
dspDeltaBuilderMap.putIfAbsent(owner, DspDelta.newBuilder().setOwner(owner));
dspDeltaBuilderMap.get(owner).addAllAdded(newDspMap.get(owner));
continue;
}
for (SecurityProfile newDsp : newDspMap.get(owner)) {
// For each new dsp, compare with the original dsp list.
// If two dsps are identical, record it to the already-processed list. And pass to the next
// new dsp.
if (originalDspMap.get(owner).contains(newDsp)) {
// Record the original dsp which is dentical to new dsp in the already_processed list.
processedOriginalDspSet.add(newDsp);
continue;
} else {
// If new dsp can't find the equivalent dsp in the original list, need to figure out it is
// an new added one or a modified one -modified one comes with the same dsp unique key
// {dspName, owner, provider, startTime}.
boolean sameDspUniqueKeyFound = false;
for (SecurityProfile originalDsp : originalDspMap.get(owner)) {
// Owner and provider are identical. Only need to compare dspName and startTime.
if (originalDsp.getName().equals(newDsp.getName())
&& (originalDsp.getControlTime().getStartTimeSeconds()
== newDsp.getControlTime().getStartTimeSeconds())) {
// Add original and new dsp to Modified field for this content owner.
dspDeltaBuilderMap.putIfAbsent(owner, DspDelta.newBuilder().setOwner(owner));
dspDeltaBuilderMap
.get(owner)
.addModifiedBuilder()
.setPrevDsp(originalDsp)
.setNewDsp(newDsp);
// Record the original dsp in the already_processed list.
processedOriginalDspSet.add(originalDsp);
sameDspUniqueKeyFound = true;
break;
}
}
if (!sameDspUniqueKeyFound) {
// Add new dsp to Added field for this content owner.
dspDeltaBuilderMap.putIfAbsent(owner, DspDelta.newBuilder().setOwner(owner));
dspDeltaBuilderMap.get(owner).addAdded(newDsp);
}
}
}
// Remove already-processed dsps from originalDspMap for this content owner.
originalDspMap.get(owner).removeAll(processedOriginalDspSet);
}
// Add all remaining dsps from originalDspMap to Removed field.
for (String owner : originalDspMap.keySet()) {
if (!originalDspMap.get(owner).isEmpty()) {
dspDeltaBuilderMap.putIfAbsent(owner, DspDelta.newBuilder().setOwner(owner));
dspDeltaBuilderMap.get(owner).addAllRemoved(originalDspMap.get(owner));
}
}
return dspDeltaBuilderMap;
}
}

View File

@@ -0,0 +1,156 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2020 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.
////////////////////////////////////////////////////////////////////////////////
package com.google.video.widevine.jts.tools;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;
import com.google.chrome.widevine.contentpartners.v1beta1.SignedDeviceSecurityProfiles;
import com.google.protobuf.ExtensionRegistry;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.video.widevine.protos.DeviceSecurityProfileListProtos.DeviceSecurityProfileList;
import com.google.video.widevine.protos.DeviceSecurityProfilesDeltaProtos.DeviceSecurityProfilesDelta;
import com.google.video.widevine.protos.SecurityProfileProtos.SecurityProfile;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/** Device Security Profiles Wrapper class to parse and discover retrieved DSPs. */
final class DeviceSecurityProfilesWrapper {
private SignedDeviceSecurityProfiles signedDspList = null;
private DeviceSecurityProfileList dspList = null;
private final Map<String, List<SecurityProfile>> dspMap = new LinkedHashMap<>();
private Set<String> ownerSet = null;
/**
* Constructor to create DeviceSecurityProfilesWrapper object with {@link
* SignedDeviceSecurityProfiles} as input.
*
* @param signedDspList SignedDeviceSecurityProfiles proto.
*/
public DeviceSecurityProfilesWrapper(SignedDeviceSecurityProfiles signedDspList)
throws InvalidProtocolBufferException {
checkNotNull(signedDspList, "'signedDspList' must not be null");
this.signedDspList = signedDspList;
parseDeviceSecurityProfilesList();
}
/**
* Constructor to create DeviceSecurityProfilesWrapper object with byte array from {@link
* SignedDeviceSecurityProfiles} serialized proto.
*
* @param signedDspListInfo a serialized SignedDeviceSecurityProfiles proto.
*/
public DeviceSecurityProfilesWrapper(byte[] signedDspListInfo)
throws InvalidProtocolBufferException {
checkNotNull(signedDspListInfo, "'signedDspListInfo' must not be null");
signedDspList =
SignedDeviceSecurityProfiles.parseFrom(
signedDspListInfo, ExtensionRegistry.getEmptyRegistry());
parseDeviceSecurityProfilesList();
}
/**
* Get DeviceSecurityProfileList.
*
* @return DeviceSecurityProfileList a deserialized DeviceSecurityProfiles message from
* SignedDeviceSecurityProfiles.
*/
public DeviceSecurityProfileList getDeviceSecurityProfileList() {
return dspList;
}
/**
* Get the number of DSPs shown in DeviceSecurityProfileList.
*
* @return int Number of DSPs shown in DeviceSecurityProfileList.
*/
public int getSize() {
return dspList.getDeviceSecurityProfilesCount();
}
/**
* Get a list of DeviceSecurityProfiles for specific content owner.
*
* @param owner content owner name in String.
* @return {@link List<SecurityProfile>} a list of DSPs for specific content owner. Return null if
* none of the DSPs belongs to the specific content owner.
*/
public List<SecurityProfile> getDeviceSecurityProfiles(String owner) {
if (isNullOrEmpty(owner)) {
return null;
}
return dspMap.get(owner);
}
/**
* Get the content owner list from the DSPs.
*
* @return {@link List<String>} a list of content owners shown in the DSPs. Return null if owner
* set is null or empty.
*/
public List<String> getOwners() {
if (ownerSet == null || ownerSet.isEmpty()) {
return null;
}
return new ArrayList<>(ownerSet);
}
/**
* Get one DeviceSecurityProfile given content owner and profile name.
*
* @param owner content owner name in String.
* @param profileName dsp name in String.
* @return {@link SecurityProfile) One specific DSP which matches content owner and profile name.
* Return null if such DSP couldn't be found.
*/
SecurityProfile getDeviceSecurityProfile(String owner, String profileName) {
List<SecurityProfile> list = dspMap.get(owner);
if (list == null) {
return null;
}
for (SecurityProfile securityProfile : list) {
if (securityProfile.getName().equals(profileName)) {
return securityProfile;
}
}
return null;
}
/**
* Get {@link DeviceSecurityProfilesDelta} of new signedDspList with current signedDspList.
*
* @param newSignedDspList new signed dsp list in SignedDeviceSecurityProfiles proto.
* @return {@link DeviceSecurityProfilesDelta}.
* @throws InvalidProtocolBufferException if failed to parse DeviceSecurityProfileList from
* SignedDeviceSecurityProfiles.
*/
public DeviceSecurityProfilesDelta getDeviceSecurityProfilesDelta(
SignedDeviceSecurityProfiles newSignedDspList) throws InvalidProtocolBufferException {
checkNotNull(newSignedDspList, "'newSignedDspList' must not be null");
DeviceSecurityProfilesDeltaWrapper dspDeltaWrapper =
new DeviceSecurityProfilesDeltaWrapper(this.signedDspList, newSignedDspList);
return dspDeltaWrapper.getDelta();
}
private void parseDeviceSecurityProfilesList() throws InvalidProtocolBufferException {
dspList =
DeviceSecurityProfileList.parseFrom(
signedDspList.getDeviceSecurityProfiles(), ExtensionRegistry.getEmptyRegistry());
// Add all DSPs into a map where key is dsp owner, value is a list of DSPs.
for (SecurityProfile dsp : dspList.getDeviceSecurityProfilesList()) {
dspMap.putIfAbsent(dsp.getOwner(), new ArrayList<>());
dspMap.get(dsp.getOwner()).add(dsp);
}
// Get owner set from key set of dspMap.
ownerSet = dspMap.keySet();
}
}

View File

@@ -0,0 +1,350 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2020 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.
////////////////////////////////////////////////////////////////////////////////
package com.google.video.widevine.jts.tools;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.google.chrome.widevine.contentpartners.v1beta1.PublishedDevices;
import com.google.chrome.widevine.contentpartners.v1beta1.SignedDeviceSecurityProfiles;
import com.google.chrome.widevine.frauddetection.v1eap1.FraudLevel;
import com.google.common.collect.ImmutableList;
import com.google.common.io.BaseEncoding;
import com.google.video.widevine.jts.providers.PublishedDevicesProvider;
import com.google.video.widevine.sdk.wvpl.WvPLEnvironment;
import com.google.video.widevine.sdk.wvpl.WvPLStatus;
import com.google.video.widevine.sdk.wvpl.WvPLStatusException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
/**
* Published Devices Client command line tool.
*
* <p>Provides a command line interface to get the latest {@code PublishedDevices} and {@code
* SignedDeviceSecurityProfiles} data from the Widevine Published Devices Service.
*
* <p>Default Widevine Service API path: widevine.googleapis.com.
*
* <p>To build: bazel build
* java/com/google/video/widevine/jts/tools:published_devices_cli_deploy.jar
*
* <p>To run: java -Djava.library.path=./path/to/license/sdk.so \ -jar
* /path/to/published_devices_cli_deploy.jar \ -data_type PUBLISHED_DEVICESOrDSPInString \
* -service_cert_path /path/to/cert_file.der \ -service_private_key_path /path/to/private_key.der \
* -service_private_key_passphrase theprivatekeypassphrase \ -service_account_path
* /path/to/service-account.json \ -provider_name providerNameInString
*/
public final class PublishedDevicesCli {
private static final String PUBLISHED_DEVICES = "PUBLISHED_DEVICES";
private static final String DSP = "DSP";
private static final String IDRL = "IDRL";
private static final String FRAUD_REPORT = "FRAUD_REPORT";
private static final ImmutableList<String> DATA_TYPE_LIST =
ImmutableList.of(PUBLISHED_DEVICES, DSP, IDRL, FRAUD_REPORT);
private static final String FRAUD_LEVEL_CRITICAL = "CRITICAL";
private static final String FRAUD_LEVEL_NONE = "NONE";
private static final ImmutableList<String> FRAUD_LEVEL_LIST =
ImmutableList.of(FRAUD_LEVEL_CRITICAL, FRAUD_LEVEL_NONE);
private WvPLEnvironment environment = null;
private PublishedDevicesProvider publishedDevices = null;
private String serviceCertPath = null;
private String privateKeyPath = null;
private String privateKeyPassphrase = null;
/** Command Line Flags. */
static class Flags {
private Flags() {}
@Parameter(
names = {"-h", "-help"},
help = true)
private boolean help = false;
@Parameter(
names = "-data_type",
description =
"Set retrieved data type. Must be \"PUBLISHED_DEVICES\", \"DSP\", \"IDRL\" or"
+ "\"FRAUD_REPORT\".")
private String dataType = "PUBLISHED_DEVICES";
@Parameter(names = "-service_cert_path", description = "Path to service certificate")
private String serviceCertPath = "";
@Parameter(
names = "-service_private_key_path",
description = "Path to private key file needed to decrypt service certificate")
private String servicePrivateKeyPath = "";
@Parameter(
names = "-service_private_key_passphrase",
description = "Passphrase needed to decrypt the private key")
private String servicePrivateKeyPassphrase = "";
@Parameter(
names = "-service_account_path",
description = "Path to a GCP Service Account json file. Required.")
private String serviceAccountPath = "";
@Parameter(
names = "-api_service_path",
description = "Optional. Path to a Widevine API service.")
private String apiServicePath = "widevine.googleapis.com";
@Parameter(
names = "-provider_name",
description =
"Set provider name. Must provide when obtaining DSP and IDRL. Optional for obtaining"
+ " PUBLISHED_DEVICES.")
private String provider = "";
@Parameter(
names = "-include_impact_analysis",
description =
"Option specifying whether or not to include impact analysis results in response.")
private boolean includeImpactAnalysis = false;
@Parameter(
names = "-last_dcsl_epoch_time_seconds",
description =
"Option that helps to calculate the DCSL impact analysis from a DCSL snapshot at"
+ " last_dcsl_epoch_time to the latest DCSL snapshot. Time is contained in the last"
+ " fetched DCSL DeviceCertificateStatusList.creation_time_seconds. Maximum"
+ " timeframe is 30 days.")
private Integer lastDcslEpochTimeSeconds = null;
@Parameter(
names = "-dcsl_type",
description =
"Option specifying which DCSL type needs to be returned in response. Valid options are"
+ " Alpha, Beta or Released. Alpha is unverified DCSL which is not a release"
+ " candidate yet. Beta is partially verified release candidate. Released is fully"
+ " verified and is a release candidate available for production. By default, a"
+ " released DCSL is returned in response.")
private String dcslType = null;
@Parameter(names = "-s", description = "Save retrieved file path.")
private String saveFilePath = null;
@Parameter(
names = "-fraud_license_request",
description =
"Base64 encoded fraud license request from the device. Required for fraud reporting.")
private String fraudLicenseRequest = null;
@Parameter(
names = "-fraud_clear_id",
description =
"Base64 encoded clear client id. Required if the one in license request is encrypted.")
private String fraudClearId = null;
@Parameter(
names = "-fraud_user_agent",
description = "User agent of the fraud license request. Optional.")
private String fraudUserAgent = null;
@Parameter(
names = "-fraud_reason",
description = "Reason the device is considered fraudulent. Optional.")
private String fraudReason = null;
@Parameter(
names = "-fraud_level",
description =
"Level of fraud, must be one of \"CRITICAL\" or \"NONE\". A \"NONE\" fraud level is"
+ " used to \"unrevoke\" a device, meaning the device is not fraudulent. Optional.")
private String fraudLevel = "CRITICAL";
}
public PublishedDevicesCli(
String dataType,
String serviceCertPath,
String privateKeyPath,
String privateKeyPassphrase,
String serviceAccountPath,
String apiServicePath,
String provider,
boolean includeImpactAnalysis,
Integer lastDcslEpochTimeSeconds,
String dcslType)
throws Exception {
this.serviceCertPath = serviceCertPath;
this.privateKeyPath = privateKeyPath;
this.privateKeyPassphrase = privateKeyPassphrase;
// Construct PublishedDevicesProvider to obtain PUBLISHED_DEVICES or DSPs.
if (dataType.equals(PUBLISHED_DEVICES)) {
initializeWvplEnvironment();
}
publishedDevices =
new PublishedDevicesProvider(
environment,
serviceAccountPath,
apiServicePath,
provider,
includeImpactAnalysis,
lastDcslEpochTimeSeconds,
dcslType);
}
/**
* Get the latest {@code PublishedDevices} from the Widevine Published Devices Service.
*
* @return The latest PublishedDevices data.
* @throws InterruptedException upon RPC failure.
* @throws WvPLStatusException upon WvPLBaseEnvironment errors.
*/
public PublishedDevices getPublishedDevices() throws InterruptedException, WvPLStatusException {
return publishedDevices.getPublishedDevices();
}
/**
* Get the latest {@code SignedDeviceSecurityProfiles} from the Widevine Published Devices
* Service.
*
* @return The latest SignedDeviceSecurityProfiles data.
*/
public SignedDeviceSecurityProfiles getSignedDeviceSecurityProfiles()
throws InterruptedException, WvPLStatusException {
return publishedDevices.getSignedDeviceSecurityProfiles();
}
/**
* Get the latest Individual Device Revocation List from the Widevine Published Devices Service.
*
* @return The latest SignedDeviceSecurityProfiles data.
*/
public byte[] getIndividualDeviceRevocationList() throws InterruptedException {
return publishedDevices.getIndividualDeviceRevocationList();
}
/**
* Report a fraudulent device to Widevine.
*
* @param licenseRequest Base64 encoded fraud license request from the device. Required.
* @param clearClientId Base64 encoded clear client id. Required if the one in license request is
* encrypted.
* @param userAgent User agent of the fraud license request. Optional.
* @param reason Reason the device is considered fraudulent. Optional.
* @param fraudScore Fraud score of the device. Required.
* @return The response message from Widevine server.
*/
public byte[] reportFraudulentDevice(
String licenseRequest,
String clearClientId,
String userAgent,
String reason,
FraudLevel.FraudScore fraudScore)
throws InterruptedException, IOException {
return publishedDevices.reportFraudulentDevice(
licenseRequest, clearClientId, userAgent, reason, fraudScore);
}
/**
* Saves byte array to a file.
*
* @param saveFilePath The path to the save file.
* @param byteArray a byte array transformed from proto.
*/
public static void saveByteArrayToFile(String saveFilePath, byte[] byteArray) throws IOException {
Files.write(Path.of(saveFilePath), BaseEncoding.base64Url().encode(byteArray).getBytes(UTF_8));
}
public static void main(String[] args) throws Exception {
Flags flags = new Flags();
JCommander jCommander = new JCommander(flags);
jCommander.parse(args);
if (flags.help) {
jCommander.usage();
return;
}
// Check flags.
if (!DATA_TYPE_LIST.contains(flags.dataType)) {
System.out.println("Data type should be selected from:" + DATA_TYPE_LIST);
System.err.println("Error selecting data type. Exit");
System.exit(-1);
}
if (flags.provider.isEmpty() && !flags.dataType.equals(PUBLISHED_DEVICES)) {
System.err.println("Failed to provide the provider name. Exit");
System.exit(-1);
}
if (flags.serviceAccountPath.isEmpty()) {
System.err.println("service_account_path is required. Exit");
System.exit(-1);
}
PublishedDevicesCli devices =
new PublishedDevicesCli(
flags.dataType,
flags.serviceCertPath,
flags.servicePrivateKeyPath,
flags.servicePrivateKeyPassphrase,
flags.serviceAccountPath,
flags.apiServicePath,
flags.provider,
flags.includeImpactAnalysis,
flags.lastDcslEpochTimeSeconds,
flags.dcslType);
byte[] content = null;
if (flags.dataType.equals(PUBLISHED_DEVICES)) {
content = devices.getPublishedDevices().toByteArray();
} else if (flags.dataType.equals(DSP)) {
content = devices.getSignedDeviceSecurityProfiles().toByteArray();
} else if (flags.dataType.equals(IDRL)) {
content = devices.getIndividualDeviceRevocationList();
} else if (flags.dataType.equals(FRAUD_REPORT)) {
content =
devices.reportFraudulentDevice(
flags.fraudLicenseRequest,
flags.fraudClearId,
flags.fraudUserAgent,
flags.fraudReason,
getFraudScore(flags.fraudLevel));
}
if (flags.saveFilePath != null) {
saveByteArrayToFile(flags.saveFilePath, content);
}
}
private void initializeWvplEnvironment() throws Exception {
environment = new WvPLEnvironment(new HashMap<>());
// Set service certificate.
WvPLStatus status =
environment.setServiceCertificate(
loadDataFromFile(serviceCertPath),
loadDataFromFile(privateKeyPath),
privateKeyPassphrase.getBytes(UTF_8));
if (!status.getStatusCode().equals(WvPLStatus.StatusCode.OK)) {
throw new Exception("Set server certificate status: " + status);
}
}
private static byte[] loadDataFromFile(String filePath) throws IOException {
return Files.readAllBytes(Path.of(filePath));
}
public static FraudLevel.FraudScore getFraudScore(String fraudLevel) {
switch (fraudLevel) {
case FRAUD_LEVEL_CRITICAL:
return FraudLevel.FraudScore.FRAUD_SCORE_CRITICAL;
case FRAUD_LEVEL_NONE:
return FraudLevel.FraudScore.FRAUD_SCORE_NONE;
default:
throw new IllegalArgumentException(
"Fraud level should be selected from: "
+ FRAUD_LEVEL_LIST
+ ". Invalid fraud level: "
+ fraudLevel);
}
}
}

View File

@@ -0,0 +1,256 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2020 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.
////////////////////////////////////////////////////////////////////////////////
package com.google.video.widevine.jts.tools;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.chrome.widevine.contentpartners.v1beta1.PublishedDevices;
import com.google.protobuf.ExtensionRegistry;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.video.widevine.protos.DeviceCertificateStatusProtos.DeviceCertificateStatus;
import com.google.video.widevine.protos.DeviceCertificateStatusProtos.PublishedDevicesList;
import com.google.video.widevine.protos.PublishedDevicesDeltaProtos.PublishedDevicesDelta;
import com.google.video.widevine.protos.PublishedDevicesDeltaProtos.PublishedDevicesDelta.Header;
import com.google.video.widevine.protos.PublishedDevicesDeltaProtos.PublishedDevicesDelta.Modified;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/** Published Devices Delta Wrapper class to get {@link PublishedDevicesDelta}. */
public final class PublishedDevicesDeltaWrapper {
private PublishedDevicesList originalList = null;
private PublishedDevicesList newList = null;
private PublishedDevicesDelta publishedDevicesDelta = null;
private final Map<Integer, DeviceCertificateStatus> dcslAddedMap = new LinkedHashMap<>();
private final Map<Integer, DeviceCertificateStatus> dcslRemovedMap = new LinkedHashMap<>();
private final Map<Integer, Modified> dcslModifiedMap = new LinkedHashMap<>();
static final int MAJOR_VERSION = 1;
static final int MINOR_VERSION = 1;
static final int RELEASE = 0;
/**
* PublishedDevicesDeltaWrapper constructor. The input is two byte array serialized
* PublishedDevices proto.
*
* @param publishedDevicesInBytes1 first serialized PublishedDevices proto in bytes.
* @param publishedDevicesInBytes2 second serialized PublishedDevices proto in bytes.
* @throws InvalidProtocolBufferException if failed to parse published devices or if failed to
* parse PublishedDevicesList.
*/
public PublishedDevicesDeltaWrapper(
byte[] publishedDevicesInBytes1, byte[] publishedDevicesInBytes2)
throws InvalidProtocolBufferException {
checkNotNull(publishedDevicesInBytes1, "'publishedDevicesInBytes1' must not be null");
checkNotNull(publishedDevicesInBytes2, "'publishedDevicesInBytes2' must not be null");
PublishedDevices publishedDevices1 =
PublishedDevices.parseFrom(publishedDevicesInBytes1, ExtensionRegistry.getEmptyRegistry());
PublishedDevices publishedDevices2 =
PublishedDevices.parseFrom(publishedDevicesInBytes2, ExtensionRegistry.getEmptyRegistry());
parsePublishedDevicesList(publishedDevices1, publishedDevices2);
this.publishedDevicesDelta = createPublishedDevicesDelta(originalList, newList);
createPublishedDevicesDeltaInfo(publishedDevicesDelta);
}
/**
* PublishedDevicesDeltaWrapper constructor. The input is two PublishedDevices protos.
*
* @param publishedDevices1 first PublishedDevices proto.
* @param publishedDevices2 second PublishedDevices proto.
* @throws InvalidProtocolBufferException if failed to parse PublishedDevicesList from published
* devices.
*/
public PublishedDevicesDeltaWrapper(
PublishedDevices publishedDevices1, PublishedDevices publishedDevices2)
throws InvalidProtocolBufferException {
checkNotNull(publishedDevices1, "'publishedDevices1' must not be null");
checkNotNull(publishedDevices2, "'publishedDevices2' must not be null");
parsePublishedDevicesList(publishedDevices1, publishedDevices2);
this.publishedDevicesDelta = createPublishedDevicesDelta(originalList, newList);
createPublishedDevicesDeltaInfo(publishedDevicesDelta);
}
/**
* PublishedDevicesDeltaWrapper constructor. The input is PublishedDevicesDelta proto.
*
* @param publishedDevicesDelta PublishedDevicesDeltaWrapper proto.
*/
public PublishedDevicesDeltaWrapper(PublishedDevicesDelta publishedDevicesDelta) {
checkNotNull(publishedDevicesDelta, "'delta' must not be null");
this.publishedDevicesDelta = publishedDevicesDelta;
createPublishedDevicesDeltaInfo(publishedDevicesDelta);
}
/** Returns {@link PublishedDevicesDelta} of originalPublishedDevices and newPublishedDevices. */
public PublishedDevicesDelta getDelta() {
return publishedDevicesDelta;
}
/**
* Returns {@link PublishedDevicesDelta} of originalPublishedDevices and newPublishedDevices.
*
* @param originalList original list in PublishedDevicesList proto
* @param newList new list in PublishedDevicesList proto
*/
static PublishedDevicesDelta createPublishedDevicesDelta(
PublishedDevicesList originalList, PublishedDevicesList newList) {
// Construct PublishedDevicesDelta.
PublishedDevicesDelta.Builder deltaBuilder = PublishedDevicesDelta.newBuilder();
// Construct Header object.
Header header =
Header.newBuilder()
.setPrevCreationTimeSeconds(originalList.getCreationTimeSeconds())
.setNewCreationTimeSeconds(newList.getCreationTimeSeconds())
.build();
deltaBuilder.setHeader(header);
// Add removed, added and modified field in to delta.
List<DeviceCertificateStatus> prevDCSL = originalList.getDeviceCertificateStatusList();
List<DeviceCertificateStatus> newDcsl = newList.getDeviceCertificateStatusList();
Map<Integer, DeviceCertificateStatus> dcslMap = new LinkedHashMap<>();
// Add all the original DCSLs into the map.
for (DeviceCertificateStatus dcsl : prevDCSL) {
dcslMap.put(dcsl.getDeviceInfo().getSystemId(), dcsl);
}
for (DeviceCertificateStatus dcsl : newDcsl) {
int systemId = dcsl.getDeviceInfo().getSystemId();
if (dcslMap.containsKey(systemId) && !dcsl.equals(dcslMap.get(systemId))) {
deltaBuilder.addModifiedBuilder().setPrevDevice(dcslMap.get(systemId)).setNewDevice(dcsl);
} else if (!dcslMap.containsKey(systemId)) {
// Add the new DCSL into added field in PublishedDevicesDeltaWrapper.
deltaBuilder.addAdded(dcsl);
}
// Remove the iterated DCSL from the map.
dcslMap.remove(systemId);
}
// Add the original DCSL into removed field in PublishedDevicesDeltaWrapper.
if (!dcslMap.isEmpty()) {
deltaBuilder.addAllRemoved(dcslMap.values());
}
return deltaBuilder.build();
}
/**
* Returns the formatted text output of the {@link PublishedDevicesDelta}.
*
* @return String String format of publishedDevices.
*/
public static String toString(PublishedDevicesDelta delta) {
return delta.toString();
}
/** Parse {@link PublishedDevicesList} from {@link PublishedDevices}. */
private void parsePublishedDevicesList(
PublishedDevices publishedDevices1, PublishedDevices publishedDevices2)
throws InvalidProtocolBufferException {
// Parse PublishedDevicesList proto.
originalList =
PublishedDevicesList.parseFrom(
publishedDevices1.getPublishedDevices(), ExtensionRegistry.getEmptyRegistry());
newList =
PublishedDevicesList.parseFrom(
publishedDevices2.getPublishedDevices(), ExtensionRegistry.getEmptyRegistry());
// Swap the PublishedDevicesList list if originalList is newer than newList.
if (originalList.getCreationTimeSeconds() > newList.getCreationTimeSeconds()) {
PublishedDevicesList temp = newList;
newList = originalList;
originalList = temp;
}
}
/**
* Get {@link DeviceCertificateStatus} of the specific system_id.
*
* @param systemId Device system_id.
* @return DeviceCertificateStatus Returns DeviceCertificateStatus of the specific system_id if it
* is added. Otherwise it will return null.
*/
public DeviceCertificateStatus getAddedDeviceCertificateStatus(int systemId) {
return dcslAddedMap.get(systemId);
}
/**
* Get {@link DeviceCertificateStatus} of the specific system_id.
*
* @param systemId Device system_id.
* @return DeviceCertificateStatus Returns the DeviceCertificateStatus of the specific system_id
* if it is removed.Otherwise it will return null.
*/
public DeviceCertificateStatus getRemovedDeviceCertificateStatus(int systemId) {
return dcslRemovedMap.get(systemId);
}
/**
* Get {@link Modified} of the specific system_id. Return null if not found.
*
* @param systemId Device system_id.
* @return Modified Returns ModifiedDeviceCertificateStatus of the specific system_id. Return null
* if not found.
*/
public Modified getModifiedDeviceCertificateStatus(int systemId) {
return dcslModifiedMap.get(systemId);
}
/**
* Get count of added DeviceCertificateStatus in delta.
*
* @return a list of system_ids for added DeviceCertificateStatus in delta.
*/
public List<Integer> getAddedDevices() {
return new ArrayList<>(dcslAddedMap.keySet());
}
/**
* Get count of modified DeviceCertificateStatus in delta.
*
* @return a list of system_ids for modified DeviceCertificateStatus in delta.
*/
public List<Integer> getModifiedDevices() {
return new ArrayList<>(dcslModifiedMap.keySet());
}
/**
* Get list of system ids for removed DeviceCertificateStatus in delta.
*
* @return a list of system_ids for removed DeviceCertificateStatus in delta.
*/
public List<Integer> getRemovedDevices() {
return new ArrayList<>(dcslRemovedMap.keySet());
}
/**
* Get the version of Published Devices Release.
*
* @return the version of Published Devices Release as a String.
*/
public String getVersion() {
return MAJOR_VERSION + "." + MINOR_VERSION + "." + RELEASE;
}
/**
* Parse PublishedDevicesDeltaWrapper to get AddedMap, RemovedMap and ModifiedMap for
* PublishedDevicesList.
*
* @param publishedDevicesDelta PublishedDevicesDelta delta.
*/
private void createPublishedDevicesDeltaInfo(PublishedDevicesDelta publishedDevicesDelta) {
for (DeviceCertificateStatus status : publishedDevicesDelta.getAddedList()) {
dcslAddedMap.put(status.getDeviceInfo().getSystemId(), status);
}
for (DeviceCertificateStatus status : publishedDevicesDelta.getRemovedList()) {
dcslRemovedMap.put(status.getDeviceInfo().getSystemId(), status);
}
for (Modified status : publishedDevicesDelta.getModifiedList()) {
dcslModifiedMap.put(status.getPrevDevice().getDeviceInfo().getSystemId(), status);
}
}
}

View File

@@ -0,0 +1,455 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2020 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.
////////////////////////////////////////////////////////////////////////////////
package com.google.video.widevine.jts.tools;
import static com.google.common.io.BaseEncoding.base64;
import static com.google.common.io.BaseEncoding.base64Url;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.video.widevine.protos.DeviceCertificateStatusProtos.DeviceCertificateStatus;
import com.google.video.widevine.protos.DeviceSecurityProfilesDeltaProtos.DeviceSecurityProfilesDelta;
import com.google.video.widevine.protos.DeviceSecurityProfilesDeltaProtos.DeviceSecurityProfilesDelta.DspDelta;
import com.google.video.widevine.protos.PublishedDevicesDeltaProtos.PublishedDevicesDelta;
import com.google.video.widevine.protos.SecurityProfileProtos.SecurityProfile;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Published Devices command line tool.
*
* <p>Provides a command line tool which could help to (1) get the diff between two PublishedDevices
* Proto, parse the PublishedDevices and PublishedDevicesDelta. (2) get the diff between two
* SignedDeviceSecurityProfiles Proto, parse the DeviceSecurityProfiles and
* DeviceSecurityProfilesDelta.
*
* <p>To build: Bazel build tools:published_devices_delta_deploy.jar
*
* <p>Commands for PublishedDevices:
*
* <p>To run: java -jar /path/to/published_devices_lib_deploy.jar -new_file_path path
* -original_file_path path -print
*
* <p>To run: java -jar /path/to/published_devices_lib_deploy.jar -new_file_path path
* -original_file_path path -system_id 100 -get_system_ids
*
* <p>To run: java -jar /path/to/published_devices_lib_deploy.jar -query path -system_id 100
*
* <p>Commands for DeviceSecurityProfiles:
*
* <p>Get DSP delta, save and print it: java -jar /path/to/published_devices_lib_deploy.jar
* -data_type DSP -new_file_path path -original_file_path path -print
*
* <p>Obtain the dsp delta per content owner: java -jar /path/to/published_devices_lib_deploy.jar
* -data_type DSP -new_file_path path -original_file_path path -content_owner owner_name
*
* <p>Obtain Added/Removed/Modified list in dsp delta: java -jar
* /path/to/published_devices_lib_deploy.jar -data_type DSP -new_file_path path -original_file_path
* path -get_specific_field_in_dsp_delta field_name
*
* <p>Get DSPs for specific owner: java -jar /path/to/published_devices_lib_deploy.jar -data_type
* DSP -query path -content_owner owner_name
*
* <p>Get owner list from one retrieved dsp list: java -jar
* /path/to/published_devices_lib_deploy.jar -data_type DSP -query path -get_owners
*
* <p>Get targeted dsp given contentOwner and profile name: java -jar
* /path/to/published_devices_lib_deploy.jar -data_type DSP -query path -content_owner owner_name
* -profile_name profile_name
*/
public class PublishedDevicesMain {
private static final Logger logger = Logger.getLogger(PublishedDevicesMain.class.getName());
private static final int ZERO_SYSTEM_ID = 0;
private static final String JSON_NAME = "signedList";
private static final String PUBLISHED_DEVICES = "PUBLISHED_DEVICES";
private static final String DSP = "DSP";
private static final ImmutableList<String> DATA_TYPE_LIST =
ImmutableList.of(PUBLISHED_DEVICES, DSP);
private static final String ADDED_FIELD_IN_DSP_DELTA = "added";
private static final String MODIFIED_FIELD_IN_DSP_DELTA = "modified";
private static final String REMOVED_FIELD_IN_DSP_DELTA = "removed";
/** Command Line Flags. */
static class Flags {
private Flags() {}
// Shared parameters for both PublishedDevices and DSP.
@Parameter(
names = "-data_type",
description = "Set parsed data type. Must be \"PUBLISHED_DEVICES\" or \"DSP\".")
private String dataType = "PUBLISHED_DEVICES";
@Parameter(
names = "-original_file_path",
description =
"Path to original file. Could be the original file for either PublishedDevices or"
+ " DeviceSecurityProfiles.")
private String originalFilePath = null;
@Parameter(
names = "-new_file_path",
description =
"Path to new file. Could be the new file for either PublishedDevices or"
+ " DeviceSecurityProfiles.")
private String newFilePath = null;
@Parameter(
names = "-print",
description = "Setting to true, if content provider wants to print the delta in String.")
private boolean print = false;
@Parameter(names = "-query", description = "Parse the input file.")
private String query = null;
// Unique parameters for PublishedDevices.
@Parameter(
names = "-system_id",
description = "Device system-id shown in PublishedDevicesList.")
private int systemId = ZERO_SYSTEM_ID;
@Parameter(
names = "-get_system_ids",
description =
"Boolean to get list of system_ids in added/removed/modified PublishedDevicesList.")
private boolean getSystemIdsList = false;
// Unique parameters for DeviceSecurityProfiles.
@Parameter(
names = "-content_owner",
description = "Content owner name to retrieve corresponding DeviceSecurityProfiles.")
private String contentOwner = null;
@Parameter(
names = "-get_specific_field_in_dsp_delta",
description =
"Optional. Obtain one specific field (added, removed or modified) in dsp delta."
+ " Returned list covers all content owners.")
private String getSpecificFieldInDspDelta = null;
@Parameter(
names = "-get_owners",
description = "Obtain owner list from one DeviceSecurityProfilesList.")
private boolean getOwners = false;
@Parameter(names = "-profile_name", description = "device security profile name.")
private String profileName = null;
}
/** Main function to run Published Devices Delta command line tool. */
public static void main(String[] args) {
Flags flags = new Flags();
new JCommander(flags).parse(args);
// Check the FLAGs.
if (!DATA_TYPE_LIST.contains(flags.dataType)) {
System.out.println("Data type should be selected from:" + DATA_TYPE_LIST);
logger.log(Level.SEVERE, "Data type should match with PUBLISHED_DEVICES or DSP.");
}
if (flags.dataType.equals(PUBLISHED_DEVICES)) {
if (flags.newFilePath != null && flags.originalFilePath != null) {
parsePublishedDevicesDelta(flags);
} else if (flags.query != null) {
queryPublishedDevices(flags);
}
} else if (flags.dataType.equals(DSP)) {
if (flags.newFilePath != null && flags.originalFilePath != null) {
parseDeviceSecurityProfilesDelta(flags);
} else if (flags.query != null) {
queryDeviceSecurityProfiles(flags);
}
}
}
/** Parse the argument related to PublishedDevicesDelta. */
private static void parsePublishedDevicesDelta(Flags flags) {
try {
byte[] originalPublishedDevices =
getPublishedDevicesOrDspFileAsBytes(flags.originalFilePath, flags.dataType);
byte[] newPublishedDevices =
getPublishedDevicesOrDspFileAsBytes(flags.newFilePath, flags.dataType);
checkLoadFileResult(flags.originalFilePath, originalPublishedDevices);
checkLoadFileResult(flags.newFilePath, newPublishedDevices);
PublishedDevicesDeltaWrapper devices =
new PublishedDevicesDeltaWrapper(originalPublishedDevices, newPublishedDevices);
PublishedDevicesDelta delta = devices.getDelta();
if (flags.print) {
System.out.println(delta);
}
parseDeltaInfo(devices, flags);
} catch (IOException e) {
logger.log(
Level.SEVERE, "IOException encountered trying to open published devices files: " + e);
}
}
/** Parse the argument related to DeviceSecurityProfilesDelta. */
private static void parseDeviceSecurityProfilesDelta(Flags flags) {
try {
byte[] originalDeviceSecurityProfiles =
getPublishedDevicesOrDspFileAsBytes(flags.originalFilePath, flags.dataType);
byte[] newDeviceSecurityProfiles =
getPublishedDevicesOrDspFileAsBytes(flags.newFilePath, flags.dataType);
checkLoadFileResult(flags.originalFilePath, originalDeviceSecurityProfiles);
checkLoadFileResult(flags.newFilePath, newDeviceSecurityProfiles);
DeviceSecurityProfilesDeltaWrapper dspDeltaWrapper =
new DeviceSecurityProfilesDeltaWrapper(
originalDeviceSecurityProfiles, newDeviceSecurityProfiles);
DeviceSecurityProfilesDelta delta = dspDeltaWrapper.getDelta();
if (flags.print) {
System.out.println(delta);
}
parseDeltaInfo(dspDeltaWrapper, flags);
} catch (IOException e) {
logger.log(
Level.SEVERE,
"IOException encountered trying to open device security profiles files: " + e);
}
}
/** Parse the detailed info shown in PublishedDevicesDelta. */
private static void parseDeltaInfo(
PublishedDevicesDeltaWrapper publishedDevicesDelta, Flags flags) {
if (flags.systemId != 0) {
if (publishedDevicesDelta.getAddedDeviceCertificateStatus(flags.systemId) != null) {
System.out.println(
flags.systemId + " system-id is found in added field of published devices delta.");
System.out.println(publishedDevicesDelta.getAddedDeviceCertificateStatus(flags.systemId));
} else if (publishedDevicesDelta.getRemovedDeviceCertificateStatus(flags.systemId) != null) {
System.out.println(
flags.systemId + " system-id is found in removed field of published devices delta.");
System.out.println(publishedDevicesDelta.getRemovedDeviceCertificateStatus(flags.systemId));
} else if (publishedDevicesDelta.getModifiedDeviceCertificateStatus(flags.systemId) != null) {
System.out.println(
flags.systemId + " system-id is found in modified field of published devices delta.");
System.out.println(
publishedDevicesDelta.getModifiedDeviceCertificateStatus(flags.systemId));
} else {
logger.log(Level.SEVERE, flags.systemId + "was not found in the PublishedDevicesDelta.");
}
}
if (flags.getSystemIdsList) {
System.out.println(
"List of system_id for added published devices in published devices delta: "
+ publishedDevicesDelta.getAddedDevices());
System.out.println(
"List of system_id for removed published devices in published devices delta: "
+ publishedDevicesDelta.getRemovedDevices());
System.out.println(
"List of system_id for removed published devices in published devices delta: "
+ publishedDevicesDelta.getModifiedDevices());
}
}
/** Parse the detailed info shown in DeviceSecurityProfilesDelta. */
private static void parseDeltaInfo(
DeviceSecurityProfilesDeltaWrapper dspDeltaWrapper, Flags flags) {
if (flags.getSpecificFieldInDspDelta != null) {
if (Ascii.toLowerCase(flags.getSpecificFieldInDspDelta).equals(ADDED_FIELD_IN_DSP_DELTA)) {
System.out.println(
"List of added DSPs in device security profiles delta: "
+ dspDeltaWrapper.getAddedDeviceSecurityProfiles());
} else if (Ascii.toLowerCase(flags.getSpecificFieldInDspDelta)
.equals(MODIFIED_FIELD_IN_DSP_DELTA)) {
System.out.println(
"List of modified DSPs in device security profiles delta: "
+ dspDeltaWrapper.getModifiedDeviceSecurityProfiles());
} else if (Ascii.toLowerCase(flags.getSpecificFieldInDspDelta)
.equals(REMOVED_FIELD_IN_DSP_DELTA)) {
System.out.println(
"List of removed DSPs in device security profiles delta: "
+ dspDeltaWrapper.getRemovedDeviceSecurityProfiles());
} else {
logger.log(
Level.SEVERE,
flags.getSpecificFieldInDspDelta
+ "is not a valid field in DeviceSecurityProfilesDelta.");
}
}
if (flags.contentOwner != null) {
DspDelta dspDelta = dspDeltaWrapper.getDeviceSecurityProfilesDelta(flags.contentOwner);
if (dspDelta != null) {
System.out.println("Obtain the dsp delta for specific content owner: " + dspDelta);
} else {
logger.log(
Level.WARNING, flags.contentOwner + "is not shown in DeviceSecurityProfilesDelta.");
}
}
}
private static void queryPublishedDevices(Flags flags) {
try {
byte[] publishedDevices = getPublishedDevicesOrDspFileAsBytes(flags.query, flags.dataType);
checkLoadFileResult(flags.query, publishedDevices);
PublishedDevicesWrapper wrapper = new PublishedDevicesWrapper(publishedDevices);
if (flags.systemId != ZERO_SYSTEM_ID) {
DeviceCertificateStatus deviceCertificateStatus =
wrapper.getDeviceCertificateStatus(flags.systemId);
if (deviceCertificateStatus == null) {
logger.log(
Level.SEVERE,
flags.systemId
+ " (system-id) was not found in PublishedDevicesList from "
+ flags.query);
System.exit(1);
}
System.out.println(deviceCertificateStatus);
// TODO(b/160253740): Add ProvisonedStatus and ProvisionedDeviceInfo functions.
}
} catch (InvalidProtocolBufferException e) {
logger.log(
Level.SEVERE,
"InvalidProtocolBufferException encountered to parse device certificate status list"
+ " or published devices proto:"
+ e);
} catch (IOException e) {
logger.log(
Level.SEVERE, "IOException encountered trying to open published devices file: " + e);
}
}
private static void queryDeviceSecurityProfiles(Flags flags) {
try {
byte[] deviceSecurityProfiles =
getPublishedDevicesOrDspFileAsBytes(flags.query, flags.dataType);
checkLoadFileResult(flags.query, deviceSecurityProfiles);
DeviceSecurityProfilesWrapper dspWrapper =
new DeviceSecurityProfilesWrapper(deviceSecurityProfiles);
if (flags.contentOwner != null && flags.profileName != null) {
SecurityProfile securityProfile =
dspWrapper.getDeviceSecurityProfile(flags.contentOwner, flags.profileName);
if (securityProfile == null) {
logger.log(
Level.WARNING,
flags.contentOwner
+ " (content owner) and"
+ flags.profileName
+ " (profile name) "
+ "were not found in DeviceSecurityProfiles from "
+ flags.query);
} else {
System.out.println(securityProfile);
}
} else if (flags.contentOwner != null) {
List<SecurityProfile> securityProfiles =
dspWrapper.getDeviceSecurityProfiles(flags.contentOwner);
if (securityProfiles == null || securityProfiles.isEmpty()) {
logger.log(
Level.WARNING,
flags.contentOwner
+ " (content owner) was not found in DeviceSecurityProfiles from "
+ flags.query);
} else {
System.out.println(Arrays.toString(securityProfiles.toArray()));
}
}
if (flags.getOwners) {
List<String> ownerList = dspWrapper.getOwners();
if (ownerList == null || ownerList.isEmpty()) {
logger.log(
Level.WARNING,
" owner list was not found in DeviceSecurityProfiles from " + flags.query);
}
System.out.println(Arrays.toString(ownerList.toArray()));
}
} catch (InvalidProtocolBufferException e) {
logger.log(
Level.SEVERE,
"InvalidProtocolBufferException encountered while parsing device security profile list"
+ " or device security profiles proto:"
+ e);
} catch (IOException e) {
logger.log(
Level.SEVERE,
"IOException encountered trying to open device security profiles file: " + e);
}
}
private static void checkLoadFileResult(String filePath, byte[] result) {
if (result == null) {
logger.log(Level.SEVERE, "Fail to load the file:" + filePath);
System.exit(1);
}
}
/**
* Load published devices or device security profiles file into string. For published devices v1,
* it would be a json file.
*
* @param filePath file path in String.
* @return byte array file content in String.
* @throws IOException if failed to read the file.
*/
public static byte[] getPublishedDevicesOrDspFileAsBytes(String filePath, String dataType)
throws IOException {
String result = loadFile(filePath);
if (!DATA_TYPE_LIST.contains(dataType) || result == null) {
return null;
}
byte[] serializedList;
if (dataType.equals(PUBLISHED_DEVICES)) {
String requestBody = "";
try {
// Parse both PublishedDevices v1 output.
JSONObject obj = new JSONObject(result);
requestBody = obj.getString(JSON_NAME);
} catch (JSONException e) {
// Parse both PublishedDevices v2 output.
requestBody = result;
}
serializedList = base64Decode(requestBody);
} else {
serializedList = base64Decode(result);
}
return serializedList;
}
/**
* Load file into string.
*
* @param filePath file path in String.
* @return String file content in String.
* @throws IOException if failed to read the file.
*/
public static String loadFile(String filePath) throws IOException {
StringBuilder result = new StringBuilder();
List<String> input = Files.readAllLines(Path.of(filePath));
for (String element : input) {
result.append(element);
}
return result.toString();
}
/**
* Decodes a base64-encoded std::string or web-safe base64-encoded string.
*
* @param base64EncodedData base64 encode data in String.
* @return byte[] decoded std::string in byte array.
*/
public static byte[] base64Decode(String base64EncodedData) {
try {
return base64().decode(base64EncodedData);
} catch (IllegalArgumentException e) {
// Do nothing. Try the url safe base64 decode next.
}
return base64Url().decode(base64EncodedData);
}
private PublishedDevicesMain() {}
}

View File

@@ -0,0 +1,110 @@
////////////////////////////////////////////////////////////////////////////////
//// Copyright 2020 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.
////////////////////////////////////////////////////////////////////////////////
package com.google.video.widevine.jts.tools;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.chrome.widevine.contentpartners.v1beta1.PublishedDevices;
import com.google.protobuf.ExtensionRegistry;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.video.widevine.protos.DeviceCertificateStatusProtos.DeviceCertificateStatus;
import com.google.video.widevine.protos.DeviceCertificateStatusProtos.PublishedDevicesList;
import com.google.video.widevine.protos.PublishedDevicesDeltaProtos.PublishedDevicesDelta;
import java.util.LinkedHashMap;
import java.util.Map;
/** Wrap the PublishedDevices proto in PublishedDevicesWrapper. */
final class PublishedDevicesWrapper {
private PublishedDevices publishedDevices = null;
private PublishedDevicesList publishedDevicesList = null;
private final Map<Integer, DeviceCertificateStatus> dcslMap = new LinkedHashMap<>();
/** Constructor to create PublishedDevicesWrapper object with {@link PublishedDevices} */
public PublishedDevicesWrapper(PublishedDevices publishedDevices)
throws InvalidProtocolBufferException {
checkNotNull(publishedDevices, "'published devices' must not be null");
this.publishedDevices = publishedDevices;
parsePublishedDevicesList();
}
/**
* Constructor to create PublishedDevicesWrapper object with byte array from {@link
* PublishedDevices} serialized proto.
*/
public PublishedDevicesWrapper(byte[] publishedDevicesInfo)
throws InvalidProtocolBufferException {
checkNotNull(publishedDevicesInfo, "'published devices' must not be null");
publishedDevices =
PublishedDevices.parseFrom(publishedDevicesInfo, ExtensionRegistry.getEmptyRegistry());
parsePublishedDevicesList();
}
private void parsePublishedDevicesList() throws InvalidProtocolBufferException {
publishedDevicesList =
PublishedDevicesList.parseFrom(
publishedDevices.getPublishedDevices(), ExtensionRegistry.getEmptyRegistry());
// Add all the original DCSLs into the map.
for (DeviceCertificateStatus dcsl : publishedDevicesList.getDeviceCertificateStatusList()) {
dcslMap.put(dcsl.getDeviceInfo().getSystemId(), dcsl);
}
}
/**
* Get PublishedDevicesList .
*
* @return {@link PublishedDevicesList} field in {@link PublishedDevices}.
*/
public PublishedDevicesList getPublishedDevicesList() {
return publishedDevicesList;
}
/**
* Get DeviceCertificateStatus for specific systemId.
*
* @param systemId systemId in Integer.
* @return {@link DeviceCertificateStatus} for specific systemId.
*/
public DeviceCertificateStatus getDeviceCertificateStatus(int systemId) {
return dcslMap.get(systemId);
}
/**
* Get the size of PublishedDevicesList.
*
* @return int Size of PublishedDevicesList.
*/
public int getSize() {
return publishedDevicesList.getDeviceCertificateStatusCount();
}
/**
* Return PublishedDevicesList as a String.
*
* @return {@link PublishedDevicesList} in String.
*/
@Override
public String toString() {
return publishedDevicesList.toString();
}
/**
* Get {@link PublishedDevicesDelta} by getting delta of new PublishedDevices with current
* PublishedDevices.
*
* @param newPublishedDevices PublishedDevices proto.
* @return {@link PublishedDevicesDelta}.
* @throws InvalidProtocolBufferException if failed to parse published devices.
*/
public PublishedDevicesDelta getPublishedDevicesDelta(PublishedDevices newPublishedDevices)
throws InvalidProtocolBufferException {
PublishedDevicesDeltaWrapper delta =
new PublishedDevicesDeltaWrapper(this.publishedDevices, newPublishedDevices);
return delta.getDelta();
}
}