134 lines
4.6 KiB
Python
134 lines
4.6 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
# Module: widevine_keys
|
||
|
# Created on: 10.12.2021
|
||
|
# Authors: medvm
|
||
|
# Version: 2.1.0
|
||
|
|
||
|
import base64, requests, sys, xmltodict
|
||
|
import headers
|
||
|
# import cookies
|
||
|
import json
|
||
|
from cdm import cdm, deviceconfig
|
||
|
from base64 import b64encode
|
||
|
from getPSSH import get_pssh
|
||
|
from wvdecryptcustom import WvDecrypt
|
||
|
from cdm.formats import wv_proto2_pb2 as wv_proto2
|
||
|
from urllib.parse import urlparse
|
||
|
import logging
|
||
|
# logging.basicConfig(level=logging.DEBUG)
|
||
|
MDP_URL = input('\nInput MPD URL: ')
|
||
|
lic_url = input('License URL: ')
|
||
|
# hardcoded for kinopoisk.ru
|
||
|
# lic_url = 'https://widevine-proxy.ott.yandex.ru/proxy'
|
||
|
responses = []
|
||
|
license_b64 = ''
|
||
|
pssh = get_pssh(MDP_URL)
|
||
|
params = None
|
||
|
params = urlparse(lic_url).query
|
||
|
# pssh = 'AAAAXHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAADwIARIQ7iYSc3cNGm7XKPe3hSn3MhoIdXNwLWNlbmMiGDdpWVNjM2NOR203WEtQZTNoU24zTWc9PSoAMgA='
|
||
|
# params from mdp_url:
|
||
|
# ottsession=5945048d6f844d1699054cc5d44548f1&
|
||
|
# puid=339572866&
|
||
|
# video_content_id=4315082489d87677b21f7c83593fcb73&
|
||
|
|
||
|
print(f'{chr(10)}PSSH obtained.\n{pssh}')
|
||
|
|
||
|
def WV_Function(pssh, lic_url, cert_b64=None):
|
||
|
"""main func, emulates license request and then decrypt obtained license
|
||
|
fileds that changes every new request is signature, expirationTimestamp, watchSessionId, puid, and rawLicenseRequestBase64 """
|
||
|
wvdecrypt = WvDecrypt(init_data_b64=pssh, cert_data_b64=cert_b64, device=deviceconfig.device_android_generic)
|
||
|
raw_request = wvdecrypt.get_challenge()
|
||
|
request = b64encode(raw_request)
|
||
|
signature = cdm.hash_object
|
||
|
# basic, mostly sites works
|
||
|
responses.append(requests.post(url=lic_url, headers=headers.headers, data=raw_request, params=params))
|
||
|
# some another sites support
|
||
|
responses.append(requests.post(url=lic_url, headers=headers.headers, params=params,
|
||
|
json={
|
||
|
"rawLicenseRequestBase64": str(request, "utf-8" ),
|
||
|
}))
|
||
|
# kakaotv support
|
||
|
responses.append(requests.post(url=lic_url, headers=headers.headers, params=params,
|
||
|
data=f'token={headers.token}&provider={headers.provider}&payload={str(request, "utf-8" )}'
|
||
|
))
|
||
|
# xfinity.com support
|
||
|
headers.headers['licenseRequest'] = str(request, "utf-8" )
|
||
|
responses.append(requests.post(url=lic_url, headers=headers.headers, params=params,
|
||
|
))
|
||
|
del headers.headers['licenseRequest']
|
||
|
# rte.ie support
|
||
|
responses.append(requests.post(url=lic_url, headers=headers.headers, params=params,
|
||
|
json={
|
||
|
"getWidevineLicense":
|
||
|
{
|
||
|
'releasePid': headers.releasePid,
|
||
|
'widevineChallenge': str(request, "utf-8" )
|
||
|
},
|
||
|
}))
|
||
|
# kinopoisk support
|
||
|
responses.append(requests.post(url=lic_url, headers=headers.headers, params=params,
|
||
|
json={
|
||
|
"rawLicenseRequestBase64": str(request, "utf-8" ),
|
||
|
"puid": '339572866',
|
||
|
"watchSessionId": 'ed0e355063ac48b783130a390dc27ba6',
|
||
|
"contentId": '4315082489d87677b21f7c83593fcb73',
|
||
|
"contentTypeId": '21',
|
||
|
"serviceName": 'ott-kp',
|
||
|
"productId": '2',
|
||
|
"monetizationModel": 'SVOD',
|
||
|
"expirationTimestamp": '1639009453',
|
||
|
"verificationRequired": 'false',
|
||
|
"signature": str(signature),
|
||
|
"version": 'V4'
|
||
|
}))
|
||
|
for idx, response in enumerate(responses):
|
||
|
try:
|
||
|
str(response.content, "utf-8")
|
||
|
except UnicodeDecodeError:
|
||
|
widevine_license = response
|
||
|
print(f'{chr(10)}license response status: {widevine_license}{chr(10)}')
|
||
|
break
|
||
|
else:
|
||
|
if len(str(response.content, "utf-8")) > 500:
|
||
|
widevine_license = response
|
||
|
print(f'{chr(10)}license response status: {widevine_license}{chr(10)}')
|
||
|
break
|
||
|
if idx == len(responses) - 1:
|
||
|
print(f'{chr(10)}license response status: {response}')
|
||
|
print(f'server reports: {str(response.content, "utf-8")}')
|
||
|
print(f'server did not issue license, make sure you have correctly pasted all the required headers in the headers.py. Also check json/raw params of POST request.{chr(10)}')
|
||
|
exit()
|
||
|
|
||
|
lic_field_names = ['license', 'payload', 'getWidevineLicenseResponse']
|
||
|
lic_field_names2 = ['license']
|
||
|
|
||
|
open('license_content.bin', 'wb').write(widevine_license.content)
|
||
|
|
||
|
try:
|
||
|
if str(widevine_license.content, 'utf-8').find(':'):
|
||
|
for key in lic_field_names:
|
||
|
try:
|
||
|
license_b64 = json.loads(widevine_license.content.decode())[key]
|
||
|
except:
|
||
|
pass
|
||
|
else:
|
||
|
for key2 in lic_field_names2:
|
||
|
try:
|
||
|
license_b64 = json.loads(widevine_license.content.decode())[key][key2]
|
||
|
except:
|
||
|
pass
|
||
|
else:
|
||
|
license_b64 = widevine_license.content
|
||
|
except:
|
||
|
license_b64 = b64encode(widevine_license.content)
|
||
|
|
||
|
wvdecrypt.update_license(license_b64)
|
||
|
Correct, keyswvdecrypt = wvdecrypt.start_process()
|
||
|
if Correct:
|
||
|
return Correct, keyswvdecrypt
|
||
|
|
||
|
correct, keys = WV_Function(pssh, lic_url)
|
||
|
|
||
|
for key in keys:
|
||
|
print('KID:KEY -> ' + key)
|