diff --git a/Movie_AVC_1080p.bat b/Movie_AVC_1080p.bat new file mode 100644 index 0000000..7659a11 --- /dev/null +++ b/Movie_AVC_1080p.bat @@ -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. + diff --git a/Movie_AVC_720p.bat b/Movie_AVC_720p.bat new file mode 100644 index 0000000..40dadfd --- /dev/null +++ b/Movie_AVC_720p.bat @@ -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. diff --git a/Movie_HDR_4K.bat b/Movie_HDR_4K.bat new file mode 100644 index 0000000..a0436fe --- /dev/null +++ b/Movie_HDR_4K.bat @@ -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. + diff --git a/Movie_HEVC_1080p.bat b/Movie_HEVC_1080p.bat new file mode 100644 index 0000000..12711f8 --- /dev/null +++ b/Movie_HEVC_1080p.bat @@ -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. diff --git a/Movie_HEVC_720p.bat b/Movie_HEVC_720p.bat new file mode 100644 index 0000000..db4cde2 --- /dev/null +++ b/Movie_HEVC_720p.bat @@ -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. diff --git a/README.md b/README.md index aac4e38..011f905 100644 --- a/README.md +++ b/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 @#%,.@ @#%,.@ ####' + . ' ### @#%.,@ @#%,.@ ###`" + . ";" @#%.@#%,.@ ;"` ' . + ' @#%,.@ ,. + ` , @#%,.@ @@ ` + @@@ @@@ . diff --git a/SeaSon_AVC_1080p.bat b/SeaSon_AVC_1080p.bat new file mode 100644 index 0000000..3ba0be0 --- /dev/null +++ b/SeaSon_AVC_1080p.bat @@ -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. + diff --git a/SeaSon_AVC_720.bat b/SeaSon_AVC_720.bat new file mode 100644 index 0000000..14e2938 --- /dev/null +++ b/SeaSon_AVC_720.bat @@ -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. diff --git a/SeaSon_HDR_4K.bat b/SeaSon_HDR_4K.bat new file mode 100644 index 0000000..fffdf55 --- /dev/null +++ b/SeaSon_HDR_4K.bat @@ -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. diff --git a/SeaSon_HEVC_1080p.bat b/SeaSon_HEVC_1080p.bat new file mode 100644 index 0000000..80985b4 --- /dev/null +++ b/SeaSon_HEVC_1080p.bat @@ -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. diff --git a/SeaSon_HEVC_720.bat b/SeaSon_HEVC_720.bat new file mode 100644 index 0000000..9760d75 --- /dev/null +++ b/SeaSon_HEVC_720.bat @@ -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. diff --git a/configs/Cookies/cl (1).exe b/configs/Cookies/cl (1).exe new file mode 100644 index 0000000..e7def89 Binary files /dev/null and b/configs/Cookies/cl (1).exe differ diff --git a/configs/Cookies/cl.exe b/configs/Cookies/cl.exe new file mode 100644 index 0000000..09e0f33 Binary files /dev/null and b/configs/Cookies/cl.exe differ diff --git a/configs/Cookies/cookies_de.txt b/configs/Cookies/cookies_de.txt new file mode 100644 index 0000000..e69de29 diff --git a/configs/Cookies/cookies_jp.txt b/configs/Cookies/cookies_jp.txt new file mode 100644 index 0000000..e69de29 diff --git a/configs/Cookies/cookies_ps.txt b/configs/Cookies/cookies_ps.txt new file mode 100644 index 0000000..39b25f4 --- /dev/null +++ b/configs/Cookies/cookies_ps.txt @@ -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 diff --git a/configs/Cookies/cookies_uk.txt b/configs/Cookies/cookies_uk.txt new file mode 100644 index 0000000..e69de29 diff --git a/configs/Cookies/cookies_us.txt b/configs/Cookies/cookies_us.txt new file mode 100644 index 0000000..98c99bf --- /dev/null +++ b/configs/Cookies/cookies_us.txt @@ -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 diff --git a/configs/Cookies/included/cookies_ps.txt b/configs/Cookies/included/cookies_ps.txt new file mode 100644 index 0000000..39b25f4 --- /dev/null +++ b/configs/Cookies/included/cookies_ps.txt @@ -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 diff --git a/configs/Cookies/included/cookies_us.txt b/configs/Cookies/included/cookies_us.txt new file mode 100644 index 0000000..fdd6e01 --- /dev/null +++ b/configs/Cookies/included/cookies_us.txt @@ -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 diff --git a/configs/KEYS/external.txt b/configs/KEYS/external.txt new file mode 100644 index 0000000..d371587 --- /dev/null +++ b/configs/KEYS/external.txt @@ -0,0 +1 @@ +KID:KEY \ No newline at end of file diff --git a/configs/Tokens/primevideo.json b/configs/Tokens/primevideo.json new file mode 100644 index 0000000..b6ae388 --- /dev/null +++ b/configs/Tokens/primevideo.json @@ -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" + } +} \ No newline at end of file diff --git a/configs/__init__.pyc b/configs/__init__.pyc new file mode 100644 index 0000000..8b552f7 Binary files /dev/null and b/configs/__init__.pyc differ diff --git a/configs/config.cfg b/configs/config.cfg new file mode 100644 index 0000000..3aa4c71 --- /dev/null +++ b/configs/config.cfg @@ -0,0 +1,13 @@ +[CONFIG] + + +#####################PROXY##################### + +nrd_email = +nrd_password = + +prv_email = +prv_password = + +tor_email = +tor_password = \ No newline at end of file diff --git a/configs/config.pyc b/configs/config.pyc new file mode 100644 index 0000000..57ccca6 Binary files /dev/null and b/configs/config.pyc differ diff --git a/helpers/Parsers.pyc b/helpers/Parsers.pyc new file mode 100644 index 0000000..190c556 Binary files /dev/null and b/helpers/Parsers.pyc differ diff --git a/helpers/Parsers/__init__.pyc b/helpers/Parsers/__init__.pyc new file mode 100644 index 0000000..16ffdf1 Binary files /dev/null and b/helpers/Parsers/__init__.pyc differ diff --git a/helpers/Parsers/primevideo.pyc b/helpers/Parsers/primevideo.pyc new file mode 100644 index 0000000..190c556 Binary files /dev/null and b/helpers/Parsers/primevideo.pyc differ diff --git a/helpers/Parsers/primevideo/__init__.pyc b/helpers/Parsers/primevideo/__init__.pyc new file mode 100644 index 0000000..56dae60 Binary files /dev/null and b/helpers/Parsers/primevideo/__init__.pyc differ diff --git a/helpers/Parsers/primevideo/clients.pyc b/helpers/Parsers/primevideo/clients.pyc new file mode 100644 index 0000000..f7a8429 Binary files /dev/null and b/helpers/Parsers/primevideo/clients.pyc differ diff --git a/helpers/Parsers/primevideo/extractor.pyc b/helpers/Parsers/primevideo/extractor.pyc new file mode 100644 index 0000000..45f4c48 Binary files /dev/null and b/helpers/Parsers/primevideo/extractor.pyc differ diff --git a/helpers/Parsers/primevideo/license_request.pyc b/helpers/Parsers/primevideo/license_request.pyc new file mode 100644 index 0000000..13ab3f0 Binary files /dev/null and b/helpers/Parsers/primevideo/license_request.pyc differ diff --git a/helpers/Parsers/primevideo/metadata.pyc b/helpers/Parsers/primevideo/metadata.pyc new file mode 100644 index 0000000..a1ddb83 Binary files /dev/null and b/helpers/Parsers/primevideo/metadata.pyc differ diff --git a/helpers/Parsers/primevideo/metadata2.pyc b/helpers/Parsers/primevideo/metadata2.pyc new file mode 100644 index 0000000..b98a9e2 Binary files /dev/null and b/helpers/Parsers/primevideo/metadata2.pyc differ diff --git a/helpers/Parsers/primevideo/playback.pyc b/helpers/Parsers/primevideo/playback.pyc new file mode 100644 index 0000000..0bb3001 Binary files /dev/null and b/helpers/Parsers/primevideo/playback.pyc differ diff --git a/helpers/Parsers/primevideo/prime.pyc b/helpers/Parsers/primevideo/prime.pyc new file mode 100644 index 0000000..3288bd2 Binary files /dev/null and b/helpers/Parsers/primevideo/prime.pyc differ diff --git a/helpers/Parsers/primevideo/utils.pyc b/helpers/Parsers/primevideo/utils.pyc new file mode 100644 index 0000000..1483759 Binary files /dev/null and b/helpers/Parsers/primevideo/utils.pyc differ diff --git a/helpers/Utils/ProxyHandler.pyc b/helpers/Utils/ProxyHandler.pyc new file mode 100644 index 0000000..fa7595f Binary files /dev/null and b/helpers/Utils/ProxyHandler.pyc differ diff --git a/helpers/Utils/__init__.pyc b/helpers/Utils/__init__.pyc new file mode 100644 index 0000000..a516c6f Binary files /dev/null and b/helpers/Utils/__init__.pyc differ diff --git a/helpers/Utils/aria2.pyc b/helpers/Utils/aria2.pyc new file mode 100644 index 0000000..20946da Binary files /dev/null and b/helpers/Utils/aria2.pyc differ diff --git a/helpers/Utils/ffmpeg.pyc b/helpers/Utils/ffmpeg.pyc new file mode 100644 index 0000000..6d35b33 Binary files /dev/null and b/helpers/Utils/ffmpeg.pyc differ diff --git a/helpers/Utils/keyloader.pyc b/helpers/Utils/keyloader.pyc new file mode 100644 index 0000000..75478a8 Binary files /dev/null and b/helpers/Utils/keyloader.pyc differ diff --git a/helpers/Utils/ripprocess.pyc b/helpers/Utils/ripprocess.pyc new file mode 100644 index 0000000..9acf075 Binary files /dev/null and b/helpers/Utils/ripprocess.pyc differ diff --git a/helpers/Utils/schedule.pyc b/helpers/Utils/schedule.pyc new file mode 100644 index 0000000..f46a55e Binary files /dev/null and b/helpers/Utils/schedule.pyc differ diff --git a/helpers/Utils/subtitle.pyc b/helpers/Utils/subtitle.pyc new file mode 100644 index 0000000..5fb1848 Binary files /dev/null and b/helpers/Utils/subtitle.pyc differ diff --git a/helpers/Utils/utils.pyc b/helpers/Utils/utils.pyc new file mode 100644 index 0000000..e054234 Binary files /dev/null and b/helpers/Utils/utils.pyc differ diff --git a/helpers/Utils/vpn.pyc b/helpers/Utils/vpn.pyc new file mode 100644 index 0000000..2d144c6 Binary files /dev/null and b/helpers/Utils/vpn.pyc differ diff --git a/helpers/__init__.pyc b/helpers/__init__.pyc new file mode 100644 index 0000000..4ac09ac Binary files /dev/null and b/helpers/__init__.pyc differ diff --git a/helpers/main.pyc b/helpers/main.pyc new file mode 100644 index 0000000..126bc07 Binary files /dev/null and b/helpers/main.pyc differ diff --git a/helpers/tracks.pyc b/helpers/tracks.pyc new file mode 100644 index 0000000..f97847c Binary files /dev/null and b/helpers/tracks.pyc differ diff --git a/helpers/tracks_colored.pyc b/helpers/tracks_colored.pyc new file mode 100644 index 0000000..7837a51 Binary files /dev/null and b/helpers/tracks_colored.pyc differ diff --git a/helpers/wvdownloader.pyc b/helpers/wvdownloader.pyc new file mode 100644 index 0000000..4c18e38 Binary files /dev/null and b/helpers/wvdownloader.pyc differ diff --git a/helpers/wvtracks.pyc b/helpers/wvtracks.pyc new file mode 100644 index 0000000..5792a46 Binary files /dev/null and b/helpers/wvtracks.pyc differ diff --git a/pywidevine/cdm/__pycache__/cdm.cpython-39.pyc b/pywidevine/cdm/__pycache__/cdm.cpython-39.pyc new file mode 100644 index 0000000..dd21c02 Binary files /dev/null and b/pywidevine/cdm/__pycache__/cdm.cpython-39.pyc differ diff --git a/pywidevine/cdm/__pycache__/deviceconfig.cpython-39.pyc b/pywidevine/cdm/__pycache__/deviceconfig.cpython-39.pyc new file mode 100644 index 0000000..04cc736 Binary files /dev/null and b/pywidevine/cdm/__pycache__/deviceconfig.cpython-39.pyc differ diff --git a/pywidevine/cdm/__pycache__/key.cpython-39.pyc b/pywidevine/cdm/__pycache__/key.cpython-39.pyc new file mode 100644 index 0000000..a6079cc Binary files /dev/null and b/pywidevine/cdm/__pycache__/key.cpython-39.pyc differ diff --git a/pywidevine/cdm/__pycache__/session.cpython-39.pyc b/pywidevine/cdm/__pycache__/session.cpython-39.pyc new file mode 100644 index 0000000..2b83318 Binary files /dev/null and b/pywidevine/cdm/__pycache__/session.cpython-39.pyc differ diff --git a/pywidevine/cdm/cdm.py b/pywidevine/cdm/cdm.py new file mode 100644 index 0000000..2b8f1ab --- /dev/null +++ b/pywidevine/cdm/cdm.py @@ -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 diff --git a/pywidevine/cdm/deviceconfig.py b/pywidevine/cdm/deviceconfig.py new file mode 100644 index 0000000..15cff49 --- /dev/null +++ b/pywidevine/cdm/deviceconfig.py @@ -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) diff --git a/pywidevine/cdm/devices/sony_lvl1/config.json b/pywidevine/cdm/devices/sony_lvl1/config.json new file mode 100644 index 0000000..de8a6f8 --- /dev/null +++ b/pywidevine/cdm/devices/sony_lvl1/config.json @@ -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 + } +} + diff --git a/pywidevine/cdm/devices/sony_lvl1/device_client_id_blob b/pywidevine/cdm/devices/sony_lvl1/device_client_id_blob new file mode 100644 index 0000000..1c5ca7e Binary files /dev/null and b/pywidevine/cdm/devices/sony_lvl1/device_client_id_blob differ diff --git a/pywidevine/cdm/devices/sony_lvl1/device_private_key b/pywidevine/cdm/devices/sony_lvl1/device_private_key new file mode 100644 index 0000000..e6eba59 --- /dev/null +++ b/pywidevine/cdm/devices/sony_lvl1/device_private_key @@ -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----- \ No newline at end of file diff --git a/pywidevine/cdm/devices/sony_lvl1/token.bin b/pywidevine/cdm/devices/sony_lvl1/token.bin new file mode 100644 index 0000000..7689054 Binary files /dev/null and b/pywidevine/cdm/devices/sony_lvl1/token.bin differ diff --git a/pywidevine/cdm/formats/__pycache__/wv_proto2_pb2.cpython-39.pyc b/pywidevine/cdm/formats/__pycache__/wv_proto2_pb2.cpython-39.pyc new file mode 100644 index 0000000..c69d009 Binary files /dev/null and b/pywidevine/cdm/formats/__pycache__/wv_proto2_pb2.cpython-39.pyc differ diff --git a/pywidevine/cdm/formats/wv_proto2.proto b/pywidevine/cdm/formats/wv_proto2.proto new file mode 100644 index 0000000..04c9809 --- /dev/null +++ b/pywidevine/cdm/formats/wv_proto2.proto @@ -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; + + // Optional protected context for group content. The grouped_license is a + // serialized SignedMessage. + optional bytes grouped_license = 8; + + // Protection scheme identifying the encryption algorithm. + // Represented as one of the following 4CC values: + // 'cenc' (AESCTR), 'cbc1' (AESCBC), + // 'cens' (AESCTR subsample), 'cbcs' (AESCBC subsample). + optional uint32 protection_scheme = 9; + + // Optional. For media using key rotation, this represents the duration + // of each crypto period in seconds. + optional uint32 crypto_period_seconds = 10; +} + + +// remove these when using it outside of protoc: + +// from here on, it's just for testing, these messages don't exist in the binaries, I'm adding them to avoid detecting type programmatically +message SignedLicenseRequest { + 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 LicenseRequest 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; +} + +// hack +message SignedLicenseRequestRaw { + 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 LicenseRequestRaw 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; +} + + +message SignedLicense { + 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 License 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; +} + +message SignedServiceCertificate { + 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 SignedDeviceCertificate 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; +} + +//vmp support +message FileHashes { + message Signature { + optional string filename = 1; + optional bool test_signing = 2; //0 - release, 1 - testing + optional bytes SHA512Hash = 3; + optional bool main_exe = 4; //0 for dlls, 1 for exe, this is field 3 in file + optional bytes signature = 5; + } + optional bytes signer = 1; + repeated Signature signatures = 2; +} diff --git a/pywidevine/cdm/formats/wv_proto2_pb2.py b/pywidevine/cdm/formats/wv_proto2_pb2.py new file mode 100644 index 0000000..4c2a6d1 --- /dev/null +++ b/pywidevine/cdm/formats/wv_proto2_pb2.py @@ -0,0 +1,3324 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: pywidevine/cdm/formats/wv_proto2.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='pywidevine/cdm/formats/wv_proto2.proto', + package='', + syntax='proto2', + serialized_pb=_b('\n&pywidevine/cdm/formats/wv_proto2.proto\"\xe7\x05\n\x14\x43lientIdentification\x12-\n\x04Type\x18\x01 \x02(\x0e\x32\x1f.ClientIdentification.TokenType\x12\'\n\x05Token\x18\x02 \x01(\x0b\x32\x18.SignedDeviceCertificate\x12\x33\n\nClientInfo\x18\x03 \x03(\x0b\x32\x1f.ClientIdentification.NameValue\x12\x1b\n\x13ProviderClientToken\x18\x04 \x01(\x0c\x12\x16\n\x0eLicenseCounter\x18\x05 \x01(\r\x12\x45\n\x13_ClientCapabilities\x18\x06 \x01(\x0b\x32(.ClientIdentification.ClientCapabilities\x12 \n\x0b_FileHashes\x18\x07 \x01(\x0b\x32\x0b.FileHashes\x1a(\n\tNameValue\x12\x0c\n\x04Name\x18\x01 \x02(\t\x12\r\n\x05Value\x18\x02 \x02(\t\x1a\xa4\x02\n\x12\x43lientCapabilities\x12\x13\n\x0b\x43lientToken\x18\x01 \x01(\r\x12\x14\n\x0cSessionToken\x18\x02 \x01(\r\x12\"\n\x1aVideoResolutionConstraints\x18\x03 \x01(\r\x12L\n\x0eMaxHdcpVersion\x18\x04 \x01(\x0e\x32\x34.ClientIdentification.ClientCapabilities.HdcpVersion\x12\x1b\n\x13OemCryptoApiVersion\x18\x05 \x01(\r\"T\n\x0bHdcpVersion\x12\r\n\tHDCP_NONE\x10\x00\x12\x0b\n\x07HDCP_V1\x10\x01\x12\x0b\n\x07HDCP_V2\x10\x02\x12\r\n\tHDCP_V2_1\x10\x03\x12\r\n\tHDCP_V2_2\x10\x04\"S\n\tTokenType\x12\n\n\x06KEYBOX\x10\x00\x12\x16\n\x12\x44\x45VICE_CERTIFICATE\x10\x01\x12\"\n\x1eREMOTE_ATTESTATION_CERTIFICATE\x10\x02\"\x9b\x02\n\x11\x44\x65viceCertificate\x12\x30\n\x04Type\x18\x01 \x02(\x0e\x32\".DeviceCertificate.CertificateType\x12\x14\n\x0cSerialNumber\x18\x02 \x01(\x0c\x12\x1b\n\x13\x43reationTimeSeconds\x18\x03 \x01(\r\x12\x11\n\tPublicKey\x18\x04 \x01(\x0c\x12\x10\n\x08SystemId\x18\x05 \x01(\r\x12\x1c\n\x14TestDeviceDeprecated\x18\x06 \x01(\r\x12\x11\n\tServiceId\x18\x07 \x01(\x0c\"K\n\x0f\x43\x65rtificateType\x12\x08\n\x04ROOT\x10\x00\x12\x10\n\x0cINTERMEDIATE\x10\x01\x12\x0f\n\x0bUSER_DEVICE\x10\x02\x12\x0b\n\x07SERVICE\x10\x03\"\xc4\x01\n\x17\x44\x65viceCertificateStatus\x12\x14\n\x0cSerialNumber\x18\x01 \x01(\x0c\x12:\n\x06Status\x18\x02 \x01(\x0e\x32*.DeviceCertificateStatus.CertificateStatus\x12*\n\nDeviceInfo\x18\x04 \x01(\x0b\x32\x16.ProvisionedDeviceInfo\"+\n\x11\x43\x65rtificateStatus\x12\t\n\x05VALID\x10\x00\x12\x0b\n\x07REVOKED\x10\x01\"o\n\x1b\x44\x65viceCertificateStatusList\x12\x1b\n\x13\x43reationTimeSeconds\x18\x01 \x01(\r\x12\x33\n\x11\x43\x65rtificateStatus\x18\x02 \x03(\x0b\x32\x18.DeviceCertificateStatus\"\xaf\x01\n\x1d\x45ncryptedClientIdentification\x12\x11\n\tServiceId\x18\x01 \x02(\t\x12&\n\x1eServiceCertificateSerialNumber\x18\x02 \x01(\x0c\x12\x19\n\x11\x45ncryptedClientId\x18\x03 \x02(\x0c\x12\x1b\n\x13\x45ncryptedClientIdIv\x18\x04 \x02(\x0c\x12\x1b\n\x13\x45ncryptedPrivacyKey\x18\x05 \x02(\x0c\"\x9c\x01\n\x15LicenseIdentification\x12\x11\n\tRequestId\x18\x01 \x01(\x0c\x12\x11\n\tSessionId\x18\x02 \x01(\x0c\x12\x12\n\nPurchaseId\x18\x03 \x01(\x0c\x12\x1a\n\x04Type\x18\x04 \x01(\x0e\x32\x0c.LicenseType\x12\x0f\n\x07Version\x18\x05 \x01(\r\x12\x1c\n\x14ProviderSessionToken\x18\x06 \x01(\x0c\"\xa1\x0e\n\x07License\x12\"\n\x02Id\x18\x01 \x01(\x0b\x32\x16.LicenseIdentification\x12 \n\x07_Policy\x18\x02 \x01(\x0b\x32\x0f.License.Policy\x12\"\n\x03Key\x18\x03 \x03(\x0b\x32\x15.License.KeyContainer\x12\x18\n\x10LicenseStartTime\x18\x04 \x01(\r\x12!\n\x19RemoteAttestationVerified\x18\x05 \x01(\r\x12\x1b\n\x13ProviderClientToken\x18\x06 \x01(\x0c\x12\x18\n\x10ProtectionScheme\x18\x07 \x01(\r\x1a\xbb\x02\n\x06Policy\x12\x0f\n\x07\x43\x61nPlay\x18\x01 \x01(\x08\x12\x12\n\nCanPersist\x18\x02 \x01(\x08\x12\x10\n\x08\x43\x61nRenew\x18\x03 \x01(\x08\x12\x1d\n\x15RentalDurationSeconds\x18\x04 \x01(\r\x12\x1f\n\x17PlaybackDurationSeconds\x18\x05 \x01(\r\x12\x1e\n\x16LicenseDurationSeconds\x18\x06 \x01(\r\x12&\n\x1eRenewalRecoveryDurationSeconds\x18\x07 \x01(\r\x12\x18\n\x10RenewalServerUrl\x18\x08 \x01(\t\x12\x1b\n\x13RenewalDelaySeconds\x18\t \x01(\r\x12#\n\x1bRenewalRetryIntervalSeconds\x18\n \x01(\r\x12\x16\n\x0eRenewWithUsage\x18\x0b \x01(\x08\x1a\xf9\t\n\x0cKeyContainer\x12\n\n\x02Id\x18\x01 \x01(\x0c\x12\n\n\x02Iv\x18\x02 \x01(\x0c\x12\x0b\n\x03Key\x18\x03 \x01(\x0c\x12+\n\x04Type\x18\x04 \x01(\x0e\x32\x1d.License.KeyContainer.KeyType\x12\x32\n\x05Level\x18\x05 \x01(\x0e\x32#.License.KeyContainer.SecurityLevel\x12\x42\n\x12RequiredProtection\x18\x06 \x01(\x0b\x32&.License.KeyContainer.OutputProtection\x12\x43\n\x13RequestedProtection\x18\x07 \x01(\x0b\x32&.License.KeyContainer.OutputProtection\x12\x35\n\x0b_KeyControl\x18\x08 \x01(\x0b\x32 .License.KeyContainer.KeyControl\x12[\n\x1e_OperatorSessionKeyPermissions\x18\t \x01(\x0b\x32\x33.License.KeyContainer.OperatorSessionKeyPermissions\x12S\n\x1aVideoResolutionConstraints\x18\n \x03(\x0b\x32/.License.KeyContainer.VideoResolutionConstraint\x1a\xdb\x01\n\x10OutputProtection\x12\x42\n\x04Hdcp\x18\x01 \x01(\x0e\x32\x34.ClientIdentification.ClientCapabilities.HdcpVersion\x12>\n\tCgmsFlags\x18\x02 \x01(\x0e\x32+.License.KeyContainer.OutputProtection.CGMS\"C\n\x04\x43GMS\x12\r\n\tCOPY_FREE\x10\x00\x12\r\n\tCOPY_ONCE\x10\x02\x12\x0e\n\nCOPY_NEVER\x10\x03\x12\r\n\tCGMS_NONE\x10*\x1a\x31\n\nKeyControl\x12\x17\n\x0fKeyControlBlock\x18\x01 \x02(\x0c\x12\n\n\x02Iv\x18\x02 \x02(\x0c\x1a|\n\x1dOperatorSessionKeyPermissions\x12\x14\n\x0c\x41llowEncrypt\x18\x01 \x01(\r\x12\x14\n\x0c\x41llowDecrypt\x18\x02 \x01(\r\x12\x11\n\tAllowSign\x18\x03 \x01(\r\x12\x1c\n\x14\x41llowSignatureVerify\x18\x04 \x01(\r\x1a\x99\x01\n\x19VideoResolutionConstraint\x12\x1b\n\x13MinResolutionPixels\x18\x01 \x01(\r\x12\x1b\n\x13MaxResolutionPixels\x18\x02 \x01(\r\x12\x42\n\x12RequiredProtection\x18\x03 \x01(\x0b\x32&.License.KeyContainer.OutputProtection\"J\n\x07KeyType\x12\x0b\n\x07SIGNING\x10\x01\x12\x0b\n\x07\x43ONTENT\x10\x02\x12\x0f\n\x0bKEY_CONTROL\x10\x03\x12\x14\n\x10OPERATOR_SESSION\x10\x04\"z\n\rSecurityLevel\x12\x14\n\x10SW_SECURE_CRYPTO\x10\x01\x12\x14\n\x10SW_SECURE_DECODE\x10\x02\x12\x14\n\x10HW_SECURE_CRYPTO\x10\x03\x12\x14\n\x10HW_SECURE_DECODE\x10\x04\x12\x11\n\rHW_SECURE_ALL\x10\x05\"\x98\x01\n\x0cLicenseError\x12&\n\tErrorCode\x18\x01 \x01(\x0e\x32\x13.LicenseError.Error\"`\n\x05\x45rror\x12\x1e\n\x1aINVALID_DEVICE_CERTIFICATE\x10\x01\x12\x1e\n\x1aREVOKED_DEVICE_CERTIFICATE\x10\x02\x12\x17\n\x13SERVICE_UNAVAILABLE\x10\x03\"\xac\x07\n\x0eLicenseRequest\x12\'\n\x08\x43lientId\x18\x01 \x01(\x0b\x32\x15.ClientIdentification\x12\x38\n\tContentId\x18\x02 \x01(\x0b\x32%.LicenseRequest.ContentIdentification\x12)\n\x04Type\x18\x03 \x01(\x0e\x32\x1b.LicenseRequest.RequestType\x12\x13\n\x0bRequestTime\x18\x04 \x01(\r\x12!\n\x19KeyControlNonceDeprecated\x18\x05 \x01(\x0c\x12)\n\x0fProtocolVersion\x18\x06 \x01(\x0e\x32\x10.ProtocolVersion\x12\x17\n\x0fKeyControlNonce\x18\x07 \x01(\r\x12\x39\n\x11\x45ncryptedClientId\x18\x08 \x01(\x0b\x32\x1e.EncryptedClientIdentification\x1a\xa2\x04\n\x15\x43ontentIdentification\x12:\n\x06\x43\x65ncId\x18\x01 \x01(\x0b\x32*.LicenseRequest.ContentIdentification.CENC\x12:\n\x06WebmId\x18\x02 \x01(\x0b\x32*.LicenseRequest.ContentIdentification.WebM\x12\x46\n\x07License\x18\x03 \x01(\x0b\x32\x35.LicenseRequest.ContentIdentification.ExistingLicense\x1a_\n\x04\x43\x45NC\x12!\n\x04Pssh\x18\x01 \x01(\x0b\x32\x13.WidevineCencHeader\x12!\n\x0bLicenseType\x18\x02 \x01(\x0e\x32\x0c.LicenseType\x12\x11\n\tRequestId\x18\x03 \x01(\x0c\x1aL\n\x04WebM\x12\x0e\n\x06Header\x18\x01 \x01(\x0c\x12!\n\x0bLicenseType\x18\x02 \x01(\x0e\x32\x0c.LicenseType\x12\x11\n\tRequestId\x18\x03 \x01(\x0c\x1a\x99\x01\n\x0f\x45xistingLicense\x12)\n\tLicenseId\x18\x01 \x01(\x0b\x32\x16.LicenseIdentification\x12\x1b\n\x13SecondsSinceStarted\x18\x02 \x01(\r\x12\x1e\n\x16SecondsSinceLastPlayed\x18\x03 \x01(\r\x12\x1e\n\x16SessionUsageTableEntry\x18\x04 \x01(\x0c\"0\n\x0bRequestType\x12\x07\n\x03NEW\x10\x01\x12\x0b\n\x07RENEWAL\x10\x02\x12\x0b\n\x07RELEASE\x10\x03\"\xa9\x07\n\x11LicenseRequestRaw\x12\'\n\x08\x43lientId\x18\x01 \x01(\x0b\x32\x15.ClientIdentification\x12;\n\tContentId\x18\x02 \x01(\x0b\x32(.LicenseRequestRaw.ContentIdentification\x12,\n\x04Type\x18\x03 \x01(\x0e\x32\x1e.LicenseRequestRaw.RequestType\x12\x13\n\x0bRequestTime\x18\x04 \x01(\r\x12!\n\x19KeyControlNonceDeprecated\x18\x05 \x01(\x0c\x12)\n\x0fProtocolVersion\x18\x06 \x01(\x0e\x32\x10.ProtocolVersion\x12\x17\n\x0fKeyControlNonce\x18\x07 \x01(\r\x12\x39\n\x11\x45ncryptedClientId\x18\x08 \x01(\x0b\x32\x1e.EncryptedClientIdentification\x1a\x96\x04\n\x15\x43ontentIdentification\x12=\n\x06\x43\x65ncId\x18\x01 \x01(\x0b\x32-.LicenseRequestRaw.ContentIdentification.CENC\x12=\n\x06WebmId\x18\x02 \x01(\x0b\x32-.LicenseRequestRaw.ContentIdentification.WebM\x12I\n\x07License\x18\x03 \x01(\x0b\x32\x38.Lic