Release v1.0.0
This commit is contained in:
parent
92e537510a
commit
c2838dec0b
|
@ -157,4 +157,4 @@ cython_debug/
|
|||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
.idea/
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
# KeyDive: Widevine L3 Extractor for Android
|
||||
|
||||
KeyDive is a sophisticated Python script designed for the precise extraction of Widevine L3 DRM (Digital Rights Management) keys from Android devices. This tool leverages the capabilities of the Widevine CDM (Content Decryption Module) to facilitate the recovery of DRM keys, enabling a deeper understanding and analysis of the Widevine L3 DRM implementation across various Android SDK versions.
|
||||
|
||||
> **Warning**
|
||||
>
|
||||
> Support for Android 14+ (SDK > 33) is currently under development. Some features may not function as expected on these newer versions.
|
||||
|
||||
## Features
|
||||
|
||||
- Automated extraction of Widevine L3 DRM keys.
|
||||
- Compatibility with a wide range of Android versions (SDK > 22), ensuring broad applicability.
|
||||
- Seamless extraction process, yielding essential DRM components such as the `client_id.bin` for device identification and the `private_key.pem` for the RSA private key.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you begin, ensure you have the following prerequisites in place:
|
||||
|
||||
1. **ADB (Android Debug Bridge):** Make sure to install [ADB](https://developer.android.com/studio/command-line/adb) and include it in your system's PATH environment variable for easy command-line access.
|
||||
2. **Frida-Server:** Install `frida-server` on your target Android device. This requires root access on the device. For installation instructions and downloads, visit the [official Frida documentation](https://frida.re/docs/installation/).
|
||||
3. **Python Requirements:** KeyDive requires specific Python libraries to function correctly. Install them using the provided `requirements.txt` file:
|
||||
```shell
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
Follow these steps to set up KeyDive:
|
||||
|
||||
1. Ensure all prerequisites are met (see above).
|
||||
2. Clone this repository to your local machine.
|
||||
3. Navigate to the cloned directory and install the required Python dependencies as mentioned.
|
||||
|
||||
## Usage
|
||||
|
||||
To use KeyDive, follow these steps:
|
||||
|
||||
1. Launch the KeyDive script.
|
||||
2. Play a DRM-protected video on the target device.
|
||||
3. The script will automatically extract the Widevine L3 keys, saving them in the following format:
|
||||
- `client_id.bin` - Contains device identification information.
|
||||
- `private_key.pem` - Contains the RSA private key.
|
||||
|
||||
### Command-Line Options
|
||||
|
||||
```sh
|
||||
usage: keydive.py [-h] [--device DEVICE]
|
||||
|
||||
Extract Widevine L3 keys from an Android device.
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--device DEVICE Target Android device ID.
|
||||
|
||||
```
|
||||
|
||||
## Temporary Disabling L1 for L3 Extraction
|
||||
|
||||
Some manufacturers (e.g., Xiaomi) allow the use of L1 keyboxes even after unlocking the bootloader. In such cases, it's necessary to install a Magisk module called [liboemcrypto-disabler](https://github.com/Magisk-Modules-Repo/liboemcryptodisabler) to temporarily disable L1, thereby facilitating L3 key extraction.
|
||||
|
||||
## Credits
|
||||
|
||||
Special thanks to the original developers and contributors who have made KeyDive possible. This tool is the culmination of collaborative efforts, research, and a deep understanding of DRM technologies.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
KeyDive is intended for educational and research purposes only. The use of this tool in unauthorized testing of protected content is strictly prohibited. Please ensure you have permission before proceeding with DRM key extraction.
|
||||
|
||||
---
|
||||
|
||||
By using KeyDive, you acknowledge and agree to the terms of use and disclaimer stated above.
|
|
@ -0,0 +1,35 @@
|
|||
# Packages
|
||||
|
||||
This document provides an overview of the external libraries, tools, and applications utilized within the KeyDive project. Each package plays a crucial role in enabling the project to efficiently extract Widevine L3 keys from Android devices for educational and research purposes.
|
||||
|
||||
## Tools and Libraries
|
||||
|
||||
### [rootAVD](https://gitlab.com/newbit/rootAVD)
|
||||
A tool designed to root Android Virtual Devices (AVDs). It enables users to gain superuser privileges on their AVDs, essential for accessing and modifying system-level files and settings that are otherwise restricted.
|
||||
|
||||
### [DRM Info](https://apkcombo.com/drm-info/com.androidfung.drminfo/download/phone-1.1.9.220313-apk)
|
||||
An Android application providing detailed information about the device's Digital Rights Management (DRM) modules, including Widevine. Useful for verifying the DRM support level (L1, L2, L3) on the target device.
|
||||
|
||||
### [Root Explorer](https://apkcombo.com/root-explorer/com.speedsoftware.rootexplorer/)
|
||||
A file manager for root users, offering access to the entire Android file system, including typically hidden or inaccessible data folders.
|
||||
|
||||
### [Firefox](https://apkcombo.com/fr/firefox/org.mozilla.firefox/)
|
||||
A free and open-source web browser for Android, used for downloading files, testing DRM content playback, and other web-related tasks during research.
|
||||
|
||||
### [liboemcrypto Disabler](https://github.com/Magisk-Modules-Repo/liboemcryptodisabler)
|
||||
A Magisk module that disables the OEMCrypto service, responsible for L1 DRM protection, forcing devices to fallback to L3 protection and enabling the extraction of L3 keys.
|
||||
|
||||
### [MagiskFrida](https://github.com/ViRb3/magisk-frida)
|
||||
Allows Frida, a dynamic instrumentation toolkit, to run as a Magisk module, ideal for environments where adb access is limited or not possible.
|
||||
|
||||
### [Frida](https://github.com/frida/frida/releases)
|
||||
A dynamic code instrumentation toolkit for injecting JavaScript or your own library into native apps on Android and other platforms.
|
||||
|
||||
### [adb (Android Debug Bridge)](https://developer.android.com/tools/adb)
|
||||
A command-line tool for communicating with a device, facilitating actions such as app installation and debugging, and providing access to a Unix shell for running various commands.
|
||||
|
||||
### [Ghidra](https://github.com/NationalSecurityAgency/ghidra)
|
||||
A software reverse engineering (SRE) framework developed by the National Security Agency (NSA) that helps analyze malicious code and malware, and understand their functionality. Ghidra is essential for decompiling and analyzing the binaries and libraries involved in the DRM mechanisms, offering insights into how they operate and can be interacted with.
|
||||
|
||||
## Usage
|
||||
The combination of these tools provides a comprehensive toolkit for DRM research, allowing for the exploration of digital content protection mechanisms on Android devices. Each tool has been selected for its ability to contribute to the setup, execution, or support of the KeyDive project, enabling detailed analysis and extraction of digital rights management keys.
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
alias ls='ls --color=auto'
|
||||
alias grep='grep --color=auto'
|
||||
alias fgrep='fgrep --color=auto'
|
||||
alias egrep='egrep --color=auto'
|
||||
alias logcat='logcat -v color'
|
||||
|
||||
alias ll='ls -alF'
|
||||
alias la='ls -A'
|
||||
alias l='ls -CF'
|
||||
alias ipa='ip -c a'
|
||||
alias rm='rm -rf'
|
||||
|
||||
tree() {
|
||||
path=${1:-.}
|
||||
find ${path} -print | sort | sed 's;[^/]*/;|---;g;s;---|; |;g'
|
||||
}
|
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,4 @@
|
|||
from .cdm import *
|
||||
from .vendor import *
|
||||
|
||||
__version__ = '1.0.0'
|
|
@ -0,0 +1,149 @@
|
|||
import logging
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import frida
|
||||
from _frida import Process
|
||||
from frida.core import Device, Session, Script, RPCException
|
||||
from Cryptodome.PublicKey import RSA
|
||||
|
||||
from extractor.license_protocol_pb2 import SignedMessage, LicenseRequest, ClientIdentification, DrmCertificate, SignedDrmCertificate
|
||||
from extractor.vendor import Vendor
|
||||
|
||||
SCRIPT_PATH = Path(__file__).parent / 'script.js'
|
||||
|
||||
|
||||
class Cdm:
|
||||
"""
|
||||
Manages the capture and processing of DRM keys from a specified device using Frida to inject custom hooks.
|
||||
"""
|
||||
|
||||
def __init__(self, device: str = None):
|
||||
self.logger = logging.getLogger('Cdm')
|
||||
self.running = True
|
||||
self.keys = {}
|
||||
self.device: Device = frida.get_device(id=device, timeout=5) if device else frida.get_usb_device(timeout=5)
|
||||
self.logger.info('Device: %s (%s)', self.device.name, self.device.id)
|
||||
|
||||
# Fetch and log device properties
|
||||
self.properties = self._fetch_device_properties()
|
||||
self.sdk_api = self.properties['ro.build.version.sdk']
|
||||
self.logger.info('SDK API: %s', self.sdk_api)
|
||||
self.logger.info('ABI CPU: %s', self.properties['ro.product.cpu.abi'])
|
||||
|
||||
# Determine vendor based on SDK API
|
||||
self.vendor = Vendor.from_sdk_api(self.sdk_api)
|
||||
self.script: str = self._prepare_hook_script()
|
||||
|
||||
def _fetch_device_properties(self) -> dict:
|
||||
"""
|
||||
Retrieves system properties from the connected device using ADB shell commands.
|
||||
"""
|
||||
# https://source.android.com/docs/core/architecture/configuration/add-system-properties?#shell-commands
|
||||
properties = {}
|
||||
for line in subprocess.getoutput(f'adb -s "{self.device.id}" shell getprop').splitlines():
|
||||
match = re.match(r'\[(.*?)\]: \[(.*?)\]', line)
|
||||
if match:
|
||||
key, value = match.groups()
|
||||
# Attempt to cast numeric and boolean values to appropriate types
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
if value.lower() in ('true', 'false'):
|
||||
value = value.lower() == 'true'
|
||||
properties[key] = value
|
||||
return properties
|
||||
|
||||
def _prepare_hook_script(self) -> str:
|
||||
"""
|
||||
Prepares and returns the hook script with the SDK API version replaced.
|
||||
"""
|
||||
script_content = SCRIPT_PATH.read_text(encoding='utf-8')
|
||||
return script_content.replace("'${SDK_API}'", str(self.sdk_api))
|
||||
|
||||
def _process_message(self, message: dict, data: bytes) -> None:
|
||||
"""
|
||||
Handles messages received from the Frida script.
|
||||
"""
|
||||
logger = logging.getLogger('Script')
|
||||
level = message.get('payload')
|
||||
|
||||
if isinstance(level, int):
|
||||
# Process logging messages from Frida script
|
||||
logger.log(level=level, msg=data.decode('utf-8'))
|
||||
if level in (logging.FATAL, logging.CRITICAL):
|
||||
self.running = False
|
||||
elif level == 'device_info':
|
||||
if data:
|
||||
self._extract_device_info(data)
|
||||
else:
|
||||
logger.critical('No data for device info, invalid argument position')
|
||||
self.running = False
|
||||
elif level == 'private_key':
|
||||
self._extract_private_key(data)
|
||||
|
||||
def _extract_private_key(self, data: bytes) -> None:
|
||||
"""
|
||||
Extracts and stores the private key from the provided data.
|
||||
"""
|
||||
key = RSA.import_key(data)
|
||||
key_id = key.n
|
||||
if key_id not in self.keys:
|
||||
self.keys[key_id] = key
|
||||
self.logger.debug('Retrieved key: \n\n%s\n', key.exportKey('PEM').decode('utf-8'))
|
||||
|
||||
def _extract_device_info(self, data: bytes) -> None:
|
||||
"""
|
||||
Extracts device information and associated private keys, storing them to disk.
|
||||
"""
|
||||
# https://github.com/devine-dl/pywidevine
|
||||
signed_message = SignedMessage()
|
||||
signed_message.ParseFromString(data)
|
||||
|
||||
license_request = LicenseRequest()
|
||||
license_request.ParseFromString(signed_message.msg)
|
||||
|
||||
client_id: ClientIdentification = license_request.client_id
|
||||
|
||||
signed_drm_certificate = SignedDrmCertificate()
|
||||
drm_certificate = DrmCertificate()
|
||||
|
||||
signed_drm_certificate.ParseFromString(client_id.token)
|
||||
drm_certificate.ParseFromString(signed_drm_certificate.drm_certificate)
|
||||
|
||||
public_key = drm_certificate.public_key
|
||||
key = RSA.importKey(public_key)
|
||||
key_id = key.n
|
||||
|
||||
private_key = self.keys.get(key_id)
|
||||
if private_key:
|
||||
path = Path() / 'device' / self.device.name / 'private_keys' / str(drm_certificate.system_id) / str(key_id)[:10]
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
path_client_id = path / 'client_id.bin'
|
||||
path_private_key = path / 'private_key.pem'
|
||||
|
||||
path_client_id.write_bytes(data=client_id.SerializeToString())
|
||||
path_private_key.write_bytes(data=private_key.exportKey('PEM'))
|
||||
|
||||
self.logger.info('Dumped client ID: %s', path_client_id)
|
||||
self.logger.info('Dumped private key: %s', path_private_key)
|
||||
self.running = False
|
||||
else:
|
||||
self.logger.warning('Failed to intercept the private key')
|
||||
|
||||
def hook_process(self, process: Process) -> bool:
|
||||
"""
|
||||
Hooks into the specified process to intercept DRM keys.
|
||||
"""
|
||||
session: Session = self.device.attach(process.name)
|
||||
script: Script = session.create_script(self.script)
|
||||
script.on('message', self._process_message)
|
||||
script.load()
|
||||
|
||||
try:
|
||||
library_info = script.exports_sync.getlibrary(self.vendor.library)
|
||||
self.logger.info('Library: %s (%s)', library_info['name'], library_info['path'])
|
||||
return script.exports_sync.hooklibrary(library_info['name'])
|
||||
except RPCException:
|
||||
return False
|
|
@ -0,0 +1,752 @@
|
|||
syntax = "proto2";
|
||||
|
||||
package pywidevine_license_protocol;
|
||||
|
||||
// need this if we are using libprotobuf-cpp-2.3.0-lite
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
option java_package = "com.rlaphoenix.pywidevine.protos";
|
||||
|
||||
enum LicenseType {
|
||||
STREAMING = 1;
|
||||
OFFLINE = 2;
|
||||
// License type decision is left to provider.
|
||||
AUTOMATIC = 3;
|
||||
}
|
||||
|
||||
enum PlatformVerificationStatus {
|
||||
// The platform is not verified.
|
||||
PLATFORM_UNVERIFIED = 0;
|
||||
// Tampering detected on the platform.
|
||||
PLATFORM_TAMPERED = 1;
|
||||
// The platform has been verified by means of software.
|
||||
PLATFORM_SOFTWARE_VERIFIED = 2;
|
||||
// The platform has been verified by means of hardware (e.g. secure boot).
|
||||
PLATFORM_HARDWARE_VERIFIED = 3;
|
||||
// Platform verification was not performed.
|
||||
PLATFORM_NO_VERIFICATION = 4;
|
||||
// Platform and secure storage capability have been verified by means of
|
||||
// software.
|
||||
PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED = 5;
|
||||
}
|
||||
|
||||
// LicenseIdentification is propagated from LicenseRequest to License,
|
||||
// incrementing version with each iteration.
|
||||
message LicenseIdentification {
|
||||
optional bytes request_id = 1;
|
||||
optional bytes session_id = 2;
|
||||
optional bytes purchase_id = 3;
|
||||
optional LicenseType type = 4;
|
||||
optional int32 version = 5;
|
||||
optional bytes provider_session_token = 6;
|
||||
}
|
||||
|
||||
message License {
|
||||
message Policy {
|
||||
// Indicates that playback of the content is allowed.
|
||||
optional bool can_play = 1 [default = false];
|
||||
|
||||
// Indicates that the license may be persisted to non-volatile
|
||||
// storage for offline use.
|
||||
optional bool can_persist = 2 [default = false];
|
||||
|
||||
// Indicates that renewal of this license is allowed.
|
||||
optional bool can_renew = 3 [default = false];
|
||||
|
||||
// For the |*duration*| fields, playback must halt when
|
||||
// license_start_time (seconds since the epoch (UTC)) +
|
||||
// license_duration_seconds is exceeded. A value of 0
|
||||
// indicates that there is no limit to the duration.
|
||||
|
||||
// Indicates the rental window.
|
||||
optional int64 rental_duration_seconds = 4 [default = 0];
|
||||
|
||||
// Indicates the viewing window, once playback has begun.
|
||||
optional int64 playback_duration_seconds = 5 [default = 0];
|
||||
|
||||
// Indicates the time window for this specific license.
|
||||
optional int64 license_duration_seconds = 6 [default = 0];
|
||||
|
||||
// The |renewal*| fields only apply if |can_renew| is true.
|
||||
|
||||
// The window of time, in which playback is allowed to continue while
|
||||
// renewal is attempted, yet unsuccessful due to backend problems with
|
||||
// the license server.
|
||||
optional int64 renewal_recovery_duration_seconds = 7 [default = 0];
|
||||
|
||||
// All renewal requests for this license shall be directed to the
|
||||
// specified URL.
|
||||
optional string renewal_server_url = 8;
|
||||
|
||||
// How many seconds after license_start_time, before renewal is first
|
||||
// attempted.
|
||||
optional int64 renewal_delay_seconds = 9 [default = 0];
|
||||
|
||||
// Specifies the delay in seconds between subsequent license
|
||||
// renewal requests, in case of failure.
|
||||
optional int64 renewal_retry_interval_seconds = 10 [default = 0];
|
||||
|
||||
// Indicates that the license shall be sent for renewal when usage is
|
||||
// started.
|
||||
optional bool renew_with_usage = 11 [default = false];
|
||||
|
||||
// Indicates to client that license renewal and release requests ought to
|
||||
// include ClientIdentification (client_id).
|
||||
optional bool always_include_client_id = 12 [default = false];
|
||||
|
||||
// Duration of grace period before playback_duration_seconds (short window)
|
||||
// goes into effect. Optional.
|
||||
optional int64 play_start_grace_period_seconds = 13 [default = 0];
|
||||
|
||||
// Enables "soft enforcement" of playback_duration_seconds, letting the user
|
||||
// finish playback even if short window expires. Optional.
|
||||
optional bool soft_enforce_playback_duration = 14 [default = false];
|
||||
|
||||
// Enables "soft enforcement" of rental_duration_seconds. Initial playback
|
||||
// must always start before rental duration expires. In order to allow
|
||||
// subsequent playbacks to start after the rental duration expires,
|
||||
// soft_enforce_playback_duration must be true. Otherwise, subsequent
|
||||
// playbacks will not be allowed once rental duration expires. Optional.
|
||||
optional bool soft_enforce_rental_duration = 15 [default = true];
|
||||
}
|
||||
|
||||
message KeyContainer {
|
||||
enum KeyType {
|
||||
SIGNING = 1; // Exactly one key of this type must appear.
|
||||
CONTENT = 2; // Content key.
|
||||
KEY_CONTROL = 3; // Key control block for license renewals. No key.
|
||||
OPERATOR_SESSION = 4; // wrapped keys for auxiliary crypto operations.
|
||||
ENTITLEMENT = 5; // Entitlement keys.
|
||||
OEM_CONTENT = 6; // Partner-specific content key.
|
||||
}
|
||||
|
||||
// The SecurityLevel enumeration allows the server to communicate the level
|
||||
// of robustness required by the client, in order to use the key.
|
||||
enum SecurityLevel {
|
||||
// Software-based whitebox crypto is required.
|
||||
SW_SECURE_CRYPTO = 1;
|
||||
|
||||
// Software crypto and an obfuscated decoder is required.
|
||||
SW_SECURE_DECODE = 2;
|
||||
|
||||
// The key material and crypto operations must be performed within a
|
||||
// hardware backed trusted execution environment.
|
||||
HW_SECURE_CRYPTO = 3;
|
||||
|
||||
// The crypto and decoding of content must be performed within a hardware
|
||||
// backed trusted execution environment.
|
||||
HW_SECURE_DECODE = 4;
|
||||
|
||||
// The crypto, decoding and all handling of the media (compressed and
|
||||
// uncompressed) must be handled within a hardware backed trusted
|
||||
// execution environment.
|
||||
HW_SECURE_ALL = 5;
|
||||
}
|
||||
|
||||
message KeyControl {
|
||||
// |key_control| is documented in:
|
||||
// Widevine Modular DRM Security Integration Guide for CENC
|
||||
// If present, the key control must be communicated to the secure
|
||||
// environment prior to any usage. This message is automatically generated
|
||||
// by the Widevine License Server SDK.
|
||||
optional bytes key_control_block = 1;
|
||||
optional bytes iv = 2;
|
||||
}
|
||||
|
||||
message OutputProtection {
|
||||
// Indicates whether HDCP is required on digital outputs, and which
|
||||
// version should be used.
|
||||
enum HDCP {
|
||||
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;
|
||||
}
|
||||
optional HDCP hdcp = 1 [default = HDCP_NONE];
|
||||
|
||||
// Indicate the CGMS setting to be inserted on analog output.
|
||||
enum CGMS {
|
||||
CGMS_NONE = 42;
|
||||
COPY_FREE = 0;
|
||||
COPY_ONCE = 2;
|
||||
COPY_NEVER = 3;
|
||||
}
|
||||
optional CGMS cgms_flags = 2 [default = CGMS_NONE];
|
||||
|
||||
enum HdcpSrmRule {
|
||||
HDCP_SRM_RULE_NONE = 0;
|
||||
// In 'required_protection', this means most current SRM is required.
|
||||
// Update the SRM on the device. If update cannot happen,
|
||||
// do not allow the key.
|
||||
// In 'requested_protection', this means most current SRM is requested.
|
||||
// Update the SRM on the device. If update cannot happen,
|
||||
// allow use of the key anyway.
|
||||
CURRENT_SRM = 1;
|
||||
}
|
||||
optional HdcpSrmRule hdcp_srm_rule = 3 [default = HDCP_SRM_RULE_NONE];
|
||||
// Optional requirement to indicate analog output is not allowed.
|
||||
optional bool disable_analog_output = 4 [default = false];
|
||||
// Optional requirement to indicate digital output is not allowed.
|
||||
optional bool disable_digital_output = 5 [default = false];
|
||||
}
|
||||
|
||||
message VideoResolutionConstraint {
|
||||
// Minimum and maximum video resolutions in the range (height x width).
|
||||
optional uint32 min_resolution_pixels = 1;
|
||||
optional uint32 max_resolution_pixels = 2;
|
||||
// Optional output protection requirements for this range. If not
|
||||
// specified, the OutputProtection in the KeyContainer applies.
|
||||
optional OutputProtection required_protection = 3;
|
||||
}
|
||||
|
||||
message OperatorSessionKeyPermissions {
|
||||
// Permissions/key usage flags for operator service keys
|
||||
// (type = OPERATOR_SESSION).
|
||||
optional bool allow_encrypt = 1 [default = false];
|
||||
optional bool allow_decrypt = 2 [default = false];
|
||||
optional bool allow_sign = 3 [default = false];
|
||||
optional bool allow_signature_verify = 4 [default = false];
|
||||
}
|
||||
|
||||
optional bytes id = 1;
|
||||
optional bytes iv = 2;
|
||||
optional bytes key = 3;
|
||||
optional KeyType type = 4;
|
||||
optional SecurityLevel level = 5 [default = SW_SECURE_CRYPTO];
|
||||
optional OutputProtection required_protection = 6;
|
||||
// NOTE: Use of requested_protection is not recommended as it is only
|
||||
// supported on a small number of platforms.
|
||||
optional OutputProtection requested_protection = 7;
|
||||
optional KeyControl key_control = 8;
|
||||
optional OperatorSessionKeyPermissions operator_session_key_permissions = 9;
|
||||
// Optional video resolution constraints. If the video resolution of the
|
||||
// content being decrypted/decoded falls within one of the specified ranges,
|
||||
// the optional required_protections may be applied. Otherwise an error will
|
||||
// be reported.
|
||||
// NOTE: Use of this feature is not recommended, as it is only supported on
|
||||
// a small number of platforms.
|
||||
repeated VideoResolutionConstraint video_resolution_constraints = 10;
|
||||
// Optional flag to indicate the key must only be used if the client
|
||||
// supports anti rollback of the user table. Content provider can query the
|
||||
// client capabilities to determine if the client support this feature.
|
||||
optional bool anti_rollback_usage_table = 11 [default = false];
|
||||
// Optional not limited to commonly known track types such as SD, HD.
|
||||
// It can be some provider defined label to identify the track.
|
||||
optional string track_label = 12;
|
||||
}
|
||||
|
||||
optional LicenseIdentification id = 1;
|
||||
optional Policy policy = 2;
|
||||
repeated KeyContainer key = 3;
|
||||
// Time of the request in seconds (UTC) as set in
|
||||
// LicenseRequest.request_time. If this time is not set in the request,
|
||||
// the local time at the license service is used in this field.
|
||||
optional int64 license_start_time = 4;
|
||||
optional bool remote_attestation_verified = 5 [default = false];
|
||||
// Client token generated by the content provider. Optional.
|
||||
optional bytes provider_client_token = 6;
|
||||
// 4cc code specifying the CENC protection scheme as defined in the CENC 3.0
|
||||
// specification. Propagated from Widevine PSSH box. Optional.
|
||||
optional uint32 protection_scheme = 7;
|
||||
// 8 byte verification field "HDCPDATA" followed by unsigned 32 bit minimum
|
||||
// HDCP SRM version (whether the version is for HDCP1 SRM or HDCP2 SRM
|
||||
// depends on client max_hdcp_version).
|
||||
// Additional details can be found in Widevine Modular DRM Security
|
||||
// Integration Guide for CENC.
|
||||
optional bytes srm_requirement = 8;
|
||||
// If present this contains a signed SRM file (either HDCP1 SRM or HDCP2 SRM
|
||||
// depending on client max_hdcp_version) that should be installed on the
|
||||
// client device.
|
||||
optional bytes srm_update = 9;
|
||||
// Indicates the status of any type of platform verification performed by the
|
||||
// server.
|
||||
optional PlatformVerificationStatus platform_verification_status = 10
|
||||
[default = PLATFORM_NO_VERIFICATION];
|
||||
// IDs of the groups for which keys are delivered in this license, if any.
|
||||
repeated bytes group_ids = 11;
|
||||
}
|
||||
|
||||
enum ProtocolVersion {
|
||||
VERSION_2_0 = 20;
|
||||
VERSION_2_1 = 21;
|
||||
VERSION_2_2 = 22;
|
||||
}
|
||||
|
||||
message LicenseRequest {
|
||||
message ContentIdentification {
|
||||
message WidevinePsshData {
|
||||
repeated bytes pssh_data = 1;
|
||||
optional LicenseType license_type = 2;
|
||||
optional bytes request_id = 3; // Opaque, client-specified.
|
||||
}
|
||||
|
||||
message WebmKeyId {
|
||||
optional bytes header = 1;
|
||||
optional LicenseType license_type = 2;
|
||||
optional bytes request_id = 3; // Opaque, client-specified.
|
||||
}
|
||||
|
||||
message ExistingLicense {
|
||||
optional LicenseIdentification license_id = 1;
|
||||
optional int64 seconds_since_started = 2;
|
||||
optional int64 seconds_since_last_played = 3;
|
||||
optional bytes session_usage_table_entry = 4;
|
||||
}
|
||||
|
||||
message InitData {
|
||||
enum InitDataType {
|
||||
CENC = 1;
|
||||
WEBM = 2;
|
||||
}
|
||||
|
||||
optional InitDataType init_data_type = 1 [default = CENC];
|
||||
optional bytes init_data = 2;
|
||||
optional LicenseType license_type = 3;
|
||||
optional bytes request_id = 4;
|
||||
}
|
||||
|
||||
oneof content_id_variant {
|
||||
// Exactly one of these must be present.
|
||||
WidevinePsshData widevine_pssh_data = 1;
|
||||
WebmKeyId webm_key_id = 2;
|
||||
ExistingLicense existing_license = 3;
|
||||
InitData init_data = 4;
|
||||
}
|
||||
}
|
||||
|
||||
enum RequestType {
|
||||
NEW = 1;
|
||||
RENEWAL = 2;
|
||||
RELEASE = 3;
|
||||
}
|
||||
|
||||
// The client_id provides information authenticating the calling device. It
|
||||
// contains the Widevine keybox token that was installed on the device at the
|
||||
// factory. This field or encrypted_client_id below is required for a valid
|
||||
// license request, but both should never be present in the same request.
|
||||
optional ClientIdentification client_id = 1;
|
||||
optional ContentIdentification content_id = 2;
|
||||
optional RequestType type = 3;
|
||||
// Time of the request in seconds (UTC) as set by the client.
|
||||
optional int64 request_time = 4;
|
||||
// Old-style decimal-encoded string key control nonce.
|
||||
optional bytes key_control_nonce_deprecated = 5;
|
||||
optional ProtocolVersion protocol_version = 6 [default = VERSION_2_0];
|
||||
// New-style uint32 key control nonce, please use instead of
|
||||
// key_control_nonce_deprecated.
|
||||
optional uint32 key_control_nonce = 7;
|
||||
// Encrypted ClientIdentification message, used for privacy purposes.
|
||||
optional EncryptedClientIdentification encrypted_client_id = 8;
|
||||
}
|
||||
|
||||
message MetricData {
|
||||
enum MetricType {
|
||||
// The time spent in the 'stage', specified in microseconds.
|
||||
LATENCY = 1;
|
||||
// The UNIX epoch timestamp at which the 'stage' was first accessed in
|
||||
// microseconds.
|
||||
TIMESTAMP = 2;
|
||||
}
|
||||
|
||||
message TypeValue {
|
||||
optional MetricType type = 1;
|
||||
// The value associated with 'type'. For example if type == LATENCY, the
|
||||
// value would be the time in microseconds spent in this 'stage'.
|
||||
optional int64 value = 2 [default = 0];
|
||||
}
|
||||
|
||||
// 'stage' that is currently processing the SignedMessage. Required.
|
||||
optional string stage_name = 1;
|
||||
// metric and associated value.
|
||||
repeated TypeValue metric_data = 2;
|
||||
}
|
||||
|
||||
message VersionInfo {
|
||||
// License SDK version reported by the Widevine License SDK. This field
|
||||
// is populated automatically by the SDK.
|
||||
optional string license_sdk_version = 1;
|
||||
// Version of the service hosting the license SDK. This field is optional.
|
||||
// It may be provided by the hosting service.
|
||||
optional string license_service_version = 2;
|
||||
}
|
||||
|
||||
message SignedMessage {
|
||||
enum MessageType {
|
||||
LICENSE_REQUEST = 1;
|
||||
LICENSE = 2;
|
||||
ERROR_RESPONSE = 3;
|
||||
SERVICE_CERTIFICATE_REQUEST = 4;
|
||||
SERVICE_CERTIFICATE = 5;
|
||||
SUB_LICENSE = 6;
|
||||
CAS_LICENSE_REQUEST = 7;
|
||||
CAS_LICENSE = 8;
|
||||
EXTERNAL_LICENSE_REQUEST = 9;
|
||||
EXTERNAL_LICENSE = 10;
|
||||
}
|
||||
|
||||
enum SessionKeyType {
|
||||
UNDEFINED = 0;
|
||||
WRAPPED_AES_KEY = 1;
|
||||
EPHERMERAL_ECC_PUBLIC_KEY = 2;
|
||||
}
|
||||
optional MessageType type = 1;
|
||||
optional bytes msg = 2;
|
||||
// Required field that contains the signature of the bytes of msg.
|
||||
// For license requests, the signing algorithm is determined by the
|
||||
// certificate contained in the request.
|
||||
// For license responses, the signing algorithm is HMAC with signing key based
|
||||
// on |session_key|.
|
||||
optional bytes signature = 3;
|
||||
// If populated, the contents of this field will be signaled by the
|
||||
// |session_key_type| type. If the |session_key_type| is WRAPPED_AES_KEY the
|
||||
// key is the bytes of an encrypted AES key. If the |session_key_type| is
|
||||
// EPHERMERAL_ECC_PUBLIC_KEY the field contains the bytes of an RFC5208 ASN1
|
||||
// serialized ECC public key.
|
||||
optional bytes session_key = 4;
|
||||
// Remote attestation data which will be present in the initial license
|
||||
// request for ChromeOS client devices operating in verified mode. Remote
|
||||
// attestation challenge data is |msg| field above. Optional.
|
||||
optional bytes remote_attestation = 5;
|
||||
|
||||
repeated MetricData metric_data = 6;
|
||||
// Version information from the SDK and license service. This information is
|
||||
// provided in the license response.
|
||||
optional VersionInfo service_version_info = 7;
|
||||
// Optional field that contains the algorithm type used to generate the
|
||||
// session_key and signature in a LICENSE message.
|
||||
optional SessionKeyType session_key_type = 8 [default = WRAPPED_AES_KEY];
|
||||
// The core message is the simple serialization of fields used by OEMCrypto.
|
||||
// This field was introduced in OEMCrypto API v16.
|
||||
optional bytes oemcrypto_core_message = 9;
|
||||
}
|
||||
|
||||
enum HashAlgorithmProto {
|
||||
// Unspecified hash algorithm: SHA_256 shall be used for ECC based algorithms
|
||||
// and SHA_1 shall be used otherwise.
|
||||
HASH_ALGORITHM_UNSPECIFIED = 0;
|
||||
HASH_ALGORITHM_SHA_1 = 1;
|
||||
HASH_ALGORITHM_SHA_256 = 2;
|
||||
HASH_ALGORITHM_SHA_384 = 3;
|
||||
}
|
||||
|
||||
// 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:
|
||||
// Widevine Modular DRM Security Integration Guide for CENC
|
||||
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 indicates that
|
||||
// the resource rating is unavailable or reporting erroneous values
|
||||
// for that device. For details see,
|
||||
// Widevine Modular DRM Security Integration Guide for CENC
|
||||
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.
|
||||
repeated 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;
|
||||
}
|
||||
|
||||
// DRM certificate definition for user devices, intermediate, service, and root
|
||||
// certificates.
|
||||
message DrmCertificate {
|
||||
enum Type {
|
||||
ROOT = 0; // ProtoBestPractices: ignore.
|
||||
DEVICE_MODEL = 1;
|
||||
DEVICE = 2;
|
||||
SERVICE = 3;
|
||||
PROVISIONER = 4;
|
||||
}
|
||||
enum ServiceType {
|
||||
UNKNOWN_SERVICE_TYPE = 0;
|
||||
LICENSE_SERVER_SDK = 1;
|
||||
LICENSE_SERVER_PROXY_SDK = 2;
|
||||
PROVISIONING_SDK = 3;
|
||||
CAS_PROXY_SDK = 4;
|
||||
}
|
||||
enum Algorithm {
|
||||
UNKNOWN_ALGORITHM = 0;
|
||||
RSA = 1;
|
||||
ECC_SECP256R1 = 2;
|
||||
ECC_SECP384R1 = 3;
|
||||
ECC_SECP521R1 = 4;
|
||||
}
|
||||
|
||||
message EncryptionKey {
|
||||
// Device public key. PKCS#1 ASN.1 DER-encoded. Required.
|
||||
optional bytes public_key = 1;
|
||||
// Required. The algorithm field contains the curve used to create the
|
||||
// |public_key| if algorithm is one of the ECC types.
|
||||
// The |algorithm| is used for both to determine the if the certificate is
|
||||
// ECC or RSA. The |algorithm| also specifies the parameters that were used
|
||||
// to create |public_key| and are used to create an ephemeral session key.
|
||||
optional Algorithm algorithm = 2 [default = RSA];
|
||||
}
|
||||
|
||||
// Type of certificate. Required.
|
||||
optional Type type = 1;
|
||||
// 128-bit globally unique serial number of certificate.
|
||||
// Value is 0 for root certificate. Required.
|
||||
optional bytes serial_number = 2;
|
||||
// POSIX time, in seconds, when the certificate was created. Required.
|
||||
optional uint32 creation_time_seconds = 3;
|
||||
// POSIX time, in seconds, when the certificate should expire. Value of zero
|
||||
// denotes indefinite expiry time. For more information on limited lifespan
|
||||
// DRM certificates see (go/limited-lifespan-drm-certificates).
|
||||
optional uint32 expiration_time_seconds = 12;
|
||||
// Device public key. PKCS#1 ASN.1 DER-encoded. Required.
|
||||
optional bytes public_key = 4;
|
||||
// Widevine system ID for the device. Required for intermediate and
|
||||
// user device certificates.
|
||||
optional uint32 system_id = 5;
|
||||
// Deprecated field, which used to indicate whether the device was a test
|
||||
// (non-production) device. The test_device field in ProvisionedDeviceInfo
|
||||
// below should be observed instead.
|
||||
optional bool test_device_deprecated = 6 [deprecated = true];
|
||||
// Service identifier (web origin) for the provider which owns the
|
||||
// certificate. Required for service and provisioner certificates.
|
||||
optional string provider_id = 7;
|
||||
// This field is used only when type = SERVICE to specify which SDK uses
|
||||
// service certificate. This repeated field is treated as a set. A certificate
|
||||
// may be used for the specified service SDK if the appropriate ServiceType
|
||||
// is specified in this field.
|
||||
repeated ServiceType service_types = 8;
|
||||
// Required. The algorithm field contains the curve used to create the
|
||||
// |public_key| if algorithm is one of the ECC types.
|
||||
// The |algorithm| is used for both to determine the if the certificate is ECC
|
||||
// or RSA. The |algorithm| also specifies the parameters that were used to
|
||||
// create |public_key| and are used to create an ephemeral session key.
|
||||
optional Algorithm algorithm = 9 [default = RSA];
|
||||
// Optional. May be present in DEVICE certificate types. This is the root
|
||||
// of trust identifier that holds an encrypted value that identifies the
|
||||
// keybox or other root of trust that was used to provision a DEVICE drm
|
||||
// certificate.
|
||||
optional bytes rot_id = 10;
|
||||
// Optional. May be present in devices that explicitly support dual keys. When
|
||||
// present the |public_key| is used for verification of received license
|
||||
// request messages.
|
||||
optional EncryptionKey encryption_key = 11;
|
||||
}
|
||||
|
||||
// DrmCertificate signed by a higher (CA) DRM certificate.
|
||||
message SignedDrmCertificate {
|
||||
// Serialized certificate. Required.
|
||||
optional bytes drm_certificate = 1;
|
||||
// Signature of certificate. Signed with root or intermediate
|
||||
// certificate specified below. Required.
|
||||
optional bytes signature = 2;
|
||||
// SignedDrmCertificate used to sign this certificate.
|
||||
optional SignedDrmCertificate signer = 3;
|
||||
// Optional field that indicates the hash algorithm used in signature scheme.
|
||||
optional HashAlgorithmProto hash_algorithm = 4;
|
||||
}
|
||||
|
||||
message WidevinePsshData {
|
||||
enum Type {
|
||||
SINGLE = 0; // Single PSSH to be used to retrieve content keys.
|
||||
ENTITLEMENT = 1; // Primary PSSH used to retrieve entitlement keys.
|
||||
ENTITLED_KEY = 2; // Secondary PSSH containing entitled key(s).
|
||||
}
|
||||
|
||||
message EntitledKey {
|
||||
// ID of entitlement key used for wrapping |key|.
|
||||
optional bytes entitlement_key_id = 1;
|
||||
// ID of the entitled key.
|
||||
optional bytes key_id = 2;
|
||||
// Wrapped key. Required.
|
||||
optional bytes key = 3;
|
||||
// IV used for wrapping |key|. Required.
|
||||
optional bytes iv = 4;
|
||||
// Size of entitlement key used for wrapping |key|.
|
||||
optional uint32 entitlement_key_size_bytes = 5 [default = 32];
|
||||
}
|
||||
|
||||
// Entitlement or content key IDs. Can onnly present in SINGLE or ENTITLEMENT
|
||||
// PSSHs. May be repeated to facilitate delivery of multiple keys in a
|
||||
// single license. Cannot be used in conjunction with content_id or
|
||||
// group_ids, which are the preferred mechanism.
|
||||
repeated bytes key_ids = 2;
|
||||
|
||||
// Content identifier which may map to multiple entitlement or content key
|
||||
// IDs to facilitate the delivery of multiple keys in a single license.
|
||||
// Cannot be present in conjunction with key_ids, but if used must be in all
|
||||
// PSSHs.
|
||||
optional bytes content_id = 4;
|
||||
|
||||
// Crypto period index, for media using key rotation. Always corresponds to
|
||||
// The content key period. This means that if using entitlement licensing
|
||||
// the ENTITLED_KEY PSSHs will have sequential crypto_period_index's, whereas
|
||||
// the ENTITELEMENT PSSHs will have gaps in the sequence. Required if doing
|
||||
// key rotation.
|
||||
optional uint32 crypto_period_index = 7;
|
||||
|
||||
// Protection scheme identifying the encryption algorithm. The protection
|
||||
// scheme is represented as a uint32 value. The uint32 contains 4 bytes each
|
||||
// representing a single ascii character in one of the 4CC protection scheme
|
||||
// values. To be deprecated in favor of signaling from content.
|
||||
// 'cenc' (AES-CTR) protection_scheme = 0x63656E63,
|
||||
// 'cbc1' (AES-CBC) protection_scheme = 0x63626331,
|
||||
// 'cens' (AES-CTR pattern encryption) protection_scheme = 0x63656E73,
|
||||
// 'cbcs' (AES-CBC pattern encryption) protection_scheme = 0x63626373.
|
||||
optional uint32 protection_scheme = 9;
|
||||
|
||||
// Optional. For media using key rotation, this represents the duration
|
||||
// of each crypto period in seconds.
|
||||
optional uint32 crypto_period_seconds = 10;
|
||||
|
||||
// Type of PSSH. Required if not SINGLE.
|
||||
optional Type type = 11 [default = SINGLE];
|
||||
|
||||
// Key sequence for Widevine-managed keys. Optional.
|
||||
optional uint32 key_sequence = 12;
|
||||
|
||||
// Group identifiers for all groups to which the content belongs. This can
|
||||
// be used to deliver licenses to unlock multiple titles / channels.
|
||||
// Optional, and may only be present in ENTITLEMENT and ENTITLED_KEY PSSHs, and
|
||||
// not in conjunction with key_ids.
|
||||
repeated bytes group_ids = 13;
|
||||
|
||||
// Copy/copies of the content key used to decrypt the media stream in which
|
||||
// the PSSH box is embedded, each wrapped with a different entitlement key.
|
||||
// May also contain sub-licenses to support devices with OEMCrypto 13 or
|
||||
// older. May be repeated if using group entitlement keys. Present only in
|
||||
// PSSHs of type ENTITLED_KEY.
|
||||
repeated EntitledKey entitled_keys = 14;
|
||||
|
||||
// Video feature identifier, which is used in conjunction with |content_id|
|
||||
// to determine the set of keys to be returned in the license. Cannot be
|
||||
// present in conjunction with |key_ids|.
|
||||
// Current values are "HDR".
|
||||
optional string video_feature = 15;
|
||||
|
||||
//////////////////////////// Deprecated Fields ////////////////////////////
|
||||
enum Algorithm {
|
||||
UNENCRYPTED = 0;
|
||||
AESCTR = 1;
|
||||
};
|
||||
optional Algorithm algorithm = 1 [deprecated = true];
|
||||
|
||||
// Content provider name.
|
||||
optional string provider = 3 [deprecated = true];
|
||||
|
||||
// Track type. Acceptable values are SD, HD and AUDIO. Used to
|
||||
// differentiate content keys used by an asset.
|
||||
optional string track_type = 5 [deprecated = true];
|
||||
|
||||
// The name of a registered policy to be used for this asset.
|
||||
optional string policy = 6 [deprecated = true];
|
||||
|
||||
// Optional protected context for group content. The grouped_license is a
|
||||
// serialized SignedMessage.
|
||||
optional bytes grouped_license = 8 [deprecated = true];
|
||||
}
|
||||
|
||||
// File Hashes for Verified Media Path (VMP) support.
|
||||
message FileHashes {
|
||||
message Signature {
|
||||
optional string filename = 1;
|
||||
optional bool test_signing = 2; //0 - release, 1 - testing
|
||||
optional bytes SHA512Hash = 3;
|
||||
optional bool main_exe = 4; //0 for dlls, 1 for exe, this is field 3 in file
|
||||
optional bytes signature = 5;
|
||||
}
|
||||
optional bytes signer = 1;
|
||||
repeated Signature signatures = 2;
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,607 @@
|
|||
# mypy: ignore-errors
|
||||
|
||||
from google.protobuf.internal import containers as _containers
|
||||
from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union
|
||||
|
||||
AUTOMATIC: LicenseType
|
||||
DESCRIPTOR: _descriptor.FileDescriptor
|
||||
HASH_ALGORITHM_SHA_1: HashAlgorithmProto
|
||||
HASH_ALGORITHM_SHA_256: HashAlgorithmProto
|
||||
HASH_ALGORITHM_SHA_384: HashAlgorithmProto
|
||||
HASH_ALGORITHM_UNSPECIFIED: HashAlgorithmProto
|
||||
OFFLINE: LicenseType
|
||||
PLATFORM_HARDWARE_VERIFIED: PlatformVerificationStatus
|
||||
PLATFORM_NO_VERIFICATION: PlatformVerificationStatus
|
||||
PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED: PlatformVerificationStatus
|
||||
PLATFORM_SOFTWARE_VERIFIED: PlatformVerificationStatus
|
||||
PLATFORM_TAMPERED: PlatformVerificationStatus
|
||||
PLATFORM_UNVERIFIED: PlatformVerificationStatus
|
||||
STREAMING: LicenseType
|
||||
VERSION_2_0: ProtocolVersion
|
||||
VERSION_2_1: ProtocolVersion
|
||||
VERSION_2_2: ProtocolVersion
|
||||
|
||||
class ClientIdentification(_message.Message):
|
||||
__slots__ = ["client_capabilities", "client_info", "device_credentials", "license_counter", "provider_client_token", "token", "type", "vmp_data"]
|
||||
class TokenType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
class ClientCapabilities(_message.Message):
|
||||
__slots__ = ["analog_output_capabilities", "anti_rollback_usage_table", "can_disable_analog_output", "can_update_srm", "client_token", "max_hdcp_version", "oem_crypto_api_version", "resource_rating_tier", "session_token", "srm_version", "supported_certificate_key_type", "video_resolution_constraints"]
|
||||
class AnalogOutputCapabilities(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
class CertificateKeyType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
class HdcpVersion(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
ANALOG_OUTPUT_CAPABILITIES_FIELD_NUMBER: _ClassVar[int]
|
||||
ANALOG_OUTPUT_NONE: ClientIdentification.ClientCapabilities.AnalogOutputCapabilities
|
||||
ANALOG_OUTPUT_SUPPORTED: ClientIdentification.ClientCapabilities.AnalogOutputCapabilities
|
||||
ANALOG_OUTPUT_SUPPORTS_CGMS_A: ClientIdentification.ClientCapabilities.AnalogOutputCapabilities
|
||||
ANALOG_OUTPUT_UNKNOWN: ClientIdentification.ClientCapabilities.AnalogOutputCapabilities
|
||||
ANTI_ROLLBACK_USAGE_TABLE_FIELD_NUMBER: _ClassVar[int]
|
||||
CAN_DISABLE_ANALOG_OUTPUT_FIELD_NUMBER: _ClassVar[int]
|
||||
CAN_UPDATE_SRM_FIELD_NUMBER: _ClassVar[int]
|
||||
CLIENT_TOKEN_FIELD_NUMBER: _ClassVar[int]
|
||||
ECC_SECP256R1: ClientIdentification.ClientCapabilities.CertificateKeyType
|
||||
ECC_SECP384R1: ClientIdentification.ClientCapabilities.CertificateKeyType
|
||||
ECC_SECP521R1: ClientIdentification.ClientCapabilities.CertificateKeyType
|
||||
HDCP_NONE: ClientIdentification.ClientCapabilities.HdcpVersion
|
||||
HDCP_NO_DIGITAL_OUTPUT: ClientIdentification.ClientCapabilities.HdcpVersion
|
||||
HDCP_V1: ClientIdentification.ClientCapabilities.HdcpVersion
|
||||
HDCP_V2: ClientIdentification.ClientCapabilities.HdcpVersion
|
||||
HDCP_V2_1: ClientIdentification.ClientCapabilities.HdcpVersion
|
||||
HDCP_V2_2: ClientIdentification.ClientCapabilities.HdcpVersion
|
||||
HDCP_V2_3: ClientIdentification.ClientCapabilities.HdcpVersion
|
||||
MAX_HDCP_VERSION_FIELD_NUMBER: _ClassVar[int]
|
||||
OEM_CRYPTO_API_VERSION_FIELD_NUMBER: _ClassVar[int]
|
||||
RESOURCE_RATING_TIER_FIELD_NUMBER: _ClassVar[int]
|
||||
RSA_2048: ClientIdentification.ClientCapabilities.CertificateKeyType
|
||||
RSA_3072: ClientIdentification.ClientCapabilities.CertificateKeyType
|
||||
SESSION_TOKEN_FIELD_NUMBER: _ClassVar[int]
|
||||
SRM_VERSION_FIELD_NUMBER: _ClassVar[int]
|
||||
SUPPORTED_CERTIFICATE_KEY_TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
VIDEO_RESOLUTION_CONSTRAINTS_FIELD_NUMBER: _ClassVar[int]
|
||||
analog_output_capabilities: ClientIdentification.ClientCapabilities.AnalogOutputCapabilities
|
||||
anti_rollback_usage_table: bool
|
||||
can_disable_analog_output: bool
|
||||
can_update_srm: bool
|
||||
client_token: bool
|
||||
max_hdcp_version: ClientIdentification.ClientCapabilities.HdcpVersion
|
||||
oem_crypto_api_version: int
|
||||
resource_rating_tier: int
|
||||
session_token: bool
|
||||
srm_version: int
|
||||
supported_certificate_key_type: _containers.RepeatedScalarFieldContainer[ClientIdentification.ClientCapabilities.CertificateKeyType]
|
||||
video_resolution_constraints: bool
|
||||
def __init__(self, client_token: bool = ..., session_token: bool = ..., video_resolution_constraints: bool = ..., max_hdcp_version: _Optional[_Union[ClientIdentification.ClientCapabilities.HdcpVersion, str]] = ..., oem_crypto_api_version: _Optional[int] = ..., anti_rollback_usage_table: bool = ..., srm_version: _Optional[int] = ..., can_update_srm: bool = ..., supported_certificate_key_type: _Optional[_Iterable[_Union[ClientIdentification.ClientCapabilities.CertificateKeyType, str]]] = ..., analog_output_capabilities: _Optional[_Union[ClientIdentification.ClientCapabilities.AnalogOutputCapabilities, str]] = ..., can_disable_analog_output: bool = ..., resource_rating_tier: _Optional[int] = ...) -> None: ...
|
||||
class ClientCredentials(_message.Message):
|
||||
__slots__ = ["token", "type"]
|
||||
TOKEN_FIELD_NUMBER: _ClassVar[int]
|
||||
TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
token: bytes
|
||||
type: ClientIdentification.TokenType
|
||||
def __init__(self, type: _Optional[_Union[ClientIdentification.TokenType, str]] = ..., token: _Optional[bytes] = ...) -> None: ...
|
||||
class NameValue(_message.Message):
|
||||
__slots__ = ["name", "value"]
|
||||
NAME_FIELD_NUMBER: _ClassVar[int]
|
||||
VALUE_FIELD_NUMBER: _ClassVar[int]
|
||||
name: str
|
||||
value: str
|
||||
def __init__(self, name: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ...
|
||||
CLIENT_CAPABILITIES_FIELD_NUMBER: _ClassVar[int]
|
||||
CLIENT_INFO_FIELD_NUMBER: _ClassVar[int]
|
||||
DEVICE_CREDENTIALS_FIELD_NUMBER: _ClassVar[int]
|
||||
DRM_DEVICE_CERTIFICATE: ClientIdentification.TokenType
|
||||
KEYBOX: ClientIdentification.TokenType
|
||||
LICENSE_COUNTER_FIELD_NUMBER: _ClassVar[int]
|
||||
OEM_DEVICE_CERTIFICATE: ClientIdentification.TokenType
|
||||
PROVIDER_CLIENT_TOKEN_FIELD_NUMBER: _ClassVar[int]
|
||||
REMOTE_ATTESTATION_CERTIFICATE: ClientIdentification.TokenType
|
||||
TOKEN_FIELD_NUMBER: _ClassVar[int]
|
||||
TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
VMP_DATA_FIELD_NUMBER: _ClassVar[int]
|
||||
client_capabilities: ClientIdentification.ClientCapabilities
|
||||
client_info: _containers.RepeatedCompositeFieldContainer[ClientIdentification.NameValue]
|
||||
device_credentials: _containers.RepeatedCompositeFieldContainer[ClientIdentification.ClientCredentials]
|
||||
license_counter: int
|
||||
provider_client_token: bytes
|
||||
token: bytes
|
||||
type: ClientIdentification.TokenType
|
||||
vmp_data: bytes
|
||||
def __init__(self, type: _Optional[_Union[ClientIdentification.TokenType, str]] = ..., token: _Optional[bytes] = ..., client_info: _Optional[_Iterable[_Union[ClientIdentification.NameValue, _Mapping]]] = ..., provider_client_token: _Optional[bytes] = ..., license_counter: _Optional[int] = ..., client_capabilities: _Optional[_Union[ClientIdentification.ClientCapabilities, _Mapping]] = ..., vmp_data: _Optional[bytes] = ..., device_credentials: _Optional[_Iterable[_Union[ClientIdentification.ClientCredentials, _Mapping]]] = ...) -> None: ...
|
||||
|
||||
class DrmCertificate(_message.Message):
|
||||
__slots__ = ["algorithm", "creation_time_seconds", "encryption_key", "expiration_time_seconds", "provider_id", "public_key", "rot_id", "serial_number", "service_types", "system_id", "test_device_deprecated", "type"]
|
||||
class Algorithm(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
class ServiceType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
class Type(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
class EncryptionKey(_message.Message):
|
||||
__slots__ = ["algorithm", "public_key"]
|
||||
ALGORITHM_FIELD_NUMBER: _ClassVar[int]
|
||||
PUBLIC_KEY_FIELD_NUMBER: _ClassVar[int]
|
||||
algorithm: DrmCertificate.Algorithm
|
||||
public_key: bytes
|
||||
def __init__(self, public_key: _Optional[bytes] = ..., algorithm: _Optional[_Union[DrmCertificate.Algorithm, str]] = ...) -> None: ...
|
||||
ALGORITHM_FIELD_NUMBER: _ClassVar[int]
|
||||
CAS_PROXY_SDK: DrmCertificate.ServiceType
|
||||
CREATION_TIME_SECONDS_FIELD_NUMBER: _ClassVar[int]
|
||||
DEVICE: DrmCertificate.Type
|
||||
DEVICE_MODEL: DrmCertificate.Type
|
||||
ECC_SECP256R1: DrmCertificate.Algorithm
|
||||
ECC_SECP384R1: DrmCertificate.Algorithm
|
||||
ECC_SECP521R1: DrmCertificate.Algorithm
|
||||
ENCRYPTION_KEY_FIELD_NUMBER: _ClassVar[int]
|
||||
EXPIRATION_TIME_SECONDS_FIELD_NUMBER: _ClassVar[int]
|
||||
LICENSE_SERVER_PROXY_SDK: DrmCertificate.ServiceType
|
||||
LICENSE_SERVER_SDK: DrmCertificate.ServiceType
|
||||
PROVIDER_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
PROVISIONER: DrmCertificate.Type
|
||||
PROVISIONING_SDK: DrmCertificate.ServiceType
|
||||
PUBLIC_KEY_FIELD_NUMBER: _ClassVar[int]
|
||||
ROOT: DrmCertificate.Type
|
||||
ROT_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
RSA: DrmCertificate.Algorithm
|
||||
SERIAL_NUMBER_FIELD_NUMBER: _ClassVar[int]
|
||||
SERVICE: DrmCertificate.Type
|
||||
SERVICE_TYPES_FIELD_NUMBER: _ClassVar[int]
|
||||
SYSTEM_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
TEST_DEVICE_DEPRECATED_FIELD_NUMBER: _ClassVar[int]
|
||||
TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
UNKNOWN_ALGORITHM: DrmCertificate.Algorithm
|
||||
UNKNOWN_SERVICE_TYPE: DrmCertificate.ServiceType
|
||||
algorithm: DrmCertificate.Algorithm
|
||||
creation_time_seconds: int
|
||||
encryption_key: DrmCertificate.EncryptionKey
|
||||
expiration_time_seconds: int
|
||||
provider_id: str
|
||||
public_key: bytes
|
||||
rot_id: bytes
|
||||
serial_number: bytes
|
||||
service_types: _containers.RepeatedScalarFieldContainer[DrmCertificate.ServiceType]
|
||||
system_id: int
|
||||
test_device_deprecated: bool
|
||||
type: DrmCertificate.Type
|
||||
def __init__(self, type: _Optional[_Union[DrmCertificate.Type, str]] = ..., serial_number: _Optional[bytes] = ..., creation_time_seconds: _Optional[int] = ..., expiration_time_seconds: _Optional[int] = ..., public_key: _Optional[bytes] = ..., system_id: _Optional[int] = ..., test_device_deprecated: bool = ..., provider_id: _Optional[str] = ..., service_types: _Optional[_Iterable[_Union[DrmCertificate.ServiceType, str]]] = ..., algorithm: _Optional[_Union[DrmCertificate.Algorithm, str]] = ..., rot_id: _Optional[bytes] = ..., encryption_key: _Optional[_Union[DrmCertificate.EncryptionKey, _Mapping]] = ...) -> None: ...
|
||||
|
||||
class EncryptedClientIdentification(_message.Message):
|
||||
__slots__ = ["encrypted_client_id", "encrypted_client_id_iv", "encrypted_privacy_key", "provider_id", "service_certificate_serial_number"]
|
||||
ENCRYPTED_CLIENT_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
ENCRYPTED_CLIENT_ID_IV_FIELD_NUMBER: _ClassVar[int]
|
||||
ENCRYPTED_PRIVACY_KEY_FIELD_NUMBER: _ClassVar[int]
|
||||
PROVIDER_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
SERVICE_CERTIFICATE_SERIAL_NUMBER_FIELD_NUMBER: _ClassVar[int]
|
||||
encrypted_client_id: bytes
|
||||
encrypted_client_id_iv: bytes
|
||||
encrypted_privacy_key: bytes
|
||||
provider_id: str
|
||||
service_certificate_serial_number: bytes
|
||||
def __init__(self, provider_id: _Optional[str] = ..., service_certificate_serial_number: _Optional[bytes] = ..., encrypted_client_id: _Optional[bytes] = ..., encrypted_client_id_iv: _Optional[bytes] = ..., encrypted_privacy_key: _Optional[bytes] = ...) -> None: ...
|
||||
|
||||
class FileHashes(_message.Message):
|
||||
__slots__ = ["signatures", "signer"]
|
||||
class Signature(_message.Message):
|
||||
__slots__ = ["SHA512Hash", "filename", "main_exe", "signature", "test_signing"]
|
||||
FILENAME_FIELD_NUMBER: _ClassVar[int]
|
||||
MAIN_EXE_FIELD_NUMBER: _ClassVar[int]
|
||||
SHA512HASH_FIELD_NUMBER: _ClassVar[int]
|
||||
SHA512Hash: bytes
|
||||
SIGNATURE_FIELD_NUMBER: _ClassVar[int]
|
||||
TEST_SIGNING_FIELD_NUMBER: _ClassVar[int]
|
||||
filename: str
|
||||
main_exe: bool
|
||||
signature: bytes
|
||||
test_signing: bool
|
||||
def __init__(self, filename: _Optional[str] = ..., test_signing: bool = ..., SHA512Hash: _Optional[bytes] = ..., main_exe: bool = ..., signature: _Optional[bytes] = ...) -> None: ...
|
||||
SIGNATURES_FIELD_NUMBER: _ClassVar[int]
|
||||
SIGNER_FIELD_NUMBER: _ClassVar[int]
|
||||
signatures: _containers.RepeatedCompositeFieldContainer[FileHashes.Signature]
|
||||
signer: bytes
|
||||
def __init__(self, signer: _Optional[bytes] = ..., signatures: _Optional[_Iterable[_Union[FileHashes.Signature, _Mapping]]] = ...) -> None: ...
|
||||
|
||||
class License(_message.Message):
|
||||
__slots__ = ["group_ids", "id", "key", "license_start_time", "platform_verification_status", "policy", "protection_scheme", "provider_client_token", "remote_attestation_verified", "srm_requirement", "srm_update"]
|
||||
class KeyContainer(_message.Message):
|
||||
__slots__ = ["anti_rollback_usage_table", "id", "iv", "key", "key_control", "level", "operator_session_key_permissions", "requested_protection", "required_protection", "track_label", "type", "video_resolution_constraints"]
|
||||
class KeyType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
class SecurityLevel(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
class KeyControl(_message.Message):
|
||||
__slots__ = ["iv", "key_control_block"]
|
||||
IV_FIELD_NUMBER: _ClassVar[int]
|
||||
KEY_CONTROL_BLOCK_FIELD_NUMBER: _ClassVar[int]
|
||||
iv: bytes
|
||||
key_control_block: bytes
|
||||
def __init__(self, key_control_block: _Optional[bytes] = ..., iv: _Optional[bytes] = ...) -> None: ...
|
||||
class OperatorSessionKeyPermissions(_message.Message):
|
||||
__slots__ = ["allow_decrypt", "allow_encrypt", "allow_sign", "allow_signature_verify"]
|
||||
ALLOW_DECRYPT_FIELD_NUMBER: _ClassVar[int]
|
||||
ALLOW_ENCRYPT_FIELD_NUMBER: _ClassVar[int]
|
||||
ALLOW_SIGNATURE_VERIFY_FIELD_NUMBER: _ClassVar[int]
|
||||
ALLOW_SIGN_FIELD_NUMBER: _ClassVar[int]
|
||||
allow_decrypt: bool
|
||||
allow_encrypt: bool
|
||||
allow_sign: bool
|
||||
allow_signature_verify: bool
|
||||
def __init__(self, allow_encrypt: bool = ..., allow_decrypt: bool = ..., allow_sign: bool = ..., allow_signature_verify: bool = ...) -> None: ...
|
||||
class OutputProtection(_message.Message):
|
||||
__slots__ = ["cgms_flags", "disable_analog_output", "disable_digital_output", "hdcp", "hdcp_srm_rule"]
|
||||
class CGMS(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
class HDCP(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
class HdcpSrmRule(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
CGMS_FLAGS_FIELD_NUMBER: _ClassVar[int]
|
||||
CGMS_NONE: License.KeyContainer.OutputProtection.CGMS
|
||||
COPY_FREE: License.KeyContainer.OutputProtection.CGMS
|
||||
COPY_NEVER: License.KeyContainer.OutputProtection.CGMS
|
||||
COPY_ONCE: License.KeyContainer.OutputProtection.CGMS
|
||||
CURRENT_SRM: License.KeyContainer.OutputProtection.HdcpSrmRule
|
||||
DISABLE_ANALOG_OUTPUT_FIELD_NUMBER: _ClassVar[int]
|
||||
DISABLE_DIGITAL_OUTPUT_FIELD_NUMBER: _ClassVar[int]
|
||||
HDCP_FIELD_NUMBER: _ClassVar[int]
|
||||
HDCP_NONE: License.KeyContainer.OutputProtection.HDCP
|
||||
HDCP_NO_DIGITAL_OUTPUT: License.KeyContainer.OutputProtection.HDCP
|
||||
HDCP_SRM_RULE_FIELD_NUMBER: _ClassVar[int]
|
||||
HDCP_SRM_RULE_NONE: License.KeyContainer.OutputProtection.HdcpSrmRule
|
||||
HDCP_V1: License.KeyContainer.OutputProtection.HDCP
|
||||
HDCP_V2: License.KeyContainer.OutputProtection.HDCP
|
||||
HDCP_V2_1: License.KeyContainer.OutputProtection.HDCP
|
||||
HDCP_V2_2: License.KeyContainer.OutputProtection.HDCP
|
||||
HDCP_V2_3: License.KeyContainer.OutputProtection.HDCP
|
||||
cgms_flags: License.KeyContainer.OutputProtection.CGMS
|
||||
disable_analog_output: bool
|
||||
disable_digital_output: bool
|
||||
hdcp: License.KeyContainer.OutputProtection.HDCP
|
||||
hdcp_srm_rule: License.KeyContainer.OutputProtection.HdcpSrmRule
|
||||
def __init__(self, hdcp: _Optional[_Union[License.KeyContainer.OutputProtection.HDCP, str]] = ..., cgms_flags: _Optional[_Union[License.KeyContainer.OutputProtection.CGMS, str]] = ..., hdcp_srm_rule: _Optional[_Union[License.KeyContainer.OutputProtection.HdcpSrmRule, str]] = ..., disable_analog_output: bool = ..., disable_digital_output: bool = ...) -> None: ...
|
||||
class VideoResolutionConstraint(_message.Message):
|
||||
__slots__ = ["max_resolution_pixels", "min_resolution_pixels", "required_protection"]
|
||||
MAX_RESOLUTION_PIXELS_FIELD_NUMBER: _ClassVar[int]
|
||||
MIN_RESOLUTION_PIXELS_FIELD_NUMBER: _ClassVar[int]
|
||||
REQUIRED_PROTECTION_FIELD_NUMBER: _ClassVar[int]
|
||||
max_resolution_pixels: int
|
||||
min_resolution_pixels: int
|
||||
required_protection: License.KeyContainer.OutputProtection
|
||||
def __init__(self, min_resolution_pixels: _Optional[int] = ..., max_resolution_pixels: _Optional[int] = ..., required_protection: _Optional[_Union[License.KeyContainer.OutputProtection, _Mapping]] = ...) -> None: ...
|
||||
ANTI_ROLLBACK_USAGE_TABLE_FIELD_NUMBER: _ClassVar[int]
|
||||
CONTENT: License.KeyContainer.KeyType
|
||||
ENTITLEMENT: License.KeyContainer.KeyType
|
||||
HW_SECURE_ALL: License.KeyContainer.SecurityLevel
|
||||
HW_SECURE_CRYPTO: License.KeyContainer.SecurityLevel
|
||||
HW_SECURE_DECODE: License.KeyContainer.SecurityLevel
|
||||
ID_FIELD_NUMBER: _ClassVar[int]
|
||||
IV_FIELD_NUMBER: _ClassVar[int]
|
||||
KEY_CONTROL: License.KeyContainer.KeyType
|
||||
KEY_CONTROL_FIELD_NUMBER: _ClassVar[int]
|
||||
KEY_FIELD_NUMBER: _ClassVar[int]
|
||||
LEVEL_FIELD_NUMBER: _ClassVar[int]
|
||||
OEM_CONTENT: License.KeyContainer.KeyType
|
||||
OPERATOR_SESSION: License.KeyContainer.KeyType
|
||||
OPERATOR_SESSION_KEY_PERMISSIONS_FIELD_NUMBER: _ClassVar[int]
|
||||
REQUESTED_PROTECTION_FIELD_NUMBER: _ClassVar[int]
|
||||
REQUIRED_PROTECTION_FIELD_NUMBER: _ClassVar[int]
|
||||
SIGNING: License.KeyContainer.KeyType
|
||||
SW_SECURE_CRYPTO: License.KeyContainer.SecurityLevel
|
||||
SW_SECURE_DECODE: License.KeyContainer.SecurityLevel
|
||||
TRACK_LABEL_FIELD_NUMBER: _ClassVar[int]
|
||||
TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
VIDEO_RESOLUTION_CONSTRAINTS_FIELD_NUMBER: _ClassVar[int]
|
||||
anti_rollback_usage_table: bool
|
||||
id: bytes
|
||||
iv: bytes
|
||||
key: bytes
|
||||
key_control: License.KeyContainer.KeyControl
|
||||
level: License.KeyContainer.SecurityLevel
|
||||
operator_session_key_permissions: License.KeyContainer.OperatorSessionKeyPermissions
|
||||
requested_protection: License.KeyContainer.OutputProtection
|
||||
required_protection: License.KeyContainer.OutputProtection
|
||||
track_label: str
|
||||
type: License.KeyContainer.KeyType
|
||||
video_resolution_constraints: _containers.RepeatedCompositeFieldContainer[License.KeyContainer.VideoResolutionConstraint]
|
||||
def __init__(self, id: _Optional[bytes] = ..., iv: _Optional[bytes] = ..., key: _Optional[bytes] = ..., type: _Optional[_Union[License.KeyContainer.KeyType, str]] = ..., level: _Optional[_Union[License.KeyContainer.SecurityLevel, str]] = ..., required_protection: _Optional[_Union[License.KeyContainer.OutputProtection, _Mapping]] = ..., requested_protection: _Optional[_Union[License.KeyContainer.OutputProtection, _Mapping]] = ..., key_control: _Optional[_Union[License.KeyContainer.KeyControl, _Mapping]] = ..., operator_session_key_permissions: _Optional[_Union[License.KeyContainer.OperatorSessionKeyPermissions, _Mapping]] = ..., video_resolution_constraints: _Optional[_Iterable[_Union[License.KeyContainer.VideoResolutionConstraint, _Mapping]]] = ..., anti_rollback_usage_table: bool = ..., track_label: _Optional[str] = ...) -> None: ...
|
||||
class Policy(_message.Message):
|
||||
__slots__ = ["always_include_client_id", "can_persist", "can_play", "can_renew", "license_duration_seconds", "play_start_grace_period_seconds", "playback_duration_seconds", "renew_with_usage", "renewal_delay_seconds", "renewal_recovery_duration_seconds", "renewal_retry_interval_seconds", "renewal_server_url", "rental_duration_seconds", "soft_enforce_playback_duration", "soft_enforce_rental_duration"]
|
||||
ALWAYS_INCLUDE_CLIENT_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
CAN_PERSIST_FIELD_NUMBER: _ClassVar[int]
|
||||
CAN_PLAY_FIELD_NUMBER: _ClassVar[int]
|
||||
CAN_RENEW_FIELD_NUMBER: _ClassVar[int]
|
||||
LICENSE_DURATION_SECONDS_FIELD_NUMBER: _ClassVar[int]
|
||||
PLAYBACK_DURATION_SECONDS_FIELD_NUMBER: _ClassVar[int]
|
||||
PLAY_START_GRACE_PERIOD_SECONDS_FIELD_NUMBER: _ClassVar[int]
|
||||
RENEWAL_DELAY_SECONDS_FIELD_NUMBER: _ClassVar[int]
|
||||
RENEWAL_RECOVERY_DURATION_SECONDS_FIELD_NUMBER: _ClassVar[int]
|
||||
RENEWAL_RETRY_INTERVAL_SECONDS_FIELD_NUMBER: _ClassVar[int]
|
||||
RENEWAL_SERVER_URL_FIELD_NUMBER: _ClassVar[int]
|
||||
RENEW_WITH_USAGE_FIELD_NUMBER: _ClassVar[int]
|
||||
RENTAL_DURATION_SECONDS_FIELD_NUMBER: _ClassVar[int]
|
||||
SOFT_ENFORCE_PLAYBACK_DURATION_FIELD_NUMBER: _ClassVar[int]
|
||||
SOFT_ENFORCE_RENTAL_DURATION_FIELD_NUMBER: _ClassVar[int]
|
||||
always_include_client_id: bool
|
||||
can_persist: bool
|
||||
can_play: bool
|
||||
can_renew: bool
|
||||
license_duration_seconds: int
|
||||
play_start_grace_period_seconds: int
|
||||
playback_duration_seconds: int
|
||||
renew_with_usage: bool
|
||||
renewal_delay_seconds: int
|
||||
renewal_recovery_duration_seconds: int
|
||||
renewal_retry_interval_seconds: int
|
||||
renewal_server_url: str
|
||||
rental_duration_seconds: int
|
||||
soft_enforce_playback_duration: bool
|
||||
soft_enforce_rental_duration: bool
|
||||
def __init__(self, can_play: bool = ..., can_persist: bool = ..., can_renew: bool = ..., rental_duration_seconds: _Optional[int] = ..., playback_duration_seconds: _Optional[int] = ..., license_duration_seconds: _Optional[int] = ..., renewal_recovery_duration_seconds: _Optional[int] = ..., renewal_server_url: _Optional[str] = ..., renewal_delay_seconds: _Optional[int] = ..., renewal_retry_interval_seconds: _Optional[int] = ..., renew_with_usage: bool = ..., always_include_client_id: bool = ..., play_start_grace_period_seconds: _Optional[int] = ..., soft_enforce_playback_duration: bool = ..., soft_enforce_rental_duration: bool = ...) -> None: ...
|
||||
GROUP_IDS_FIELD_NUMBER: _ClassVar[int]
|
||||
ID_FIELD_NUMBER: _ClassVar[int]
|
||||
KEY_FIELD_NUMBER: _ClassVar[int]
|
||||
LICENSE_START_TIME_FIELD_NUMBER: _ClassVar[int]
|
||||
PLATFORM_VERIFICATION_STATUS_FIELD_NUMBER: _ClassVar[int]
|
||||
POLICY_FIELD_NUMBER: _ClassVar[int]
|
||||
PROTECTION_SCHEME_FIELD_NUMBER: _ClassVar[int]
|
||||
PROVIDER_CLIENT_TOKEN_FIELD_NUMBER: _ClassVar[int]
|
||||
REMOTE_ATTESTATION_VERIFIED_FIELD_NUMBER: _ClassVar[int]
|
||||
SRM_REQUIREMENT_FIELD_NUMBER: _ClassVar[int]
|
||||
SRM_UPDATE_FIELD_NUMBER: _ClassVar[int]
|
||||
group_ids: _containers.RepeatedScalarFieldContainer[bytes]
|
||||
id: LicenseIdentification
|
||||
key: _containers.RepeatedCompositeFieldContainer[License.KeyContainer]
|
||||
license_start_time: int
|
||||
platform_verification_status: PlatformVerificationStatus
|
||||
policy: License.Policy
|
||||
protection_scheme: int
|
||||
provider_client_token: bytes
|
||||
remote_attestation_verified: bool
|
||||
srm_requirement: bytes
|
||||
srm_update: bytes
|
||||
def __init__(self, id: _Optional[_Union[LicenseIdentification, _Mapping]] = ..., policy: _Optional[_Union[License.Policy, _Mapping]] = ..., key: _Optional[_Iterable[_Union[License.KeyContainer, _Mapping]]] = ..., license_start_time: _Optional[int] = ..., remote_attestation_verified: bool = ..., provider_client_token: _Optional[bytes] = ..., protection_scheme: _Optional[int] = ..., srm_requirement: _Optional[bytes] = ..., srm_update: _Optional[bytes] = ..., platform_verification_status: _Optional[_Union[PlatformVerificationStatus, str]] = ..., group_ids: _Optional[_Iterable[bytes]] = ...) -> None: ...
|
||||
|
||||
class LicenseIdentification(_message.Message):
|
||||
__slots__ = ["provider_session_token", "purchase_id", "request_id", "session_id", "type", "version"]
|
||||
PROVIDER_SESSION_TOKEN_FIELD_NUMBER: _ClassVar[int]
|
||||
PURCHASE_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
REQUEST_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
SESSION_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
VERSION_FIELD_NUMBER: _ClassVar[int]
|
||||
provider_session_token: bytes
|
||||
purchase_id: bytes
|
||||
request_id: bytes
|
||||
session_id: bytes
|
||||
type: LicenseType
|
||||
version: int
|
||||
def __init__(self, request_id: _Optional[bytes] = ..., session_id: _Optional[bytes] = ..., purchase_id: _Optional[bytes] = ..., type: _Optional[_Union[LicenseType, str]] = ..., version: _Optional[int] = ..., provider_session_token: _Optional[bytes] = ...) -> None: ...
|
||||
|
||||
class LicenseRequest(_message.Message):
|
||||
__slots__ = ["client_id", "content_id", "encrypted_client_id", "key_control_nonce", "key_control_nonce_deprecated", "protocol_version", "request_time", "type"]
|
||||
class RequestType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
class ContentIdentification(_message.Message):
|
||||
__slots__ = ["existing_license", "init_data", "webm_key_id", "widevine_pssh_data"]
|
||||
class ExistingLicense(_message.Message):
|
||||
__slots__ = ["license_id", "seconds_since_last_played", "seconds_since_started", "session_usage_table_entry"]
|
||||
LICENSE_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
SECONDS_SINCE_LAST_PLAYED_FIELD_NUMBER: _ClassVar[int]
|
||||
SECONDS_SINCE_STARTED_FIELD_NUMBER: _ClassVar[int]
|
||||
SESSION_USAGE_TABLE_ENTRY_FIELD_NUMBER: _ClassVar[int]
|
||||
license_id: LicenseIdentification
|
||||
seconds_since_last_played: int
|
||||
seconds_since_started: int
|
||||
session_usage_table_entry: bytes
|
||||
def __init__(self, license_id: _Optional[_Union[LicenseIdentification, _Mapping]] = ..., seconds_since_started: _Optional[int] = ..., seconds_since_last_played: _Optional[int] = ..., session_usage_table_entry: _Optional[bytes] = ...) -> None: ...
|
||||
class InitData(_message.Message):
|
||||
__slots__ = ["init_data", "init_data_type", "license_type", "request_id"]
|
||||
class InitDataType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
CENC: LicenseRequest.ContentIdentification.InitData.InitDataType
|
||||
INIT_DATA_FIELD_NUMBER: _ClassVar[int]
|
||||
INIT_DATA_TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
LICENSE_TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
REQUEST_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
WEBM: LicenseRequest.ContentIdentification.InitData.InitDataType
|
||||
init_data: bytes
|
||||
init_data_type: LicenseRequest.ContentIdentification.InitData.InitDataType
|
||||
license_type: LicenseType
|
||||
request_id: bytes
|
||||
def __init__(self, init_data_type: _Optional[_Union[LicenseRequest.ContentIdentification.InitData.InitDataType, str]] = ..., init_data: _Optional[bytes] = ..., license_type: _Optional[_Union[LicenseType, str]] = ..., request_id: _Optional[bytes] = ...) -> None: ...
|
||||
class WebmKeyId(_message.Message):
|
||||
__slots__ = ["header", "license_type", "request_id"]
|
||||
HEADER_FIELD_NUMBER: _ClassVar[int]
|
||||
LICENSE_TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
REQUEST_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
header: bytes
|
||||
license_type: LicenseType
|
||||
request_id: bytes
|
||||
def __init__(self, header: _Optional[bytes] = ..., license_type: _Optional[_Union[LicenseType, str]] = ..., request_id: _Optional[bytes] = ...) -> None: ...
|
||||
class WidevinePsshData(_message.Message):
|
||||
__slots__ = ["license_type", "pssh_data", "request_id"]
|
||||
LICENSE_TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
PSSH_DATA_FIELD_NUMBER: _ClassVar[int]
|
||||
REQUEST_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
license_type: LicenseType
|
||||
pssh_data: _containers.RepeatedScalarFieldContainer[bytes]
|
||||
request_id: bytes
|
||||
def __init__(self, pssh_data: _Optional[_Iterable[bytes]] = ..., license_type: _Optional[_Union[LicenseType, str]] = ..., request_id: _Optional[bytes] = ...) -> None: ...
|
||||
EXISTING_LICENSE_FIELD_NUMBER: _ClassVar[int]
|
||||
INIT_DATA_FIELD_NUMBER: _ClassVar[int]
|
||||
WEBM_KEY_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
WIDEVINE_PSSH_DATA_FIELD_NUMBER: _ClassVar[int]
|
||||
existing_license: LicenseRequest.ContentIdentification.ExistingLicense
|
||||
init_data: LicenseRequest.ContentIdentification.InitData
|
||||
webm_key_id: LicenseRequest.ContentIdentification.WebmKeyId
|
||||
widevine_pssh_data: LicenseRequest.ContentIdentification.WidevinePsshData
|
||||
def __init__(self, widevine_pssh_data: _Optional[_Union[LicenseRequest.ContentIdentification.WidevinePsshData, _Mapping]] = ..., webm_key_id: _Optional[_Union[LicenseRequest.ContentIdentification.WebmKeyId, _Mapping]] = ..., existing_license: _Optional[_Union[LicenseRequest.ContentIdentification.ExistingLicense, _Mapping]] = ..., init_data: _Optional[_Union[LicenseRequest.ContentIdentification.InitData, _Mapping]] = ...) -> None: ...
|
||||
CLIENT_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
CONTENT_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
ENCRYPTED_CLIENT_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
KEY_CONTROL_NONCE_DEPRECATED_FIELD_NUMBER: _ClassVar[int]
|
||||
KEY_CONTROL_NONCE_FIELD_NUMBER: _ClassVar[int]
|
||||
NEW: LicenseRequest.RequestType
|
||||
PROTOCOL_VERSION_FIELD_NUMBER: _ClassVar[int]
|
||||
RELEASE: LicenseRequest.RequestType
|
||||
RENEWAL: LicenseRequest.RequestType
|
||||
REQUEST_TIME_FIELD_NUMBER: _ClassVar[int]
|
||||
TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
client_id: ClientIdentification
|
||||
content_id: LicenseRequest.ContentIdentification
|
||||
encrypted_client_id: EncryptedClientIdentification
|
||||
key_control_nonce: int
|
||||
key_control_nonce_deprecated: bytes
|
||||
protocol_version: ProtocolVersion
|
||||
request_time: int
|
||||
type: LicenseRequest.RequestType
|
||||
def __init__(self, client_id: _Optional[_Union[ClientIdentification, _Mapping]] = ..., content_id: _Optional[_Union[LicenseRequest.ContentIdentification, _Mapping]] = ..., type: _Optional[_Union[LicenseRequest.RequestType, str]] = ..., request_time: _Optional[int] = ..., key_control_nonce_deprecated: _Optional[bytes] = ..., protocol_version: _Optional[_Union[ProtocolVersion, str]] = ..., key_control_nonce: _Optional[int] = ..., encrypted_client_id: _Optional[_Union[EncryptedClientIdentification, _Mapping]] = ...) -> None: ...
|
||||
|
||||
class MetricData(_message.Message):
|
||||
__slots__ = ["metric_data", "stage_name"]
|
||||
class MetricType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
class TypeValue(_message.Message):
|
||||
__slots__ = ["type", "value"]
|
||||
TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
VALUE_FIELD_NUMBER: _ClassVar[int]
|
||||
type: MetricData.MetricType
|
||||
value: int
|
||||
def __init__(self, type: _Optional[_Union[MetricData.MetricType, str]] = ..., value: _Optional[int] = ...) -> None: ...
|
||||
LATENCY: MetricData.MetricType
|
||||
METRIC_DATA_FIELD_NUMBER: _ClassVar[int]
|
||||
STAGE_NAME_FIELD_NUMBER: _ClassVar[int]
|
||||
TIMESTAMP: MetricData.MetricType
|
||||
metric_data: _containers.RepeatedCompositeFieldContainer[MetricData.TypeValue]
|
||||
stage_name: str
|
||||
def __init__(self, stage_name: _Optional[str] = ..., metric_data: _Optional[_Iterable[_Union[MetricData.TypeValue, _Mapping]]] = ...) -> None: ...
|
||||
|
||||
class SignedDrmCertificate(_message.Message):
|
||||
__slots__ = ["drm_certificate", "hash_algorithm", "signature", "signer"]
|
||||
DRM_CERTIFICATE_FIELD_NUMBER: _ClassVar[int]
|
||||
HASH_ALGORITHM_FIELD_NUMBER: _ClassVar[int]
|
||||
SIGNATURE_FIELD_NUMBER: _ClassVar[int]
|
||||
SIGNER_FIELD_NUMBER: _ClassVar[int]
|
||||
drm_certificate: bytes
|
||||
hash_algorithm: HashAlgorithmProto
|
||||
signature: bytes
|
||||
signer: SignedDrmCertificate
|
||||
def __init__(self, drm_certificate: _Optional[bytes] = ..., signature: _Optional[bytes] = ..., signer: _Optional[_Union[SignedDrmCertificate, _Mapping]] = ..., hash_algorithm: _Optional[_Union[HashAlgorithmProto, str]] = ...) -> None: ...
|
||||
|
||||
class SignedMessage(_message.Message):
|
||||
__slots__ = ["metric_data", "msg", "oemcrypto_core_message", "remote_attestation", "service_version_info", "session_key", "session_key_type", "signature", "type"]
|
||||
class MessageType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
class SessionKeyType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
CAS_LICENSE: SignedMessage.MessageType
|
||||
CAS_LICENSE_REQUEST: SignedMessage.MessageType
|
||||
EPHERMERAL_ECC_PUBLIC_KEY: SignedMessage.SessionKeyType
|
||||
ERROR_RESPONSE: SignedMessage.MessageType
|
||||
EXTERNAL_LICENSE: SignedMessage.MessageType
|
||||
EXTERNAL_LICENSE_REQUEST: SignedMessage.MessageType
|
||||
LICENSE: SignedMessage.MessageType
|
||||
LICENSE_REQUEST: SignedMessage.MessageType
|
||||
METRIC_DATA_FIELD_NUMBER: _ClassVar[int]
|
||||
MSG_FIELD_NUMBER: _ClassVar[int]
|
||||
OEMCRYPTO_CORE_MESSAGE_FIELD_NUMBER: _ClassVar[int]
|
||||
REMOTE_ATTESTATION_FIELD_NUMBER: _ClassVar[int]
|
||||
SERVICE_CERTIFICATE: SignedMessage.MessageType
|
||||
SERVICE_CERTIFICATE_REQUEST: SignedMessage.MessageType
|
||||
SERVICE_VERSION_INFO_FIELD_NUMBER: _ClassVar[int]
|
||||
SESSION_KEY_FIELD_NUMBER: _ClassVar[int]
|
||||
SESSION_KEY_TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
SIGNATURE_FIELD_NUMBER: _ClassVar[int]
|
||||
SUB_LICENSE: SignedMessage.MessageType
|
||||
TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
UNDEFINED: SignedMessage.SessionKeyType
|
||||
WRAPPED_AES_KEY: SignedMessage.SessionKeyType
|
||||
metric_data: _containers.RepeatedCompositeFieldContainer[MetricData]
|
||||
msg: bytes
|
||||
oemcrypto_core_message: bytes
|
||||
remote_attestation: bytes
|
||||
service_version_info: VersionInfo
|
||||
session_key: bytes
|
||||
session_key_type: SignedMessage.SessionKeyType
|
||||
signature: bytes
|
||||
type: SignedMessage.MessageType
|
||||
def __init__(self, type: _Optional[_Union[SignedMessage.MessageType, str]] = ..., msg: _Optional[bytes] = ..., signature: _Optional[bytes] = ..., session_key: _Optional[bytes] = ..., remote_attestation: _Optional[bytes] = ..., metric_data: _Optional[_Iterable[_Union[MetricData, _Mapping]]] = ..., service_version_info: _Optional[_Union[VersionInfo, _Mapping]] = ..., session_key_type: _Optional[_Union[SignedMessage.SessionKeyType, str]] = ..., oemcrypto_core_message: _Optional[bytes] = ...) -> None: ...
|
||||
|
||||
class VersionInfo(_message.Message):
|
||||
__slots__ = ["license_sdk_version", "license_service_version"]
|
||||
LICENSE_SDK_VERSION_FIELD_NUMBER: _ClassVar[int]
|
||||
LICENSE_SERVICE_VERSION_FIELD_NUMBER: _ClassVar[int]
|
||||
license_sdk_version: str
|
||||
license_service_version: str
|
||||
def __init__(self, license_sdk_version: _Optional[str] = ..., license_service_version: _Optional[str] = ...) -> None: ...
|
||||
|
||||
class WidevinePsshData(_message.Message):
|
||||
__slots__ = ["algorithm", "content_id", "crypto_period_index", "crypto_period_seconds", "entitled_keys", "group_ids", "grouped_license", "key_ids", "key_sequence", "policy", "protection_scheme", "provider", "track_type", "type", "video_feature"]
|
||||
class Algorithm(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
class Type(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
class EntitledKey(_message.Message):
|
||||
__slots__ = ["entitlement_key_id", "entitlement_key_size_bytes", "iv", "key", "key_id"]
|
||||
ENTITLEMENT_KEY_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
ENTITLEMENT_KEY_SIZE_BYTES_FIELD_NUMBER: _ClassVar[int]
|
||||
IV_FIELD_NUMBER: _ClassVar[int]
|
||||
KEY_FIELD_NUMBER: _ClassVar[int]
|
||||
KEY_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
entitlement_key_id: bytes
|
||||
entitlement_key_size_bytes: int
|
||||
iv: bytes
|
||||
key: bytes
|
||||
key_id: bytes
|
||||
def __init__(self, entitlement_key_id: _Optional[bytes] = ..., key_id: _Optional[bytes] = ..., key: _Optional[bytes] = ..., iv: _Optional[bytes] = ..., entitlement_key_size_bytes: _Optional[int] = ...) -> None: ...
|
||||
AESCTR: WidevinePsshData.Algorithm
|
||||
ALGORITHM_FIELD_NUMBER: _ClassVar[int]
|
||||
CONTENT_ID_FIELD_NUMBER: _ClassVar[int]
|
||||
CRYPTO_PERIOD_INDEX_FIELD_NUMBER: _ClassVar[int]
|
||||
CRYPTO_PERIOD_SECONDS_FIELD_NUMBER: _ClassVar[int]
|
||||
ENTITLED_KEY: WidevinePsshData.Type
|
||||
ENTITLED_KEYS_FIELD_NUMBER: _ClassVar[int]
|
||||
ENTITLEMENT: WidevinePsshData.Type
|
||||
GROUPED_LICENSE_FIELD_NUMBER: _ClassVar[int]
|
||||
GROUP_IDS_FIELD_NUMBER: _ClassVar[int]
|
||||
KEY_IDS_FIELD_NUMBER: _ClassVar[int]
|
||||
KEY_SEQUENCE_FIELD_NUMBER: _ClassVar[int]
|
||||
POLICY_FIELD_NUMBER: _ClassVar[int]
|
||||
PROTECTION_SCHEME_FIELD_NUMBER: _ClassVar[int]
|
||||
PROVIDER_FIELD_NUMBER: _ClassVar[int]
|
||||
SINGLE: WidevinePsshData.Type
|
||||
TRACK_TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
TYPE_FIELD_NUMBER: _ClassVar[int]
|
||||
UNENCRYPTED: WidevinePsshData.Algorithm
|
||||
VIDEO_FEATURE_FIELD_NUMBER: _ClassVar[int]
|
||||
algorithm: WidevinePsshData.Algorithm
|
||||
content_id: bytes
|
||||
crypto_period_index: int
|
||||
crypto_period_seconds: int
|
||||
entitled_keys: _containers.RepeatedCompositeFieldContainer[WidevinePsshData.EntitledKey]
|
||||
group_ids: _containers.RepeatedScalarFieldContainer[bytes]
|
||||
grouped_license: bytes
|
||||
key_ids: _containers.RepeatedScalarFieldContainer[bytes]
|
||||
key_sequence: int
|
||||
policy: str
|
||||
protection_scheme: int
|
||||
provider: str
|
||||
track_type: str
|
||||
type: WidevinePsshData.Type
|
||||
video_feature: str
|
||||
def __init__(self, key_ids: _Optional[_Iterable[bytes]] = ..., content_id: _Optional[bytes] = ..., crypto_period_index: _Optional[int] = ..., protection_scheme: _Optional[int] = ..., crypto_period_seconds: _Optional[int] = ..., type: _Optional[_Union[WidevinePsshData.Type, str]] = ..., key_sequence: _Optional[int] = ..., group_ids: _Optional[_Iterable[bytes]] = ..., entitled_keys: _Optional[_Iterable[_Union[WidevinePsshData.EntitledKey, _Mapping]]] = ..., video_feature: _Optional[str] = ..., algorithm: _Optional[_Union[WidevinePsshData.Algorithm, str]] = ..., provider: _Optional[str] = ..., track_type: _Optional[str] = ..., policy: _Optional[str] = ..., grouped_license: _Optional[bytes] = ...) -> None: ...
|
||||
|
||||
class LicenseType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
|
||||
class PlatformVerificationStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
|
||||
class ProtocolVersion(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
||||
|
||||
class HashAlgorithmProto(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
|
||||
__slots__ = []
|
|
@ -0,0 +1,206 @@
|
|||
/**
|
||||
* KeyDive: Widevine L3 Extractor for Android Devices
|
||||
* Enhances DRM key extraction for research and educational purposes.
|
||||
* Source: https://github.com/hyugogirubato/KeyDive
|
||||
*/
|
||||
|
||||
const SDK_API = '${SDK_API}'; // Dynamically replaced with the actual SDK API level.
|
||||
const OEM_CRYPTO_API = [
|
||||
// Mapping of function names across different API levels (obfuscated names may vary).
|
||||
'rnmsglvj',
|
||||
'polorucp',
|
||||
'kqzqahjq',
|
||||
'pldrclfq',
|
||||
'kgaitijd',
|
||||
'cwkfcplc',
|
||||
'crhqcdet',
|
||||
'ulns', // 11, 13
|
||||
'dnvffnze', // 15
|
||||
'ygjiljer', // 15
|
||||
'qbjxtubz', // 16
|
||||
'qkfrcjtw', // 16
|
||||
'rbhjspoh' // 17
|
||||
// Add more as needed for different versions.
|
||||
];
|
||||
|
||||
|
||||
// Logging levels to synchronize with Python's logging module.
|
||||
const Level = {
|
||||
NOTSET: 0,
|
||||
DEBUG: 10,
|
||||
INFO: 20,
|
||||
// WARN: WARNING,
|
||||
WARNING: 30,
|
||||
ERROR: 40,
|
||||
// FATAL: CRITICAL,
|
||||
CRITICAL: 50
|
||||
};
|
||||
|
||||
// Utility for encoding strings into byte arrays.
|
||||
// https://gist.github.com/Yaffle/5458286#file-textencodertextdecoder-js
|
||||
function TextEncoder() {}
|
||||
TextEncoder.prototype.encode = function (string) {
|
||||
let octets = [];
|
||||
let i = 0;
|
||||
while (i < string.length) {
|
||||
let codePoint = string.codePointAt(i);
|
||||
let c = 0;
|
||||
let bits = 0;
|
||||
if (codePoint <= 0x007F) {
|
||||
c = 0;
|
||||
bits = 0x00;
|
||||
} else if (codePoint <= 0x07FF) {
|
||||
c = 6;
|
||||
bits = 0xC0;
|
||||
} else if (codePoint <= 0xFFFF) {
|
||||
c = 12;
|
||||
bits = 0xE0;
|
||||
} else if (codePoint <= 0x1FFFFF) {
|
||||
c = 18;
|
||||
bits = 0xF0;
|
||||
}
|
||||
octets.push(bits | (codePoint >> c));
|
||||
while (c >= 6) {
|
||||
c -= 6;
|
||||
octets.push(0x80 | ((codePoint >> c) & 0x3F));
|
||||
}
|
||||
i += codePoint >= 0x10000 ? 2 : 1;
|
||||
}
|
||||
return octets;
|
||||
};
|
||||
|
||||
const print = (level, message) => {
|
||||
message = typeof message === 'object' ? JSON.stringify(message) : message;
|
||||
send(level, new TextEncoder().encode(message));
|
||||
}
|
||||
|
||||
// Identifies and returns the specified library.
|
||||
const getLibrary = (name) => Process.getModuleByName(name);
|
||||
|
||||
// Hooks into specified functions within a library, aiming to extract keys and disable privacy mode.
|
||||
const hookLibrary = (name) => {
|
||||
// https://github.com/poxyran/misc/blob/master/frida-enumerate-imports.py
|
||||
const library = getLibrary(name);
|
||||
const functions = [...library.enumerateExports(), ...library.enumerateImports()];
|
||||
const targetFunction = functions.find(func => OEM_CRYPTO_API.includes(func.name));
|
||||
|
||||
let hookedCount = 0;
|
||||
functions.forEach((func) => {
|
||||
const funcName = func.name;
|
||||
const funcAddr = func.address;
|
||||
|
||||
try {
|
||||
let funcHooked = true;
|
||||
if (funcName.includes('UsePrivacyMode')) {
|
||||
disablePrivacyMode(funcAddr);
|
||||
} else if (funcName.includes('PrepareKeyRequest')) {
|
||||
prepareKeyRequest(funcAddr);
|
||||
} else if (targetFunction === func || (!targetFunction && funcName.match(/^[a-z]+$/))) {
|
||||
getPrivateKey(funcAddr);
|
||||
} else {
|
||||
funcHooked = false;
|
||||
}
|
||||
|
||||
if (funcHooked) {
|
||||
hookedCount++;
|
||||
print(Level.DEBUG, `Hooked (${funcAddr}): ${funcName}`);
|
||||
}
|
||||
} catch (e) {
|
||||
print(Level.ERROR, `${funcName} (${funcAddr}): ${e.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
if (hookedCount < 3) {
|
||||
print(Level.ERROR, 'Insufficient functions hooked');
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const disablePrivacyMode = (address) => {
|
||||
Interceptor.attach(ptr(address), {
|
||||
onLeave: function (retval) {
|
||||
retval.replace(ptr(0));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const prepareKeyRequest = (address) => {
|
||||
Interceptor.attach(ptr(address), {
|
||||
onEnter: function (args) {
|
||||
let index;
|
||||
if ([23, 31, 32, 33].includes(SDK_API)) {
|
||||
index = 5;
|
||||
} else if ([24, 25, 26, 27, 28, 29, 30].includes(SDK_API)) {
|
||||
index = 4;
|
||||
} else {
|
||||
index = 5; // Default index assignment
|
||||
print(Level.WARNING, SDK_API < 23 ? 'SDK API too old' : 'SDK API not implemented');
|
||||
print(Level.WARNING, `Defaulting to args[${index}] for PrepareKeyRequest`);
|
||||
}
|
||||
this.ret = args[index];
|
||||
},
|
||||
onLeave: function () {
|
||||
if (this.ret) {
|
||||
const size = Memory.readU32(ptr(this.ret).add(Process.pointerSize));
|
||||
const data = Memory.readByteArray(this.ret.add(Process.pointerSize * 2).readPointer(), size);
|
||||
send('device_info', data);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const getPrivateKey = (address) => {
|
||||
Interceptor.attach(ptr(address), {
|
||||
onEnter: function (args) {
|
||||
if (!args[6].isNull()) {
|
||||
const size = args[6].toInt32();
|
||||
if (size >= 1000 && size <= 2000 && !args[5].isNull()) {
|
||||
const buffer = args[5].readByteArray(size);
|
||||
const bytes = new Uint8Array(buffer);
|
||||
// Check for DER encoding markers for the beginning of a private key (MII).
|
||||
if (bytes[0] === 0x30 && bytes[1] === 0x82) {
|
||||
try {
|
||||
// Attempt to extract and send the private key.
|
||||
const binaryString = a2bs(bytes);
|
||||
const keyLength = getKeyLength(binaryString); // ASN.1 DER
|
||||
const key = bytes.slice(0, keyLength);
|
||||
print(Level.DEBUG, `Function getPrivateKey() at ${address}`);
|
||||
send('private_key', key);
|
||||
} catch (e) {
|
||||
print(Level.ERROR, `${e.message} (${address})`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const a2bs = (bytes) => Array.from(bytes).map(byte => String.fromCharCode(byte)).join('');
|
||||
|
||||
const getKeyLength = (key) => {
|
||||
let pos = 1; // Skip the initial tag
|
||||
// Extract length byte, ignoring the long-form indicator bit
|
||||
let lengthByte = key.charCodeAt(pos++) & 0x7F;
|
||||
// If lengthByte indicates a short form, return early.
|
||||
/*
|
||||
if (lengthByte < 0x80) {
|
||||
return pos + lengthByte;
|
||||
}
|
||||
*/
|
||||
|
||||
// For long-form, calculate the length value.
|
||||
let lengthValue = 0;
|
||||
for (let i = 0; i < lengthByte; i++) {
|
||||
lengthValue = (lengthValue << 8) + key.charCodeAt(pos++);
|
||||
}
|
||||
return pos + Math.abs(lengthValue);
|
||||
}
|
||||
|
||||
|
||||
// Exposing functions for RPC calls.
|
||||
rpc.exports = {
|
||||
getlibrary: getLibrary,
|
||||
hooklibrary: hookLibrary
|
||||
};
|
|
@ -0,0 +1,60 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('Vendor')
|
||||
|
||||
|
||||
class Vendor:
|
||||
"""
|
||||
Represents Widevine DRM Vendor details for different Android SDK versions.
|
||||
"""
|
||||
# https://developer.android.com/tools/releases/platforms
|
||||
SDK_VERSIONS = {
|
||||
34: (18, '18.0.0', 'android.hardware.drm-service.widevine', 'android.hardware.drm-service.widevine'),
|
||||
33: (17, '17.0.0', 'android.hardware.drm-service.widevine', 'libwvaidl.so'),
|
||||
32: (16, '16.1.0', 'android.hardware.drm@1.4-service.widevine', 'libwvhidl.so'),
|
||||
31: (16, '16.1.0', 'android.hardware.drm@1.4-service.widevine', 'libwvhidl.so'),
|
||||
30: (16, '16.0.0', 'android.hardware.drm@1.3-service.widevine', 'libwvhidl.so'),
|
||||
29: (15, '15.0.0', 'android.hardware.drm@1.2-service.widevine', 'libwvhidl.so'),
|
||||
28: (14, '14.0.0', 'android.hardware.drm@1.1-service.widevine', 'libwvhidl.so'),
|
||||
27: (13, '5.1.0', 'android.hardware.drm@1.0-service.widevine', 'libwvhidl.so'),
|
||||
26: (13, '1.0', 'android.hardware.drm@1.0-service.widevine', 'libwvhidl.so'),
|
||||
25: (11, '1.0', 'mediadrmserver', 'libwvdrmengine.so'),
|
||||
24: (11, '1.0', 'mediadrmserver', 'libwvdrmengine.so'),
|
||||
23: (11, '1.0', 'mediaserver', 'libwvdrmengine.so')
|
||||
}
|
||||
|
||||
def __init__(self, oem: int, version: str, process: str, library: str):
|
||||
"""
|
||||
Initialize a Vendor instance.
|
||||
|
||||
:param oem: OEM Crypto API level.
|
||||
:param version: Widevine CDM version.
|
||||
:param process: The process name associated with the Widevine DRM.
|
||||
:param library: The library file name used by the DRM process.
|
||||
"""
|
||||
self.oem = oem
|
||||
self.version = version
|
||||
self.process = process
|
||||
self.library = library
|
||||
|
||||
@classmethod
|
||||
def from_sdk_api(cls, sdk_api: int) -> Vendor:
|
||||
"""
|
||||
Creates a Vendor instance based on the Android SDK API level.
|
||||
|
||||
:param sdk_api: Android SDK API level.
|
||||
:return: A Vendor instance with DRM details.
|
||||
"""
|
||||
assert sdk_api > 22, 'Widevine not implemented for SDK <= 22'
|
||||
|
||||
vendor_details = cls.SDK_VERSIONS.get(sdk_api)
|
||||
if not vendor_details:
|
||||
vendor_details = cls.SDK_VERSIONS[max(cls.SDK_VERSIONS.keys())]
|
||||
logger.warning('CMD version is not yet implemented')
|
||||
logger.warning('Using closest supported CDM version: %s', vendor_details[1])
|
||||
else:
|
||||
logger.info('CDM version: %s' % vendor_details[1])
|
||||
logger.info('OEM Crypto API: %s' % vendor_details[0])
|
||||
return cls(*vendor_details)
|
|
@ -0,0 +1,45 @@
|
|||
import argparse
|
||||
import logging
|
||||
import time
|
||||
|
||||
import coloredlogs
|
||||
from _frida import Process
|
||||
|
||||
from extractor.cdm import Cdm
|
||||
|
||||
coloredlogs.install(
|
||||
fmt='%(asctime)s [%(levelname).1s] %(name)s: %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S',
|
||||
level=logging.DEBUG)
|
||||
|
||||
if __name__ == '__main__':
|
||||
logger = logging.getLogger('KeyDive')
|
||||
|
||||
# Parse command line arguments for device ID
|
||||
parser = argparse.ArgumentParser(description='Extract Widevine L3 keys from an Android device.')
|
||||
parser.add_argument('--device', type=str, help='Target Android device ID.')
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
# Initialize CDM handler with given device
|
||||
cdm = Cdm(device=args.device)
|
||||
|
||||
# Find Widevine process on the device
|
||||
process: Process = next((p for p in cdm.device.enumerate_processes() if cdm.vendor.process == p.name), None)
|
||||
if not process:
|
||||
raise Exception('Failed to find Widevine process')
|
||||
logger.info('Process: %s (%s)', process.pid, process.name)
|
||||
|
||||
# Hook into the process to extract DRM keys
|
||||
if not cdm.hook_process(process):
|
||||
raise Exception('Failed to hook into the process')
|
||||
logger.info('Successfully hooked. To test, play a DRM-protected video: https://bitmovin.com/demos/drm')
|
||||
|
||||
# Keep script running while extracting keys
|
||||
while cdm.running:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.critical(e)
|
||||
logger.info('Exiting')
|
|
@ -0,0 +1,5 @@
|
|||
frida~=16.1.4
|
||||
pathlib~=1.0.1
|
||||
coloredlogs~=15.0.1
|
||||
pycryptodomex~=3.20.0
|
||||
protobuf~=4.25.1
|
Loading…
Reference in New Issue