Содержание статьи:
CAS (Conditional Access System / Система условного доступа) — это сервис, разработанный drmnow!, который позволяет индивидуально переопределять ограничения доступа к защищенному контенту на основе запросов с DRM-сервисов.
Примеры использования:
Это руководство поможет вам сделать собственный вариант CAS, отвечающий вашим сценариям использования, и интегрировать его с нашими DRM-сервисами.
DRM-сервис при каждом запросе лицензии отправляет в CAS информацию про исходные заголовки запроса, ключи шифрования контента, прототип ответа с ограничениями для лицензии по умолчанию, а также данные, зависящие от типа DRM. CAS обрабатывает полученные данные, и возвращает прототип ответа с замененными в нем ограничениями обратно в DRM-сервис, после чего тот применяет полученные ограничения к лицензии. Если в итоговых ограничениях воспроизведение разрешено, то генерируется лицензия с новыми ограничениями из CAS, и она возвращается клиенту. В противном случае клиенту возвращается ошибка. Если CAS недоступен или вернул ответ с кодом HTTP 4xx, 5xx, то лицензия также не будет выдана, а клиент получит ошибку.
Этот принцип работы также показан в виде схемы на изображении ниже.
Логика, по которой происходит замена ограничений, определяется вашим сценарием использования, и не является строго фиксированной, она может быть воплощена с использованием любых удобных для вас языков программирования и фреймворков. Однако формат обмена данными при этом должен соответствовать описанному в данном руководстве.
Общий вид информации, отправляемой в CAS (см. детали для каждого типа DRM в разделах «Структура запроса» и ответа и «Примеры»):
{
"original_headers": "[DICTIONARY]",
"key_data": "[LIST OF DICTIONARIES]",
"response_prototype": "[DICTIONARY, MAY CONTAIN LISTS]"
}
Поле original_headers содержит исходные заголовки запроса к DRM-сервису, а также содержит специальное
поле с ключом QUERY_ARGS, значением которого является строка параметров запроса (например, arg1=val1&arg2=val2
).
Поле key_data содержит информацию о ключах: идентификатор контента (Content ID), идентификатор ключа (Key Id), тип трека (если применяется).
Поле response_prototype содержит свойства и ограничения лицензии по умолчанию, сгенерированные DRM-сервисом. Они могут быть переопределены.
Примечание: этот формат отличается для разных DRM-систем и может включать дополнительные поля. Widevine включает поле "parse_only_data", Playready включает поле "client_info". Также в поле "misc" может присутствовать произвольная невложенная информация.
Обмен данными (направление запроса и получение ответа) происходит в JSON-формате. Для отправки и
получения данных используется протокол HTTP. Для передачи запроса с DRM-сервиса к CAS используются
POST-запросы. Соответственно, CAS должен иметь endpoint для приема таких запросов (например http://some.host.tld:port/v2/cas
)
JSON запроса должен содержать следующие поля:
JSON запроса также может содержать проиизвольную невложенную информацию.
Валидное поле original_headers представляет собой непустой невложенный словарь, который ДОЛЖЕН содержать обязательное строковое поле QUERY_ARGS (строка параметров запроса). Пример:
{
"original_headers": {
"host": "127.0.0.1:8000",
"user-agent": "curl/7.68.0",
"accept": "*/*",
"x-project": "[project]",
"content-length": "8346",
"content-type": "application/x-www-form-urlencoded",
"expect": "100-continue",
"X-Project": "[project]",
"QUERY_ARGS": "arg1=val1&arg2=val2"
}
}
Если в запросе строка параметров запроса была пустой, то поле QUERY_ARGS тоже должно быть пустым.
Валидное поле key_data представляет собой непустой список словарей.
Каждый из словарей должен содержать поля content_id
и key_id
. Примечание: для Fairplay key_id необязателен.
content_id — произвольная строка.
key_id — шестнадцатеричная (hex) строка длины 32 (кроме Widevine), либо представление в base64
этой строки (только Widevine), которое может быть получено следующим образом:
$ python3
>>> import base64
>>> import binascii
>>> base64.b64encode(binascii.unhexlify('97ed5004a0d0a59dcc13e1ec26b23177'.encode())).decode()
'l+1QBKDQpZ3ME+HsJrIxdw=='
Здесь '97ed5004a0d0a59dcc13e1ec26b23177' — исходная hex-строка, а 'l+1QBKDQpZ3ME+HsJrIxdw==' — ее представление в base64.
Также может присутствовать необязательное поле track_type (SD, HD, UHD1, UHD, AUDIO).
Пример:
{
"key_data": [
{
"track_type": "UHD1",
"content_id": "abcde",
"key_id": "SBBgssxKQlisxKCRJtBGfw=="
}
]
}
Валидное поле response_prototype представляет собой непустой словарь, содержащий обязательное поле content_key_specs, значением которого является список словарей, структура которых отличается в зависимости от DRM-системы (см. подробности в примерах далее).
Также могут присутствовать другие поля в зависимости от вида DRM.
Примечание: в key_data и response_prototype должно присутствовать одинаковое количество key_id, при этом они должны совпадать (т.е. в response_prototype не могут присутствовать те ключи, которых нет в key_data, и не могут отсутствовать те ключи, которые есть в key_data).
Валидное поле parse_only_data представляет собой непустой словарь. Используется только с Widevine.
Валидное поле client_info представляет собой непустой словарь. Используется только с Playready.
Ответ должен иметь такую же структуру, как поле response_prototype запроса, при этом значения некоторых его полей могут быть переопределены (см. примеры).
Ниже приведены примеры запросов и ответов для разных DRM-систем.
{
"original_headers": {
"host": "127.0.0.1:8000",
"user-agent": "curl/7.68.0",
"accept": "*/*",
"x-project": "[project]",
"content-length": "8346",
"content-type": "application/x-www-form-urlencoded",
"expect": "100-continue",
"X-Project": "[project]",
"QUERY_ARGS": "arg1=val1&arg2=val2"
},
"key_data": [
{
"track_type": "UHD",
"content_id": "ZXhwNTY=",
"key_id": "SBBgssxKQlisxKCRJtBGfw=="
}
],
"parse_only_data": {
"status": "OK",
"status_message": "",
"license_metadata": {
"content_id": "ZXhwNTY=",
"license_type": "STREAMING",
"request_type": "NEW"
},
"supported_tracks": [
{
"type": "UHD1",
"key_id": "qUSciifJUaCcliHbl3zY5w=="
},
{
"type": "HD",
"key_id": "Ex6k7P0WUACFcnLoPcZRAg=="
},
{
"type": "SD",
"key_id": "5o+8VuETV8SEbxEAUtiImA=="
},
{
"type": "AUDIO",
"key_id": "YzEzHAB4WL+bxXpSRzLKKA=="
}
],
"make": "Google",
"model": "ChromeCDM-Linux-x64-4",
"security_level": 3,
"internal_status": 0,
"session_state": {
"license_id": {
"request_id": "YAbdFo8FyLr2PYTSdv5/0Q==",
"session_id": "YAbdFo8FyLr2PYTSdv5/0Q==",
"purchase_id": "",
"type": "STREAMING",
"version": 0,
"original_rental_duration_seconds": 0,
"original_playback_duration_seconds": 0,
"original_start_time_seconds": 1623862478
},
"signing_key": "Pjod4jIVOdkTiu5SP2fRSyHiSDHk2JzmjneksAhR9mJo/QZ7KE5uqqg6mI6L/5kEc6q/U/99HMfeEvXb84Wesw==",
"keybox_system_id": 20121,
"license_counter": 0
},
"drm_cert_serial_number": "NjFhNTZjOTJjNzc0ZDBjMGRmNDViODMzZTQzOTFiYmEwYQ==",
"device_whitelist_state": "DEVICE_NOT_WHITELISTED",
"platform": "linux",
"device_state": "RELEASED",
"pssh_data": {
"key_id": [
"YjAyMjg3NTIwODdjMjM2YzU1YTQ0NTM0YWU3ZGE0MTQ="
],
"content_id": "YmJi"
},
"client_max_hdcp_version": "HDCP_NONE",
"client_info": [
{
"name": "architecture_name",
"value": "x86-64"
},
{
"name": "company_name",
"value": "Google"
},
{
"name": "model_name",
"value": "ChromeCDM"
},
{
"name": "platform_name",
"value": "Linux"
},
{
"name": "widevine_cdm_version",
"value": "4.10.2209.0"
}
],
"signature_expiration_secs": 188559989,
"platform_verification_status": "PLATFORM_UNVERIFIED",
"content_owner": "***",
"content_provider": "***",
"system_id": 20121,
"oem_crypto_api_version": 16,
"resource_rating_tier": 0,
"default_device_security_profiles": {
"profile_name": [
"minimum"
]
},
"service_version_info": {
"license_sdk_version": "16.4.2 Built on Mar 29 2021 23:40:46 (1617086373)",
"license_service_version": "widevine_license_wls_20210302_202486-RC04"
}
},
"response_prototype": {
"content_key_specs": [
{
"key_id": "SBBgssxKQlisxKCRJtBGfw==",
"security_level": 1,
"required_output_protection": {
"hdcp": "HDCP_NONE",
"disable_analog_output": false,
"hdcp_srm_rule": "HDCP_SRM_RULE_NONE"
}
}
],
"session_init": {
"override_device_revocation": true
},
"use_policy_overrides_exclusively": true,
"policy_overrides": {
"license_duration_seconds": 0,
"playback_duration_seconds": 0,
"can_play": true,
"can_persist": false,
"can_renew": false
},
"allow_unverified_platform": true
}
}
заголовки {'User-Agent': 'drmnow! / widevine / 1.1', 'X-Project':
'[project]'}
policy_overrides.can_play
— если значение установлено true, то контент может быть воспроизведен. В противном случае контент не будет воспроизводиться. Используйте этот параметр для запрещения проигрывания, если данные о клиенте не удовлетворяют заданным вами условиям.
policy_overrides.can_persist
— если значение установлено true, то сгенерированная лицензия может быть сохранена на клиентском устройстве, что позволяет воспроизводить защищенный контент в режиме офлайн. По умолчанию false.
policy_overrides.license_duration_seconds
— количество времени в секундах, в течение которого лицензия действительна с момента получения. 0 (по умолчанию) — без ограничений.
policy_overrides.playback_duration_seconds
— количество времени в секундах, в течение которого лицензия действительна после начала воспроизведения. 0 (по умолчанию) — без ограничений. Применяется только при can_persist = true, в противном случае игнорируется.
policy_orverrides.rental_duration_seconds
— количество времени в секундах, в течение которого можно начать использовать лицензию. 0 (по умолчанию) — без ограничений. Применяется только при can_persist = true, в противном случае игнорируется.
policy_overrides.soft_enforce_playback_duration
— определяет, будет ли продолжено воспроизведение контента при истечении лицензии во время просмотра. По умолчанию — false (воспроизведение контента будет сразу же остановлено). При установке этого параметра равным true воспроизведение контента продолжится до первой остановки воспроизведения.
policy_overrides.soft_enforce_rental_duration
— определяет, будет ли продолжено воспроизведение контента при начале его воспроизведения после количества времени, равного rental_duration_seconds. По умолчанию — true (будет продолжено). При установке этого параметра равным false — воспроизведение будет запрещено.
policy.overrides.time_shift_limit_seconds
— сдвиг начала действия лицензии в секундах. 0 (по умолчанию) — сдвиг отсутствует.
content_key_specs[].security_level
— уровень защиты лицензии с данным ключом от 1 до 5 по классификации EME, который определяет, как будет происходить расшифровка ключа и декодирование медиа. Обратите внимание, что классификация уровней защиты лицензии по EME и Widevine различаются между собой (см табл. ниже).
Уровень защиты лицензии EME | Псевдоним уровня защиты | Соответствующий уровень защиты Widevine | Описание уровня защиты |
1 | SW_SECURE_CRYPTO | L3 | Расшифровка ключа выполняется на программном уровне |
2 | SW_SECURE_DECODE | L3 | Декодирование медиа выполняется на программном уровне |
3 | HW_SECURE_CRYPTO | L2 | Расшифровка ключа выполняется на аппаратном уровне* |
4 | HW_SECURE_DECODE | L1 | Декодирование медиа выполняется на аппаратном уровне* |
5 | HW_SECURE_ALL | L1 | Расшифровка ключа и декодирование медиа выполняются на аппаратном уровне*. |
* При использовании этого варианта настройки предполагается, что устройства и приложения клиентов поддерживают выполнение операций в доверенной среде исполнения (TEE, Trusted Execution Environment) — специальной безопасной среде исполнения программ, работающей изолированно от операционной системы.
Рекомендуется использовать как можно более высокое значение этого параметра.
content_key_specs[].required_output_protection.hdcp
— уровень защиты HDCP для лицензии. Доступные значения: HDCP_NONE (без HDCP, значение по умолчанию), HDCP_V1 (клиентское устройство должно поддерживать HDCP_V1), HDCP_V2 (клиентское устройство должно поддерживать HDCP 2.0), HDCP_V2_1 (клиентское устройство должно поддерживать HDCP 2.1), HDCP_V2_2 (клиентское устройство должно поддерживать HDCP 2.2), HDCP_V2_3 (клиентское устройство должно поддерживать HDCP 2.3), HDCP_NO_DIGITAL_OUTPUT (видеоконтент нельзя ретранслировать на другие видеоустройства, воспроизведение разрешено только непосредственно на экране клиентского устройства).
content_key_specs[].required_output_protection.disable_analog_output
— запрет вывода видеосигнала по аналоговым интерфейсам для данного ключа. Если true, то вывод запрещен. Если false, то вывод разрешен. По умолчанию false.
content_key_specs[].required_output_protection.hdcp_srm_rule
— определяет, возможна ли выдача лицензии для данного ключа, если клиентское устройство не поддерживает сообщения о возможности обновления системы (SRM). По умолчанию равно HDCP_SRM_RULE_NONE. При установке этого параметра равным CURRENT_SRM — данный ключ нельзя будет использовать на устройствах без поддержки обновлений SRM. Не рекомендуется изменять этот параметр без особой необходимости.
content_key_specs[].required_output_protection.cgms_flags
— определяет, должно ли клиентское устройство поддерживать CGMS. Доступные значения: CGMS_NONE (значение по умолчанию), COPY_FREE, COPY_ONCE, COPY_NEVER. Этот параметр НЕ указывается, если предполагается воспроизведение в браузерах на десктоп-устройствах. Не рекомендуется указывать и/или изменять этот параметр без особой необходимости.
session_init.override_device_revocation
— определяет, разрешено ли использовать лицензию на устройствах, признанных Widevine отозванными из-за недостаточной поддержки спецификации. По умолчанию — true. При установке в false лицензия не будет выдана. Не рекомендуется изменять этот параметр без особой необходимости.
policy_overrides.allow_unverified_platform
— определяет, разрешено ли использовать лицензию на платформах, признанных Widevine отозванными из-за недостаточной поддержки всех спецификации. По умолчанию — true. При установке в false лицензия не будет выдана. Не рекомендуется изменять этот параметр без особой необходимости.
{
"content_key_specs": [
{
"key_id": "SBBgssxKQlisxKCRJtBGfw==",
"security_level": 3,
"required_output_protection": {
"hdcp": "HDCP_NONE",
"disable_analog_output": false,
"hdcp_srm_rule": "HDCP_SRM_RULE_NONE"
}
}
],
"session_init": {
"override_device_revocation": true
},
"use_policy_overrides_exclusively": true,
"policy_overrides": {
"license_duration_seconds": 3600,
"playback_duration_seconds": 3600,
"can_play": true,
"can_persist": true,
"can_renew": false
},
"allow_unverified_platform": true
}
{
"original_headers": {
"host": "127.0.0.1:8000",
"user-agent": "curl/7.68.0",
"accept": "*/*",
"x-project": "[project]",
"content-length": "8346",
"content-type": "application/x-www-form-urlencoded",
"expect": "100-continue",
"X-Project": "[project]",
"QUERY_ARGS": "arg1=val1&arg2=val2"
},
"key_data": [
{
"track_type": "UHD1",
"content_id": "abcdef",
"key_id": "97ed5004a0d0a59dcc13e1ec26b23177"
}
],
"response_prototype": {
"resultCode": "success",
"contentid": "cHJlcHJvZDI5MDYyMWZpeGVk",
"keyAndPolicy": [
{
"userPolicy": {
"beginDate": 1625057628,
"expirationDate": 1625147628
},
"distributionMode": "VOD",
"keyInfo": {
"keyId": "97ed5004a0d0a59dcc13e1ec26b23177",
"keyEncryptedIV": "7g2GivJ8x+9Q17anjvPPZw=="
},
"contentPolicy": {
"securityLevel": 1,
"licenseType": "PERSISTENT",
"outputControl": 0
}
}
]
}
}
заголовки {'User-Agent': 'drmnow! / wiseplay / 1.1', 'X-Project':
'[project]'}
{
"resultCode": "success",
"contentid": "cHJlcHJvZDI5MDYyMWZpeGVk",
"keyAndPolicy": [
{
"userPolicy": {
"beginDate": 1625057628,
"expirationDate": 1625147628
},
"distributionMode": "VOD",
"keyInfo": {
"keyId": "97ed5004a0d0a59dcc13e1ec26b23177",
"keyEncryptedIV": "7g2GivJ8x+9Q17anjvPPZw=="
},
"contentPolicy": {
"securityLevel": 1,
"licenseType": "PERSISTENT",
"outputControl": 0
}
}
]
}
{
"original_headers": {
"host": "127.0.0.1:8000",
"user-agent": "curl/7.68.0",
"accept": "*/*",
"x-project": "[project]",
"content-length": "8346",
"content-type": "application/x-www-form-urlencoded",
"expect": "100-continue",
"X-Project": "[project]",
"QUERY_ARGS": "arg1=val1&arg2=val2"
},
"client_info": {
"client_security_level":"0",
"kext_deny_list_version":"0",
"device_type":"0",
"device_major":0,
"device_minor":0,
"device_extra":0,
"client_capabilities_provided":1,
"lease_supported":1,
"offline_key_tllv_supported":1,
"offline_key_tllv_v2_supported":0,
"hdcp_enforcement_supported":1,
"dual_expiry_supported":0,
"check_in_supported":0,
"streaming_type_indicator":"0",
"ar":"...",
"hu":"...",
"r1":"...",
"r2":"...",
"DAS_k":"...",
"sk":"..."
},
"key_data": [
{
"content_id": "content_id",
"key_id": "unknown"
}
],
"response_prototype": {
"content_key_specs": [
{
"content_id": "content_id",
"key_id": "unknown",
"rental_duration_seconds": 0,
"playback_duration_seconds": 0,
"persistence_is_allowed": false,
"persistence_duration_seconds": 0,
"lease_duration_seconds": 0,
"force_offline_key_tllv": true,
"required_hdcp_level": 1
}
]
}
}
заголовки {'User-Agent': 'drmnow! / fairplay / 1.1', 'X-Project':
'[project]'}
content_key_specs[].can_play
— если значение установлено true (по умолчанию), то контент может быть
воспроизведен. В противном случае контент не будет воспроизводиться. Используйте этот параметр для
запрещения проигрывания, если данные о клиенте не удовлетворяют заданным вами условиям.
content_key_specs[].persistence_is_allowed
— если значение установлено true, то сгенерированная лицензия
может быть сохранена на клиентском устройстве, что позволяет воспроизводить защищенный контент в режиме
офлайн. По умолчанию — false.
content_key_specs[].persistence_duration
— количество времени в секундах, в течение которого
офлайн-лицензия может быть активирована. Иными словами, это количество времени, в течение которого нужно
начать воспроизведение. 0 (по умолчанию) — без ограничений. Применяется только при
persistence_is_allowed = true, в противном случае игнорируется.
content_key_specs[].playback_duration
— количество времени в секундах, в течение которого лицензия
действительна после воспроизведения. 0 (по умолчанию) — без ограничений. Применяется только при
persistence_is_allowed = true, в противном случае игнорируется. Если этот период времени истек во время
воспроизведения, видео будет играть до первой остановки.
content_key_specs[].rental_duration
— работает аналогично persistence_duration, но применяется только для
онлайн-лицензий.
content_key_specs[].lease_duration
— количество времени в секундах, в течение которого лицензия
действительна с момента получения. 0 (по умолчанию) — без ограничений. Не применяется с
persistence_is_allowed = true. Если этот период времени истек во время воспроизведения, видео будет
остановлено немедленно. Используется для сложных сценариев использования (например, возобновляемые
лицензии). Для подробностей рекомендуем ознакомиться с руководством Fairplay.
content_key_specs[].force_offline_key_tllv
— указывает на структуру офлайн-лицензии. По умолчанию true.
Если вам нужно поддерживать старые устройства на iOS (до 11.0), установите это значение равным
false.
content_key_specs[].content_id
— идентификатор контента, без кодирования в base64. В ответе его значение
должно быть тем же, что и в запросе. Вы можете использовать это значение для формирования условий
переопределения настроек.
content_key_specs[].key_id
— идентификатор ключа (hex-строка). В ответе его значение должно быть тем же,
что и в запросе. Если ее исходно не было в запросе лицензии, то передается значение по умолчанию
"unknown". Вы можете использовать это значение для формирования условий переопределения настроек.
{
"content_key_specs": [
{
"content_id": "content_id",
"key_id": "unknown",
"rental_duration_seconds": 0,
"playback_duration_seconds": 0,
"can_play": true,
"persistence_is_allowed": false,
"persistence_duration_seconds": 0,
"lease_duration_seconds": 0,
"force_offline_key_tllv": true,
"required_hdcp_level": 1
}
]
}
{
"original_headers": {
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "*/*",
"Expect": "100-continue",
"Host": "127.0.0.1:8000",
"User-Agent": "curl/7.68.0",
"Content-Length": "9434",
"X-Project": "[project]",
"QUERY_ARGS": "arg1=val1&arg2=val2"
},
"key_data": [
{
"key_id": "c23841e3-be07-507f-7f12-7fdc579663ed",
"content_id": "content_id",
"quality": "legacy"
}
],
"client_info": {
"client_version": "10.0.16384.10011",
"client_supports_v3": true,
"client_security_level": 0
},
"response_prototype": {
"content_key_specs": [
{
"key_id": "c23841e3-be07-507f-7f12-7fdc579663ed",
"can_play": true,
"can_persist": true,
"security_level": "2000",
"license_duration_seconds": 0,
"playback_duration_seconds": 0
}
]
}
}
заголовки {'User-Agent': 'drmnow! / playready / 1.1', 'X-Project':
'[project]'}
content_key_specs[].can_play
— если значение установлено true (по умолчанию), то контент может быть воспроизведен. В противном случае контент не будет воспроизводиться. Используйте этот параметр для запрещения проигрывания, если данные о клиенте не удовлетворяют заданным вами условиям.
content_key_specs[].can_persist
— если значение установлено true (по умолчанию), то сгенерированная лицензия может быть сохранена на клиентском устройстве, что позволяет воспроизводить защищенный контент в режиме офлайн.
content_key_specs[].license_duration_seconds
— количество времени в секундах, в течение которого лицензия действительна с момента получения. 0 (по умолчанию) — без ограничений.
content_key_specs[].playback_duration_seconds
— количество времени в секундах, в течение которого лицензия действительна после начала воспроизведения. 0 (по умолчанию) — без ограничений. Применяется только при can_persist = true, в противном случае игнорируется.
content_key_specs[].grace_period_seconds
— количество времени в секундах, в течение которого офлайн-лицензия может быть активирована. Иными словами, это количество времени, в течение которого нужно начать воспроизведение. 0 (по умолчанию) — без ограничений. Применяется только при can_persist = true, в противном случае игнорируется.
content_key_specs[].security_level
— принимает одно из двух значений "2000" (по умолчанию) или "3000". "2000" — расшифровка ключа и декодирование медиа выполняются на программном уровне. "3000" — расшифровка ключа и декодирование медиа выполняются на аппаратном уровне.
content_key_specs[].key_id
— идентификатор ключа (hex-строка, форматированная в виде UUID). В ответе его значение должно быть тем же, что и в запросе. Вы можете использовать это значение для формирования условий переопределения настроек.
{
"content_key_specs": [
{
"key_id": "c23841e3-be07-507f-7f12-7fdc579663ed",
"can_play": true,
"can_persist": true,
"security_level": "3000",
"license_duration_seconds": 3600,
"playback_duration_seconds": 3600
}
]
}