New
This commit is contained in:
parent
ca452cf3ad
commit
de9a72801e
11
Movie_AVC_1080p.bat
Normal file
11
Movie_AVC_1080p.bat
Normal file
@ -0,0 +1,11 @@
|
||||
@@ECHO OFF
|
||||
|
||||
set/p URL="Enter Primevideo URL= "
|
||||
set/p aud="Enter Audio Language= "
|
||||
|
||||
|
||||
wvripper.pyc %URL% --region eu --smart-select -vp AVC -q 1080 -gr FUCKU -o Downloads ^
|
||||
--audio-language %aud%
|
||||
|
||||
pause.
|
||||
|
11
Movie_AVC_720p.bat
Normal file
11
Movie_AVC_720p.bat
Normal file
@ -0,0 +1,11 @@
|
||||
@@ECHO OFF
|
||||
|
||||
set/p URL="Enter Primevideo URL= "
|
||||
set/p aud="Enter Audio Language= "
|
||||
|
||||
|
||||
wvripper.pyc %URL% --region eu --smart-select -vp AVC -q 720 -gr FUCKU -o Downloads ^
|
||||
--audio-language %aud%
|
||||
|
||||
|
||||
pause.
|
12
Movie_HDR_4K.bat
Normal file
12
Movie_HDR_4K.bat
Normal file
@ -0,0 +1,12 @@
|
||||
@@ECHO OFF
|
||||
|
||||
set/p URL="Enter Primevideo URL= "
|
||||
|
||||
|
||||
wvripper.pyc %URL% --region eu -vp UHD —android -q 2160 -gr FUCKU -o Downloads ^
|
||||
--audio-language en ^
|
||||
--subtitle-language en
|
||||
|
||||
|
||||
pause.
|
||||
|
11
Movie_HEVC_1080p.bat
Normal file
11
Movie_HEVC_1080p.bat
Normal file
@ -0,0 +1,11 @@
|
||||
@@ECHO OFF
|
||||
|
||||
set/p URL="Enter Primevideo URL= "
|
||||
set/p aud="Enter Audio Language= "
|
||||
|
||||
|
||||
wvripper.pyc %URL% --region eu --smart-select -vp HEVC -q 1080 -gr FUCKU -o Downloads ^
|
||||
--audio-language %aud%
|
||||
|
||||
|
||||
pause.
|
11
Movie_HEVC_720p.bat
Normal file
11
Movie_HEVC_720p.bat
Normal file
@ -0,0 +1,11 @@
|
||||
@@ECHO OFF
|
||||
|
||||
set/p URL="Enter Primevideo URL= "
|
||||
set/p aud="Enter Audio Language= "
|
||||
|
||||
|
||||
wvripper.pyc %URL% --region eu --smart-select -vp HEVC -q 720 -gr FUCKU -o Downloads ^
|
||||
--audio-language %aud%
|
||||
|
||||
|
||||
pause.
|
27
README.md
27
README.md
@ -1,2 +1,29 @@
|
||||
# WV-AMZN-4K-RIPPER
|
||||
Tool To download Amazon 4k SDR HDR 1080, CDM IS Not Included
|
||||
|
||||
For CDM You can Mail :- wvfuck@cyberfiends.net
|
||||
|
||||
Leaked with Lots of Love
|
||||
|
||||
__________
|
||||
.~#########%%;~.
|
||||
/############%%;`\
|
||||
/######/~\/~\%%;,;,\
|
||||
|#######\ /;;;;.,.|
|
||||
|#########\/%;;;;;.,.|
|
||||
XX |##/~~\####%;;;/~~\;,| XX
|
||||
XX..X |#| o \##%;/ o |.| X..XX
|
||||
XX.....X |##\____/##%;\____/.,| X.....XX
|
||||
XXXXX.....XX \#########/\;;;;;;,, / XX.....XXXXX
|
||||
X |......XX%,.@ \######/%;\;;;;, / @#%,XX......| X
|
||||
X |.....X @#%,.@ |######%%;;;;,.| @#%,.@ X.....| X
|
||||
X \...X @#%,.@ |# # # % ; ; ;,| @#%,.@ X.../ X
|
||||
X# \.X @#%,.@ @#%,.@ X./ #
|
||||
## X @#%,.@ @#%,.@ X #
|
||||
, "# #X @#%,.@ @#%,.@ X ##
|
||||
`###X @#%,.@ @#%,.@ ####'
|
||||
. ' ### @#%.,@ @#%,.@ ###`"
|
||||
. ";" @#%.@#%,.@ ;"` ' .
|
||||
' @#%,.@ ,.
|
||||
` , @#%,.@ @@ `
|
||||
@@@ @@@ .
|
||||
|
12
SeaSon_AVC_1080p.bat
Normal file
12
SeaSon_AVC_1080p.bat
Normal file
@ -0,0 +1,12 @@
|
||||
@@ECHO OFF
|
||||
|
||||
set/p URL="Enter Primevideo URL= "
|
||||
set/p aud="Enter Audio Language= "
|
||||
set/p Episode="Enter Episode= "
|
||||
|
||||
wvripper.pyc %URL% --region eu --smart-select -vp AVC -q 1080 -s 1 -e %Episode% -gr SaiTama -o Downloads ^
|
||||
--audio-language %aud%
|
||||
|
||||
|
||||
pause.
|
||||
|
11
SeaSon_AVC_720.bat
Normal file
11
SeaSon_AVC_720.bat
Normal file
@ -0,0 +1,11 @@
|
||||
@@ECHO OFF
|
||||
|
||||
set/p URL="Enter Primevideo URL= "
|
||||
set/p aud="Enter Audio Language= "
|
||||
set/p Episode="Enter Episode= "
|
||||
|
||||
wvripper.pyc %URL% --region eu --smart-select -vp AVC -q 720 -s 1 -e %Episode% -gr SaiTama -o Downloads ^
|
||||
--audio-language %aud%
|
||||
|
||||
|
||||
pause.
|
11
SeaSon_HDR_4K.bat
Normal file
11
SeaSon_HDR_4K.bat
Normal file
@ -0,0 +1,11 @@
|
||||
@@ECHO OFF
|
||||
|
||||
set/p URL="Enter Primevideo URL= "
|
||||
set/p Season="Enter Season Number= "
|
||||
|
||||
wvripper.pyc %URL% --region eu -vp UHD —android -q 2160 -s %Season% -gr SaiTama -o Downloads ^
|
||||
--audio-language en ^
|
||||
--subtitle-language en
|
||||
|
||||
|
||||
pause.
|
11
SeaSon_HEVC_1080p.bat
Normal file
11
SeaSon_HEVC_1080p.bat
Normal file
@ -0,0 +1,11 @@
|
||||
@@ECHO OFF
|
||||
|
||||
set/p URL="Enter Primevideo URL= "
|
||||
set/p aud="Enter Audio Language= "
|
||||
set/p Episode="Enter Episode= "
|
||||
|
||||
wvripper.pyc %URL% --region eu --smart-select -vp HEVC -q 1080 -s 1 -e %Episode% -gr FUCKU -o Downloads ^
|
||||
--audio-language %aud%
|
||||
|
||||
|
||||
pause.
|
11
SeaSon_HEVC_720.bat
Normal file
11
SeaSon_HEVC_720.bat
Normal file
@ -0,0 +1,11 @@
|
||||
@@ECHO OFF
|
||||
|
||||
set/p URL="Enter Primevideo URL= "
|
||||
set/p aud="Enter Audio Language= "
|
||||
set/p Episode="Enter Episode= "
|
||||
|
||||
wvripper.pyc %URL% --region eu --smart-select -vp HEVC -q 720 -s 1 -e %Episode% -gr SaiTama -o Downloads ^
|
||||
--audio-language %aud%
|
||||
|
||||
|
||||
pause.
|
BIN
configs/Cookies/cl (1).exe
Normal file
BIN
configs/Cookies/cl (1).exe
Normal file
Binary file not shown.
BIN
configs/Cookies/cl.exe
Normal file
BIN
configs/Cookies/cl.exe
Normal file
Binary file not shown.
0
configs/Cookies/cookies_de.txt
Normal file
0
configs/Cookies/cookies_de.txt
Normal file
0
configs/Cookies/cookies_jp.txt
Normal file
0
configs/Cookies/cookies_jp.txt
Normal file
17
configs/Cookies/cookies_ps.txt
Normal file
17
configs/Cookies/cookies_ps.txt
Normal file
@ -0,0 +1,17 @@
|
||||
# HTTP Cookie File downloaded with cookies.txt by Genuinous @genuinous
|
||||
# This file can be used by wget, curl, aria2c and other standard compliant tools.
|
||||
# Usage Examples:
|
||||
# 1) wget -x --load-cookies cookies.txt "https://www.primevideo.com/detail/The-Handmaids-Tale-Series/0QW7NQ3CQIVMI5ED05XYS19P9U"
|
||||
# 2) curl --cookie cookies.txt "https://www.primevideo.com/detail/The-Handmaids-Tale-Series/0QW7NQ3CQIVMI5ED05XYS19P9U"
|
||||
# 3) aria2c --load-cookies cookies.txt "https://www.primevideo.com/detail/The-Handmaids-Tale-Series/0QW7NQ3CQIVMI5ED05XYS19P9U"
|
||||
#
|
||||
.primevideo.com TRUE / TRUE 1653714942 session-id 257-0038347-0468755
|
||||
.primevideo.com TRUE / TRUE 1653714942 ubid-main-av 259-1850744-5064919
|
||||
.primevideo.com TRUE / FALSE 1652961059 i18n-prefs USD
|
||||
.primevideo.com TRUE / TRUE 1652961059 x-main-av "ScrnSKLUeYEm9QlZHlwt3dZo782g6?buizSHPaFdhvjGMEbmm8?7OjHJTqsIQG7?"
|
||||
.primevideo.com TRUE / TRUE 1652961059 at-main-av Atza|IwEBIPO8plmb1MYDw9U1C7pP8JqQyay6o4juc-tcKRWs1W94AYF7XzYzmuEnqnRafyMRsxqaZNAzceuYSWmM4hitJz9u0jbH3HGbdBzoiQBQNeFuAvub0p2Fmp3fpjy8c0185XsQkTjMxfWSoFs6brScaTfrgEl-xfoLNRCT7SNh6I8QgJh9V6onZA9KuRriLQvEBFPEow78ro1rFZZA4rWha2l9
|
||||
.primevideo.com TRUE / TRUE 1652961059 sess-at-main-av "pdEPotZ1sWXm/+f/qV0SJ1aLA+yLWwSM04HLxIlWf3g="
|
||||
.primevideo.com TRUE / FALSE 1655626116 lc-main-av de_DE
|
||||
.primevideo.com TRUE / TRUE 1653714712 session-token "mtRVF4lOAzfyhTEkcJTnul8r9UdIC5egMEPYermEqJg+SvS7WOQDkOQbk878qqNiKv5vFCF67IFoHc8JoJeehwOzyjeKEUtEqrct0T26fWavDo9ih4TuAmm78jI06nIutdB8QyhHcImqxDCHA9GtnAYD6/JSA8aPsY23+1g0dI85LcKTXb7rBk3LxihUBw9f6RVM3p1LwrtvMHMy1D7WZQ=="
|
||||
.primevideo.com TRUE / FALSE 1653714942 session-id-time 2082758401l
|
||||
www.primevideo.com FALSE / FALSE 1652418943 csm-hit tb:V4A6B5JR131JV44WAJEY+s-P6F7H6HX20CJ3MG3NNVX|1622178943543&t:1622178943544&adb:adblk_yes
|
0
configs/Cookies/cookies_uk.txt
Normal file
0
configs/Cookies/cookies_uk.txt
Normal file
22
configs/Cookies/cookies_us.txt
Normal file
22
configs/Cookies/cookies_us.txt
Normal file
@ -0,0 +1,22 @@
|
||||
# HTTP Cookie File downloaded with cookies.txt by Genuinous @genuinous
|
||||
# This file can be used by wget, curl, aria2c and other standard compliant tools.
|
||||
# Usage Examples:
|
||||
# 1) wget -x --load-cookies cookies.txt "https://www.amazon.com/gp/video/detail/B08WYHRBBH/ref=atv_3p_hbo_c_7bUTvm_brws_2_1"
|
||||
# 2) curl --cookie cookies.txt "https://www.amazon.com/gp/video/detail/B08WYHRBBH/ref=atv_3p_hbo_c_7bUTvm_brws_2_1"
|
||||
# 3) aria2c --load-cookies cookies.txt "https://www.amazon.com/gp/video/detail/B08WYHRBBH/ref=atv_3p_hbo_c_7bUTvm_brws_2_1"
|
||||
#
|
||||
.associates-amazon.com TRUE / TRUE 1624841566 abid e6bcb1cc-6df0-b57d-a226-76cb8be24829
|
||||
.amazon.com TRUE / TRUE 1653958585 ubid-main 131-9705044-5253861
|
||||
.amazon.com TRUE / FALSE 2082787201 lc-main en_US
|
||||
.amazon.com TRUE / TRUE 1653958161 ubid-main-av 135-0014401-4583503
|
||||
.amazon.com TRUE / TRUE 1653958016 lc-main-av zh_TW
|
||||
.amazon.com TRUE / FALSE 0 skin noskin
|
||||
.amazon.com TRUE / TRUE 1653958585 session-id 147-2017101-9262448
|
||||
.amazon.com TRUE / TRUE 1653958573 session-token "SmO/Pi7JXxECw4qGIpgvs60FQ04zRGX5ioS27++ZRs02GwR9E+7l9oC1H9oAIKjQOJL+7sU9ZXJswsGFu+Qdv1//hzkBsusXQJV28mHsY1Wn7RvcG+j8bzezebCTHMEMRFnn+E3QEhxTvgpOkiF66P8Z8ATlYYc0zf1GwPq+c6cYfy+k9xQrCIqdQ+YBdO4xPLP03jeKaBS8Ewebw5iIF9QHy8t98IDW16EU5zbqS4s="
|
||||
.amazon.com TRUE / TRUE 1653958573 x-main "9atgnNhYPLAdgqLwOubcLzhra0MEaqE9k0oOqn6APNo@e8g8QuzWUdaLTpqJ4KJ0"
|
||||
.amazon.com TRUE / TRUE 1653958447 at-main Atza|IwEBIOZTPeWg1i5KCfjkoVaDH7vU_Bj6fu4KqrDpv7xp1GcfTQyDNgoFJKaApqF7eG1lsEvwjTXgoIiCGteEhixyG_GO-dhQRQ8eYhkNNrL95YUnZvRCdCA68Mm1n5fR87hkUBFPTp6IO_wx4UOTdPcI-3AKP7D4b_3oA6xdYSkfZo4wrBjMCGX4ZkR2wKGC9rl_Bvs6vsgq4eIXtH_wqAHd0vP4
|
||||
.amazon.com TRUE / TRUE 1653958447 sess-at-main "YWyWD1e0HRsfoRcPJXFKGs+gDXtQTbas47fEVJzWn6A="
|
||||
.amazon.com TRUE / TRUE 1653958447 sst-main Sst1|PQEk337sIEiZEXH7Oe-lynv5CepAtSy7zc8lh22uwm5L-xvvDzQOc7tYMEpgNwBqURUo8mWwKPTVBSscQjMyNLam308eCgTuowo1WfAn7of4Ylspz5aFOamAE6cGqQUpzEiYTEDwSc7ARrs7RjDQ64Pet7vxHXE-ouJorEup35y-yAIv9UtaVIYXFI5WXnjTmO7phqWYvy2hINgw8XQUjhvT_dGYN6l8m_bpRGC1VwtN5d2qFXIUhe7anAGbg_lCufeXIPJZhsDNYXemHG2diPUYxW_Q7mF30t7l6XfHAjIqkM8
|
||||
.amazon.com TRUE / FALSE 1653958573 i18n-prefs USD
|
||||
.amazon.com TRUE / FALSE 1653958585 session-id-time 2082787201l
|
||||
www.amazon.com FALSE / FALSE 1652662578 csm-hit tb:s-KP49VK73TBB0WT6V4WM8|1622422577773&t:1622422578481&adb:adblk_no
|
17
configs/Cookies/included/cookies_ps.txt
Normal file
17
configs/Cookies/included/cookies_ps.txt
Normal file
@ -0,0 +1,17 @@
|
||||
# HTTP Cookie File downloaded with cookies.txt by Genuinous @genuinous
|
||||
# This file can be used by wget, curl, aria2c and other standard compliant tools.
|
||||
# Usage Examples:
|
||||
# 1) wget -x --load-cookies cookies.txt "https://www.primevideo.com/detail/The-Handmaids-Tale-Series/0QW7NQ3CQIVMI5ED05XYS19P9U"
|
||||
# 2) curl --cookie cookies.txt "https://www.primevideo.com/detail/The-Handmaids-Tale-Series/0QW7NQ3CQIVMI5ED05XYS19P9U"
|
||||
# 3) aria2c --load-cookies cookies.txt "https://www.primevideo.com/detail/The-Handmaids-Tale-Series/0QW7NQ3CQIVMI5ED05XYS19P9U"
|
||||
#
|
||||
.primevideo.com TRUE / TRUE 1653714942 session-id 257-0038347-0468755
|
||||
.primevideo.com TRUE / TRUE 1653714942 ubid-main-av 259-1850744-5064919
|
||||
.primevideo.com TRUE / FALSE 1652961059 i18n-prefs USD
|
||||
.primevideo.com TRUE / TRUE 1652961059 x-main-av "ScrnSKLUeYEm9QlZHlwt3dZo782g6?buizSHPaFdhvjGMEbmm8?7OjHJTqsIQG7?"
|
||||
.primevideo.com TRUE / TRUE 1652961059 at-main-av Atza|IwEBIPO8plmb1MYDw9U1C7pP8JqQyay6o4juc-tcKRWs1W94AYF7XzYzmuEnqnRafyMRsxqaZNAzceuYSWmM4hitJz9u0jbH3HGbdBzoiQBQNeFuAvub0p2Fmp3fpjy8c0185XsQkTjMxfWSoFs6brScaTfrgEl-xfoLNRCT7SNh6I8QgJh9V6onZA9KuRriLQvEBFPEow78ro1rFZZA4rWha2l9
|
||||
.primevideo.com TRUE / TRUE 1652961059 sess-at-main-av "pdEPotZ1sWXm/+f/qV0SJ1aLA+yLWwSM04HLxIlWf3g="
|
||||
.primevideo.com TRUE / FALSE 1655626116 lc-main-av de_DE
|
||||
.primevideo.com TRUE / TRUE 1653714712 session-token "mtRVF4lOAzfyhTEkcJTnul8r9UdIC5egMEPYermEqJg+SvS7WOQDkOQbk878qqNiKv5vFCF67IFoHc8JoJeehwOzyjeKEUtEqrct0T26fWavDo9ih4TuAmm78jI06nIutdB8QyhHcImqxDCHA9GtnAYD6/JSA8aPsY23+1g0dI85LcKTXb7rBk3LxihUBw9f6RVM3p1LwrtvMHMy1D7WZQ=="
|
||||
.primevideo.com TRUE / FALSE 1653714942 session-id-time 2082758401l
|
||||
www.primevideo.com FALSE / FALSE 1652418943 csm-hit tb:V4A6B5JR131JV44WAJEY+s-P6F7H6HX20CJ3MG3NNVX|1622178943543&t:1622178943544&adb:adblk_yes
|
23
configs/Cookies/included/cookies_us.txt
Normal file
23
configs/Cookies/included/cookies_us.txt
Normal file
@ -0,0 +1,23 @@
|
||||
# HTTP Cookie File for domains related to amazon.com.
|
||||
# Downloaded with cookies.txt Chrome Extension (https://chrome.google.com/webstore/detail/njabckikapfpffapmjgojcnbfjonfjfg)
|
||||
# Example: wget -x --load-cookies cookies.txt https://www.amazon.com/
|
||||
#
|
||||
.amazon.com TRUE / TRUE 1654080101 session-id 131-7859327-9648921
|
||||
.amazon.com TRUE / TRUE 1654080101 ubid-main 131-7622153-8191118
|
||||
.associates-amazon.com TRUE / TRUE 1624466535 abid a6bcba4b-184a-79ca-fd5d-4f939a73eff4
|
||||
.amazon.com TRUE / FALSE 2082787202 lc-main en_US
|
||||
.amazon.com TRUE / TRUE 1654068574 x-main "qMm01hOue0S61wKyr3cHx7JQW37zGNOnb@rWK7AbQW0XAur8YvcW@GCwGUXUT?nF"
|
||||
.amazon.com TRUE / TRUE 1652926420 at-main Atza|IwEBIJrxfwLUK6HS-5D8TJsvmL4GYytj6qGK2hQrp7WUkEiIb8f_Qyzh456DDgS9JQtoZKFf2j42hQeuN6Yyzp75vnDKBfdFibHk2ESKyrT-1F6wV6ygKbI2HCR6pPX9N93UZf2sBU6J0azAIeVu4no3G6GIlatI9h3SlLfxJxsOhQuDfhgKTNn1G6mekXGd903QYZdXRP9rWcO-jsW08B5yCT6u
|
||||
.amazon.com TRUE / TRUE 1652926420 sess-at-main "OyzHXBcU9E8NV4+67WI32JiY5Po+mdIs+9n8r/uz+oA="
|
||||
.amazon.com TRUE / TRUE 1652926420 sst-main Sst1|PQHUAEZSCnHyQoZlK5WlmQbGCdlKfFzmrEJi0D-iWa_UjWbZ6lny2eCUPGXNSdWqnb8GhfqNAqBf98GYYekTHdbxYc9YrS3pc-YHUc-0tDmNk4faQOAjV546D0zh8T0X05l-HsTZ5oU_vaZJkB1Zec-tb3ZRG3qxX5Lxb7vy6IAxG57036Ykv9IMyGOOCqdgoHa7YS09Ht_pAfAQZYN6VM-LzvnF68iAvAmtISSkyXnQme_PHgozlGGRRj27cJSDXLvQP6ZFNGhFq9kptOxx9Slw6UQBmx1eo0TyfeiCt_6kpAI
|
||||
.amazon.com TRUE / FALSE 1654068574 i18n-prefs USD
|
||||
.aws.amazon.com TRUE / FALSE 1653735711 aws-priv eyJ2IjoxLCJldSI6MCwic3QiOjB9
|
||||
.amazon.com TRUE / FALSE 1653736859 s_fid 08CC8BDD22AD0635-1817E9A096C69103
|
||||
.amazon.com TRUE / FALSE 1653736859 regStatus pre-register
|
||||
.amazon.com TRUE / FALSE 1655895709 aws-target-data %7B%22support%22%3A%221%22%7D
|
||||
.amazon.com TRUE / FALSE 1653735710 aws-target-visitor-id 1622199709297-329210.34_0
|
||||
.aws.amazon.com TRUE / FALSE 1653735710 _mkto_trk id:112-TZM-766&token:_mch-aws.amazon.com-1622199710412-51353
|
||||
.amazon.com TRUE / FALSE 0 skin noskin
|
||||
.amazon.com TRUE / FALSE 1654080101 session-id-time 2082787201l
|
||||
.amazon.com TRUE / FALSE 2082787201 session-token "K6hL7YwHIrZL5wFPfIRl1z+QoXk7m9aQEuDBE4eskYhfrFNtBFgI3XlG7WedhSP9drA1Ouawulrw+Sr+Gz8tsVgQVqOBLr6M30Eu3MmJkKCGDMe6CFLAqXuS3x8f5pnKA2WhkzmHePItVp+1UCp6PrrDB6MWvgK6kdTcWj3RLVd4graGyeAijznY1oHfOOaV7WsNZQpDvE6TW9m3sUOP/A=="
|
||||
www.amazon.com FALSE / FALSE 1652784100 csm-hit tb:s-MSQPAFJNYPA20RVBJ170|1622544099143&t:1622544100333&adb:adblk_no
|
1
configs/KEYS/external.txt
Normal file
1
configs/KEYS/external.txt
Normal file
@ -0,0 +1 @@
|
||||
KID:KEY
|
108
configs/Tokens/primevideo.json
Normal file
108
configs/Tokens/primevideo.json
Normal file
@ -0,0 +1,108 @@
|
||||
{
|
||||
"refresh_token": "Atnr|EwICIFqjcW3qfSYbPsGahXGhAsWeKSW8YbXqDEBQiFrzSH_SdXqEplGhBfcOGC03hdRtqmpIj3oFGwYx0TIwyaJWRqLU2mx0JR6E3iTt_9cSCgICt1hlV9LejnNHKvck5fEsZ1343oeDhfZ6br6FZkwsU_x-LNlvDyBBfRYddPMqLq-X7qBJWAXZuvG0YLxWDDuAN1A2BFU4QSWDYvEFuoeIcPgZQtkt5ebVdJxv1keiXF6F-Lgft5b1aBL-eK83G7ZFJPH-lanJZ_3tk_xTOTv13G8XBLeSwwPJ-r70tz8Ccao12g",
|
||||
"access_token": "bearer Atna|EwICID3U3hvIE0TqTBEPqG2B3Nn8Yhr-SFJ3e-aFoEec7oYtssLaFBAKBTmwz9arRM_fRHca2C_aRPMcA5SRto1HkreyzVEdKIfVKiibsZtAxEjeztj9XCCzkoFskeWdBf9nGtMC2872jbigtj0g6QDKiYvH2eh1I6l1Y3yDl-fA1DncGr5qum_lT88JIlMdT9sH8OkJ2YW1-aJhYuZDNXetN-tWTIrPanUQ6OEy9vTEa4ZpOLQEOTeKsJMV0M7aFEc68ZnGmBe-jHxKpbqbw20tnm6mFoVQ5gxySDh_L1qlZPd6WXZ9fiRNSEQwp58m5Vtkitw",
|
||||
"cookies": "session-id=145-5028685-5307043; ubid-main=134-7715513-6480940; x-main=\"1pTJtEcDLpLk25jg7gq0CiWz?6O2Y7EoeAgdCMGvicmGKGSsWY9cvH5XW@oZoTIw\"; at-main=\"Atza|IwEBIKY9z1_GBAJM5wyewJwl8Y_WWW45FX6YvfH_Xd8M8sPI6st3xj2x6DVYN90J508W0y2fFT_gJE2BYSDkezb9maFVMz3EZuIwvylzWOc2a4Qay8LME9fYqRZu2NA4EvsESRrWUgKhW-iX56dDL5Xg-9LJsNYD2l1kvVz4N1Tx4ZTDjfkiV5wRa8asBS-6htJGyOZ93xvhjz8oS3Aubed4SUHRfS1NPusExThL-6Pl4qsNFtuth_uVQRengHgzgj-7WAoGiW4NytrtlpxcXiL14MWseo0TdEZAuZEloLblad06a7t1vIy9HZfCnSUfLsKmAdBLAl2f9wd8uJgW5EHOeTHG\"; sess-at-main=\"eIH1m+wPTEwWNixmBz9UJe6KOE8CJT2qZSUcz3kt5Ug=\"",
|
||||
"device_serial": "9925GGTLDIMG71I5N730OKVLFL29M9FFYBTAZ4EA",
|
||||
"device_type": "A1KAXIG6VXSG8Y",
|
||||
"expire_in": 1622857693,
|
||||
"params": {
|
||||
"public_code": "BYXUY9",
|
||||
"private_code": "06be357b-269f-4a3e-9ea5-b13bb1ad6f2b",
|
||||
"domain": "com",
|
||||
"region": "ps",
|
||||
"ANDROID_REGISTRATION": {
|
||||
"domain": "Device",
|
||||
"app_name": "AIV",
|
||||
"app_version": "3.12.0",
|
||||
"device_model": "SHIELD Android TV",
|
||||
"os_version": "28",
|
||||
"device_type": "A1KAXIG6VXSG8Y",
|
||||
"device_serial": "9925GGTLDIMG71I5N730OKVLFL29M9FFYBTAZ4EA",
|
||||
"device_name": "%FIRST_NAME%'s%DUPE_STRATEGY_1ST% Shield TV",
|
||||
"software_version": "248"
|
||||
}
|
||||
},
|
||||
"RESPONSE": {
|
||||
"response": {
|
||||
"success": {
|
||||
"extensions": {
|
||||
"device_info": {
|
||||
"device_name": "Rajdeep's 3rd Shield TV",
|
||||
"device_serial_number": "9925GGTLDIMG71I5N730OKVLFL29M9FFYBTAZ4EA",
|
||||
"device_type": "A1KAXIG6VXSG8Y"
|
||||
},
|
||||
"customer_info": {
|
||||
"account_pool": "Amazon",
|
||||
"user_id": "amzn1.account.AELPYORG7577ZOG4WH33UGEQPU5A",
|
||||
"home_region": "NA",
|
||||
"name": "Rajdeep Biswas",
|
||||
"given_name": "Rajdeep"
|
||||
}
|
||||
},
|
||||
"tokens": {
|
||||
"website_cookies": [
|
||||
{
|
||||
"Path": "/",
|
||||
"Secure": "true",
|
||||
"Value": "145-5028685-5307043",
|
||||
"Expires": "30 May 2041 05:19:13 GMT",
|
||||
"Domain": ".amazon.com",
|
||||
"HttpOnly": "false",
|
||||
"Name": "session-id"
|
||||
},
|
||||
{
|
||||
"Path": "/",
|
||||
"Secure": "true",
|
||||
"Value": "134-7715513-6480940",
|
||||
"Expires": "30 May 2041 05:19:13 GMT",
|
||||
"Domain": ".amazon.com",
|
||||
"HttpOnly": "false",
|
||||
"Name": "ubid-main"
|
||||
},
|
||||
{
|
||||
"Path": "/",
|
||||
"Secure": "true",
|
||||
"Value": "\"1pTJtEcDLpLk25jg7gq0CiWz?6O2Y7EoeAgdCMGvicmGKGSsWY9cvH5XW@oZoTIw\"",
|
||||
"Expires": "30 May 2041 05:19:13 GMT",
|
||||
"Domain": ".amazon.com",
|
||||
"HttpOnly": "false",
|
||||
"Name": "x-main"
|
||||
},
|
||||
{
|
||||
"Path": "/",
|
||||
"Secure": "true",
|
||||
"Value": "\"Atza|IwEBIKY9z1_GBAJM5wyewJwl8Y_WWW45FX6YvfH_Xd8M8sPI6st3xj2x6DVYN90J508W0y2fFT_gJE2BYSDkezb9maFVMz3EZuIwvylzWOc2a4Qay8LME9fYqRZu2NA4EvsESRrWUgKhW-iX56dDL5Xg-9LJsNYD2l1kvVz4N1Tx4ZTDjfkiV5wRa8asBS-6htJGyOZ93xvhjz8oS3Aubed4SUHRfS1NPusExThL-6Pl4qsNFtuth_uVQRengHgzgj-7WAoGiW4NytrtlpxcXiL14MWseo0TdEZAuZEloLblad06a7t1vIy9HZfCnSUfLsKmAdBLAl2f9wd8uJgW5EHOeTHG\"",
|
||||
"Expires": "5 Jun 2021 05:19:13 GMT",
|
||||
"Domain": ".amazon.com",
|
||||
"HttpOnly": "true",
|
||||
"Name": "at-main"
|
||||
},
|
||||
{
|
||||
"Path": "/",
|
||||
"Secure": "true",
|
||||
"Value": "\"eIH1m+wPTEwWNixmBz9UJe6KOE8CJT2qZSUcz3kt5Ug=\"",
|
||||
"Expires": "5 Jun 2021 05:19:13 GMT",
|
||||
"Domain": ".amazon.com",
|
||||
"HttpOnly": "true",
|
||||
"Name": "sess-at-main"
|
||||
}
|
||||
],
|
||||
"store_authentication_cookie": {
|
||||
"cookie": "7oCEXDSy60WboDHQYCe/T0jQ+1NDez+6/5MbLVNZDtNmk1fz/74JBIZXldC+Auu3Kn2sLJsZ6a5nq4rGPWub3H47nq6NJEkcKkk/2GU49dTUX05cVFw+HDHStc/G0Wd5KwL0ad4VBuZ3FA0izAi2P0SFDIqt52JERuZfcfUf7kwIhz3B9cvOboS7EMBNzlLD"
|
||||
},
|
||||
"mac_dms": {
|
||||
"device_private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA0dXVFsllaGZK1SM9hr96Wc9qY6be/+39My9xGXv5FNyjvkjR\nK73WEq/jANaZ9Bv4vPHK75Qa/MmqGv9WDrjFCW+/525rz9MdgBSwtKidEHR7uCaI\nINtWlR5gPaMUmlHLYchRfWmxDIErO0WUhK5h7mhanp2EfA1UwMe2dZxJWp+esgSd\nxdvn+pW3hiGU0jB7+HGFLnxwPlASUXpLQkjXdeanBTtNCfX5m6QKREy0wTzpklCt\nJqMadva8ibYM12s3LF7N0hJhE7Vyp5pKAmbFG8pNgYbJ2CVG97bmgKYpgx/Yt+Mb\n70Z70xXZPUx3ZOSWsESmi3ah3VMsInJukwmp0wIDAQABAoIBAQDOXvbHBWX1yn0Y\nqCMPzmv/vi3NOkYiASc/2w3p2XK8oM9uCuGlmnvbPx8MKQObAJOfCFLlU41DRvdE\nvBEgQ3qN8FkVvRTNfb2KxU6RLy/iCh3PnB3P6sh5ujk/BY/ywjU7wy7aIEOfn74r\n+h+6amMofXt6ekLdWqMbQo/hlcbTF1W0zzI+aWbbGNdh+xavvD3LC37jGRD1VxNe\nvPATuX5MpCdTa/qrDbrnqlnKoXXbWO+DvNgMWi5IjCancqp9BW8J9rAQ5OA6fRLD\n7pFtWi+V18/Gttj6rOGvOpmC7LRp4JNSG2rQzR8PTg43R7dyd2dQIn4krswxKYPA\nOpRvi2gpAoGBAP5pudhprE5aaHeZTt+MytmJ4BiLgjoVbWOCtup/VVktEQbN7EUJ\nlJcWGF5Qo0C3WaN4vOlRKm5e/Kt1QvaHb8faE0zNh1WTqz47G8iVy+qehYE1PywG\nUabz0tau0y/ZZE/LXrQwVD2Dt4nwlfysc3/xnDs1pM0Wxxg2nMeehuHlAoGBANMk\n64EHCGHAvxU/y5mWre8zc4Fd66eipc8MFgPlJVCj9ffRpnUU9N9aRKuuilMbA82Z\nEL7MAXkfAlPCal2MGFlbJSYGvFygPeFg0NvQXoK+uc3A5nDJJBZrxGlutDVobs67\nnSpglt6b1A79029C3sYgs55ZDYsLPnQ6xKFp/QFXAoGALuHy0nOewg2dBLUmuMFs\nPCxRNJS42dVLBDR5Wrs+UiGg6p0V9h7u1+zFbtPzUaC4DwzX2zMO26n7a+k+/sk0\niyKfvd5+GwIMY3pnA06/diviNE5Ipuc/MsHpz5AF8OJhvuaePBxP2colD3FbYmWx\nHxm6DZs0FGsnI0olGBAZkRUCgYA3KOEBXzX4W6NPyM/Guw+LevSE6mVi8OHCOlUS\nBW3/KCZc7H9A/Q2PwARKmZl3ZopSztcKET+dRdWUzih9cdj2lxIYYhAZXp9r3qAR\nnNjj7TGbJ/bLpjBwTw0Lwij//t1b0rWXWY5wb2Mx22u+GEkcl3R1PKyGE5ToRmYS\nCR9YlwKBgQDfVuQZCfPJmDpKldf3up0hgY+ZC5SrpX3QM0Fz5RrhKwPfRvWENJMT\nEqyOXOGJ2RaDRcEjtF2b9/v3mTD5RB68Qozj0vTRPxjzbrjs5rg8VCVeZGBUlGRx\nnkUNDaeO4PYd4+UW2nNFEoawQYB1J71paOvk20QKueM1MRUpEI6gdA==\n-----END RSA PRIVATE KEY-----\n",
|
||||
"adp_token": "{enc:0p/vZ2wugGUr8L3A6hBKRzlgo5pHXOH2/Iu9fK4VYGxIUHhRrMtMonE9CFjTTNX6TcHPRXjUqWRPqvZo6Sg5NNua16bO/+SVtdOTGFyVg9dX6c81bCUtS6GyljEsvUSuq4kfbjo7l0Xgh27Y5ihASgwpMybD58+ifUqkgIuxNpUmWDOZiVZYi7+EUdM8EDilRlHf7LwOhkzGDSmaKKeSxRNNBgqozaUraUciM1+a3CM7lOFlJJePf/li4NpfdAC9A2Ww8TroIEE9HuiWCV/6t1HlBYKZH8RTgMiwQDpi7qc8hSlfE2gJwAAmlJEQUY8A7P+4EYa4fKU3tsIe0e1DF4pxMBzVSaTZH3EpHmrrpBKF4xywYKosCY/c/jxfS6gog4BecNXAIW8YL+nfpLtTLIqlTiKNHfTwm6AhT5TIgKq3sSWY9pwPO0BNov6tcXQ5caQllul1PJhLPN2PjPQ9/7fyzKjrxi3oZiVdS7gh7mlievOmplAVixc9K4fc/xiTy8fyQZqck1v/9paWqi6NJnQdYqt0wtK5qYz0p3oaLZI3G8/zz6MLeZxpKyXr88jqbvNthLpdxXe1cB5sRMNJRp8gSdluH5mR1dB05GAYwbU+GbckkEoQIqHB3vQLSMJUjMTXv/a43+uTqd8E1vjtMgrpy4mhbmcBrrPF1K0LWR8z3T2Z75sRWxNb8KpW43ouw0ck3Sm05cYegIma5b8P9/NbiRfZm/9Gz0GVQz25U1NqE4a7pBOk3jE3oooscsf3GZM+5TENvZDx4MAV979FEA/Z2k4jgrrOduTIYwfuF4PGjNkN1QsMuYczxHmrjsvuNJ3TM76/IcG5jBGfFQZBQO5Fzv3R7l4dlqISYtV17MkCDNnXwJ6rohnz8H0C+gxbR/Kcl+tWdtAAHkKYIgmCfkZgPByQTSKwC1je4EuC6YL96sR+LV5GUg8kaLJ/Ud3RZ/fnxZ+gi/jRskjKN2Hxn/8ZugMoxySfSc7RRBaSowUErnt8iPgg/8owi8OZUpMsDPr0sIKgjZScRklsJeAeDv1l031Y9EgabbCUm7O7Vtp1l6fXQWfKZcxYYKqypGBh}{key:Vc+00rapFNEmIT8NYQRlJqt8S9ZxCFpz+MVaGdng4jVu5EE00X/tFQzusA8FGaWW3rpbYqHSpKEZVa2gJapE4sgB9dWLaiT6tidNE4aKmgopF2/+ubc9lahrDjVTsvczx9RoTvvEtMgJtvwJ04rOb4N93q25K/IyUEFve7U/NcASSumpoB07Q2OIiW6DPHimqms/Y00W4NFq6F/unnO5vdEZWYeql+W3NTJd4pR+zC1rpgOpvFbquiibcgdxVm2Y4fQqNil6GdA9biCx7Otr6BudaJzxij1to9tH4tBt+LgibGrRA+aYWtrnt4rijor4tLdlqRo3jKaSBBZ+pYTO3w==}{iv:qThxxSnffr/fJPoOCU5U4w==}{name:QURQVG9rZW5FbmNyeXB0aW9uS2V5}{serial:Mg==}"
|
||||
},
|
||||
"bearer": {
|
||||
"access_token": "Atna|EwICIA7Jo3vvomqEX-KCECWtgoCap7ksP5_4S75wLzPrYDAGPYI2Jk06lVQJRuOZBCH7znrUETXpPp1Fbvsp-Ucn0CtT5Ql0gx5_damtPsnxoG1BiBo75Zjq_EZeyal8f12U53IVcXhLD2vjiis9s21PuP0uL3K6hH1qLgTktX6WfwAnt-Y8t7Kj_jFPf-0cjFlLHvs5gmEnKmqWDpxa4OSbougPMU1cmH5iXrWKuYAE-542nw",
|
||||
"refresh_token": "Atnr|EwICIFqjcW3qfSYbPsGahXGhAsWeKSW8YbXqDEBQiFrzSH_SdXqEplGhBfcOGC03hdRtqmpIj3oFGwYx0TIwyaJWRqLU2mx0JR6E3iTt_9cSCgICt1hlV9LejnNHKvck5fEsZ1343oeDhfZ6br6FZkwsU_x-LNlvDyBBfRYddPMqLq-X7qBJWAXZuvG0YLxWDDuAN1A2BFU4QSWDYvEFuoeIcPgZQtkt5ebVdJxv1keiXF6F-Lgft5b1aBL-eK83G7ZFJPH-lanJZ_3tk_xTOTv13G8XBLeSwwPJ-r70tz8Ccao12g",
|
||||
"expires_in": "3600"
|
||||
}
|
||||
},
|
||||
"customer_id": "amzn1.account.AELPYORG7577ZOG4WH33UGEQPU5A"
|
||||
}
|
||||
},
|
||||
"request_id": "e6229631-03db-41f4-a3ce-8aba27c91de7"
|
||||
}
|
||||
}
|
BIN
configs/__init__.pyc
Normal file
BIN
configs/__init__.pyc
Normal file
Binary file not shown.
13
configs/config.cfg
Normal file
13
configs/config.cfg
Normal file
@ -0,0 +1,13 @@
|
||||
[CONFIG]
|
||||
|
||||
|
||||
#####################PROXY#####################
|
||||
|
||||
nrd_email =
|
||||
nrd_password =
|
||||
|
||||
prv_email =
|
||||
prv_password =
|
||||
|
||||
tor_email =
|
||||
tor_password =
|
BIN
configs/config.pyc
Normal file
BIN
configs/config.pyc
Normal file
Binary file not shown.
BIN
helpers/Parsers.pyc
Normal file
BIN
helpers/Parsers.pyc
Normal file
Binary file not shown.
BIN
helpers/Parsers/__init__.pyc
Normal file
BIN
helpers/Parsers/__init__.pyc
Normal file
Binary file not shown.
BIN
helpers/Parsers/primevideo.pyc
Normal file
BIN
helpers/Parsers/primevideo.pyc
Normal file
Binary file not shown.
BIN
helpers/Parsers/primevideo/__init__.pyc
Normal file
BIN
helpers/Parsers/primevideo/__init__.pyc
Normal file
Binary file not shown.
BIN
helpers/Parsers/primevideo/clients.pyc
Normal file
BIN
helpers/Parsers/primevideo/clients.pyc
Normal file
Binary file not shown.
BIN
helpers/Parsers/primevideo/extractor.pyc
Normal file
BIN
helpers/Parsers/primevideo/extractor.pyc
Normal file
Binary file not shown.
BIN
helpers/Parsers/primevideo/license_request.pyc
Normal file
BIN
helpers/Parsers/primevideo/license_request.pyc
Normal file
Binary file not shown.
BIN
helpers/Parsers/primevideo/metadata.pyc
Normal file
BIN
helpers/Parsers/primevideo/metadata.pyc
Normal file
Binary file not shown.
BIN
helpers/Parsers/primevideo/metadata2.pyc
Normal file
BIN
helpers/Parsers/primevideo/metadata2.pyc
Normal file
Binary file not shown.
BIN
helpers/Parsers/primevideo/playback.pyc
Normal file
BIN
helpers/Parsers/primevideo/playback.pyc
Normal file
Binary file not shown.
BIN
helpers/Parsers/primevideo/prime.pyc
Normal file
BIN
helpers/Parsers/primevideo/prime.pyc
Normal file
Binary file not shown.
BIN
helpers/Parsers/primevideo/utils.pyc
Normal file
BIN
helpers/Parsers/primevideo/utils.pyc
Normal file
Binary file not shown.
BIN
helpers/Utils/ProxyHandler.pyc
Normal file
BIN
helpers/Utils/ProxyHandler.pyc
Normal file
Binary file not shown.
BIN
helpers/Utils/__init__.pyc
Normal file
BIN
helpers/Utils/__init__.pyc
Normal file
Binary file not shown.
BIN
helpers/Utils/aria2.pyc
Normal file
BIN
helpers/Utils/aria2.pyc
Normal file
Binary file not shown.
BIN
helpers/Utils/ffmpeg.pyc
Normal file
BIN
helpers/Utils/ffmpeg.pyc
Normal file
Binary file not shown.
BIN
helpers/Utils/keyloader.pyc
Normal file
BIN
helpers/Utils/keyloader.pyc
Normal file
Binary file not shown.
BIN
helpers/Utils/ripprocess.pyc
Normal file
BIN
helpers/Utils/ripprocess.pyc
Normal file
Binary file not shown.
BIN
helpers/Utils/schedule.pyc
Normal file
BIN
helpers/Utils/schedule.pyc
Normal file
Binary file not shown.
BIN
helpers/Utils/subtitle.pyc
Normal file
BIN
helpers/Utils/subtitle.pyc
Normal file
Binary file not shown.
BIN
helpers/Utils/utils.pyc
Normal file
BIN
helpers/Utils/utils.pyc
Normal file
Binary file not shown.
BIN
helpers/Utils/vpn.pyc
Normal file
BIN
helpers/Utils/vpn.pyc
Normal file
Binary file not shown.
BIN
helpers/__init__.pyc
Normal file
BIN
helpers/__init__.pyc
Normal file
Binary file not shown.
BIN
helpers/main.pyc
Normal file
BIN
helpers/main.pyc
Normal file
Binary file not shown.
BIN
helpers/tracks.pyc
Normal file
BIN
helpers/tracks.pyc
Normal file
Binary file not shown.
BIN
helpers/tracks_colored.pyc
Normal file
BIN
helpers/tracks_colored.pyc
Normal file
Binary file not shown.
BIN
helpers/wvdownloader.pyc
Normal file
BIN
helpers/wvdownloader.pyc
Normal file
Binary file not shown.
BIN
helpers/wvtracks.pyc
Normal file
BIN
helpers/wvtracks.pyc
Normal file
Binary file not shown.
BIN
pywidevine/cdm/__pycache__/cdm.cpython-39.pyc
Normal file
BIN
pywidevine/cdm/__pycache__/cdm.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pywidevine/cdm/__pycache__/deviceconfig.cpython-39.pyc
Normal file
BIN
pywidevine/cdm/__pycache__/deviceconfig.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pywidevine/cdm/__pycache__/key.cpython-39.pyc
Normal file
BIN
pywidevine/cdm/__pycache__/key.cpython-39.pyc
Normal file
Binary file not shown.
BIN
pywidevine/cdm/__pycache__/session.cpython-39.pyc
Normal file
BIN
pywidevine/cdm/__pycache__/session.cpython-39.pyc
Normal file
Binary file not shown.
369
pywidevine/cdm/cdm.py
Normal file
369
pywidevine/cdm/cdm.py
Normal file
@ -0,0 +1,369 @@
|
||||
#if __name__ == 'pywidevine.cdm':
|
||||
import base64
|
||||
|
||||
import os
|
||||
import time
|
||||
import binascii
|
||||
|
||||
from google.protobuf.message import DecodeError
|
||||
from google.protobuf import text_format
|
||||
|
||||
from pywidevine.cdm.formats import wv_proto2_pb2 as wv_proto2
|
||||
from pywidevine.cdm.session import Session
|
||||
from pywidevine.cdm.key import Key
|
||||
from Cryptodome.Random import get_random_bytes
|
||||
from Cryptodome.Random import random
|
||||
from Cryptodome.Cipher import PKCS1_OAEP, AES
|
||||
from Cryptodome.Hash import CMAC, SHA256, HMAC, SHA1
|
||||
from Cryptodome.PublicKey import RSA
|
||||
from Cryptodome.Signature import pss
|
||||
from Cryptodome.Util import Padding
|
||||
import logging
|
||||
import warnings
|
||||
|
||||
|
||||
class Cdm:
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self.sessions = {}
|
||||
|
||||
def open_session(self, init_data_b64, device, raw_init_data = None, offline=False):
|
||||
self.logger.debug("open_session(init_data_b64={}, device={}".format(init_data_b64, device))
|
||||
self.logger.info("opening new cdm session")
|
||||
if device.session_id_type == 'android':
|
||||
# format: 16 random hexdigits, 2 digit counter, 14 0s
|
||||
rand_ascii = ''.join(random.choice('ABCDEF0123456789') for _ in range(16))
|
||||
counter = '01' # this resets regularly so its fine to use 01
|
||||
rest = '00000000000000'
|
||||
session_id = rand_ascii + counter + rest
|
||||
session_id = session_id.encode('ascii')
|
||||
elif device.session_id_type == 'chrome':
|
||||
rand_bytes = get_random_bytes(16)
|
||||
session_id = rand_bytes
|
||||
else:
|
||||
# other formats NYI
|
||||
self.logger.error("device type is unusable")
|
||||
return 1
|
||||
if raw_init_data and isinstance(raw_init_data, (bytes, bytearray)):
|
||||
# used for NF key exchange, where they don't provide a valid PSSH
|
||||
init_data = raw_init_data
|
||||
self.raw_pssh = True
|
||||
else:
|
||||
init_data = self._parse_init_data(init_data_b64)
|
||||
self.raw_pssh = False
|
||||
|
||||
if init_data:
|
||||
new_session = Session(session_id, init_data, device, offline)
|
||||
else:
|
||||
self.logger.error("unable to parse init data")
|
||||
return 1
|
||||
self.sessions[session_id] = new_session
|
||||
self.logger.info("session opened and init data parsed successfully")
|
||||
return session_id
|
||||
|
||||
|
||||
def _parse_init_data(self, init_data_b64):
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter('error')
|
||||
parsed_init_data = wv_proto2.WidevineCencHeader()
|
||||
try:
|
||||
self.logger.debug("trying to parse init_data directly")
|
||||
parsed_init_data.ParseFromString(base64.b64decode(init_data_b64))
|
||||
except (DecodeError, SystemError):
|
||||
self.logger.debug("unable to parse as-is, trying with removed pssh box header")
|
||||
try:
|
||||
id_bytes = parsed_init_data.ParseFromString(base64.b64decode(init_data_b64)[32:])
|
||||
except (DecodeError, SystemError):
|
||||
self.logger.error("unable to parse, unsupported init data format")
|
||||
return None
|
||||
self.logger.debug("init_data:")
|
||||
for line in text_format.MessageToString(parsed_init_data).splitlines():
|
||||
self.logger.debug(line)
|
||||
return parsed_init_data
|
||||
|
||||
|
||||
def close_session(self, session_id):
|
||||
self.logger.debug("close_session(session_id={})".format(session_id))
|
||||
self.logger.info("closing cdm session")
|
||||
if session_id in self.sessions:
|
||||
self.sessions.pop(session_id)
|
||||
self.logger.info("cdm session closed")
|
||||
return 0
|
||||
else:
|
||||
self.logger.info("session {} not found".format(session_id))
|
||||
return 1
|
||||
|
||||
def set_service_certificate(self, session_id, cert_b64):
|
||||
self.logger.debug("set_service_certificate(session_id={}, cert={})".format(session_id, cert_b64))
|
||||
self.logger.info("setting service certificate")
|
||||
|
||||
if session_id not in self.sessions:
|
||||
self.logger.error("session id doesn't exist")
|
||||
return 1
|
||||
|
||||
session = self.sessions[session_id]
|
||||
|
||||
message = wv_proto2.SignedMessage()
|
||||
|
||||
try:
|
||||
message.ParseFromString(base64.b64decode(cert_b64))
|
||||
except DecodeError:
|
||||
self.logger.error("failed to parse cert as SignedMessage")
|
||||
|
||||
service_certificate = wv_proto2.SignedDeviceCertificate()
|
||||
|
||||
if message.Type:
|
||||
self.logger.debug("service cert provided as signedmessage")
|
||||
try:
|
||||
service_certificate.ParseFromString(message.Msg)
|
||||
except DecodeError:
|
||||
self.logger.error("failed to parse service certificate")
|
||||
return 1
|
||||
else:
|
||||
self.logger.debug("service cert provided as signeddevicecertificate")
|
||||
try:
|
||||
service_certificate.ParseFromString(base64.b64decode(cert_b64))
|
||||
except DecodeError:
|
||||
self.logger.error("failed to parse service certificate")
|
||||
return 1
|
||||
|
||||
self.logger.debug("service certificate:")
|
||||
for line in text_format.MessageToString(service_certificate).splitlines():
|
||||
self.logger.debug(line)
|
||||
|
||||
session.service_certificate = service_certificate
|
||||
session.privacy_mode = True
|
||||
|
||||
return 0
|
||||
|
||||
def get_license_request(self, session_id):
|
||||
self.logger.debug("get_license_request(session_id={})".format(session_id))
|
||||
self.logger.info("getting license request")
|
||||
|
||||
if session_id not in self.sessions:
|
||||
self.logger.error("session ID does not exist")
|
||||
return 1
|
||||
|
||||
session = self.sessions[session_id]
|
||||
|
||||
# raw pssh will be treated as bytes and not parsed
|
||||
if self.raw_pssh:
|
||||
license_request = wv_proto2.SignedLicenseRequestRaw()
|
||||
else:
|
||||
license_request = wv_proto2.SignedLicenseRequest()
|
||||
client_id = wv_proto2.ClientIdentification()
|
||||
|
||||
if not os.path.exists(session.device_config.device_client_id_blob_filename):
|
||||
self.logger.error("no client ID blob available for this device")
|
||||
return 1
|
||||
|
||||
with open(session.device_config.device_client_id_blob_filename, "rb") as f:
|
||||
try:
|
||||
cid_bytes = client_id.ParseFromString(f.read())
|
||||
except DecodeError:
|
||||
self.logger.error("client id failed to parse as protobuf")
|
||||
return 1
|
||||
|
||||
self.logger.debug("building license request")
|
||||
if not self.raw_pssh:
|
||||
license_request.Type = wv_proto2.SignedLicenseRequest.MessageType.Value('LICENSE_REQUEST')
|
||||
license_request.Msg.ContentId.CencId.Pssh.CopyFrom(session.init_data)
|
||||
else:
|
||||
license_request.Type = wv_proto2.SignedLicenseRequestRaw.MessageType.Value('LICENSE_REQUEST')
|
||||
license_request.Msg.ContentId.CencId.Pssh = session.init_data # bytes
|
||||
|
||||
if session.offline:
|
||||
license_type = wv_proto2.LicenseType.Value('OFFLINE')
|
||||
else:
|
||||
license_type = wv_proto2.LicenseType.Value('DEFAULT')
|
||||
license_request.Msg.ContentId.CencId.LicenseType = license_type
|
||||
license_request.Msg.ContentId.CencId.RequestId = session_id
|
||||
license_request.Msg.Type = wv_proto2.LicenseRequest.RequestType.Value('NEW')
|
||||
license_request.Msg.RequestTime = int(time.time())
|
||||
license_request.Msg.ProtocolVersion = wv_proto2.ProtocolVersion.Value('CURRENT')
|
||||
if session.device_config.send_key_control_nonce:
|
||||
license_request.Msg.KeyControlNonce = random.randrange(1, 2**31)
|
||||
|
||||
if session.privacy_mode:
|
||||
if session.device_config.vmp:
|
||||
self.logger.debug("vmp required, adding to client_id")
|
||||
self.logger.debug("reading vmp hashes")
|
||||
vmp_hashes = wv_proto2.FileHashes()
|
||||
with open(session.device_config.device_vmp_blob_filename, "rb") as f:
|
||||
try:
|
||||
vmp_bytes = vmp_hashes.ParseFromString(f.read())
|
||||
except DecodeError:
|
||||
self.logger.error("vmp hashes failed to parse as protobuf")
|
||||
return 1
|
||||
client_id._FileHashes.CopyFrom(vmp_hashes)
|
||||
self.logger.debug("privacy mode & service certificate loaded, encrypting client id")
|
||||
self.logger.debug("unencrypted client id:")
|
||||
for line in text_format.MessageToString(client_id).splitlines():
|
||||
self.logger.debug(line)
|
||||
cid_aes_key = get_random_bytes(16)
|
||||
cid_iv = get_random_bytes(16)
|
||||
|
||||
cid_cipher = AES.new(cid_aes_key, AES.MODE_CBC, cid_iv)
|
||||
|
||||
encrypted_client_id = cid_cipher.encrypt(Padding.pad(client_id.SerializeToString(), 16))
|
||||
|
||||
service_public_key = RSA.importKey(session.service_certificate._DeviceCertificate.PublicKey)
|
||||
|
||||
service_cipher = PKCS1_OAEP.new(service_public_key)
|
||||
|
||||
encrypted_cid_key = service_cipher.encrypt(cid_aes_key)
|
||||
|
||||
encrypted_client_id_proto = wv_proto2.EncryptedClientIdentification()
|
||||
|
||||
encrypted_client_id_proto.ServiceId = session.service_certificate._DeviceCertificate.ServiceId
|
||||
encrypted_client_id_proto.ServiceCertificateSerialNumber = session.service_certificate._DeviceCertificate.SerialNumber
|
||||
encrypted_client_id_proto.EncryptedClientId = encrypted_client_id
|
||||
encrypted_client_id_proto.EncryptedClientIdIv = cid_iv
|
||||
encrypted_client_id_proto.EncryptedPrivacyKey = encrypted_cid_key
|
||||
|
||||
license_request.Msg.EncryptedClientId.CopyFrom(encrypted_client_id_proto)
|
||||
else:
|
||||
license_request.Msg.ClientId.CopyFrom(client_id)
|
||||
|
||||
if session.device_config.private_key_available:
|
||||
key = RSA.importKey(open(session.device_config.device_private_key_filename).read())
|
||||
session.device_key = key
|
||||
else:
|
||||
self.logger.error("need device private key, other methods unimplemented")
|
||||
return 1
|
||||
|
||||
self.logger.debug("signing license request")
|
||||
|
||||
hash = SHA1.new(license_request.Msg.SerializeToString())
|
||||
signature = pss.new(key).sign(hash)
|
||||
|
||||
license_request.Signature = signature
|
||||
|
||||
session.license_request = license_request
|
||||
|
||||
self.logger.debug("license request:")
|
||||
for line in text_format.MessageToString(session.license_request).splitlines():
|
||||
self.logger.debug(line)
|
||||
self.logger.info("license request created")
|
||||
self.logger.debug("license request b64: {}".format(base64.b64encode(license_request.SerializeToString())))
|
||||
return license_request.SerializeToString()
|
||||
|
||||
def provide_license(self, session_id, license_b64):
|
||||
self.logger.debug("provide_license(session_id={}, license_b64={})".format(session_id, license_b64))
|
||||
self.logger.info("decrypting provided license")
|
||||
|
||||
if session_id not in self.sessions:
|
||||
self.logger.error("session does not exist")
|
||||
return 1
|
||||
|
||||
session = self.sessions[session_id]
|
||||
|
||||
if not session.license_request:
|
||||
self.logger.error("generate a license request first!")
|
||||
return 1
|
||||
|
||||
license = wv_proto2.SignedLicense()
|
||||
try:
|
||||
license.ParseFromString(base64.b64decode(license_b64))
|
||||
except DecodeError:
|
||||
self.logger.error("unable to parse license - check protobufs")
|
||||
return 1
|
||||
|
||||
session.license = license
|
||||
|
||||
self.logger.debug("license:")
|
||||
for line in text_format.MessageToString(license).splitlines():
|
||||
self.logger.debug(line)
|
||||
|
||||
self.logger.debug("deriving keys from session key")
|
||||
|
||||
oaep_cipher = PKCS1_OAEP.new(session.device_key)
|
||||
|
||||
session.session_key = oaep_cipher.decrypt(license.SessionKey)
|
||||
|
||||
lic_req_msg = session.license_request.Msg.SerializeToString()
|
||||
|
||||
enc_key_base = b"ENCRYPTION\000" + lic_req_msg + b"\0\0\0\x80"
|
||||
auth_key_base = b"AUTHENTICATION\0" + lic_req_msg + b"\0\0\2\0"
|
||||
|
||||
enc_key = b"\x01" + enc_key_base
|
||||
auth_key_1 = b"\x01" + auth_key_base
|
||||
auth_key_2 = b"\x02" + auth_key_base
|
||||
auth_key_3 = b"\x03" + auth_key_base
|
||||
auth_key_4 = b"\x04" + auth_key_base
|
||||
|
||||
cmac_obj = CMAC.new(session.session_key, ciphermod=AES)
|
||||
cmac_obj.update(enc_key)
|
||||
|
||||
enc_cmac_key = cmac_obj.digest()
|
||||
|
||||
cmac_obj = CMAC.new(session.session_key, ciphermod=AES)
|
||||
cmac_obj.update(auth_key_1)
|
||||
auth_cmac_key_1 = cmac_obj.digest()
|
||||
|
||||
cmac_obj = CMAC.new(session.session_key, ciphermod=AES)
|
||||
cmac_obj.update(auth_key_2)
|
||||
auth_cmac_key_2 = cmac_obj.digest()
|
||||
|
||||
cmac_obj = CMAC.new(session.session_key, ciphermod=AES)
|
||||
cmac_obj.update(auth_key_3)
|
||||
auth_cmac_key_3 = cmac_obj.digest()
|
||||
|
||||
cmac_obj = CMAC.new(session.session_key, ciphermod=AES)
|
||||
cmac_obj.update(auth_key_4)
|
||||
auth_cmac_key_4 = cmac_obj.digest()
|
||||
|
||||
auth_cmac_combined_1 = auth_cmac_key_1 + auth_cmac_key_2
|
||||
auth_cmac_combined_2 = auth_cmac_key_3 + auth_cmac_key_4
|
||||
|
||||
session.derived_keys['enc'] = enc_cmac_key
|
||||
session.derived_keys['auth_1'] = auth_cmac_combined_1
|
||||
session.derived_keys['auth_2'] = auth_cmac_combined_2
|
||||
|
||||
self.logger.debug('verifying license signature')
|
||||
|
||||
lic_hmac = HMAC.new(session.derived_keys['auth_1'], digestmod=SHA256)
|
||||
lic_hmac.update(license.Msg.SerializeToString())
|
||||
|
||||
self.logger.debug("calculated sig: {} actual sig: {}".format(lic_hmac.hexdigest(), binascii.hexlify(license.Signature)))
|
||||
|
||||
if lic_hmac.digest() != license.Signature:
|
||||
self.logger.info("license signature doesn't match - writing bin so they can be debugged")
|
||||
with open("original_lic.bin", "wb") as f:
|
||||
f.write(base64.b64decode(license_b64))
|
||||
with open("parsed_lic.bin", "wb") as f:
|
||||
f.write(license.SerializeToString())
|
||||
self.logger.info("continuing anyway")
|
||||
|
||||
self.logger.debug("key count: {}".format(len(license.Msg.Key)))
|
||||
for key in license.Msg.Key:
|
||||
if key.Id:
|
||||
key_id = key.Id
|
||||
else:
|
||||
key_id = wv_proto2.License.KeyContainer.KeyType.Name(key.Type).encode('utf-8')
|
||||
encrypted_key = key.Key
|
||||
iv = key.Iv
|
||||
type = wv_proto2.License.KeyContainer.KeyType.Name(key.Type)
|
||||
|
||||
cipher = AES.new(session.derived_keys['enc'], AES.MODE_CBC, iv=iv)
|
||||
decrypted_key = cipher.decrypt(encrypted_key)
|
||||
if type == "OPERATOR_SESSION":
|
||||
permissions = []
|
||||
perms = key._OperatorSessionKeyPermissions
|
||||
for (descriptor, value) in perms.ListFields():
|
||||
if value == 1:
|
||||
permissions.append(descriptor.name)
|
||||
#print(permissions)
|
||||
else:
|
||||
permissions = []
|
||||
session.keys.append(Key(key_id, type, Padding.unpad(decrypted_key, 16), permissions))
|
||||
|
||||
self.logger.info("decrypted all keys")
|
||||
return 0
|
||||
|
||||
def get_keys(self, session_id):
|
||||
if session_id in self.sessions:
|
||||
return self.sessions[session_id].keys
|
||||
else:
|
||||
self.logger.error("session not found")
|
||||
return 1
|
57
pywidevine/cdm/deviceconfig.py
Normal file
57
pywidevine/cdm/deviceconfig.py
Normal file
@ -0,0 +1,57 @@
|
||||
import os
|
||||
|
||||
device_sony_lvl1 = {
|
||||
'name': 'sony_lvl1',
|
||||
'description': 'sony d6503 firmware 6.0.1 lvl1 security level',
|
||||
'security_level': 1,
|
||||
'session_id_type': 'android',
|
||||
'private_key_available': True,
|
||||
'vmp': False,
|
||||
'send_key_control_nonce': True
|
||||
}
|
||||
|
||||
devices_available = [device_sony_lvl1]
|
||||
|
||||
#from __main__ import dirPath
|
||||
main_folder = os.path.dirname(__file__)
|
||||
#main_folder = os.path.join('.', 'pywidevine', 'cdm')
|
||||
|
||||
FILES_FOLDER = 'devices'
|
||||
|
||||
class DeviceConfig:
|
||||
def __init__(self, device):
|
||||
self.device_name = device['name']
|
||||
self.description = device['description']
|
||||
self.security_level = device['security_level']
|
||||
self.session_id_type = device['session_id_type']
|
||||
self.private_key_available = device['private_key_available']
|
||||
self.vmp = device['vmp']
|
||||
self.send_key_control_nonce = device['send_key_control_nonce']
|
||||
|
||||
if 'keybox_filename' in device:
|
||||
self.keybox_filename = os.path.join(main_folder, FILES_FOLDER, device['name'], device['keybox_filename'])
|
||||
else:
|
||||
self.keybox_filename = os.path.join(main_folder, FILES_FOLDER, device['name'], 'keybox')
|
||||
|
||||
if 'device_cert_filename' in device:
|
||||
self.device_cert_filename = os.path.join(main_folder, FILES_FOLDER, device['name'], device['device_cert_filename'])
|
||||
else:
|
||||
self.device_cert_filename = os.path.join(main_folder, FILES_FOLDER, device['name'], 'device_cert')
|
||||
|
||||
if 'device_private_key_filename' in device:
|
||||
self.device_private_key_filename = os.path.join(main_folder, FILES_FOLDER, device['name'], device['device_private_key_filename'])
|
||||
else:
|
||||
self.device_private_key_filename = os.path.join(main_folder, FILES_FOLDER, device['name'], 'device_private_key')
|
||||
|
||||
if 'device_client_id_blob_filename' in device:
|
||||
self.device_client_id_blob_filename = os.path.join(main_folder, FILES_FOLDER, device['name'], device['device_client_id_blob_filename'])
|
||||
else:
|
||||
self.device_client_id_blob_filename = os.path.join(main_folder, FILES_FOLDER, device['name'], 'device_client_id_blob')
|
||||
|
||||
if 'device_vmp_blob_filename' in device:
|
||||
self.device_vmp_blob_filename = os.path.join(main_folder, FILES_FOLDER, device['name'], device['device_vmp_blob_filename'])
|
||||
else:
|
||||
self.device_vmp_blob_filename = os.path.join(main_folder, FILES_FOLDER, device['name'], 'device_vmp_blob')
|
||||
|
||||
def __repr__(self):
|
||||
return "DeviceConfig(name={}, description={}, security_level={}, session_id_type={}, private_key_available={}, vmp={})".format(self.device_name, self.description, self.security_level, self.session_id_type, self.private_key_available, self.vmp)
|
21
pywidevine/cdm/devices/sony_lvl1/config.json
Normal file
21
pywidevine/cdm/devices/sony_lvl1/config.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"token": "z2.token",
|
||||
"client_info":
|
||||
{
|
||||
"company_name": "Sony",
|
||||
"model_name": "D6503",
|
||||
"architecture_name": "armeabi-v7a",
|
||||
"device_name": "D6503",
|
||||
"product_name": "D6503",
|
||||
"build_info": "Sony/D6503/D6503:6.0.1/23.5.A.1.291/2769308465:user/release-keys",
|
||||
"device_id": "U09OWV9YUEVSSUFfMDdkZV8wMDc1ZTkwMAAAAAAAAAA=",
|
||||
"os_version": "6.0.1"
|
||||
},
|
||||
"capabilities":
|
||||
{
|
||||
"session_token": 1,
|
||||
"max_hdcp_version": "HDCP_V2_2",
|
||||
"oem_crypto_api_version": 11
|
||||
}
|
||||
}
|
||||
|
BIN
pywidevine/cdm/devices/sony_lvl1/device_client_id_blob
Normal file
BIN
pywidevine/cdm/devices/sony_lvl1/device_client_id_blob
Normal file
Binary file not shown.
28
pywidevine/cdm/devices/sony_lvl1/device_private_key
Normal file
28
pywidevine/cdm/devices/sony_lvl1/device_private_key
Normal file
@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCsw9uELBU9gKUT
|
||||
Lt1eK+NKWHOJVrfxRFvF0yunCO5IH63QXyBVbZWziYJluHjLE9Fz6avSq3NB60Wy
|
||||
n2blN/gxAd2zTX9Nz2aqTU5N0lzfbi+aIr1KNmYxnJ76VOK7BA5n7+nLpA3DeUDW
|
||||
AXVK00yqO83ArElJXej+Lj74AFPzkN4ciGHH4MtN/OuYCe0PzCXGM23eaueHp9Gb
|
||||
w6dPOTUsKpywDNKPElFhJ4FWTr6Lw8y/hrmSlYGXh0HtQ4NtR2q/qWFW/ItukCDl
|
||||
IIRnxZeC2U/G9lOH02oYehJRe3Td44KmUE+XF0dD5gklhNDGbGCr3bbGAVt0Fxq8
|
||||
Qu0mIct1AgMBAAECggEANw78KHU8F6ul8CUhW1+1Qf5KY9yFJpjYfoU8BjGsbsPZ
|
||||
yuxmC5Ou/9a0/eH6bQ3V1JEFt0/4nWtzfOecDTT3HfcwRnJOPef5GhElAVwnSPV+
|
||||
qiqkFMCddPYrHDBtSZiTVsB4y/Nuh3tfFFTGoqAQcLg6bEr72CvbkxX97197YcWl
|
||||
VxEkJY/6ubb3j85zyq/3naktLcZQCE/9CCEo/ynqAEgITwWyFKtgVC7kYHj+OgvB
|
||||
1vMcKvgsbiF3ME2QUyjsbkbUGeM6hobZfR4gsbgS4WVZyr2nTY3jL8wuKEe3/jT/
|
||||
Qn7gGhJ/fGRSTYr/zi97UDSlsZjNOk31KLutBCiyCQKBgQDTrZlznpjUcL6ACIv2
|
||||
5umjS/MlxE1kZB5oTGBKTMA/nWITtnMMluNbkdyT7O0YLtI2Sth3P36P25bbqBvW
|
||||
ENBh7XwTZlumcFKdgJooF8SJildU0DFwPRoZTOPBVlzoJIWhOEeDGvkwmitlOdxw
|
||||
F9i5X4kbx9oE9buiwC2hPZkU8wKBgQDQ8G9ec46cNxEm0E6Us9VRQgfdVO5AZ2p1
|
||||
S8g/zzodj+9h74zEcfOGpN5SyPHhv3D2yYlgVCn/wJ9dTQPH7O9zz13vhxRNLkVb
|
||||
iIQh0uyT+I3OWOfR30Op6FmSNQXin0WLcNoRVUerc8arDkezy2O10HYql80wjovq
|
||||
PHJhHXBX9wKBgApV76g1lkDJQIP/5tWncMEIdFbjvuOn/9QX5pu6I6j3hlJwFi4H
|
||||
MBLIjHyslOcZYipEfU1VTFi54CPZkYJiT8p4ThakaNU3ptEJ9nz+rBoLJzh88e0J
|
||||
Dr0tg/FsxhyPq/azSMqeBozY+kV3DcxrXamvgIJav8p+NSs9Nv0ohNEBAoGAEiF7
|
||||
GJAghO/GXj7fQsokLav0MMGo9w/CTjFoTBk4as5NsIrwBS/6OOnfnQFw8+z/6Xpt
|
||||
oF1NU7MsYRVIybWGxd7twNZQ7a2hOSMsjB7YhrKf45MVcsHUBk9yTlypiRPXHhWJ
|
||||
5s0mrfsa90cC89gna+SODH4lwRvtikL6jfDXCdsCgYEApnOtzB40/B6+9heGC1Gl
|
||||
NP8f43sulFd5SmfTlO1aYR9ZgM2iyitFFJmmB2tFK5FShwrWPZGLD7z2yTwoZKKq
|
||||
IMlIwvqpyhVEY+qAVjxUA49EKfKph02M9gDkKY00dAKDIStGwAEDGHCTTUgVzoCv
|
||||
Px5FCz0Oml6fgbJD7kh1OPs=
|
||||
-----END PRIVATE KEY-----
|
BIN
pywidevine/cdm/devices/sony_lvl1/token.bin
Normal file
BIN
pywidevine/cdm/devices/sony_lvl1/token.bin
Normal file
Binary file not shown.
BIN
pywidevine/cdm/formats/__pycache__/wv_proto2_pb2.cpython-39.pyc
Normal file
BIN
pywidevine/cdm/formats/__pycache__/wv_proto2_pb2.cpython-39.pyc
Normal file
Binary file not shown.
466
pywidevine/cdm/formats/wv_proto2.proto
Normal file
466
pywidevine/cdm/formats/wv_proto2.proto
Normal file
@ -0,0 +1,466 @@
|
||||
syntax = "proto2";
|
||||
|
||||
// from x86 (partial), most of it from the ARM version:
|
||||
message ClientIdentification {
|
||||
enum TokenType {
|
||||
KEYBOX = 0;
|
||||
DEVICE_CERTIFICATE = 1;
|
||||
REMOTE_ATTESTATION_CERTIFICATE = 2;
|
||||
}
|
||||
message NameValue {
|
||||
required string Name = 1;
|
||||
required string Value = 2;
|
||||
}
|
||||
message ClientCapabilities {
|
||||
enum HdcpVersion {
|
||||
HDCP_NONE = 0;
|
||||
HDCP_V1 = 1;
|
||||
HDCP_V2 = 2;
|
||||
HDCP_V2_1 = 3;
|
||||
HDCP_V2_2 = 4;
|
||||
}
|
||||
optional uint32 ClientToken = 1;
|
||||
optional uint32 SessionToken = 2;
|
||||
optional uint32 VideoResolutionConstraints = 3;
|
||||
optional HdcpVersion MaxHdcpVersion = 4;
|
||||
optional uint32 OemCryptoApiVersion = 5;
|
||||
}
|
||||
required TokenType Type = 1;
|
||||
//optional bytes Token = 2; // by default the client treats this as blob, but it's usually a DeviceCertificate, so for usefulness sake, I'm replacing it with this one:
|
||||
optional SignedDeviceCertificate Token = 2; // use this when parsing, "bytes" when building a client id blob
|
||||
repeated NameValue ClientInfo = 3;
|
||||
optional bytes ProviderClientToken = 4;
|
||||
optional uint32 LicenseCounter = 5;
|
||||
optional ClientCapabilities _ClientCapabilities = 6; // how should we deal with duped names? will have to look at proto docs later
|
||||
optional FileHashes _FileHashes = 7; // vmp blob goes here
|
||||
}
|
||||
|
||||
message DeviceCertificate {
|
||||
enum CertificateType {
|
||||
ROOT = 0;
|
||||
INTERMEDIATE = 1;
|
||||
USER_DEVICE = 2;
|
||||
SERVICE = 3;
|
||||
}
|
||||
required CertificateType Type = 1; // the compiled code reused this as ProvisionedDeviceInfo.WvSecurityLevel, however that is incorrect (compiler aliased it as they're both identical as a structure)
|
||||
optional bytes SerialNumber = 2;
|
||||
optional uint32 CreationTimeSeconds = 3;
|
||||
optional bytes PublicKey = 4;
|
||||
optional uint32 SystemId = 5;
|
||||
optional uint32 TestDeviceDeprecated = 6; // is it bool or int?
|
||||
optional bytes ServiceId = 7; // service URL for service certificates
|
||||
}
|
||||
|
||||
// missing some references,
|
||||
message DeviceCertificateStatus {
|
||||
enum CertificateStatus {
|
||||
VALID = 0;
|
||||
REVOKED = 1;
|
||||
}
|
||||
optional bytes SerialNumber = 1;
|
||||
optional CertificateStatus Status = 2;
|
||||
optional ProvisionedDeviceInfo DeviceInfo = 4; // where is 3? is it deprecated?
|
||||
}
|
||||
|
||||
message DeviceCertificateStatusList {
|
||||
optional uint32 CreationTimeSeconds = 1;
|
||||
repeated DeviceCertificateStatus CertificateStatus = 2;
|
||||
}
|
||||
|
||||
message EncryptedClientIdentification {
|
||||
required string ServiceId = 1;
|
||||
optional bytes ServiceCertificateSerialNumber = 2;
|
||||
required bytes EncryptedClientId = 3;
|
||||
required bytes EncryptedClientIdIv = 4;
|
||||
required bytes EncryptedPrivacyKey = 5;
|
||||
}
|
||||
|
||||
// todo: fill (for this top-level type, it might be impossible/difficult)
|
||||
enum LicenseType {
|
||||
ZERO = 0;
|
||||
DEFAULT = 1; // 1 is STREAMING/temporary license; on recent versions may go up to 3 (latest x86); it might be persist/don't persist type, unconfirmed
|
||||
OFFLINE = 2;
|
||||
}
|
||||
|
||||
// todo: fill (for this top-level type, it might be impossible/difficult)
|
||||
// this is just a guess because these globals got lost, but really, do we need more?
|
||||
enum ProtocolVersion {
|
||||
CURRENT = 21; // don't have symbols for this
|
||||
}
|
||||
|
||||
|
||||
message LicenseIdentification {
|
||||
optional bytes RequestId = 1;
|
||||
optional bytes SessionId = 2;
|
||||
optional bytes PurchaseId = 3;
|
||||
optional LicenseType Type = 4;
|
||||
optional uint32 Version = 5;
|
||||
optional bytes ProviderSessionToken = 6;
|
||||
}
|
||||
|
||||
|
||||
message License {
|
||||
message Policy {
|
||||
optional bool CanPlay = 1; // changed from uint32 to bool
|
||||
optional bool CanPersist = 2;
|
||||
optional bool CanRenew = 3;
|
||||
optional uint32 RentalDurationSeconds = 4;
|
||||
optional uint32 PlaybackDurationSeconds = 5;
|
||||
optional uint32 LicenseDurationSeconds = 6;
|
||||
optional uint32 RenewalRecoveryDurationSeconds = 7;
|
||||
optional string RenewalServerUrl = 8;
|
||||
optional uint32 RenewalDelaySeconds = 9;
|
||||
optional uint32 RenewalRetryIntervalSeconds = 10;
|
||||
optional bool RenewWithUsage = 11; // was uint32
|
||||
}
|
||||
message KeyContainer {
|
||||
enum KeyType {
|
||||
SIGNING = 1;
|
||||
CONTENT = 2;
|
||||
KEY_CONTROL = 3;
|
||||
OPERATOR_SESSION = 4;
|
||||
}
|
||||
enum SecurityLevel {
|
||||
SW_SECURE_CRYPTO = 1;
|
||||
SW_SECURE_DECODE = 2;
|
||||
HW_SECURE_CRYPTO = 3;
|
||||
HW_SECURE_DECODE = 4;
|
||||
HW_SECURE_ALL = 5;
|
||||
}
|
||||
message OutputProtection {
|
||||
enum CGMS {
|
||||
COPY_FREE = 0;
|
||||
COPY_ONCE = 2;
|
||||
COPY_NEVER = 3;
|
||||
CGMS_NONE = 0x2A; // PC default!
|
||||
}
|
||||
optional ClientIdentification.ClientCapabilities.HdcpVersion Hdcp = 1; // it's most likely a copy of Hdcp version available here, but compiler optimized it away
|
||||
optional CGMS CgmsFlags = 2;
|
||||
}
|
||||
message KeyControl {
|
||||
required bytes KeyControlBlock = 1; // what is this?
|
||||
required bytes Iv = 2;
|
||||
}
|
||||
message OperatorSessionKeyPermissions {
|
||||
optional uint32 AllowEncrypt = 1;
|
||||
optional uint32 AllowDecrypt = 2;
|
||||
optional uint32 AllowSign = 3;
|
||||
optional uint32 AllowSignatureVerify = 4;
|
||||
}
|
||||
message VideoResolutionConstraint {
|
||||
optional uint32 MinResolutionPixels = 1;
|
||||
optional uint32 MaxResolutionPixels = 2;
|
||||
optional OutputProtection RequiredProtection = 3;
|
||||
}
|
||||
optional bytes Id = 1;
|
||||
optional bytes Iv = 2;
|
||||
optional bytes Key = 3;
|
||||
optional KeyType Type = 4;
|
||||
optional SecurityLevel Level = 5;
|
||||
optional OutputProtection RequiredProtection = 6;
|
||||
optional OutputProtection RequestedProtection = 7;
|
||||
optional KeyControl _KeyControl = 8; // duped names, etc
|
||||
optional OperatorSessionKeyPermissions _OperatorSessionKeyPermissions = 9; // duped names, etc
|
||||
repeated VideoResolutionConstraint VideoResolutionConstraints = 10;
|
||||
}
|
||||
optional LicenseIdentification Id = 1;
|
||||
optional Policy _Policy = 2; // duped names, etc
|
||||
repeated KeyContainer Key = 3;
|
||||
optional uint32 LicenseStartTime = 4;
|
||||
optional uint32 RemoteAttestationVerified = 5; // bool?
|
||||
optional bytes ProviderClientToken = 6;
|
||||
// there might be more, check with newer versions (I see field 7-8 in a lic)
|
||||
// this appeared in latest x86:
|
||||
optional uint32 ProtectionScheme = 7; // type unconfirmed fully, but it's likely as WidevineCencHeader describesit (fourcc)
|
||||
}
|
||||
|
||||
message LicenseError {
|
||||
enum Error {
|
||||
INVALID_DEVICE_CERTIFICATE = 1;
|
||||
REVOKED_DEVICE_CERTIFICATE = 2;
|
||||
SERVICE_UNAVAILABLE = 3;
|
||||
}
|
||||
//LicenseRequest.RequestType ErrorCode; // clang mismatch
|
||||
optional Error ErrorCode = 1;
|
||||
}
|
||||
|
||||
message LicenseRequest {
|
||||
message ContentIdentification {
|
||||
message CENC {
|
||||
//optional bytes Pssh = 1; // the client's definition is opaque, it doesn't care about the contents, but the PSSH has a clear definition that is understood and requested by the server, thus I'll replace it with:
|
||||
optional WidevineCencHeader Pssh = 1;
|
||||
optional LicenseType LicenseType = 2; // unfortunately the LicenseType symbols are not present, acceptable value seems to only be 1 (is this persist/don't persist? look into it!)
|
||||
optional bytes RequestId = 3;
|
||||
}
|
||||
message WebM {
|
||||
optional bytes Header = 1; // identical to CENC, aside from PSSH and the parent field number used
|
||||
optional LicenseType LicenseType = 2;
|
||||
optional bytes RequestId = 3;
|
||||
}
|
||||
message ExistingLicense {
|
||||
optional LicenseIdentification LicenseId = 1;
|
||||
optional uint32 SecondsSinceStarted = 2;
|
||||
optional uint32 SecondsSinceLastPlayed = 3;
|
||||
optional bytes SessionUsageTableEntry = 4; // interesting! try to figure out the connection between the usage table blob and KCB!
|
||||
}
|
||||
optional CENC CencId = 1;
|
||||
optional WebM WebmId = 2;
|
||||
optional ExistingLicense License = 3;
|
||||
}
|
||||
enum RequestType {
|
||||
NEW = 1;
|
||||
RENEWAL = 2;
|
||||
RELEASE = 3;
|
||||
}
|
||||
optional ClientIdentification ClientId = 1;
|
||||
optional ContentIdentification ContentId = 2;
|
||||
optional RequestType Type = 3;
|
||||
optional uint32 RequestTime = 4;
|
||||
optional bytes KeyControlNonceDeprecated = 5;
|
||||
optional ProtocolVersion ProtocolVersion = 6; // lacking symbols for this
|
||||
optional uint32 KeyControlNonce = 7;
|
||||
optional EncryptedClientIdentification EncryptedClientId = 8;
|
||||
}
|
||||
|
||||
// raw pssh hack
|
||||
message LicenseRequestRaw {
|
||||
message ContentIdentification {
|
||||
message CENC {
|
||||
optional bytes Pssh = 1; // the client's definition is opaque, it doesn't care about the contents, but the PSSH has a clear definition that is understood and requested by the server, thus I'll replace it with:
|
||||
//optional WidevineCencHeader Pssh = 1;
|
||||
optional LicenseType LicenseType = 2; // unfortunately the LicenseType symbols are not present, acceptable value seems to only be 1 (is this persist/don't persist? look into it!)
|
||||
optional bytes RequestId = 3;
|
||||
}
|
||||
message WebM {
|
||||
optional bytes Header = 1; // identical to CENC, aside from PSSH and the parent field number used
|
||||
optional LicenseType LicenseType = 2;
|
||||
optional bytes RequestId = 3;
|
||||
}
|
||||
message ExistingLicense {
|
||||
optional LicenseIdentification LicenseId = 1;
|
||||
optional uint32 SecondsSinceStarted = 2;
|
||||
optional uint32 SecondsSinceLastPlayed = 3;
|
||||
optional bytes SessionUsageTableEntry = 4; // interesting! try to figure out the connection between the usage table blob and KCB!
|
||||
}
|
||||
optional CENC CencId = 1;
|
||||
optional WebM WebmId = 2;
|
||||
optional ExistingLicense License = 3;
|
||||
}
|
||||
enum RequestType {
|
||||
NEW = 1;
|
||||
RENEWAL = 2;
|
||||
RELEASE = 3;
|
||||
}
|
||||
optional ClientIdentification ClientId = 1;
|
||||
optional ContentIdentification ContentId = 2;
|
||||
optional RequestType Type = 3;
|
||||
optional uint32 RequestTime = 4;
|
||||
optional bytes KeyControlNonceDeprecated = 5;
|
||||
optional ProtocolVersion ProtocolVersion = 6; // lacking symbols for this
|
||||
optional uint32 KeyControlNonce = 7;
|
||||
optional EncryptedClientIdentification EncryptedClientId = 8;
|
||||
}
|
||||
|
||||
|
||||
message ProvisionedDeviceInfo {
|
||||
enum WvSecurityLevel {
|
||||
LEVEL_UNSPECIFIED = 0;
|
||||
LEVEL_1 = 1;
|
||||
LEVEL_2 = 2;
|
||||
LEVEL_3 = 3;
|
||||
}
|
||||
optional uint32 SystemId = 1;
|
||||
optional string Soc = 2;
|
||||
optional string Manufacturer = 3;
|
||||
optional string Model = 4;
|
||||
optional string DeviceType = 5;
|
||||
optional uint32 ModelYear = 6;
|
||||
optional WvSecurityLevel SecurityLevel = 7;
|
||||
optional uint32 TestDevice = 8; // bool?
|
||||
}
|
||||
|
||||
|
||||
// todo: fill
|
||||
message ProvisioningOptions {
|
||||
}
|
||||
|
||||
// todo: fill
|
||||
message ProvisioningRequest {
|
||||
}
|
||||
|
||||
// todo: fill
|
||||
message ProvisioningResponse {
|
||||
}
|
||||
|
||||
message RemoteAttestation {
|
||||
optional EncryptedClientIdentification Certificate = 1;
|
||||
optional string Salt = 2;
|
||||
optional string Signature = 3;
|
||||
}
|
||||
|
||||
// todo: fill
|
||||
message SessionInit {
|
||||
}
|
||||
|
||||
// todo: fill
|
||||
message SessionState {
|
||||
}
|
||||
|
||||
// todo: fill
|
||||
message SignedCertificateStatusList {
|
||||
}
|
||||
|
||||
message SignedDeviceCertificate {
|
||||
|
||||
//optional bytes DeviceCertificate = 1; // again, they use a buffer where it's supposed to be a message, so we'll replace it with what it really is:
|
||||
optional DeviceCertificate _DeviceCertificate = 1; // how should we deal with duped names? will have to look at proto docs later
|
||||
optional bytes Signature = 2;
|
||||
optional SignedDeviceCertificate Signer = 3;
|
||||
}
|
||||
|
||||
|
||||
// todo: fill
|
||||
message SignedProvisioningMessage {
|
||||
}
|
||||
|
||||
// the root of all messages, from either server or client
|
||||
message SignedMessage {
|
||||
enum MessageType {
|
||||
LICENSE_REQUEST = 1;
|
||||
LICENSE = 2;
|
||||
ERROR_RESPONSE = 3;
|
||||
SERVICE_CERTIFICATE_REQUEST = 4;
|
||||
SERVICE_CERTIFICATE = 5;
|
||||
}
|
||||
optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel
|
||||
optional bytes Msg = 2; // this has to be casted dynamically, to LicenseRequest, License or LicenseError (? unconfirmed), for Request, no other fields but Type need to be present
|
||||
// for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE
|
||||
optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now)
|
||||
optional bytes SessionKey = 4; // often RSA wrapped for licenses
|
||||
optional RemoteAttestation RemoteAttestation = 5;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This message is copied from google's docs, not reversed:
|
||||
message WidevineCencHeader {
|
||||
enum Algorithm {
|
||||
UNENCRYPTED = 0;
|
||||
AESCTR = 1;
|
||||
};
|
||||
optional Algorithm algorithm = 1;
|
||||
repeated bytes key_id = 2;
|
||||
|
||||
// Content provider name.
|
||||
optional string provider = 3;
|
||||
|
||||
// A content identifier, specified by content provider.
|
||||
optional bytes content_id = 4;
|
||||
|
||||
// Track type. Acceptable values are SD, HD and AUDIO. Used to
|
||||
// differentiate content keys used by an asset.
|
||||
optional string track_type_deprecated = 5;
|
||||
|
||||
// The name of a registered policy to be used for this asset.
|
||||
optional string policy = 6;
|
||||
|
||||
// Crypto period index, for media using key rotation.
|
||||
optional uint32 crypto_period_index = 7;
|
||||