diff --git a/tools/bin/published_devices_client/published_devices_cli_deploy.jar b/tools/bin/published_devices_client/published_devices_cli_deploy.jar old mode 100755 new mode 100644 index 40936b0..ab0ddf4 Binary files a/tools/bin/published_devices_client/published_devices_cli_deploy.jar and b/tools/bin/published_devices_client/published_devices_cli_deploy.jar differ diff --git a/tools/bin/published_devices_delta/published_devices_delta_deploy.jar b/tools/bin/published_devices_delta/published_devices_delta_deploy.jar old mode 100755 new mode 100644 index cc18dd5..e59e6ec Binary files a/tools/bin/published_devices_delta/published_devices_delta_deploy.jar and b/tools/bin/published_devices_delta/published_devices_delta_deploy.jar differ diff --git a/tools/source/published_devices_client/WORKSPACE b/tools/source/published_devices_client/WORKSPACE index c09fe7a..d6827ee 100644 --- a/tools/source/published_devices_client/WORKSPACE +++ b/tools/source/published_devices_client/WORKSPACE @@ -103,65 +103,29 @@ maven_install( ) # GRPC -git_repository( +http_archive( name = "io_grpc_grpc_java", - remote = "https://github.com/grpc/grpc-java.git", - tag = "v1.23.0", + strip_prefix = "grpc-java-1.36.0", + url = "https://github.com/grpc/grpc-java/archive/v1.36.0.zip", ) -# GRPC Core +load("@io_grpc_grpc_java//:repositories.bzl", "IO_GRPC_GRPC_JAVA_ARTIFACTS") +load("@io_grpc_grpc_java//:repositories.bzl", "IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS") + maven_install( - name = "grpc_core", - artifacts = ["io.grpc:grpc-core:1.23.0"], + artifacts = IO_GRPC_GRPC_JAVA_ARTIFACTS, + generate_compat_repositories = True, + override_targets = IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS, repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", + "https://repo.maven.apache.org/maven2/", ], ) -# GRPC Netty -maven_install( - name = "grpc_netty", - artifacts = ["io.grpc:grpc-netty:1.23.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) +load("@maven//:compat.bzl", "compat_repositories") +compat_repositories() -maven_install( - name = "grpc_netty_all", - artifacts = ["io.netty:netty-all:4.1.41.Final"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -# GRPC Stub -maven_install( - name = "grpc_stub", - artifacts = ["io.grpc:grpc-stub:1.23.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -# GRPC Contenxt -maven_install( - name = "grpc_context", - artifacts = ["io.grpc:grpc-context:1.18.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) +load("@io_grpc_grpc_java//:repositories.bzl", "grpc_java_repositories") +grpc_java_repositories() # GSON maven_install( @@ -174,80 +138,6 @@ maven_install( ], ) -# Netty TcNative (for GRPC) -maven_install( - name = "netty_tcnative", - artifacts = ["io.netty:netty-tcnative:2.0.25.Final"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -# Netty BoringSSL -maven_install( - name = "netty_boringssl", - artifacts = ["io.netty:netty-tcnative-boringssl-static:2.0.25.Final"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -# Open Census (for GRPC) -maven_install( - name = "open_census_api", - artifacts = ["io.opencensus:opencensus-api:0.24.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -maven_install( - name = "open_census", - artifacts = ["io.opencensus:opencensus-contrib-http-jetty-client:0.19.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -maven_install( - name = "open_census_util", - artifacts = ["io.opencensus:opencensus-contrib-http-util:0.19.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -maven_install( - name = "open_census_grpc_metrics", - artifacts = ["io.opencensus:opencensus-contrib-grpc-metrics:0.24.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -# Perfmark (GRPC) -maven_install( - name = "io_perfmark_api", - artifacts = ["io.perfmark:perfmark-api:0.17.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - # Google Protobuf Protos PROTOBUF_BUILD_FILE = """ package(default_visibility = ["//visibility:public"]) @@ -361,9 +251,5 @@ bind( actual = "@com_google_protobuf//:protobuf_java", ) -# Loads necessary bazel rules for later consumption. -load("@io_grpc_grpc_java//:repositories.bzl", "grpc_java_repositories") -grpc_java_repositories() - load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") protobuf_deps() diff --git a/tools/source/published_devices_client/google/chrome/widevine/contentpartners/v1beta1/BUILD b/tools/source/published_devices_client/google/chrome/widevine/contentpartners/v1beta1/BUILD index 4175520..b7c2d55 100644 --- a/tools/source/published_devices_client/google/chrome/widevine/contentpartners/v1beta1/BUILD +++ b/tools/source/published_devices_client/google/chrome/widevine/contentpartners/v1beta1/BUILD @@ -28,7 +28,6 @@ proto_library( java_proto_library( name = "published_devices_java_proto", deps = [":published_devices_proto"], - ) java_grpc_library( @@ -38,3 +37,8 @@ java_grpc_library( ":published_devices_java_proto", ], ) + +java_proto_library( + name = "device_security_profiles_java_proto", + deps = [":device_security_profiles_proto"], +) diff --git a/tools/source/published_devices_client/google/chrome/widevine/contentpartners/v1beta1/device_security_profiles.proto b/tools/source/published_devices_client/google/chrome/widevine/contentpartners/v1beta1/device_security_profiles.proto index 83abdc2..afe2a9d 100644 --- a/tools/source/published_devices_client/google/chrome/widevine/contentpartners/v1beta1/device_security_profiles.proto +++ b/tools/source/published_devices_client/google/chrome/widevine/contentpartners/v1beta1/device_security_profiles.proto @@ -91,4 +91,6 @@ enum HashAlgorithm { HASH_ALGORITHM_SHA_1 = 1; // Secure Hash Algorithm 2 256 bits (SHA-256). HASH_ALGORITHM_SHA_256 = 2; + // Secure Hash Algorithm 2 384 bits (SHA-384). + HASH_ALGORITHM_SHA_384 = 3; } diff --git a/tools/source/published_devices_client/google/chrome/widevine/contentpartners/v1beta1/published_devices.proto b/tools/source/published_devices_client/google/chrome/widevine/contentpartners/v1beta1/published_devices.proto old mode 100755 new mode 100644 diff --git a/tools/source/published_devices_client/interfaces/BUILD b/tools/source/published_devices_client/interfaces/BUILD index 4735f66..c75c5e0 100644 --- a/tools/source/published_devices_client/interfaces/BUILD +++ b/tools/source/published_devices_client/interfaces/BUILD @@ -10,6 +10,7 @@ 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", diff --git a/tools/source/published_devices_client/interfaces/DeviceSecurityProfile.java b/tools/source/published_devices_client/interfaces/DeviceSecurityProfile.java new file mode 100644 index 0000000..624a17e --- /dev/null +++ b/tools/source/published_devices_client/interfaces/DeviceSecurityProfile.java @@ -0,0 +1,16 @@ +// Copyright 2020 Google LLC. All rights reserved. +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; +} diff --git a/tools/source/published_devices_client/providers/BUILD b/tools/source/published_devices_client/providers/BUILD index 506b639..be099ba 100644 --- a/tools/source/published_devices_client/providers/BUILD +++ b/tools/source/published_devices_client/providers/BUILD @@ -9,29 +9,24 @@ package( java_library( name = "providers", srcs = glob(["*.java"]), - runtime_deps = [ - "@grpc_context//:io_grpc_grpc_context", - "@grpc_netty_all//:io_netty_netty_all", - "@io_perfmark_api//:io_perfmark_perfmark_api", - "@netty_boringssl//:io_netty_netty_tcnative_boringssl_static", - "@open_census//:io_opencensus_opencensus_contrib_http_jetty_client", - "@open_census_api//:io_opencensus_opencensus_api", - "@open_census_grpc_metrics//:io_opencensus_opencensus_contrib_grpc_metrics", - "@open_census_util//:io_opencensus_opencensus_contrib_http_util", - ], deps = [ ":libwvpl_cas_proxy_sdk_lib.jar", + "//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", "//httpclient", "//interfaces", "@google_guice//:com_google_inject_guice", "@json//:org_json_json", + "@jsr305_annotations//:com_google_code_findbugs_jsr305", "@com_google_protobuf//:protobuf_java", "@google_guava//:com_google_guava_guava", - "@grpc_core//:io_grpc_grpc_core", - "@grpc_netty//:io_grpc_grpc_netty", - "@grpc_stub//:io_grpc_grpc_stub", "@io_grpc_grpc_java//api", + "@io_grpc_grpc_java//netty", + "@io_grpc_grpc_java//protobuf", + "@io_grpc_grpc_java//stub", + "@maven//:com_google_api_grpc_proto_google_common_protos", + "@maven//:com_google_code_findbugs_jsr305", + "@maven//:com_google_code_gson_gson", ], ) diff --git a/tools/source/published_devices_client/providers/PublishedDevicesProvider.java b/tools/source/published_devices_client/providers/PublishedDevicesProvider.java index ff5733d..7018325 100644 --- a/tools/source/published_devices_client/providers/PublishedDevicesProvider.java +++ b/tools/source/published_devices_client/providers/PublishedDevicesProvider.java @@ -2,13 +2,18 @@ package com.google.video.widevine.jts.providers; +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.SignedDeviceSecurityProfiles; import com.google.inject.Inject; +import com.google.protobuf.ExtensionRegistry; import com.google.protobuf.TextFormat; 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.sdk.wvpl.WvPLBaseEnvironment; import com.google.video.widevine.sdk.wvpl.WvPLStatusException; import io.grpc.ManagedChannel; @@ -19,18 +24,22 @@ import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; +import javax.annotation.Nullable; /** - * Provides the latest {@code PublishedDevices} data from the Widevine Published Devices Service. + * Provides the latest {@code PublishedDevices} or {@code SignedDeviceSecurityProfiles} data from + * the Widevine Published Devices Service. * - * This implementation uses the Widevine Published Devices API, and support a gRPC method for - * retrieving PublishedDevices with an embedded Published Devices list. + *

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 { +public class PublishedDevicesProvider implements DeviceCertificate, DeviceSecurityProfile { 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; /** * PublishedDevicesProvider constructor. @@ -38,15 +47,21 @@ public class PublishedDevicesProvider implements DeviceCertificate { * @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. * @throws IOException upon failure creating OAUTH credentials. */ @Inject public PublishedDevicesProvider( - WvPLBaseEnvironment environment, String serviceAccountPath, String apiServicePath) + WvPLBaseEnvironment environment, + String serviceAccountPath, + String apiServicePath, + @Nullable String provider) throws IOException { this.environment = environment; this.apiServicePath = apiServicePath; credentials = new Credentials(serviceAccountPath); + this.provider = provider; } /** @@ -57,7 +72,7 @@ public class PublishedDevicesProvider implements DeviceCertificate { * @throws WvPLStatusException upon WvPLBaseEnvironment errors. */ @Override - public PublishedDevices getPublishedDevices() throws InterruptedException, WvPLStatusException{ + public PublishedDevices getPublishedDevices() throws InterruptedException, WvPLStatusException { ManagedChannel channel = null; PublishedDevices devicesResponse = null; @@ -70,10 +85,20 @@ public class PublishedDevicesProvider implements DeviceCertificate { PublishedDevicesServiceGrpc.PublishedDevicesServiceBlockingStub blockingStub = PublishedDevicesServiceGrpc.newBlockingStub(channel) .withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)); - devicesResponse = blockingStub.getPublishedDevices(PublishedDevicesRequest.parseFrom( - environment.generateDeviceStatusListRequest())); - logger.log(Level.INFO, "GRPC Call to PublishedDevicesService.GetSignedList returned:\n %s" - + TextFormat.printer().printToString(devicesResponse)); + devicesResponse = + blockingStub.getPublishedDevices( + PublishedDevicesRequest.parseFrom( + environment.generateDeviceStatusListRequest(), + ExtensionRegistry.getEmptyRegistry())); + 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 %s" + + truncatedLogString); } catch (IOException e) { logger.log(Level.INFO, "IOException encountered trying to retrieve the signed list: " + e); } finally { @@ -85,6 +110,50 @@ public class PublishedDevicesProvider implements DeviceCertificate { 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); + Metadata metadata = new Metadata(); + String token = "Bearer " + credentials.getAccessToken(); + metadata.put(Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER), token); + + PublishedDevicesServiceGrpc.PublishedDevicesServiceBlockingStub blockingStub = + PublishedDevicesServiceGrpc.newBlockingStub(channel) + .withInterceptors(MetadataUtils.newAttachHeadersInterceptor(metadata)); + + signedMessageInResponse = + blockingStub.listDeviceSecurityProfiles(listDspRequest).getSignedDeviceSecurityProfiles(); + logger.log( + Level.INFO, + "GRPC Call to PublishedDevicesService.ListDeviceSecurityProfiles returned:\n %s" + + TextFormat.printer().printToString(signedMessageInResponse)); + } catch (IOException e) { + logger.log( + Level.INFO, "IOException encountered trying to retrieve the signed dsp list: " + e); + } finally { + if (channel != null) { + channel.shutdown(); + channel.awaitTermination(1, TimeUnit.SECONDS); + } + } + return signedMessageInResponse; + } + private static ManagedChannel createRpcChannel(String host) { return NettyChannelBuilder.forTarget(host).build(); } diff --git a/tools/source/published_devices_client/providers/libwvpl_cas_proxy_sdk_lib.jar b/tools/source/published_devices_client/providers/libwvpl_cas_proxy_sdk_lib.jar new file mode 100644 index 0000000..e4c25d4 Binary files /dev/null and b/tools/source/published_devices_client/providers/libwvpl_cas_proxy_sdk_lib.jar differ diff --git a/tools/source/published_devices_client/tools/BUILD b/tools/source/published_devices_client/tools/BUILD index 4e9dccb..dbb35da 100644 --- a/tools/source/published_devices_client/tools/BUILD +++ b/tools/source/published_devices_client/tools/BUILD @@ -9,18 +9,8 @@ java_library( srcs = [ "PublishedDevicesCli.java", ], - runtime_deps = [ - "@grpc_context//:io_grpc_grpc_context", - "@grpc_netty_all//:io_netty_netty_all", - "@io_perfmark_api//:io_perfmark_perfmark_api", - "@netty_boringssl//:io_netty_netty_tcnative_boringssl_static", - "@netty_tcnative//:io_netty_netty_tcnative", - "@open_census//:io_opencensus_opencensus_contrib_http_jetty_client", - "@open_census_api//:io_opencensus_opencensus_api", - "@open_census_grpc_metrics//:io_opencensus_opencensus_contrib_grpc_metrics", - "@open_census_util//:io_opencensus_opencensus_contrib_http_util", - ], 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", "//providers", diff --git a/tools/source/published_devices_client/tools/PublishedDevicesCli.java b/tools/source/published_devices_client/tools/PublishedDevicesCli.java index 92219fe..ccd72cd 100644 --- a/tools/source/published_devices_client/tools/PublishedDevicesCli.java +++ b/tools/source/published_devices_client/tools/PublishedDevicesCli.java @@ -7,6 +7,8 @@ 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.common.collect.ImmutableList; import com.google.common.io.BaseEncoding; import com.google.video.widevine.jts.providers.PublishedDevicesProvider; import com.google.video.widevine.sdk.wvpl.WvPLCASProxyEnvironment; @@ -18,25 +20,28 @@ import java.nio.file.Paths; import java.util.HashMap; /** - * Published Devices Command Line Interface. + * Published Devices Client command line tool. * - * Provides a command line interface to get the latest {@code PublishedDevices} data from the - * Widevine Published Devices Service. + *

Provides a command line interface to get the latest {@code PublishedDevices} and {@code + * SignedDeviceSecurityProfiles} data from the Widevine Published Devices Service. * - * Default Widevine Service API path: widevine.googleapis.com. + *

Default Widevine Service API path: widevine.googleapis.com. * - * To build: - * Bazel build java/com/google/video/widevine/jts/tools:published_devices_client_deploy.jar + *

To build: bazel build + * java/com/google/video/widevine/jts/tools:published_devices_cli_deploy.jar * - * To run: - * java -Djava.library.path=./path/to/license/sdk.so \ - * -jar /path/to/published_devices_client_deploy.jar \ - * -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 \ + *

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 ImmutableList DATA_TYPE_LIST = + ImmutableList.of(PUBLISHED_DEVICES, DSP); + private WvPLCASProxyEnvironment environment = null; private PublishedDevicesProvider publishedDevices = null; private String serviceCertPath = null; @@ -44,42 +49,68 @@ public final class PublishedDevicesCli { private String privateKeyPassphrase = null; /** Command Line Flags. */ - static class Flags{ - private Flags() {} + static class Flags { + private Flags() {} - @Parameter(names = "-service_cert_path", description = "Path to service certificate") - private String serviceCertPath = ""; + @Parameter( + names = "-data_type", + description = + "Set retrieved data type. Must be \"PUBLISHED_DEVICES\" or \"DSP\". Default is" + + " \"PUBLISHED_DEVICES\".") + private String dataType = "PUBLISHED_DEVICES"; - @Parameter(names = "-service_private_key_path", - description = "Path to private key file needed to decrypt service certificate") - private String servicePrivateKeyPath = ""; + @Parameter(names = "-service_cert_path", description = "Path to service certificate") + private String serviceCertPath = ""; - @Parameter(names = "-service_private_key_passphrase", - description = "Passphrase needed to decrypt the private key") - private String servicePrivateKeyPassphrase = ""; + @Parameter( + names = "-service_private_key_path", + description = "Path to private key file needed to decrypt service certificate") + private String servicePrivateKeyPath = ""; - @Parameter(names = "-service_account_path", - description = "Path to a GCP Service Account json file") - private String serviceAccountPath = ""; + @Parameter( + names = "-service_private_key_passphrase", + description = "Passphrase needed to decrypt the private key") + private String servicePrivateKeyPassphrase = ""; - @Parameter(names = "-api_service_path", - description = "Optional. Path to a Widevine API service.") - private String apiServicePath = "widevine.googleapis.com"; + @Parameter( + names = "-service_account_path", + description = "Path to a GCP Service Account json file") + private String serviceAccountPath = ""; - @Parameter(names = "-s", - description = "Save PublishedDevices file path.") - private String saveFilePath = null; + @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 DSPs. Optional for obtaining" + + " PUBLISHED_DEVICES.") + private String provider = ""; + + @Parameter(names = "-s", description = "Save retrieved file path.") + private String saveFilePath = null; } - public PublishedDevicesCli(String serviceCertPath, String privateKeyPath, - String privateKeyPassphrase, String serviceAccountPath, String apiServicePath) + public PublishedDevicesCli( + String dataType, + String serviceCertPath, + String privateKeyPath, + String privateKeyPassphrase, + String serviceAccountPath, + String apiServicePath, + String provider) throws Exception { this.serviceCertPath = serviceCertPath; this.privateKeyPath = privateKeyPath; this.privateKeyPassphrase = privateKeyPassphrase; - initializeWvplEnvironment(); - publishedDevices = new PublishedDevicesProvider( - environment, serviceAccountPath, apiServicePath); + // Construct PublishedDevicesProvider to obtain PUBLISHED_DEVICES or DSPs. + if (dataType.equals(PUBLISHED_DEVICES)) { + initializeWvplEnvironment(); + } + publishedDevices = + new PublishedDevicesProvider(environment, serviceAccountPath, apiServicePath, provider); } /** @@ -94,30 +125,61 @@ public final class PublishedDevicesCli { } /** - * Saves PublishedDevices proto data to a file. + * 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(); + } + + /** + * Saves byte array to a file. * * @param saveFilePath The path to the save file. - * @param publishedDevices A PublishedDevices proto. + * @param byteArray a byte array transformed from proto. */ - public static void savePublishedDevicesDataToFile(String saveFilePath, - PublishedDevices publishedDevices) throws IOException { + public static void saveByteArrayToFile(String saveFilePath, byte[] byteArray) throws IOException { Files.write( - Paths.get(saveFilePath), - BaseEncoding.base64Url().encode(publishedDevices.toByteArray()).getBytes(UTF_8)); + Paths.get(saveFilePath), BaseEncoding.base64Url().encode(byteArray).getBytes(UTF_8)); } public static void main(String[] args) throws Exception { Flags flags = new Flags(); new JCommander(flags, args); - PublishedDevicesCli devices = new PublishedDevicesCli( - flags.serviceCertPath, - flags.servicePrivateKeyPath, - flags.servicePrivateKeyPassphrase, - flags.serviceAccountPath, - flags.apiServicePath); - PublishedDevices publishedDevices = devices.getPublishedDevices(); - if (flags.saveFilePath != null) { - savePublishedDevicesDataToFile(flags.saveFilePath, publishedDevices); + // 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.dataType.equals(DSP) && flags.provider.isEmpty()) { + System.err.println("Failed to provide the provider name for obtaining DSP list. Exit"); + System.exit(-1); + } + + PublishedDevicesCli devices = + new PublishedDevicesCli( + flags.dataType, + flags.serviceCertPath, + flags.servicePrivateKeyPath, + flags.servicePrivateKeyPassphrase, + flags.serviceAccountPath, + flags.apiServicePath, + flags.provider); + if (flags.dataType.equals(PUBLISHED_DEVICES)) { + PublishedDevices publishedDevices = devices.getPublishedDevices(); + if (flags.saveFilePath != null) { + saveByteArrayToFile(flags.saveFilePath, publishedDevices.toByteArray()); + } + } else { + SignedDeviceSecurityProfiles signedDeviceSecurityProfiles = + devices.getSignedDeviceSecurityProfiles(); + if (flags.saveFilePath != null) { + saveByteArrayToFile(flags.saveFilePath, signedDeviceSecurityProfiles.toByteArray()); + } } } diff --git a/tools/source/published_devices_delta/WORKSPACE b/tools/source/published_devices_delta/WORKSPACE index c09fe7a..d6827ee 100644 --- a/tools/source/published_devices_delta/WORKSPACE +++ b/tools/source/published_devices_delta/WORKSPACE @@ -103,65 +103,29 @@ maven_install( ) # GRPC -git_repository( +http_archive( name = "io_grpc_grpc_java", - remote = "https://github.com/grpc/grpc-java.git", - tag = "v1.23.0", + strip_prefix = "grpc-java-1.36.0", + url = "https://github.com/grpc/grpc-java/archive/v1.36.0.zip", ) -# GRPC Core +load("@io_grpc_grpc_java//:repositories.bzl", "IO_GRPC_GRPC_JAVA_ARTIFACTS") +load("@io_grpc_grpc_java//:repositories.bzl", "IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS") + maven_install( - name = "grpc_core", - artifacts = ["io.grpc:grpc-core:1.23.0"], + artifacts = IO_GRPC_GRPC_JAVA_ARTIFACTS, + generate_compat_repositories = True, + override_targets = IO_GRPC_GRPC_JAVA_OVERRIDE_TARGETS, repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", + "https://repo.maven.apache.org/maven2/", ], ) -# GRPC Netty -maven_install( - name = "grpc_netty", - artifacts = ["io.grpc:grpc-netty:1.23.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) +load("@maven//:compat.bzl", "compat_repositories") +compat_repositories() -maven_install( - name = "grpc_netty_all", - artifacts = ["io.netty:netty-all:4.1.41.Final"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -# GRPC Stub -maven_install( - name = "grpc_stub", - artifacts = ["io.grpc:grpc-stub:1.23.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -# GRPC Contenxt -maven_install( - name = "grpc_context", - artifacts = ["io.grpc:grpc-context:1.18.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) +load("@io_grpc_grpc_java//:repositories.bzl", "grpc_java_repositories") +grpc_java_repositories() # GSON maven_install( @@ -174,80 +138,6 @@ maven_install( ], ) -# Netty TcNative (for GRPC) -maven_install( - name = "netty_tcnative", - artifacts = ["io.netty:netty-tcnative:2.0.25.Final"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -# Netty BoringSSL -maven_install( - name = "netty_boringssl", - artifacts = ["io.netty:netty-tcnative-boringssl-static:2.0.25.Final"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -# Open Census (for GRPC) -maven_install( - name = "open_census_api", - artifacts = ["io.opencensus:opencensus-api:0.24.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -maven_install( - name = "open_census", - artifacts = ["io.opencensus:opencensus-contrib-http-jetty-client:0.19.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -maven_install( - name = "open_census_util", - artifacts = ["io.opencensus:opencensus-contrib-http-util:0.19.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -maven_install( - name = "open_census_grpc_metrics", - artifacts = ["io.opencensus:opencensus-contrib-grpc-metrics:0.24.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - -# Perfmark (GRPC) -maven_install( - name = "io_perfmark_api", - artifacts = ["io.perfmark:perfmark-api:0.17.0"], - repositories = [ - "https://jcenter.bintray.com", - "https://maven.google.com", - "https://repo1.maven.org/maven2", - ], -) - # Google Protobuf Protos PROTOBUF_BUILD_FILE = """ package(default_visibility = ["//visibility:public"]) @@ -361,9 +251,5 @@ bind( actual = "@com_google_protobuf//:protobuf_java", ) -# Loads necessary bazel rules for later consumption. -load("@io_grpc_grpc_java//:repositories.bzl", "grpc_java_repositories") -grpc_java_repositories() - load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") protobuf_deps() diff --git a/tools/source/published_devices_delta/google/chrome/widevine/contentpartners/v1beta1/BUILD b/tools/source/published_devices_delta/google/chrome/widevine/contentpartners/v1beta1/BUILD index 4175520..b7c2d55 100644 --- a/tools/source/published_devices_delta/google/chrome/widevine/contentpartners/v1beta1/BUILD +++ b/tools/source/published_devices_delta/google/chrome/widevine/contentpartners/v1beta1/BUILD @@ -28,7 +28,6 @@ proto_library( java_proto_library( name = "published_devices_java_proto", deps = [":published_devices_proto"], - ) java_grpc_library( @@ -38,3 +37,8 @@ java_grpc_library( ":published_devices_java_proto", ], ) + +java_proto_library( + name = "device_security_profiles_java_proto", + deps = [":device_security_profiles_proto"], +) diff --git a/tools/source/published_devices_delta/google/chrome/widevine/contentpartners/v1beta1/device_security_profiles.proto b/tools/source/published_devices_delta/google/chrome/widevine/contentpartners/v1beta1/device_security_profiles.proto index 83abdc2..afe2a9d 100644 --- a/tools/source/published_devices_delta/google/chrome/widevine/contentpartners/v1beta1/device_security_profiles.proto +++ b/tools/source/published_devices_delta/google/chrome/widevine/contentpartners/v1beta1/device_security_profiles.proto @@ -91,4 +91,6 @@ enum HashAlgorithm { HASH_ALGORITHM_SHA_1 = 1; // Secure Hash Algorithm 2 256 bits (SHA-256). HASH_ALGORITHM_SHA_256 = 2; + // Secure Hash Algorithm 2 384 bits (SHA-384). + HASH_ALGORITHM_SHA_384 = 3; } diff --git a/tools/source/published_devices_delta/google/chrome/widevine/contentpartners/v1beta1/published_devices.proto b/tools/source/published_devices_delta/google/chrome/widevine/contentpartners/v1beta1/published_devices.proto old mode 100755 new mode 100644 diff --git a/tools/source/published_devices_delta/tools/BUILD b/tools/source/published_devices_delta/tools/BUILD index fd75ed8..f9f6e6c 100644 --- a/tools/source/published_devices_delta/tools/BUILD +++ b/tools/source/published_devices_delta/tools/BUILD @@ -21,6 +21,22 @@ java_library( ], ) +java_library( + name = "device_security_profiles_lib", + srcs = [ + "DeviceSecurityProfilesDeltaWrapper.java", + "DeviceSecurityProfilesWrapper.java", + ], + deps = [ + "//google/chrome/widevine/contentpartners/v1beta1:device_security_profiles_java_proto", + "@com_google_protobuf//:protobuf_java", + "//video/widevine/protos/public:device_security_profile_list_java_proto", + "//video/widevine/protos/public:device_security_profiles_delta_java_proto", + "//video/widevine/protos/public:security_profile_java_proto", + "@google_guava//:com_google_guava_guava", + ], +) + java_binary( name = "published_devices_delta", srcs = [ @@ -28,14 +44,18 @@ java_binary( ], main_class = "com.google.video.widevine.jts.tools.PublishedDevicesMain", deps = [ + ":device_security_profiles_lib", ":published_devices_delta_lib", "@com_google_protobuf//:protobuf_java", "@jcommander//:com_beust_jcommander", "@json//:org_json_json", "//video/widevine/protos/public:device_certificate_status_java_proto", "//video/widevine/protos/public:device_certificate_status_proto", + "//video/widevine/protos/public:device_security_profiles_delta_java_proto", + "//video/widevine/protos/public:device_security_profiles_delta_proto", "//video/widevine/protos/public:published_devices_delta_java_proto", "//video/widevine/protos/public:published_devices_delta_proto", + "//video/widevine/protos/public:security_profile_java_proto", "@google_guava//:com_google_guava_guava", ], ) diff --git a/tools/source/published_devices_delta/tools/DeviceSecurityProfilesDeltaWrapper.java b/tools/source/published_devices_delta/tools/DeviceSecurityProfilesDeltaWrapper.java new file mode 100644 index 0000000..a47e628 --- /dev/null +++ b/tools/source/published_devices_delta/tools/DeviceSecurityProfilesDeltaWrapper.java @@ -0,0 +1,259 @@ +// Copyright 2020 Google LLC. All rights reserved. +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 dspAddedList = new ArrayList<>(); + private final List dspRemovedList = new ArrayList<>(); + private final List 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 getAddedDeviceSecurityProfiles() { + return dspAddedList; + } + + /** Returns a list of removed DSPs from DeviceSecurityProfilesDelta for all content owners. */ + public List getRemovedDeviceSecurityProfiles() { + return dspRemovedList; + } + + /** Returns a list of modified DSPs from DeviceSecurityProfilesDelta for all content owners. */ + public List 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> originalDspMap = initializeDspMap(originalDspList); + Map> newDspMap = initializeDspMap(newDspList); + // Create a map to store DspDelta.Builder. Key is content owner name, value is dspDelta for each + // owner. + Map 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> initializeDspMap( + DeviceSecurityProfileList dspList) { + Map> 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 calculateDspDelta( + Map> originalDspMap, + Map> newDspMap) { + Map dspDeltaBuilderMap = new LinkedHashMap<>(); + for (String owner : newDspMap.keySet()) { + // Record already-processed original dsp set. + Set 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; + } +} diff --git a/tools/source/published_devices_delta/tools/DeviceSecurityProfilesWrapper.java b/tools/source/published_devices_delta/tools/DeviceSecurityProfilesWrapper.java new file mode 100644 index 0000000..39266fb --- /dev/null +++ b/tools/source/published_devices_delta/tools/DeviceSecurityProfilesWrapper.java @@ -0,0 +1,150 @@ +// Copyright 2020 Google LLC. All rights reserved. +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> dspMap = new LinkedHashMap<>(); + private Set 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} a list of DSPs for specific content owner. Return null if + * none of the DSPs belongs to the specific content owner. + */ + public List getDeviceSecurityProfiles(String owner) { + if (isNullOrEmpty(owner)) { + return null; + } + return dspMap.get(owner); + } + + /** + * Get the content owner list from the DSPs. + * + * @return {@link List} a list of content owners shown in the DSPs. Return null if owner + * set is null or empty. + */ + public List 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 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(); + } +} diff --git a/tools/source/published_devices_delta/tools/PublishedDevicesMain.java b/tools/source/published_devices_delta/tools/PublishedDevicesMain.java index 4709723..ce867a4 100644 --- a/tools/source/published_devices_delta/tools/PublishedDevicesMain.java +++ b/tools/source/published_devices_delta/tools/PublishedDevicesMain.java @@ -6,12 +6,18 @@ 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.Paths; +import java.util.Arrays; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -21,47 +27,93 @@ import org.json.JSONObject; /** * Published Devices command line tool. * - *

Provides a command line tool to get the diff between two PublishedDevices Proto, parse the - * PublishedDevices and PublishedDevicesDelta. + *

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. * *

To build: Bazel build tools:published_devices_delta_deploy.jar * - *

To run: java -jar /path/to/published_devices_lib_deploy.jar -new_published_devices_path path - * -original_published_devices_path path -print + *

Commands for PublishedDevices: * - *

To run: java -jar /path/to/published_devices_lib_deploy.jar -new_published_devices_path path - * -original_published_devices_path path -system_id 100 -get_system_ids + *

To run: java -jar /path/to/published_devices_lib_deploy.jar -new_file_path path + * -original_file_path path -print + * + *

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 * *

To run: java -jar /path/to/published_devices_lib_deploy.jar -query path -system_id 100 + * + *

Commands for DeviceSecurityProfiles: + * + *

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 + * + *

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 + * + *

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 + * + *

Get DSPs for specific owner: java -jar /path/to/published_devices_lib_deploy.jar -data_type + * DSP -query path -content_owner owner_name + * + *

Get owner list from one retrieved dsp list: java -jar + * /path/to/published_devices_lib_deploy.jar -data_type DSP -query path -get_owners + * + *

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 */ final 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 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 = "-original_published_devices_path", - description = "Path to original PublishedDevices file") - private String originalPublishedDevicesPath = null; + names = "-data_type", + description = "Set parsed data type. Must be \"PUBLISHED_DEVICES\" or \"DSP\".") + private String dataType = "PUBLISHED_DEVICES"; @Parameter( - names = "-new_published_devices_path", - description = "Path to new PublishedDevices file") - private String newPublishedDevicesPath = null; + 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 want to print the published devices in String.") + description = "Setting to true, if content provider wants to print the delta in String.") private boolean print = false; - @Parameter(names = "-query", description = "Parse published devices file.") + @Parameter(names = "-query", description = "Parse the input file.") private String query = null; - @Parameter(names = "-system_id", description = "Device system-id.") + // Unique parameters for PublishedDevices. + @Parameter( + names = "-system_id", + description = "Device system-id shown in PublishedDevicesList.") private int systemId = ZERO_SYSTEM_ID; @Parameter( @@ -69,48 +121,51 @@ final class PublishedDevicesMain { description = "Boolean to get list of system_ids in added/removed/modified PublishedDevicesList.") private boolean getSystenIdsList = 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, args); - if (flags.newPublishedDevicesPath != null && flags.originalPublishedDevicesPath != null) { - parsePublishedDevicesDelta(flags); - } else if (flags.query != null) { - try { - byte[] publishedDevices = getPublishedDevicesAsBytes(flags.query); - 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) does not found in PublishedDevicesList from " - + flags.query); - System.exit(1); - } - System.out.println(deviceCertificateStatus); - // TODO(b/160253740): Add ProvisonedStatus, ProvisionedDeviceInfo and function. - } - } 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); + // 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); } - } else if (flags.originalPublishedDevicesPath != null - || flags.newPublishedDevicesPath != null) { - logger.log( - Level.SEVERE, - "Both originalPublishedDevicesPath and newPublishedDevicesPath should be specified."); } } @@ -118,10 +173,11 @@ final class PublishedDevicesMain { private static void parsePublishedDevicesDelta(Flags flags) { try { byte[] originalPublishedDevices = - getPublishedDevicesAsBytes(flags.originalPublishedDevicesPath); - byte[] newPublishedDevices = getPublishedDevicesAsBytes(flags.newPublishedDevicesPath); - checkLoadFileResult(flags.originalPublishedDevicesPath, originalPublishedDevices); - checkLoadFileResult(flags.newPublishedDevicesPath, newPublishedDevices); + 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(); @@ -135,7 +191,31 @@ final class PublishedDevicesMain { } } - /** Parse the argument related to PublishedDevicesDelta. */ + /** 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) { @@ -154,7 +234,7 @@ final class PublishedDevicesMain { System.out.println( publishedDevicesDelta.getModifiedDeviceCertificateStatus(flags.systemId)); } else { - logger.log(Level.SEVERE, flags.systemId + "does not found in the PublishedDevicesDelta."); + logger.log(Level.SEVERE, flags.systemId + "was not found in the PublishedDevicesDelta."); } } if (flags.getSystenIdsList) { @@ -170,6 +250,131 @@ final class PublishedDevicesMain { } } + /** 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 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 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); @@ -178,29 +383,35 @@ final class PublishedDevicesMain { } /** - * Load published devices Json file into string. + * 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[] getPublishedDevicesAsBytes(String filePath) throws IOException { + public static byte[] getPublishedDevicesOrDSPFileAsBytes(String filePath, String dataType) + throws IOException { String result = loadFile(filePath); - if (result == null) { + if (!DATA_TYPE_LIST.contains(dataType) || result == null) { return null; } - 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; + 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); } - byte[] serializedDCSL = base64Decode(requestBody); - - return serializedDCSL; + return serializedList; } /** diff --git a/tools/source/published_devices_delta/video/widevine/protos/public/BUILD b/tools/source/published_devices_delta/video/widevine/protos/public/BUILD index e4c94f5..3eca1fe 100644 --- a/tools/source/published_devices_delta/video/widevine/protos/public/BUILD +++ b/tools/source/published_devices_delta/video/widevine/protos/public/BUILD @@ -19,9 +19,11 @@ proto_library( srcs = [ "client_identification.proto", "errors.proto", + "external_license.proto", "hash_algorithm.proto", "license_protocol.proto", "license_server_sdk.proto", + "playready.proto", "provisioned_device_info.proto", "remote_attestation.proto", "sdk_license_data_config.proto", @@ -38,6 +40,7 @@ proto_library( "device_certificate_status.proto", "drm_certificate.proto", "errors.proto", + "external_license.proto", "hash_algorithm.proto", "license_protocol.proto", "license_server_sdk.proto", @@ -60,6 +63,20 @@ java_proto_library( deps = [":exported_wvdrm_license_server_sdk_proto"], ) +proto_library( + name = "external_license_proto", + srcs = ["external_license.proto"], + deps = [ + ":client_identification_proto", + ":license_protocol_proto", + ], +) + +java_proto_library( + name = "external_license_java_proto", + deps = [":external_license_proto"], +) + proto_library( name = "client_identification_proto", srcs = ["client_identification.proto"], @@ -245,6 +262,7 @@ java_proto_library( proto_library( name = "signed_drm_certificate_proto", srcs = ["signed_drm_certificate.proto"], + deps = [":hash_algorithm_proto"], ) java_proto_library( @@ -297,3 +315,92 @@ java_proto_library( name = "hash_algorithm_java_proto", deps = [":hash_algorithm_proto"], ) + +proto_library( + name = "device_security_profile_data_proto", + srcs = ["device_security_profile_data.proto"], + deps = [ + ":client_identification_proto", + ":device_common_proto", + ":provisioned_device_info_proto", + ], +) + +cc_proto_library( + name = "device_security_profile_data_cc_proto", + deps = [":device_security_profile_data_proto"], +) + +java_proto_library( + name = "device_security_profile_data_java_proto", + deps = [":device_security_profile_data_proto"], +) + +proto_library( + name = "security_profile_proto", + srcs = ["security_profile.proto"], + deps = [ + ":device_common_proto", + ":device_security_profile_data_proto", + ], +) + +cc_proto_library( + name = "security_profile_cc_proto", + deps = [":security_profile_proto"], +) + +java_proto_library( + name = "security_profile_java_proto", + deps = [":security_profile_proto"], +) + +proto_library( + name = "device_security_profile_list_proto", + srcs = ["device_security_profile_list.proto"], + deps = [ + ":hash_algorithm_proto", + ":security_profile_proto", + ], +) + +java_proto_library( + name = "device_security_profile_list_java_proto", + deps = [":device_security_profile_list_proto"], +) + +cc_proto_library( + name = "device_security_profile_list_cc_proto", + deps = [":device_security_profile_list_proto"], +) + +proto_library( + name = "device_security_profiles_delta_proto", + srcs = ["device_security_profiles_delta.proto"], + deps = [":security_profile_proto"], +) + +cc_proto_library( + name = "device_security_profiles_delta_cc_proto", + deps = [":device_security_profiles_delta_proto"], +) + +java_proto_library( + name = "device_security_profiles_delta_java_proto", + deps = [":device_security_profiles_delta_proto"], +) + +proto_library( + name = "playready_proto", + srcs = ["playready.proto"], +) + +cc_proto_library( + name = "playready_cc_proto", + deps = [":playready_proto"], +) + +java_proto_library( + name = "playready_java_proto", + deps = [":playready_proto"], +) diff --git a/tools/source/published_devices_delta/video/widevine/protos/public/client_identification.proto b/tools/source/published_devices_delta/video/widevine/protos/public/client_identification.proto new file mode 100644 index 0000000..d94ad4a --- /dev/null +++ b/tools/source/published_devices_delta/video/widevine/protos/public/client_identification.proto @@ -0,0 +1,132 @@ +// Copyright 2016 Google LLC. All rights reserved. + +// Author: tinskip@google.com (Thomas Inskip) +// +// Description: +// ClientIdentification messages used by provisioning and license protocols. + +syntax = "proto2"; + +package video_widevine; + + +// go/jspb-correct-proto2 + +option java_package = "com.google.video.widevine.protos"; +option java_outer_classname = "ClientIdentificationProtos"; + + + // See http://go/go-api-flag. + + +// ClientIdentification message used to authenticate the client device. +message ClientIdentification { + enum TokenType { + KEYBOX = 0; + DRM_DEVICE_CERTIFICATE = 1; + REMOTE_ATTESTATION_CERTIFICATE = 2; + OEM_DEVICE_CERTIFICATE = 3; + } + + message NameValue { + optional string name = 1; + optional string value = 2; + } + + // Capabilities which not all clients may support. Used for the license + // exchange protocol only. + message ClientCapabilities { + enum HdcpVersion { + HDCP_NONE = 0; + HDCP_V1 = 1; + HDCP_V2 = 2; + HDCP_V2_1 = 3; + HDCP_V2_2 = 4; + HDCP_V2_3 = 5; + HDCP_NO_DIGITAL_OUTPUT = 0xff; + } + + enum CertificateKeyType { + RSA_2048 = 0; + RSA_3072 = 1; + ECC_SECP256R1 = 2; + ECC_SECP384R1 = 3; + ECC_SECP521R1 = 4; + } + + enum AnalogOutputCapabilities { + ANALOG_OUTPUT_UNKNOWN = 0; + ANALOG_OUTPUT_NONE = 1; + ANALOG_OUTPUT_SUPPORTED = 2; + ANALOG_OUTPUT_SUPPORTS_CGMS_A = 3; + } + + optional bool client_token = 1 [default = false]; + optional bool session_token = 2 [default = false]; + optional bool video_resolution_constraints = 3 [default = false]; + optional HdcpVersion max_hdcp_version = 4 [default = HDCP_NONE]; + optional uint32 oem_crypto_api_version = 5; + // Client has hardware support for protecting the usage table, such as + // storing the generation number in secure memory. For Details, see: + // https://docs.google.com/document/d/1Mm8oB51SYAgry62mEuh_2OEkabikBiS61kN7HsDnh9Y/edit#heading=h.xgjl2srtytjt + optional bool anti_rollback_usage_table = 6 [default = false]; + // The client shall report |srm_version| if available. + optional uint32 srm_version = 7; + // A device may have SRM data, and report a version, but may not be capable + // of updating SRM data. + optional bool can_update_srm = 8 [default = false]; + repeated CertificateKeyType supported_certificate_key_type = 9; + optional AnalogOutputCapabilities analog_output_capabilities = 10 + [default = ANALOG_OUTPUT_UNKNOWN]; + optional bool can_disable_analog_output = 11 [default = false]; + // Clients can indicate a performance level supported by OEMCrypto. + // This will allow applications and providers to choose an appropriate + // quality of content to serve. Currently defined tiers are + // 1 (low), 2 (medium) and 3 (high). Any other value indicate that + // the resource rating is unavailable or reporting erroneous values + // for that device. For details see, + // https://docs.google.com/document/d/1wodSYK-Unj3AgTSXqujWuBCAFC00qF85G1AhfLtqdko + optional uint32 resource_rating_tier = 12 [default = 0]; + } + + message ClientCredentials { + optional TokenType type = 1 [default = KEYBOX]; + optional bytes token = 2; + } + + // Type of factory-provisioned device root of trust. Optional. + optional TokenType type = 1 [default = KEYBOX]; + // Factory-provisioned device root of trust. Required. + optional bytes token = 2; + // Optional client information name/value pairs. + repeated NameValue client_info = 3; + // Client token generated by the content provider. Optional. + optional bytes provider_client_token = 4; + // Number of licenses received by the client to which the token above belongs. + // Only present if client_token is specified. + optional uint32 license_counter = 5; + // List of non-baseline client capabilities. + optional ClientCapabilities client_capabilities = 6; + // Serialized VmpData message. Optional. + optional bytes vmp_data = 7; + // Optional field that may contain additional provisioning credentials. + optional ClientCredentials device_credentials = 8; +} + +// EncryptedClientIdentification message used to hold ClientIdentification +// messages encrypted for privacy purposes. +message EncryptedClientIdentification { + // Provider ID for which the ClientIdentifcation is encrypted (owner of + // service certificate). + optional string provider_id = 1; + // Serial number for the service certificate for which ClientIdentification is + // encrypted. + optional bytes service_certificate_serial_number = 2; + // Serialized ClientIdentification message, encrypted with the privacy key + // using AES-128-CBC with PKCS#5 padding. + optional bytes encrypted_client_id = 3; + // Initialization vector needed to decrypt encrypted_client_id. + optional bytes encrypted_client_id_iv = 4; + // AES-128 privacy key, encrypted with the service public key using RSA-OAEP. + optional bytes encrypted_privacy_key = 5; +} diff --git a/tools/source/published_devices_delta/video/widevine/protos/public/device_certificate_status.proto b/tools/source/published_devices_delta/video/widevine/protos/public/device_certificate_status.proto index 7f01e43..b9a2c44 100644 --- a/tools/source/published_devices_delta/video/widevine/protos/public/device_certificate_status.proto +++ b/tools/source/published_devices_delta/video/widevine/protos/public/device_certificate_status.proto @@ -61,6 +61,19 @@ message DeviceCertificateStatus { // RevokedIdentifiers collect all the serial_numbers or unique_id_hashes used // for individual drm certificate revocation. optional RevokedIdentifiers revoked_identifiers = 7; + + // Vulnerability levels as defined by NIST national vulnerability database. + // https://nvd.nist.gov/vuln-metrics/cvss + enum DeviceVulnerabilityLevel { + DEVICE_VULNERABILITY_LEVEL_UNSPECIFIED = 0; + DEVICE_VULNERABILITY_LEVEL_NONE = 1; + DEVICE_VULNERABILITY_LEVEL_LOW = 2; + DEVICE_VULNERABILITY_LEVEL_MEDIUM = 3; + DEVICE_VULNERABILITY_LEVEL_HIGH = 4; + DEVICE_VULNERABILITY_LEVEL_CRITICAL = 5; + } + // Specifies the device vulnerability level. + optional DeviceVulnerabilityLevel device_vulnerability_level = 8; } // List of DeviceCertificateStatus. Used to propagate certificate revocation diff --git a/tools/source/published_devices_delta/video/widevine/protos/public/device_common.proto b/tools/source/published_devices_delta/video/widevine/protos/public/device_common.proto index f7e5428..7451375 100644 --- a/tools/source/published_devices_delta/video/widevine/protos/public/device_common.proto +++ b/tools/source/published_devices_delta/video/widevine/protos/public/device_common.proto @@ -10,9 +10,9 @@ package video_widevine; option java_package = "com.google.video.widevine.protos"; -// MOE:begin_strip + // See http://go/go-api-flag. -// MOE:end_strip + // Allows additional make/models to be associated with a system_id. message DeviceModel { @@ -73,6 +73,12 @@ message DeviceModel { // service should honor requests (classic nor MDRM/CENC) from one of // these devices. The device serial number and its REVOKED status will // appear in keysmith's certificate status list. +// REVOKED_LICENSING: Indicates that the device was revoked. A device series +// in this state will still allow provisioning of those devices. Licensing +// will be prohibited just as the REVOKED state. However, the licensing +// host can choose to override this state and allow licensing. +// The device serial number and the REVOKED status (not REVOKED_LICENSING) +// will appear in keysmith's certificate status list. // // Devices in the above states have the following behaviors in widevince // services: @@ -85,7 +91,12 @@ message DeviceModel { // PRE_RELEASE Yes Yes Yes Yes VALID false yes // RELEASED Yes Yes Yes Yes VALID false no // REVOKED No No No Yes REVOKED false no +// REVOKED_ No No No Yes REVOKED false no +// LICENSING No* No* Yes Yes REVOKED false no // DELETED No No No No n/a n/a n/a +// +// * - REVOKED_LICENSING will not issue licenses by default but can be +// overridden by the host. enum DeviceState { DEVICE_STATE_UNKNOWN = 0; IN_TESTING = 1; @@ -94,6 +105,7 @@ enum DeviceState { TEST_ONLY = 4; REVOKED = 5; PRE_RELEASE = 6; + REVOKED_LICENSING = 7; } // Specifies the device type, or form factor of a device. @@ -167,3 +179,42 @@ enum DeviceSecurityLevel { LEVEL_2 = 2; LEVEL_3 = 3; } + +// Defines the possible key types that can be issued for certificates. +// Historically, we only supported RSA with a standard Euler totient +// calculation. b/65383373 required that we distinguish between Euler and +// Carmichael totients for RSA keys. We also now support elliptic curve (EC) +// keys for certificates. +enum CertificateKeyType { + // Code should treat UNSPECIFIED as RSA key type. + // Code should treat UNSPECIFIED as NONE for a secondary encryption key in a + // dual keyed cert. + CERTIFICATE_KEY_TYPE_UNSPECIFIED = 0; + // RSA key with the Euler OR Carmichael totient may be used. + CERTIFICATE_KEY_TYPE_RSA = 1; + // RSA key with ONLY Euler totient may be used. + CERTIFICATE_KEY_TYPE_RSA_EULER = 2; + // EC key. + CERTIFICATE_KEY_TYPE_EC = 3; +} + +// Vulnerability levels as defined by NIST national vulnerability database. +// https://nvd.nist.gov/vuln-metrics/cvss +enum VulnerabilityLevel { + VULNERABILITY_LEVEL_UNSPECIFIED = 0; + VULNERABILITY_LEVEL_NONE = 1; + VULNERABILITY_LEVEL_LOW = 2; + VULNERABILITY_LEVEL_MEDIUM = 3; + VULNERABILITY_LEVEL_HIGH = 4; + VULNERABILITY_LEVEL_CRITICAL = 5; +} + +// Device vulnerability information. +message VulnerabilityInfo { + // Description of the vulnerability. + string description = 1; + // Buganizer number associated with the vulnerability. + uint64 buganizer_number = 2; + // Vulnerablity level of a device. + VulnerabilityLevel vulnerability_level = 3; +} diff --git a/tools/source/published_devices_delta/video/widevine/protos/public/device_security_profile_data.proto b/tools/source/published_devices_delta/video/widevine/protos/public/device_security_profile_data.proto new file mode 100644 index 0000000..0d21f5e --- /dev/null +++ b/tools/source/published_devices_delta/video/widevine/protos/public/device_security_profile_data.proto @@ -0,0 +1,70 @@ +// Copyright 2020 Google LLC. All rights reserved. + +syntax = "proto2"; + +package video_widevine; + +import "video/widevine/protos/public/client_identification.proto"; +import "video/widevine/protos/public/device_common.proto"; +import "video/widevine/protos/public/provisioned_device_info.proto"; + +option java_package = "com.google.video.widevine.protos"; + + + + + // See http://go/go-api-flag. + + +enum SecurityProfileLevel { + SECURITY_PROFILE_LEVEL_UNDEFINED = 0; + SECURITY_PROFILE_LEVEL_1 = 1; + SECURITY_PROFILE_LEVEL_2 = 2; + SECURITY_PROFILE_LEVEL_3 = 3; + SECURITY_PROFILE_LEVEL_4 = 4; + SECURITY_PROFILE_LEVEL_5 = 5; +} + +message OutputRequirement { + // Version of HDCP. + optional ClientIdentification.ClientCapabilities.HdcpVersion hdcp_version = 1; + // Analog output capabilities. + optional ClientIdentification.ClientCapabilities.AnalogOutputCapabilities + analog_output_capabilities = 2; +} + +message SecurityRequirement { + // Version of OEMCrypto. + optional uint32 oemcrypto_api_version = 1; + // Required. Security level. + optional ProvisionedDeviceInfo.WvSecurityLevel security_level = 2; + // Resource rating tier based on: + // https://docs.google.com/document/d/1wodSYK-Unj3AgTSXqujWuBCAFC00qF85G1AhfLtqdko/edit + optional uint32 resource_rating_tier = 3; + // Security vulnerability level. + optional VulnerabilityLevel vulnerability_level = 4; +} + +message DeviceException { + enum ExceptionAction { + DEVICE_EXCEPTION_UNSPECIFIED = 0; + // Include this profile for the system Id regardless of other profile + // requirements. + DEVICE_EXCEPTION_ALLOW = 1; + // Do not use this profile for the system Id regardless of other profile + // requirements. + DEVICE_EXCEPTION_BLOCK = 2; + } + // System Id for the exceptional device. + optional uint32 system_id = 1; + // Override action is taken if override is specified. + optional ExceptionAction action = 2; +} + +message ControlTime { + // Timestamp that this dsp should be effective (in seconds since epoch). + optional int64 start_time_seconds = 1; + // Timestamp that this dsp should be invalid (in seconds since epoch). + // Value 0 (default) means never expire. + optional int64 end_time_seconds = 2 [default = 0]; +} diff --git a/tools/source/published_devices_delta/video/widevine/protos/public/device_security_profile_list.proto b/tools/source/published_devices_delta/video/widevine/protos/public/device_security_profile_list.proto new file mode 100644 index 0000000..b9b3c67 --- /dev/null +++ b/tools/source/published_devices_delta/video/widevine/protos/public/device_security_profile_list.proto @@ -0,0 +1,39 @@ +// Copyright 2020 Google LLC. All rights reserved. +// +// +// Description: +// Device security profile list object definitions. + +syntax = "proto2"; + +package video_widevine; + +import "video/widevine/protos/public/hash_algorithm.proto"; +import "video/widevine/protos/public/security_profile.proto"; + +option java_outer_classname = "DeviceSecurityProfileListProtos"; +option java_package = "com.google.video.widevine.protos"; + +// List of DeviceSecurityProfiles. Used to propagate device security profiles to +// a list. +message DeviceSecurityProfileList { + // POSIX time, in seconds, when the list was created. Required. + optional uint64 creation_time_seconds = 1; + // A list of DeviceSecurityProfiles. + repeated SecurityProfile device_security_profiles = 2; +} + +// TODO(b/169442909): Consider removing the duplicated proto defition. +// A signed message which contains a serialized DeviceSecurityProfileList and +// the signature. +// LINT.IfChange +message SignedDeviceSecurityProfiles { + // Serialized DeviceSecurityProfileList. Required. + optional bytes device_security_profiles = 1; + // Signature of device_security_profiles. Signed with root + // certificate private key using RSASSA-PSS. Required. + optional bytes signature = 2; + // Optional field that indicates the hash algorithm used in signature scheme. + optional HashAlgorithmProto hash_algorithm = 3; +} +// LINT.ThenChange(//depot/google3/google/chrome/widevine/contentpartners/v1beta1/device_security_profiles.proto) diff --git a/tools/source/published_devices_delta/video/widevine/protos/public/device_security_profiles_delta.proto b/tools/source/published_devices_delta/video/widevine/protos/public/device_security_profiles_delta.proto new file mode 100644 index 0000000..6231508 --- /dev/null +++ b/tools/source/published_devices_delta/video/widevine/protos/public/device_security_profiles_delta.proto @@ -0,0 +1,45 @@ +// Copyright 2020 Google LLC. All rights reserved. +// +// Description: +// DeviceSecurityProfilesDelta object definitions. + +syntax = "proto2"; + +package video_widevine; + +import "video/widevine/protos/public/security_profile.proto"; + +option java_outer_classname = "DeviceSecurityProfilesDeltaProtos"; +option java_package = "com.google.video.widevine.protos"; + +// Represents the delta of two lists of DeviceSecurityProfiles. This message +// contains a header of creation time for both pieces of DSPs, and also a list +// of DspDeltas. +message DeviceSecurityProfilesDelta { + // POSIX time, in seconds, show the delta of creation_time_seconds. Optional. + message Header { + optional uint64 prev_creation_time_seconds = 1; + optional uint64 new_creation_time_seconds = 2; + } + + message Modified { + optional SecurityProfile prev_dsp = 1; + optional SecurityProfile new_dsp = 2; + } + + // DspDelta contains the content owner name and added/removed/modified fields + // for DSPs. + message DspDelta { + // Content owner who the compared Dsps belongs to. + optional string owner = 1; + // List of added DeviceSecurityProfiles. + repeated SecurityProfile added = 2; + // List of removed DeviceSecurityProfiles. + repeated SecurityProfile removed = 3; + // List of modified DeviceSecurityProfiles. + repeated Modified modified = 4; + } + + optional Header header = 1; + repeated DspDelta dsp_delta = 2; +} diff --git a/tools/source/published_devices_delta/video/widevine/protos/public/hash_algorithm.proto b/tools/source/published_devices_delta/video/widevine/protos/public/hash_algorithm.proto index a0a17ea..47ca34e 100644 --- a/tools/source/published_devices_delta/video/widevine/protos/public/hash_algorithm.proto +++ b/tools/source/published_devices_delta/video/widevine/protos/public/hash_algorithm.proto @@ -11,5 +11,6 @@ enum HashAlgorithmProto { HASH_ALGORITHM_UNSPECIFIED = 0; HASH_ALGORITHM_SHA_1 = 1; HASH_ALGORITHM_SHA_256 = 2; + HASH_ALGORITHM_SHA_384 = 3; } // LINT.ThenChange(//depot/google3/google/chrome/widevine/contentpartners/v1beta1/device_security_profiles.proto) diff --git a/tools/source/published_devices_delta/video/widevine/protos/public/provisioned_device_info.proto b/tools/source/published_devices_delta/video/widevine/protos/public/provisioned_device_info.proto index 8415bce..5f3183c 100644 --- a/tools/source/published_devices_delta/video/widevine/protos/public/provisioned_device_info.proto +++ b/tools/source/published_devices_delta/video/widevine/protos/public/provisioned_device_info.proto @@ -12,11 +12,12 @@ import "video/widevine/protos/public/device_common.proto"; option java_package = "com.google.video.widevine.protos"; option java_outer_classname = "ProvisionedDeviceInfoProto"; -// MOE:begin_strip + // See http://go/go-api-flag. -// MOE:end_strip + // Contains device model information for a provisioned device. +// Next Id: 12 message ProvisionedDeviceInfo { enum WvSecurityLevel { // Defined in Widevine Security Integration Guide for DASH on Android: @@ -75,4 +76,6 @@ message ProvisionedDeviceInfo { optional ProvisioningMethod provisioning_method = 9; // A list of ModelInfo using the same system_id. repeated DeviceModel model_info = 10; + // The platform that the device is using. + optional Platform platform = 11; } diff --git a/tools/source/published_devices_delta/video/widevine/protos/public/security_profile.proto b/tools/source/published_devices_delta/video/widevine/protos/public/security_profile.proto new file mode 100644 index 0000000..5a54c75 --- /dev/null +++ b/tools/source/published_devices_delta/video/widevine/protos/public/security_profile.proto @@ -0,0 +1,86 @@ +// Copyright 2019 Google LLC. All rights reserved. +// +// Definitions of the protocol buffer messages for security profile level. + +syntax = "proto2"; + +package video_widevine; + +import "video/widevine/protos/public/device_common.proto"; +import "video/widevine/protos/public/device_security_profile_data.proto"; + +option java_outer_classname = "SecurityProfileProtos"; +option java_package = "com.google.video.widevine.protos"; + + + // See http://go/go-api-flag. + + +message SecurityProfile { + // TODO(hali): Remove this enum and use enum defined in + // protos/internal/devcei_data.proto. + enum DeviceState { + DEVICE_STATE_UNKNOWN = 0; + IN_TESTING = 1; + RELEASED = 2; + DELETED = 3; + TEST_ONLY = 4; + REVOKED = 5; + PRE_RELEASE = 6; + } + + message ClientInfo { + message ProductInfo { + // The 'product_name' as specified in the + // ClientIdentification.names_values. Optional. + optional string product_name = 1; + // The 'build_info' as specified in the + // ClientIdentification.names_values. Optional. + optional string build_info = 2; + // The 'oem_crypto_security_patch_level' as specified in the + // ClientIdentification.names_values. Optional. + optional string oem_crypto_security_patch_level = 3; + // Widevine device platform. + optional string platform = 4; + } + // The 'device_name' as specified in the + // ClientIdentification.names_values. Optional. + optional string device_name = 1; + // Product fields that are specified in the + // ClientIdentification.names_values. + optional ProductInfo product_info = 2; + } + + message DrmInfo { + // Make/Model specified by the client. + optional DeviceModel request_model_info = 1; + // Widevine device system id. + optional uint32 system_id = 2; + // Output requirements + optional OutputRequirement output = 3; + // Security requirements + optional SecurityRequirement security = 4; + // Device status such as RELEASED or REVOKED. + optional DeviceState device_model_state = 5; + // ClientIdentification.names_values fields. + optional ClientInfo client_info = 6; + } + + // Required. Widevine security profile name. + optional string name = 1; + // Optional. Widevine security profile level. + optional SecurityProfileLevel level = 2; + // Minimum output requirements for this profile. + optional OutputRequirement min_output_requirements = 3; + // Minimum security requirements for this profile. + optional SecurityRequirement min_security_requirements = 4; + // Name of content owner who owns this security profile. + optional string owner = 5 [default = "Widevine"]; + // Special handling of devices to override the default capabilities of a + // device. + repeated DeviceException device_exceptions = 6; + // Optional. Control time indicates the timestamps when this profile is + // effective and when it is expired. For default DSPs provided by Widevine, + // they are always being active and never expiring. + optional ControlTime control_time = 7; +}