Entitlement rotation support
Updates also include: - Add APIs to query current Simulcrypt channel & stream status; - EMM format change (used only to carry fingerprinting and service blocking info); - Key fetcher example to use curl key fetcher.
This commit is contained in:
@@ -71,25 +71,26 @@ class SecurityProfileList {
|
||||
// contain single record. For custom DSP, it may contain multiple records
|
||||
// since active dsp and inactive dsp could share the same dsp_name under the
|
||||
// same owner.
|
||||
bool GetProfileByNameAndOwner(
|
||||
virtual bool GetProfileByNameAndOwner(
|
||||
const std::string& name, const std::string& owner,
|
||||
std::vector<SecurityProfile>* security_profiles) const;
|
||||
|
||||
// Populates |security_profiles| owned by the content owner.
|
||||
int GetProfilesByOwner(const std::string& owner,
|
||||
std::vector<SecurityProfile>* security_profiles) const;
|
||||
virtual int GetProfilesByOwner(
|
||||
const std::string& owner,
|
||||
std::vector<SecurityProfile>* security_profiles) const;
|
||||
|
||||
// Populates |owner_list| for security profiles. |is_default_dsp| boolean
|
||||
// indicates the owner_list for default dsp or custom dsp.
|
||||
int GetProfilesOwnerList(const bool is_default_dsp,
|
||||
std::vector<std::string>* owner_list) const;
|
||||
virtual int GetProfilesOwnerList(const bool is_default_dsp,
|
||||
std::vector<std::string>* owner_list) const;
|
||||
|
||||
// Return the device security capabilities. |drm_info| is populated with
|
||||
// data from |client_id| and |device_info|. |drm_info| must not be null and
|
||||
// is owned by the caller.
|
||||
bool GetDrmInfo(const ClientIdentification& client_id,
|
||||
const ProvisionedDeviceInfo& device_info,
|
||||
SecurityProfile::DrmInfo* drm_info) const;
|
||||
virtual bool GetDrmInfo(const ClientIdentification& client_id,
|
||||
const ProvisionedDeviceInfo& device_info,
|
||||
SecurityProfile::DrmInfo* drm_info) const;
|
||||
|
||||
// Return the number of profiles in the list.
|
||||
int NumProfiles() const;
|
||||
@@ -110,6 +111,12 @@ class SecurityProfileList {
|
||||
HashAlgorithm hash_algorithm, const std::string& signature,
|
||||
int* added_profile_num);
|
||||
|
||||
// Returns an instance of the Security profile list for default security
|
||||
// profiles. Default security profiles are owned by Widevine.
|
||||
// TODO (b/187073516): This singleton can be moved to the "Environment" class
|
||||
// as a non-static API.
|
||||
static SecurityProfileList* GetInstanceForDefaultSecurityProfiles();
|
||||
|
||||
protected:
|
||||
void ClearAllProfiles();
|
||||
|
||||
|
||||
@@ -151,11 +151,11 @@ const char kTestEmmgPrivateDataProvision[] = {
|
||||
'\x47', '\x40', '\x00', '\x10', '\x0a', '\x0d', '\x77', '\x69', '\x64',
|
||||
'\x65', '\x76', '\x69', '\x6e', '\x65', '\x5f', '\x74', '\x65', '\x73',
|
||||
'\x74', '\x12', '\x09', '\x43', '\x61', '\x73', '\x54', '\x73', '\x46',
|
||||
'\x61', '\x6b', '\x65', '\x1a', '\x10', '\x66', '\x61', '\x6b', '\x65',
|
||||
'\x4b', '\x65', '\x79', '\x49', '\x64', '\x31', '\x4b', '\x65', '\x79',
|
||||
'\x49', '\x64', '\x31', '\x1a', '\x10', '\x66', '\x61', '\x6b', '\x65',
|
||||
'\x4b', '\x65', '\x79', '\x49', '\x64', '\x32', '\x4b', '\x65', '\x79',
|
||||
'\x49', '\x64', '\x32', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x61', '\x6b', '\x65', '\x22', '\x0a', '\x66', '\x61', '\x6b', '\x65',
|
||||
'\x47', '\x72', '\x6f', '\x75', '\x70', '\x31', '\x22', '\x0c', '\x66',
|
||||
'\x61', '\x6b', '\x65', '\x47', '\x72', '\x6f', '\x75', '\x70', '\x49',
|
||||
'\x64', '\x32', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
'\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
|
||||
@@ -191,22 +191,22 @@ const char kTestEmmgEmmDataProvision[] = {
|
||||
// Start of the first TS packet (188 bytes).
|
||||
'\x47', '\x40', '\x00', '\x10', // TS packet header (4 bytes).
|
||||
'\x00', // Pointer field (1 byte).
|
||||
'\x82', '\x70', '\xbe', // Secyion header (3 bytes).
|
||||
'\x82', '\x70', '\xbc', // Section header (3 bytes).
|
||||
// Start of Widevine EMM.
|
||||
'\x01', '\x08', '\x00', '\x00', '\x00', '\x00', '\x5f', '\x3d', '\xc1',
|
||||
'\xfb', '\x00', '\x6b', '\x0a', '\x14', '\x0a', '\x03', '\x43', '\x48',
|
||||
'\x31', '\x0a', '\x03', '\x43', '\x48', '\x32', '\x12', '\x08', '\x63',
|
||||
'\x6f', '\x6e', '\x74', '\x72', '\x6f', '\x6c', '\x73', '\x0a', '\x16',
|
||||
'\x0a', '\x03', '\x43', '\x48', '\x33', '\x12', '\x0f', '\x61', '\x6e',
|
||||
'\x6f', '\x74', '\x68', '\x65', '\x72', '\x20', '\x63', '\x6f', '\x6e',
|
||||
'\x74', '\x72', '\x6f', '\x6c', '\x12', '\x18', '\x0a', '\x03', '\x43',
|
||||
'\x48', '\x31', '\x0a', '\x03', '\x43', '\x48', '\x32', '\x12', '\x06',
|
||||
'\x47', '\x72', '\x6f', '\x75', '\x70', '\x31', '\x20', '\xd2', '\x85',
|
||||
'\xd8', '\xcc', '\x04', '\x12', '\x21', '\x0a', '\x03', '\x43', '\x48',
|
||||
'\x33', '\x12', '\x06', '\x47', '\x72', '\x6f', '\x75', '\x70', '\x32',
|
||||
'\x12', '\x06', '\x47', '\x72', '\x6f', '\x75', '\x70', '\x33', '\x18',
|
||||
'\xd2', '\x85', '\xd8', '\xcc', '\x04', '\x20', '\xd3', '\x85', '\xd8',
|
||||
'\xcc', '\x04', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78',
|
||||
'\x0a', '\x71', '\x0a', '\x14', '\x0a', '\x03', '\x43', '\x48', '\x31',
|
||||
'\x0a', '\x03', '\x43', '\x48', '\x32', '\x12', '\x08', '\x63', '\x6f',
|
||||
'\x6e', '\x74', '\x72', '\x6f', '\x6c', '\x73', '\x0a', '\x16', '\x0a',
|
||||
'\x03', '\x43', '\x48', '\x33', '\x12', '\x0f', '\x61', '\x6e', '\x6f',
|
||||
'\x74', '\x68', '\x65', '\x72', '\x20', '\x63', '\x6f', '\x6e', '\x74',
|
||||
'\x72', '\x6f', '\x6c', '\x12', '\x18', '\x0a', '\x03', '\x43', '\x48',
|
||||
'\x31', '\x0a', '\x03', '\x43', '\x48', '\x32', '\x12', '\x06', '\x47',
|
||||
'\x72', '\x6f', '\x75', '\x70', '\x31', '\x20', '\xd2', '\x85', '\xd8',
|
||||
'\xcc', '\x04', '\x12', '\x21', '\x0a', '\x03', '\x43', '\x48', '\x33',
|
||||
'\x12', '\x06', '\x47', '\x72', '\x6f', '\x75', '\x70', '\x32', '\x12',
|
||||
'\x06', '\x47', '\x72', '\x6f', '\x75', '\x70', '\x33', '\x18', '\xd2',
|
||||
'\x85', '\xd8', '\xcc', '\x04', '\x20', '\xd3', '\x85', '\xd8', '\xcc',
|
||||
'\x04', '\x18', '\xfb', '\x83', '\xf7', '\xf9', '\x05', '\x12', '\x47',
|
||||
'\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78',
|
||||
'\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78',
|
||||
'\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78',
|
||||
'\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78',
|
||||
@@ -216,8 +216,8 @@ const char kTestEmmgEmmDataProvision[] = {
|
||||
// Start of the second TS packet (188 bytes).
|
||||
'\x47', '\x00', '\x00', '\x11', // TS packet header (4 bytes).
|
||||
// Continued Widevine EMM.
|
||||
'\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78',
|
||||
'\x78', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
|
||||
'\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\x78', '\xff',
|
||||
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
|
||||
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
|
||||
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
|
||||
'\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff', '\xff',
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -12,8 +12,11 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/status.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_curl_key_fetcher.h"
|
||||
#include "media_cas_packager_sdk/public/wv_cas_key_fetcher.h"
|
||||
|
||||
const char kCasEncryptionServerUrl[] =
|
||||
"https://license.uat.widevine.com/cas/getcontentkey/widevine_test";
|
||||
const char kContentId[] = "21140844";
|
||||
const char kContentProvider[] = "widevine";
|
||||
const char kTrackType[] = "SD";
|
||||
@@ -22,36 +25,16 @@ const char kSigningProvider[] = "widevine_test";
|
||||
const char kSingingKey[] =
|
||||
"1ae8ccd0e7985cc0b6203a55855a1034afc252980e970ca90e5202689f947ab9";
|
||||
const char kSingingIv[] = "d58ce954203b7c9a9a9d467f59839249";
|
||||
const char kHttpResponse[] =
|
||||
"{\"response\":"
|
||||
"\"eyJzdGF0dXMiOiJPSyIsImNvbnRlbnRfaWQiOiJNakV4TkRBNE5EUT0iLCJlbnRpdGxlbWVu"
|
||||
"dF9rZXlzIjpbeyJrZXlfaWQiOiJNUGFndXhNb1hNNkUxUzhEOUF3RkNBPT0iLCJrZXkiOiJoZ1"
|
||||
"JycmdqeUg4NjQycjY3VHd0OHJ1cU5MUGNMRmtKcWRVSUROdm5GZDBNPSIsInRyYWNrX3R5cGUi"
|
||||
"OiJTRCIsImtleV9zbG90IjoiU0lOR0xFIn1dfQ==\"}";
|
||||
|
||||
using widevine::Status;
|
||||
using widevine::cas::EntitlementKeyInfo;
|
||||
using widevine::cas::EntitlementRequestParams;
|
||||
using widevine::cas::WvCasKeyFetcher;
|
||||
|
||||
class ExampleKeyFetcher : public WvCasKeyFetcher {
|
||||
public:
|
||||
ExampleKeyFetcher(const std::string& signing_provider,
|
||||
const std::string& signing_key,
|
||||
const std::string& signing_iv)
|
||||
: WvCasKeyFetcher(signing_provider, signing_key, signing_iv) {}
|
||||
|
||||
// An example that always returns the same response.
|
||||
Status MakeHttpRequest(const std::string& signed_request_json,
|
||||
std::string* http_response_json) const override {
|
||||
*http_response_json = kHttpResponse;
|
||||
return widevine::OkStatus();
|
||||
}
|
||||
};
|
||||
using widevine::cas::WvCasCurlKeyFetcher;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// Initialize key fetcher.
|
||||
ExampleKeyFetcher key_fetcher(kSigningProvider, kSingingKey, kSingingIv);
|
||||
// Initialize key fetcher with server url.
|
||||
WvCasCurlKeyFetcher key_fetcher(kCasEncryptionServerUrl, kSigningProvider,
|
||||
kSingingKey, kSingingIv);
|
||||
|
||||
// Create request string.
|
||||
std::string request_str;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -50,9 +50,7 @@ class WvCasCaDescriptor {
|
||||
// |ca_pid| the 13-bit PID of the ECMs
|
||||
// |provider| provider name, put in private data for client to construct pssh
|
||||
// |content_id| content ID, put in private data for client to construct pssh
|
||||
// |entitlement_key_ids| entitlement key ids, put in private data for client
|
||||
// to select entitlement keys from single fat license. This field is only used
|
||||
// when client uses single fat license.
|
||||
// |group_ids| the groups ids this channel belongs to. Optional.
|
||||
// |serialized_ca_desc| a std::string object to receive the encoded descriptor.
|
||||
//
|
||||
// Notes:
|
||||
@@ -60,11 +58,11 @@ class WvCasCaDescriptor {
|
||||
// section (for an EMM stream) or into a TS Program Map Table section (for an
|
||||
// ECM stream). The descriptor will be 6 bytes plus any bytes added as
|
||||
// (user-defined) private data.
|
||||
virtual Status GenerateCaDescriptor(
|
||||
uint16_t ca_pid, const std::string& provider,
|
||||
const std::string& content_id,
|
||||
const std::vector<std::string>& entitlement_key_ids,
|
||||
std::string* serialized_ca_desc) const;
|
||||
virtual Status GenerateCaDescriptor(uint16_t ca_pid,
|
||||
const std::string& provider,
|
||||
const std::string& content_id,
|
||||
const std::vector<std::string>& group_ids,
|
||||
std::string* serialized_ca_desc) const;
|
||||
|
||||
// Return the base size (before private data is added) of the CA
|
||||
// descriptor. The user can call this to plan the layout of the Table section
|
||||
@@ -74,7 +72,7 @@ class WvCasCaDescriptor {
|
||||
// Return private data in the CA descriptor.
|
||||
virtual std::string GeneratePrivateData(
|
||||
const std::string& provider, const std::string& content_id,
|
||||
const std::vector<std::string>& entitlement_key_ids) const;
|
||||
const std::vector<std::string>& group_ids) const;
|
||||
};
|
||||
|
||||
} // namespace cas
|
||||
|
||||
@@ -61,6 +61,7 @@ struct WvCasEcmParameters {
|
||||
uint16_t cas_id = 0x4AD4;
|
||||
EcmVersion ecm_version = EcmVersion::kV2;
|
||||
std::string ecc_private_signing_key;
|
||||
EntitlementKeyRotationInfo entitlement_rotation;
|
||||
};
|
||||
|
||||
// Class for generating Widevine CAS ECMs.
|
||||
@@ -94,6 +95,20 @@ class WvCasEcm {
|
||||
virtual void SetServiceBlocking(
|
||||
const EcmServiceBlockingParams* service_blocking);
|
||||
|
||||
// Sets the current value of the entitlement key rotation window left to
|
||||
// |entitlement_rotation_window_left|. The value will be used in
|
||||
// GenerateEcm/GenerateSingleKeyEcm if entitlement rotation is enabled as
|
||||
// specified at initializing, and is automatically decreased by 1 on each call
|
||||
// to GenerateEcm/GenerateSingleKeyEcm, until it reaches 1.
|
||||
virtual Status SetEntitlementRotationWindowLeft(
|
||||
uint32_t entitlement_rotation_window_left);
|
||||
|
||||
// Gets the current value of the entitlement key rotation window left. The
|
||||
// value is used in GenerateEcm/GenerateSingleKeyEcm, and is automatically
|
||||
// decreased by 1 on each call to GenerateEcm/GenerateSingleKeyEcm, until it
|
||||
// reaches 1.
|
||||
virtual uint32_t GetEntitlementRotationWindowLeft() const;
|
||||
|
||||
// Constructs a Widevine ECM using the provided key info.
|
||||
// Args:
|
||||
// |even_key| information for even key to be encoded into ECM.
|
||||
@@ -106,6 +121,9 @@ class WvCasEcm {
|
||||
// consistent with the initialized settings.
|
||||
// The even_key and odd_key will be wrapped using the appropriate
|
||||
// entitlement key.
|
||||
// If entitlement rotation is enabled as specified at initializing, the
|
||||
// entitlement key rotation window left value will be automatically decreased
|
||||
// by 1, until it reaches 1.
|
||||
virtual Status GenerateEcm(const WvCasContentKeyInfo& even_key,
|
||||
const WvCasContentKeyInfo& odd_key,
|
||||
const std::string& track_type,
|
||||
@@ -122,6 +140,9 @@ class WvCasEcm {
|
||||
// |serialized_ecm| caller-supplied std::string pointer to receive the ECM.
|
||||
// The |key| contents (specifically IV sizes) must be consistent
|
||||
// with the initialized settings.
|
||||
// If entitlement rotation is enabled as specified at initializing, the
|
||||
// entitlement key rotation window left value will be automatically decreased
|
||||
// by 1, until it reaches 1.
|
||||
virtual Status GenerateSingleKeyEcm(const WvCasContentKeyInfo& key,
|
||||
const std::string& track_type,
|
||||
const std::vector<std::string>& group_ids,
|
||||
|
||||
@@ -74,6 +74,12 @@ class WvCasEcmgClientHandler {
|
||||
size_t& response_length,
|
||||
size_t& processed_request_length);
|
||||
|
||||
// Retrieves the current channel status;
|
||||
WvEcmgChannelStatus GetChannelStatus() const;
|
||||
|
||||
// Retrieves the status of all open streams;
|
||||
std::vector<WvEcmgStreamStatus> GetStreamStatus() const;
|
||||
|
||||
protected:
|
||||
// For unit test only.
|
||||
explicit WvCasEcmgClientHandler(
|
||||
|
||||
@@ -29,12 +29,17 @@ namespace cas {
|
||||
// |group_id| optional field indicates if this is a key used for a group of
|
||||
// contents. If this field is not empty, entitlement key would be generated
|
||||
// for the group instead of the single content.
|
||||
// |entitlement_rotation_enabled| Whether entitlement rotation is enabled.
|
||||
// |entitlement_period_index| The requested entitlement period index if
|
||||
// entitlement rotation is enabled.
|
||||
struct EntitlementRequestParams {
|
||||
std::string content_id;
|
||||
std::string content_provider;
|
||||
std::vector<std::string> track_types;
|
||||
bool key_rotation;
|
||||
std::string group_id;
|
||||
bool entitlement_rotation_enabled = false;
|
||||
uint32_t entitlement_period_index;
|
||||
};
|
||||
|
||||
// WV CAS KeyFetcher. Performs the communication with the Widevine License
|
||||
|
||||
@@ -86,6 +86,23 @@ struct EntitlementKeyInfo {
|
||||
|
||||
enum class EcmVersion : int { kV2 = 0, kV3 = 1 };
|
||||
|
||||
// Used for generating ECMs if entitlement key rotation is enabled.
|
||||
struct EntitlementKeyRotationInfo {
|
||||
// Indicates if entitlement key rotation feature is used. If enabled,
|
||||
// |period_index| and |rotation_window_left| must also be specified and will
|
||||
// be carried in generated ECMs.
|
||||
bool rotation_enabled = false;
|
||||
// The current entitlement key period index. Only used if |rotation_enabled|
|
||||
// is set to true.
|
||||
uint32_t period_index = 0;
|
||||
// It tells the client how many crypto periods (unique ECMs) left (including
|
||||
// this ECM) that a new license request must be made. Must be a positive
|
||||
// value. Only used if |rotation_enabled| is set to true.
|
||||
// The value will automatically decrease by 1 on each call to GenerateEcm or
|
||||
// GenerateSingleKeyEcm, until the value reaches 1.
|
||||
uint32_t rotation_window_left = 0;
|
||||
};
|
||||
|
||||
// A struct that captures the Simulcrypt ECMG configurations. Most fields are
|
||||
// Simulcrypt standard fields (See ETSI TS 103 197 V1.5.1 (2008-10)
|
||||
// Section 5.3).
|
||||
@@ -177,6 +194,8 @@ struct EcmgCustomParameters {
|
||||
// when the ECM is received, and stops util the device is no longer in
|
||||
// |device_groups|.
|
||||
std::vector<std::string> service_blocking_groups;
|
||||
// Used if entitlement key rotation is enabled.
|
||||
EntitlementKeyRotationInfo entitlement_rotation;
|
||||
};
|
||||
|
||||
// A custom access control processing function used by ECMG to get information
|
||||
@@ -233,6 +252,33 @@ typedef std::function<EcmServiceBlockingParams(uint16_t channel_id,
|
||||
uint16_t stream_id)>
|
||||
ServiceBlockingSettingFunc;
|
||||
|
||||
struct WvEcmgChannelStatus {
|
||||
// If the channel has been set up or not.
|
||||
bool has_setup;
|
||||
uint16_t channel_id;
|
||||
uint16_t ca_system_id;
|
||||
int num_of_open_streams;
|
||||
EcmVersion ecm_version;
|
||||
// Below are Simulcrypt parameters values (see ETSI TS 103 197 V1.5.1
|
||||
// (2008-10) Section 5.3).
|
||||
uint8_t section_tspkt_flag;
|
||||
uint16_t delay_start;
|
||||
uint16_t delay_stop;
|
||||
uint16_t ecm_pep_period;
|
||||
uint16_t max_streams;
|
||||
uint16_t min_cp_duration;
|
||||
uint8_t lead_cw;
|
||||
uint8_t cw_per_message;
|
||||
uint16_t max_comp_time;
|
||||
};
|
||||
|
||||
struct WvEcmgStreamStatus {
|
||||
uint16_t stream_id;
|
||||
uint16_t ecm_id;
|
||||
CryptoMode crypto_mode;
|
||||
int age_restriction;
|
||||
};
|
||||
|
||||
struct WvCasEncryptionRequest {
|
||||
std::string content_id;
|
||||
std::string provider;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -19,9 +19,8 @@ message CaDescriptorPrivateData {
|
||||
// Content ID.
|
||||
optional bytes content_id = 2;
|
||||
|
||||
// Entitlement key IDs for current content per track. Each track will allow up
|
||||
// to 2 entitlement key ids (odd and even entitlement keys).
|
||||
repeated bytes entitlement_key_ids = 3;
|
||||
// Deprecated.
|
||||
repeated bytes deprecated_entitlement_key_ids = 3;
|
||||
|
||||
// The groups ids this channel belongs to.
|
||||
repeated bytes group_ids = 4;
|
||||
@@ -78,6 +77,17 @@ message EcmMetaData {
|
||||
// Optional. The minimum age required to watch the content. The value
|
||||
// represents actual age, with 0 means no restriction.
|
||||
optional uint32 age_restriction = 2 [default = 0];
|
||||
// If specified, it means entitlement key rotation is enabled. The value will
|
||||
// be included in the license request. The server is expected to return
|
||||
// entitlement keys accordingly (e.g., keys for |entitlement_period_index| and
|
||||
// |entitlement_period_index| + 1).
|
||||
optional uint32 entitlement_period_index = 3;
|
||||
// Used only if entitlement key rotation is enabled. This parameter controls
|
||||
// the probability of requesting a new license by clients upon receiving this
|
||||
// ECM. The purpose is to spread out requests to avoid request storms. A
|
||||
// client will request a new license with possibility = 1 /
|
||||
// |entitlement_rotation_window_left|.
|
||||
optional uint32 entitlement_rotation_window_left = 4 [default = 1];
|
||||
}
|
||||
|
||||
message EcmKeyData {
|
||||
|
||||
Reference in New Issue
Block a user