encrypt video

Описание формата обмена данными с CAS

Содержание статьи:

Общие сведения

CAS (Conditional Access System / Система условного доступа) — это сервис, разработанный drmnow!, который позволяет индивидуально переопределять ограничения доступа к защищенному контенту на основе запросов с DRM-сервисов.

Примеры использования:

  • Ограничение максимально доступного качества видео.
  • Запрет воспроизведения для пользователей с определенными характеристиками устройства.
  • Настройка длительности доступа к воспроизведению.

Это руководство поможет вам сделать собственный вариант CAS, отвечающий вашим сценариям использования, и интегрировать его с нашими DRM-сервисами.

Как это работает?

DRM-сервис при каждом запросе лицензии отправляет в CAS информацию про исходные заголовки запроса, ключи шифрования контента, прототип ответа с ограничениями для лицензии по умолчанию, а также данные, зависящие от типа DRM. CAS обрабатывает полученные данные, и возвращает прототип ответа с замененными в нем ограничениями обратно в DRM-сервис, после чего тот применяет полученные ограничения к лицензии. Если в итоговых ограничениях воспроизведение разрешено, то генерируется лицензия с новыми ограничениями из CAS, и она возвращается клиенту. В противном случае клиенту возвращается ошибка. Если CAS недоступен или вернул ответ с кодом HTTP 4xx, 5xx, то лицензия также не будет выдана, а клиент получит ошибку.

Этот принцип работы также показан в виде схемы на изображении ниже.

принцип работы CAS

Логика, по которой происходит замена ограничений, определяется вашим сценарием использования, и не является строго фиксированной, она может быть воплощена с использованием любых удобных для вас языков программирования и фреймворков. Однако формат обмена данными при этом должен соответствовать описанному в данном руководстве.

Общий вид информации, отправляемой в 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 запроса должен содержать следующие поля:

  • original_headers
  • key_data
  • response_prototype
  • parse_only_data — только для Widevine
  • client_info — только для Playready

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-систем.

Widevine

Структура запроса:

{
  "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]'}

  • [project] — название вашего проекта в личном кабинете cdnnow!

Структура ответа:

{
  "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
}

Wiseplay

Структура запроса:

{
  "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]'}

  • [project] — название вашего проекта в личном кабинете cdnnow!

Структура ответа:

{
  "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
      }
    }
  ]
}

Fairplay

Структура запроса:

{
  "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": {
    "key_data": [
      {
        "content_id": "content_id"
      }
    ]
  },
  "response_prototype": {
    "content_key_specs": [
      {
        "content_id": "content_id",
        "rental_duration_seconds": 0,
        "playback_duration_seconds": 0,
        "persistence_is_allowed": false,
        "persistence_duration": 0,
        "lease_duration": 0,
        "force_offline_key_tllv": true
      }
    ]
  }
}

заголовки {'User-Agent': 'drmnow! / fairplay / 1.1', 'X-Project': '[project]'}

  • [project] — название вашего проекта в личном кабинете cdnnow!

Структура ответа:

{
  "content_key_specs": [
    {
      "content_id": "content_id",
      "rental_duration_seconds": 0,
      "playback_duration_seconds": 0,
      "persistence_is_allowed": false,
      "persistence_duration": 0,
      "lease_duration": 0,
      "force_offline_key_tllv": true
    }
  ]
}

Playready

Структура запроса:

{
  "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]'}

  • [project] — название вашего проекта в личном кабинете cdnnow!

Структура ответа:

{
  "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
    }
  ]
}
↑ Наверх
Оставьте заявку

если вас заинтересовал наш сервис или есть вопросы

Напишите нам