xbmc / inputstream.adaptive

kodi inputstream addon for several manifest types
Other
452 stars 241 forks source link

Question How inputstream get the licence key #279

Closed annetube closed 3 years ago

annetube commented 5 years ago

Hi @peak3d ,

I've a question about how inputstream is working get widwine licence by CURL. I'm triying to reproduce programticaly a session to get the licence token, I already know, your plugin imputsream.adaptive can handle it because it's used by catchuptv for this situation.

They provide these informations to inputstream :

URL_LICENCE_KEY = 'https://lic.drmtoday.com/license-proxy-widevine/cenc/|Content-Type=&User-Agent=Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3041.0 Safari/537.36&Host=lic.drmtoday.com&x-dt-auth-token=%s|R{SSM}|JBlicense'

                item.property['inputstreamaddon'] = 'inputstream.adaptive'
                item.property['inputstream.adaptive.manifest_type'] = 'mpd'
                item.property[
                    'inputstream.adaptive.license_type'] = 'com.widevine.alpha'
                item.property[
                    'inputstream.adaptive.license_key'] = URL_LICENCE_KEY % token 

When the licence key is requested on Chrome the request is :

Request URL: https://lic.drmtoday.com/license-proxy-widevine/cenc/ Request Method: POST Status Code: 200 OK

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36 x-dt-auth-token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJvcHREYXRhIjoie1widXNlcklkXCI6XCJhODJjOWM....

With a payload : 

When i'm triying to reproduce the request with CURL, i got

HTTP Status 412 But the request is not excatly the same because i've no idea about whats on the payload ...

Could you please help me to know whats missing on my request to get this licence, it seems be the payload but im not sure of anything,

Kindly Anne

peak3d commented 5 years ago

@annetube the license_key looks fine for me, pls. enable libCurl component debugging in kodi settings and try to figure out where the difference is. The request you posted from chrome is a server-certificate request, if the payload of the first widevine request inside kodi is not 2 byte, then this could be the reason for the precondition failure.

To force the server certificate you could also try to set this flag: inputstream.adaptive.license_flags='persistent_storage'

annetube commented 5 years ago

@peak3d Thanks for your your quick answer, unfortunately I dont have kodi environement to debug, I made and android app, who use Exoplayer. I would like be able to play the mpd, and so far the only solution I saw. Its the solution made by catchupTV team . I asked them about this too, and they told me, they dont really know how, they just send the information above to your plugin.

To be honnest, I'm not really confortable with the terms and dataflow for DRM stream ... Is it enought to get a reponse from server certificate to be able to play it stream ? Knowing Exoplayers needs apparently these kind of informations to play DRM :

{ "name": "TEST DRM", "uri": "URI_OF_VIDEO", "drm_scheme": "widevine", "drm_license_url": "URL_LICENSE", "drm_key_request_properties": { "Authorization" : "CORRECT_TOKEN" } }

And idealy, if you could try, when you've a bit time to see what's on the payload when your plugin make this "server-certificate request".

Thanks for your time

peak3d commented 5 years ago

There are 2 communication ways: 1.) simple: no server certificate, payload is the key request (for decryption) 2.) with server certificate: first server certificate is requested (with the 2 byte payload, in general b0801 IIRC), response passed to DRM and after that the key request -> there are 2 requests before media can be decrypted.

You don't have to deal with the content of the payload, its meant as opaque and you send things returned from DRM calls to license server and you feed the DRM with the response of the server. In case of success you can decrypt media, at least for a limited time.

But: you should be able to send at least the 2 byte payload to the licenseserver without having a failure (even you won't have to pass hardcoded 2 bytes in later versions because the 2 bytes come from DRM call.

If you still have your curl errors either your payload is wrong (compare in Chrome if it's really b0801, or something is wrong with your headers or token.

annetube commented 5 years ago

Thank you very much, it works the 2 bytes were "BS EOT" , i tought i tried it but apperently i didnt , anyway again thanks without you i probably gave up

annetube commented 5 years ago

@peak3d Sorry to bother you again, and for the long post comming ... i tought getting these reponse was enough but it doesnt work on my player, you was right the type of response is : "SERVICE_CERTIFICATE"

{status: "OK",…} client_info: [] content_owner: "castlabs" content_provider: "castlabs" internal_status: 139 license: "CAUSxwUKwQIIAxIQFwW5F8wSBIaLBjM6L3cqjBiCtIKSBSKOAjCCAQoCggEBAJntWzsyfateJO/DtiqVtZhSCtW8yzdQPgZFuBTYdrjfQFEEQa2M462xG7iMTnJaXkqeB5UpHVhYQCOn4a8OOKkSeTkwCGELbxWMh4x+Ib/7/up34QGeHleB6KRfRiY9FOYOgFioYHrc4E+shFexN6jWfM3rM3BdmDoh+07svUoQykdJDKR+ql1DghjduvHK3jOS8T1v+2RC/THhv0CwxgTRxLpMlSCkv5fuvWCSmvzu9Vu69WTi0Ods18Vcc6CCuZYSC4NZ7c4kcHCCaA1vZ8bYLErF8xNEkKdO7DevSy8BDFnoKEPiWC8La59dsPxebt9k+9MItHEbzxJQAZyfWgkCAwEAAToUbGljZW5zZS53aWRldmluZS5jb20SgAOuNHMUtag1KX8nE4j7e7jLUnfSSYI83dHaMLkzOVEes8y96gS5RLknwSE0bv296snUE5F+bsF2oQQ4RgpQO8GVK5uk5M4PxL/CCpgIqq9L/NGcHc/N9XTMrCjRtBBBbPneiAQwHL2zNMr80NQJeEI6ZC5UYT3wr8+WykqSSdhV5Cs6cD7xdn9qm9Nta/gr52u/DLpP3lnSq8x2/rZCR7hcQx+8pSJmthn8NpeVQ/ypy727+voOGlXnVaPHvOZV+WRvWCq5z3CqCLl5+Gf2Ogsrf9s2LFvE7NVV2FvKqcWTw4PIV9Sdqrd+QLeFHd/SSZiAjjWyWOddeOrAyhb3BHMEwg2T7eTo/xxvF+YkPj89qPwXCYcOxF+6gjomPwzvofcJOxkJkoMmMzcFBDopvab5tDQsyN9UPLGhGC98X/8z8QSQ+spbJTYLdgFenFoGq47gLwDS6NWYYQSqzE3Udf2W7pzk4ybyG4PHBYV3s4cyzdq8amvtE/sNSdOKReuHpfQ=" message_type: "SERVICE_CERTIFICATE" status: "OK" supported_tracks: []

On the http flow, chrome makes an other "SERVICE_CERTIFICATE" request and got the same response and after these 2 requests it makes 2 another one (apparently called "LICENSE" from the message type) { : and get these responses (same structure but values are a bit different) :

{status: "OK",…} client_info: [{name: "architecture_name", value: "x86-64"}, {name: "company_name", value: "Google"},…] client_max_hdcp_version: "HDCP_V1" content_owner: "castlabs" content_provider: "castlabs" device_state: "RELEASED" device_whitelist_state: "DEVICE_NOT_WHITELISTED" drm_cert_serial_number: "ZWEyZTY5OGQ5NWIyODQ0MTQyMmY0OTk3ZDZmZmQ1NDUwYQ==" internal_status: 0 license: "CAISnwMKKgoQI8ih0fnhHAH8oYaOavtHshIQI8ih0fnhHAH8oYaOavtHshoAIAEoABIMCAEQARgAIAAoAGABGmYSEBieExuXuyR+DFv6nmLYB1kaUFxsgJQ5aggflr9yZEk1FykdiDx74onkgXTM0z2XNbVfg8B+bFXgLyu4FJ43JDZE7yWsJMPq9FkMnAFnmRr56otmlyWaGEXhFkNjUxYEXuRBIAEaUQoQkdlGe9eVMYugfEhn/0OB5xIQLW+C6DHrlWEzvlXvT00tYhogmX3hAuE/j4yrQ23hC1i39RrFX/EbCL7nzs3Ksad8pFEgAigBYgVBVURJTxpOChCTS8fGoCk9W54mGnMkd3UDEhAoGioa3ZcWRwZ0K+LjBnJmGiBBIRSwcRoTNeOTP1FK0DJ6tnH05RhnfefORZ7hvaqTkiACKAFiAlNEGk4KEDmhLCtAmDD+qC9mpdb3vacSEMSLi29JXaA4Y0Arx0H6m2caIBr43YFKtoV0ySHC/sCHRwECak5BVqt+4NdO/elpxvxJIAIoAWICSEQgl52B6AU4AFAFGiA8hDZ0ZsDJyYlfmAaVDSDkjq4Op8pGwU24eSbOTdcw5SKAAgxNh4UGhygQUMTEy1a0at6HNmtkaXuaoa3qU3QsxjuScuhb2KBoH+h0nbvh+sI3hhu4IKABL9OJTDHuoQHeuxp+jQ8WWbUrA2da/C8qcIceQfEiazSb8cfgt70e3zyy1FbnQ4lO8Zj2YkIE6JiOIxgoO7hZUJ/g9nGI5GxKfuLVdQBDdIqeUAXxPYKRosnBeR6A+LqKifqK+26s7N+ALLQ3f6rPWishRqDYohT6WYwfJbK8FsAVGANx8UI3OGlZpKpOfvaoU/4S75drljNkyv8/0qgR6o2umb5YluePll6/XN4/kHEAjsdbDpABHwJ7AhsUa3Xr2ydZA6Ol7+tTcsg=" license_metadata: {,…} content_id: "CmMKTQgBEhCR2UZ715Uxi6B8SGf/Q4HnGghjYXN0bGFicyIkZXlKaGMzTmxkRWxrSWpvaVpHRnphR05sYm1OZlRUWWlmUT09MgdkZWZhdWx0EAEaECPIodH54RwB/KGGjmr7R7I=" license_type: "STREAMING" request_type: "NEW" make: "Google" message_type: "LICENSE" model: "ChromeCDM-Windows-2" platform: "chrome" platform_verification_status: "PLATFORM_SECURE_STORAGE_SOFTWARE_VERIFIED" pssh_data: {key_id: ["kdlGe9eVMYugfEhn/0OB5w=="], content_id: "ZXlKaGMzTmxkRWxrSWpvaVpHRnphR05sYm1OZlRUWWlmUT09"} content_id: "ZXlKaGMzTmxkRWxrSWpvaVpHRnphR05sYm1OZlRUWWlmUT09" key_id: ["kdlGe9eVMYugfEhn/0OB5w=="] 0: "kdlGe9eVMYugfEhn/0OB5w==" security_level: 3 session_state: {,…} keybox_system_id: 13701 license_counter: 0 license_id: {request_id: "I8ih0fnhHAH8oYaOavtHsg==", session_id: "I8ih0fnhHAH8oYaOavtHsg==", purchase_id: "",…} purchase_id: "" request_id: "I8ih0fnhHAH8oYaOavtHsg==" session_id: "I8ih0fnhHAH8oYaOavtHsg==" type: "STREAMING" version: 0 signing_key: "/CmTvEpMBviItprLzCMUI+ZexBKX9ePbqIegeJuQO+W7OmKp6LaNOmHCYKQcfvl4TN15FK4N400aPA0gqGyjYg==" status: "OK" supported_tracks: [{type: "AUDIO", key_id: "kdlGe9eVMYugfEhn/0OB5w=="}, {type: "SD", key_id: "k0vHxqApPVueJhpzJHd1Aw=="},…] 0: {type: "AUDIO", key_id: "kdlGe9eVMYugfEhn/0OB5w=="} 1: {type: "SD", key_id: "k0vHxqApPVueJhpzJHd1Aw=="} key_id: "k0vHxqApPVueJhpzJHd1Aw==" type: "SD" 2: {type: "HD", key_id: "OaEsK0CYMP6oL2al1ve9pw=="} key_id: "OaEsK0CYMP6oL2al1ve9pw==" type: "HD" system_id: 13701

To get these kind of response, the request contain a long payload encrypted :

Ä"e

c M‘ÙF{ו1‹ |HgÿCçcastlabs"$eyJhc3NldElkIjoiZGFzaGNlbmNfTTYifQ==2default#È¡Ñùáü¡†ŽjûG² —è0BÐ! license.widevine.com¹Ì†‹3:/wŒãð¤‹g•¡ýݲ`x?·Ñ;X6òþÄSË_ ¨áJHD#—¼l’5LîËë”~¿ùOãÔböî(j²¤ÃAÀqÓ/[¨FÑKŒïÍÕ!Ï\&7fçi˜òý",×hªðMÂÿTöܶè³,W‚üÇøbošœL›<A‡¥Àª¸šß-ÊÇ¡;Æ´ë²× û£˜Èy U·)>šæÝ~@3Sw‡ð“8õ¾gD§|èðO@Ž©{½F9ª²h‘LfÞºLú?yÌ@ v ïyâ‚?)€ÚÃcބëÎ>ÝõÝQöŒ×ájEè%Û^æòÎ ý²µ|pÌ⇢_=@*çg}З=SÀy/áRVÖK7»P2[å‰æ¶¤%‡Öïd¨­X¼p™Üû\~>€ø:œ$ ‰Fl­Žj”ÚíþxÜx«½ó^å}Å\=ÔÒ캟æ3šßçIàRĤp¢Ñ©ì~ƒ‰ngž>Ox °#) 9Y4‚56«(?‡‘òS‰ øїêx¦vTLùÂýçHÛLÑ®|ë,¾^âëÕåq†žÎcæaŒ“­e¢°&@úœõŠ!"㯝WÐÙXHr6‚Ì Xÿ!gqÛ¡±"¢¡~ßËL'ƒÍ¨Ñ~EXΩÕԏ^fš†|Ãò&t\ÉÁtºØ1Á¡÷݁8¯ãÞ4X´–ÍnÍ6!e.\ Æ6ù>6°6f=ÿ¢o,UUæ‡W—×ç¯r™¡B–­Qà•drÙÙ>=k)üMOŽ¥Î€9@etê|k'˜‰3N«…“Ãò—@´k8ڎöÉýs=œìMªvíH <òӃ2Ôpê£f4tr@0J^¥!5C‹\šrÚ§˜m½Âç:Þïé„éS/ê¹.E´SdFƒ8¸.ðÐnÐËý½‰Ð °Â•_J͊äz¬¡B”¢GGo=ªRüÞ÷Þ¦ýWKÕÀÀzï#¼ã¸ªÐCN É»u฻_°Bm¡ qãëãžÅr&‹˜L ç†×› 0ú&3µ™oÆïõ•,CÛ @¹¿+ Ê å<E‰f ‹Cž ’ÔûuÐ VZzûrå)ß "Ý®Ò^;ÆJÏW²í?ÚkhÉ-Uí&Å֛4ŠdÅc-‡á_›ÍßÉK.„YZ8Ƅò×Ϊ›ð[ºC,U_ƒ NpWƒÈr¶'8+wĵúuº„aƒ"È¿pÍ\Y™=fׇ]LŸ ¬üÙ>Ï÷2Ûj¼­€ìàÁ ô¶Ûɹ·ü[µÎ¡Ó˜jSE§ZÜPóëmÕޒeyý2ßb^u\ DlҋëÅVu¬8Ìŏ™P|U ò4åRñõ&F£†±ˆ•<]X;‚‡+~N¹)“ñÔHr5ҝ.®%Ðh2Œ%ª ºŠ› ã%H§êö^sKFÌÑÛüén|ŠRŽTì»b“à¨ÀX8)¢„Š½'aŸFKQø%&:§±²’-àøŽ8]>d³‰Û€‘U¤eŸ£­ÍbÞådÏàÝ#±mJ û%ޔèø6¬O¥N?ð+&ƒI¨\ ªå)DfðÁ#¬U•›ÿkŒ 6z’bù»ùù%8Ì$Èm d+Mo6¶V¿¼'Âm YZ,Ò#¤²Õ•ðˆpˆ’‰ªæ€ˆ™®åp±dïИjÿ€Ç˜¹¬°pÛV;ãÎ_ѱ ˜iÆøAhõ’Pxìߕ·Òð(…àù…©ÀXÐ)%׫•¾µ&T¥o™|€8ã µÔ…9cðp}ØúÌn”½ þ/"°õ†f~AÊ쯑Ԭ6v=vX-Ç2Òî:®òQ <6÷lÆSóå^Œr\W‘(\†"‡œZX¶ÐµãjÔItچŽÜH-0d‡jOµŒÂsŠÏÓf¢°Y ÐÌ0ûù8éðæCÔÎØMP=bû#¯frèË3ðp"b@šê؎q¹:«EçëYڒ}\Þ¢ÓK”F[.ˆ±0¬E½±n7¶v3ß^'åÕTÖû½Ô<z×]ATž©¼Øµ;Íg͹'ÕD9ü†µoU#ùNåv ®µbK¸$F”düŠ…¸%¹®ÈßÿÁJ ìHV0!ë'da#¤ê¦¶"g¹”ï7¤Ck>HÍ}B՞n'”oÞßöiHçaÃÒÔC{_4Ýþ¸£¦È­‚\”Ö—×å™aè÷ØjÝ3…7yzä«5MPÙÔÛb[žcàX6nõñÕAÕ¢ ¾Ó-–<v$ü”êx¼Büô‡‘ë×jbæüÜ÷ì㠜š$žå76þ8Ð#ɂôk%ï٩؈]Šé§ ¬¨öE à<•nZüèùßoñ?=úIx áÐ^x^…òL•6Úwy%ËéY¿K }æ›f7ðÓbÀp/ä]ç¼5Ï_(‚þº“‹Ü\·DI¯š×XCRìœ/¬Rז®_€‘ EÛ#c×ZÈτœ gx<Èà‡)֟ýŽsâ8÷ߟjఝÍh÷\WYazÞî ÑSçUxçá¬J™½Åì{ „#$äœH¼µˆè©×ãI‘Ó¹*C‰Ê•Èp?Ü¥48—D³š^‘húSì_‡žòeù¦<©¥Æ†CpJÊ9—¼í˜4uU ƍ@µóì;ÉƦúÛ~2z¸¬£UJMTK‰!±š4M!/òô0DnŠš­HVpë´V ¦xj¥ ¹›–¸71q%ß$öú­ç9ËÕõÖÞwcЄ¶ 7\½-/>‰Œ—¦¸F4/‘X…ì„íŽÍ‘J¨G„-½¹DSUÑ;_;g¡4ÐeAÍ©sîÑ'M1â¤ÙrMº1óBµÇx«€.{þ¦Õþeí/Ï벳üë7Jå|Ís¿ê,ۘSÕcæM<Ÿ×/Lò0ã3ƒÒ.ŸÙdã9î›ã#@ñ×ĒYo%F” S Ûé…ëB@¦®µëkOh•Mv1Ώ+?3$ëÓb·-ù‹F‹ó€KžE){숍ó0´q˜ÖNŸÅ ÛÆÕãoוƒÊøÑ )ôw2NTlöS‡ÐD< 2É"¶U®©myÍÓNuàKÁ[B4hò±VÑ¡܂ķó³áÞÃ~ô¸À£yìjå3"ëð{ð²#Ç(yc&Œñ .•V¤2çë…áp/±ØÜJˆë×}mU«³ºØ ®F@¢žÄùq (ÚCu.ÆôHpÞ®$ž;p/Äqåòôen¹e·4ôX½/¦Ut"ºªÈ—í%˜tñ3ôe “úF_Ÿ«%Á“Ö žÆ»ˆn€‚“(“FG5¹¹¦s!å³{RΪÖú.òÚòdUÔ¡O"u©}ÿ¶5§™6û,üP7¤í°Åóù==Ö ™:L=ÈVS¤…>Ÿ]Úh7”×Kžþï‡ ¥0[ìÝc)¯D™2ק¹aÊ#¨h†ÊYOfuj.·øtäiæƒrLGû˝÷ËTJ}ý°‚üéï÷Ki„‹ÂŽ¦ E¿óP<Ê©·D˜ ¬Í­‘•BƒSO}bVÿkN÷¶rD:®½€V§˜uÁšÅõ €+‰ÌÚËëD93 °É3”̤)}{ácö­ŽHs?}|Ê»Â2Ø1PHÁt°¥VèZCçûåx%TüKNYTSÎöZØwð"„:áTY)¾.능Î"J¤ê,äדkúó¿yڜR£RìbO!®oèåOµÛKÆ])ŒOPWðà'&n p¨‘é!­¬öÌñ†¢—| â&f¸ŠÆîQ/Ø­•6 æ?ï¨5,D„i‡$³œÛscñä˜æ[3oH×vûbþ%:vÂtÁ´ùÒØ+×vµñǨ¤ØZwɲUnêö]Æ2áj°T¦ióG¡ßCE4V+òìê­æ yQ0©ÅÇfãm{b€Â>{ƒÝQÒ/\ýb.ÀyþwçàϞ6t3l«,!’Íòv¤£/p² Úß¼GÜáü t0ԄÜU¨–Äy϶p5üùi9ÈÞg(ó¾-Ãþ›ñô{g£}&@ü —42[ñÃ3¨•É†à§eS¥/~“Kˋ³“ì8å\ƒ9¦Àžý5ä鏉¡$ õú>u0 ø¸\Q§'¨M¸¶@ª«Õ_ö!K²€ƒÙž©Ã ¾Hjü²WçðÊ^ëP¤<*B¢{Úw²êÒ݈B”nÕS@±Ó ðôQ-üógaÜÞœ^0Ц«ÿÌ5½þU¾E° ®Ð i¸‰ÑÉxVgáÜ<^+\"®þxÅsI}ýØMÎyä÷¯õÿVz"„,;{¯…'ux:€Wã{+ ۈÀèDzŸ_N†ÜG:¨¶ežú28òîM÷eœÊÐþtpâ'ãlbèuzï Ñ0šbk ÏÌçƒ"6T¼R«ÌN!«·½‹ÅO¤øcÑâèf…šÑ¯'Xuʼn0&⁑ìËÿÄýäV«â®ô:Åv'*åmb!¼°Z›®±8Ò![³O«ÍùW‹´I‚4™ŒxùÈê‘þíç—>?ÌF÷Àà¨<-­Ÿ Vòçõ¡Bف0[Õ­G ü)iwᄦg‘A酸²2,$ +´ãU˜¨}ךå舌ã/mö´ mñ„ …}Ï΄Ÿ‹¯ ´ÎJfÍJ^¥ òûöƒŸXÏéÉFhÊ;¢¨ô֜oԓà›W_åWÀ§ëiúü›Ù‰™”'Ø%€,⭙÷.*êùU?ÑD¥¢n$Qõ¯/#§)µ!m쀣ZósÛ)ÑQâ숕A]ìó½—qvn ?Ö®:y)1i*å¶Z}ÀÐѓý|°?Ž.Ñ((0¶‚"BB+ÇÞG3Šš‘G1áÍ/‹ƒö”6áÔÙqe!ÛìÆgœ´\T•C”ëc©c§·X4²w£… Ÿ è•f1¥3ÐC}i«Òe9™¬Ü¼W‰á"!¥ۃ,m »3,»8,×ۍëï"-òö]Ý|«æÅòÍD‡w®×d#¥¸ÔGÂ薭¬8t®Î¸çÕ.}Ò~ë½,ö[§KŽ–¢ß׺¢4 ûì‹HOEˆ‘ü$QϺ¶„w0úȏœ÷}ÎÒØ$‡Àí‹Ì°¹Ô:’…·¥JÇÞÁ…””#Ҏ®ƒDp´"Â^° ä"·Šßð’'ÿ€{«š Àš´Á“Oñ#z¥ž7›&b 4_Á¸ D$¦¢¸ Ø?ûW¸>ÊzF¯„«o³}çOO‹kH]iŸ÷5ïW¤·Åª&|¾Ã>æôâ?\¯}rќ#;žc€<´ä¦ÚdÙBt71¬)ZL(ÒSª­±—Û³!–tgYyÈ LÁäѼGZƒÛ=v"n‡ŠÝˆ9ȀVzÞ^ÊÜL! ( Ãý“ž‘>ŸE¬ñÁ†•È-ûÛÙ\¤MF‡‘<3îÙ|ɜÎõ~m»Ô%•Æõe¶¿ÿÙÛ§ nŠÄƒ¹ý‚—€¶~%X€—)àÇ»rÌ·§::¬KÜӛÖdîV6I™ÃÙÍÒÆx‘>XH~–Je<óW·eȚW•&Ê)n¹Ï<¸i¨™lÑj«ø=°"[Ún íáˆI˜T¯ù½ ª×?Ÿz‘¬´øDQ.¤1ïPÓCsw¡çÛÍ. àí÷·ÅpIz=ƯS<Eú%Q‹Ôc.©”Îƈp=¥LOie;ÆïÊfl×B[DÃÒæ¨+VÈZ!¸á&Æꨣ¿¨Wz)=ªo½™» @smyÝ>ÐSPº«‰–8)˜Û£‘ƒ)þ7Ÿ×ŸŸZ¾ò¾Dè­Úàn$IÙY¹²Ø$x

So considering all that, I have two main questions: 1) Does my player requires, the licence value contained on the "server_certificate" json response , to be able to play content (currently not working) or should i pass it the licence from "LICENCE" json response ? 2) In the case where i would have to pass the licence from "LICENCE" json response to the player to play the stream, How is the payload request formed , what kind of information it contains and how can i get it ? is it base64 encoded, i tried to decrypt it, but nothing really clear with a regular base64 decoder...

Sorry for the long post, I never did that and i'm really lost on it, Anne

peak3d commented 5 years ago

@annetube if you use exoplayer, you can overwrite the part where the license server does it's request with your license server format logic. There is no need that you reinvent the wheel, the complete logic is already part of exoplayer (IIRC something with PostRequestHandler, look around here: https://github.com/google/ExoPlayer/blob/release-v2/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java#L392

Edit: I would also suggest that you set some breakpoints in the source file I linked above and play one of the widevine samples which come with the exoplayer app. The workflow is not very complex.

annetube commented 5 years ago

@annetube if you use exoplayer, you can overwrite the part where the license server does it's request with your license server format logic. There is no need that you reinvent the wheel, the complete logic is already part of exoplayer (IIRC something with PostRequestHandler, look around here: https://github.com/google/ExoPlayer/blob/release-v2/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java#L392

Edit: I would also suggest that you set some breakpoints in the source file I linked above and play one of the widevine samples which come with the exoplayer app. The workflow is not very complex.

@peak3d Thanks for answering ,I know its not really about your plugin so i really appreciate your help. On my previous post I already did that youre suggesting , i'm able to get the server_certificate using exoplayer demo just added the post body data="\b\u0004".getBytes(); on the method executepost on httpmediaDRMcallback and added this item on the mediaexolist.json of exoplayer demo (on assets):

  {
    "name": "M6",
    "uri": "https://m6.live.6cloud.fr/out/u/m6_sd_p.mpd",
    "drm_scheme": "widevine",
    "drm_license_url": "https://lic.staging.drmtoday.com/license-proxy-widevine/cenc/",
    "drm_key_request_properties": {
      "x-dt-auth-token": "tcImFjY291bnRpbmdJZFwiOlwiXCIsXCJhc3NldElkXCI6XCJNNlwiLFwicHJvZmlsZVwiOntcInB1cmNoYXNlXCI6e319LFwib3V0cHV0UHJvdGVjdGlvblwiOntcImRpZ2l0YWxcIjpmYWxzZSxcImFuYWxvZ3VlXCI6ZmFsc2UsXCJlbmZvcmNlXCI6ZmFsc2V9fV0iLCJpYXQiOjE1NjA0MTUyMjYsImp0aSI6IkJDY2I0MEJcL0tqVkh4T1lLaWhnM0hRPT0ifQ.H9L_hjdMoZ7E5VKSLJWkI_K1C0N8-n96ulKuivwG6ekuZ_kQUuPvtudTIHrM0yMAs7cahKAAocyVt1yKxLr30Q",
      "Origin": "https://www.6play.fr",
      "Referer": "https://www.6play.fr/m6/direct",
      "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3041.0 Safari/537.36"
    }
  },

this works i able to get the json Server_ceritificate on exoplayer reponse but it cant apparently handle the key provided , i got an error ===>

2019-06-13 18:48:59.080 9668-10273/com.google.android.exoplayer2.demo E/MediaDrm-JNI: Illegal state exception: Failed to handle key response: General DRM error (-2000) 2019-06-13 18:48:59.084 9668-9668/com.google.android.exoplayer2.demo E/EventLogger: internalError [4.94, 284.95, window=0, period=0, drmSessionManagerError] android.media.MediaDrm$MediaDrmStateException: Failed to handle key response: General DRM error at android.media.MediaDrm.provideKeyResponse(Native Method) at com.google.android.exoplayer2.drm.FrameworkMediaDrm.provideKeyResponse(FrameworkMediaDrm.java:172) at com.google.android.exoplayer2.drm.DefaultDrmSession.onKeyResponse(DefaultDrmSession.java:416) at com.google.android.exoplayer2.drm.DefaultDrmSession.access$100(DefaultDrmSession.java:49) at com.google.android.exoplayer2.drm.DefaultDrmSession$PostResponseHandler.handleMessage(DefaultDrmSession.java:479) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:193) at android.os.HandlerThread.run(HandlerThread.java:65) 2019-06-13 18:48:59.085 9668-10273/com.google.android.exoplayer2.demo E/ExoPlayerImplInternal: Playback error. com.google.android.exoplayer2.ExoPlaybackException: com.google.android.exoplayer2.drm.DrmSession$DrmSessionException: android.media.MediaDrm$MediaDrmStateException: Failed to handle key response: General DRM error

Thats why i was asking : 1) Does my player requires, the licence value contained on the "server_certificate" json response , to be able to play content (currently not working) or should i pass it the licence from "LICENCE" json response ? 2) In the case where i would have to pass the licence from "LICENCE" json response to the player to play the stream, How is the payload request formed , what kind of information it contains and how can i get it ? is it base64 encoded, i tried to decrypt it, but nothing really clear with a regular base64 decoder...

Thanks for your time

peak3d commented 5 years ago

1.) Service certificate could be required. You have to search in exoplayer how "private-mode" is enabled. This will switch on automatic server certificate request. Please refer to MediaDRM android documentation, there you will also find lot of useful information about the workflow.

2.) MediaDRM expects all the data you feed as binary data. If your responses are JSON or b64decoded or whatever, you'll have to parse / decode them by your own. As said in previous post, look what data formats are provided from the sample streams (IIRC its pure binary payload),

annetube commented 5 years ago

@thanks for all the informations provided on this topic, it really helped me, such bad i felt so close, i'm giving up unfortunately. All of this its propably too much complicated and abstract for me, after some quick search, i found nothing about "private-mode", however i'm gonna follow your suggestion and look other samples and propably how they do in mobile app when i would have more tie. I spend already weeks on it wthout sucees. Considering all that , congratualations for your work, its really amazing to handle that !

peak3d commented 5 years ago

https://github.com/google/ExoPlayer/issues/1624

annetube commented 5 years ago

Thanks for the link, i tied as explained but it doesnt change anything, i also tried to decompile the original app, but its still crypted apparently about what i understand the key is extract from json and decode b64.

public byte[] executeKeyRequest(java.util.UUID r4, com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest r5) { / JADX: method processing error / / Error: java.lang.NullPointerException / / r3 = this; r0 = r3.getUpfrontTokenUseCase; if (r0 == 0) goto L_0x0017; L_0x0004: r0 = r0.execute(); Catch:{ Exception -> 0x0017 } r0 = (io.reactivex.Single) r0; Catch:{ Exception -> 0x0017 } r0 = r0.blockingGet(); Catch:{ Exception -> 0x0017 } r0 = (java.lang.String) r0; Catch:{ Exception -> 0x0017 } r1 = r3.delegate; Catch:{ Exception -> 0x0017 } r2 = "x-dt-auth-token"; Catch:{ Exception -> 0x0017 } r1.setKeyRequestProperty(r2, r0); Catch:{ Exception -> 0x0017 } L_0x0017: r0 = r3.delegate; r1 = "Content-Type"; r2 = "text/xml"; r0.setKeyRequestProperty(r1, r2); r0 = r3.delegate; r4 = r0.executeKeyRequest(r4, r5); r5 = "bytes"; Catch:{ JSONException -> 0x0048 } kotlin.jvm.internal.Intrinsics.checkExpressionValueIsNotNull(r4, r5); Catch:{ JSONException -> 0x0048 } r5 = new java.lang.String; Catch:{ JSONException -> 0x0048 } r0 = kotlin.text.Charsets.UTF_8; Catch:{ JSONException -> 0x0048 } r5.(r4, r0); Catch:{ JSONException -> 0x0048 } r4 = new org.json.JSONObject; Catch:{ JSONException -> 0x0048 } r4.(r5); Catch:{ JSONException -> 0x0048 } r5 = "license"; Catch:{ JSONException -> 0x0048 } r4 = r4.getString(r5); Catch:{ JSONException -> 0x0048 } r5 = 0; Catch:{ JSONException -> 0x0048 } r4 = android.util.Base64.decode(r4, r5); Catch:{ JSONException -> 0x0048 } r5 = "Base64.decode(jsonObject…icense\"), Base64.DEFAULT)"; Catch:{ JSONException -> 0x0048 } kotlin.jvm.internal.Intrinsics.checkExpressionValueIsNotNull(r4, r5); Catch:{ JSONException -> 0x0048 } return r4; L_0x0048: r4 = move-exception; r5 = new java.lang.RuntimeException; r4 = (java.lang.Throwable) r4; r0 = "Error while parsing response"; r5.(r0, r4); r5 = (java.lang.Throwable) r5; throw r5; / throw new UnsupportedOperationException("Method not decompiled: fr.m6.m6replay.media.drm.WidevineDrmTodayMediaDrmCallback.executeKeyRequest(java.util.UUID, com.google.android.exoplayer2.drm.ExoMediaDrm$KeyRequest):byte[]"); }

I trieid to do kind of the same

public byte[] provideKeyResponse(byte[] scope, byte[] response) throws NotProvisionedException, DeniedByServerException { lic = new JSONObject(new String(response)); extract= lic.getString("license"); return mediaDrm.provideKeyResponse(scope,Base64.decode((extract), Base64.DEFAULT)); }

--> getting same error , I also tried using private mode ---> getting a new error like key not found

2019-06-14 11:05:39.766 11754-12073/com.google.android.exoplayer2.demo E/ExoPlayerImplInternal: Playback error. com.google.android.exoplayer2.ExoPlaybackException: android.media.MediaCodec$CryptoException: Crypto key not available at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.feedInputBuffer(MediaCodecRenderer.java:1110) at com.google.android.exoplayer2.mediacodec.MediaCodecRenderer.render(MediaCodecRenderer.java:653) at com.google.android.exoplayer2.ExoPlayerImplInternal.doSomeWork(ExoPlayerImplInternal.java:575) at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:326) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:193) at android.os.HandlerThread.run(HandlerThread.java:65) Caused by: android.media.MediaCodec$CryptoException: Crypto key not available

Thanks for your help, when I will get a decent environement i would try with your plugin in Kodi, to see wath request are sent and how reponse are handled.

peak3d commented 5 years ago

seems that you are close to the goal, fix your type errors and you could be finished. I don't develop in exoplayer, It's long time ago I looked at the sources -> it's your project.

annetube commented 5 years ago

@peak3d Yes its my project, i know but i just cant found solution, many users wantsthis channels and cmplain about it ... i tried again many times and still go some errors. Could you tell me if the algrytm of your plugin : make just the server request and switch to private mode or it was just a ssuggestion for my case ?

Also i would like try your plugin on debug mode, where should i start to get a kodi environement (with eclipse IDE would be perfect), the only tutorial i found are really out of date and still talk about xbmc. One important thing is i dont have TV... just a laptop.

Thanks in advance for your help...

peak3d commented 5 years ago

The 412 response code is not because of the ServerCertificate call. From reading about 412 its more a missing header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/412

Investigate which headers are send in browser, and add them all (!)

Edit: If 412 HTTP error is not the one you're searching, pls. provide a new XAuth token to me so I can try to reproduce. You can use my mail (see my githum accound for it)

coveroid commented 5 years ago

Thanks @peak3d for your answer, it's been long time i solved the problem of 412 error, by following your advices ( adding 2 bytes were "BS EOT" on payload) so to resume, I'm getting the server certicate key, but exo player can't handle it even after decrypting via base64 decode and setting the parameters to switch the player in private mode.

I know you don't develop anymore on exoplayer, i just would want to know more are the add-on inputstream adaptative works to try to obtain same result, so for example I would like to know if you use something like switching the player on private mode or if it was just a suggestion to avoid the last request to get the license key (not the request for server certificate) , and if you have any suggestions how to use your add-on on development mode without tv?

Btw: It's Anne on wrong account...

peak3d commented 5 years ago

I did a short test with your stream data, but added:

this.mediaDrm.setPropertyString("privacyMode","enable"); here: https://github.com/google/ExoPlayer/blob/release-v2/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java#L166

The firrst request is a 2 byte one, but I get the 412 error, so would be great if you provide the solution how to solve this.

annetube commented 5 years ago

Hi its Anne from github,

Thanks very much for your help so far i use a php script to gt the token :

The new one i can provide you

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJvcHREYXRhIjoie1widXNlcklkXCI6XCJhODJjOWMzZmYxNmQ0NjFiYTMxODFhYjI4MWM4MGJlOVwiLFwibWVyY2hhbnRcIjpcIm02XCIsXCJzZXNzaW9uSWRcIjpcIjZwbGF5XCJ9IiwiY3J0IjoiW3tcImFjY291bnRpbmdJZFwiOlwiXCIsXCJhc3NldElkXCI6XCJNNlwiLFwicHJvZmlsZVwiOntcInB1cmNoYXNlXCI6e319LFwib3V0cHV0UHJvdGVjdGlvblwiOntcImRpZ2l0YWxcIjpmYWxzZSxcImFuYWxvZ3VlXCI6ZmFsc2UsXCJlbmZvcmNlXCI6ZmFsc2V9fV0iLCJpYXQiOjE1NjIwNzc0NTksImp0aSI6InRLekFqa1FmMzk3djNjOU51TkpsWkE9PSJ9.2XQTwhIa8i02HmxIL_SDADhcL5VndcsE0tiC_54gKHruv67hJxSBmmEn-x0O1gzROEvYKGDKLrweMKf4mMJO5Q I put also in attachments the php script who i use to make test requets and get new auth token

Thanks again for your time

Le mer. 3 juil. 2019 à 00:04, Markus Pfau notifications@github.com a écrit :

I did a short test with your stream data, but added:

this.mediaDrm.setPropertyString("privacyMode","enable"); here:

https://github.com/google/ExoPlayer/blob/release-v2/library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java#L166

The firrst request is a 2 byte one, but I get the 412 error, so would be great if you provide the solution how to solve this.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/peak3d/inputstream.adaptive/issues/279?email_source=notifications&email_token=AL7IBNEXKW4NHFAWZQAFTSTP5NN6RA5CNFSM4HWZOZ3KYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODZBMFKY#issuecomment-507691691, or mute the thread https://github.com/notifications/unsubscribe-auth/AL7IBNCFER24QUN6U5ALU2DP5NN6RANCNFSM4HWZOZ3A .

annetube commented 5 years ago

Maybe you already did, but just in case you need to set the 2bytes payload of the method executePost

on https://github.com/google/ExoPlayer/blob/release-v2/library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java#142

i dit it directly on method line 157 with replacing data by

"\b\u0004".getBytes(),

sefuns commented 4 years ago

@peak3d

I have query related to similar "mpd" stream license request from Kodi inputstream.adaptive addon I am using addon like below from Kodi (not exoplayer)

item.property['inputstreamaddon'] = 'inputstream.adaptive' item.property['inputstream.adaptive.manifest_type'] = 'mpd' item.property[inputstream.adaptive.license_type'] = 'com.widevine.alpha' item.property['inputstream.adaptive.license_key'] = 'http://[server]/enc|[headers]|R{SSM}|'

But, inputstream addon is NOT posting first 2 bytes viz. '\b\4' as required, instead it's posting 1748 bytes directly and response from key server is HTTP 400 (Bad request). Also, tried by setting property inputstream.adaptive.license_flags='persistent_storage' but still received same HTTP 400.

However, if first two bytes are explicitly added as below, license server gives 'appliction/binary' (its not json base64) response (HTTP 200) with some kind of binary data most probably a server certificate. But inputstream addon doesn't continue to make another 2 requests to reach to actual key for decrypting video stream

item.property['inputstreamaddon'] = 'inputstream.adaptive' item.property['inputstream.adaptive.manifest_type'] = 'mpd' item.property[inputstream.adaptive.license_type'] = 'com.widevine.alpha' item.property['inputstream.adaptive.license_key'] = 'http://[server]/enc|[headers]|\b\4|'

As mentioned in earlier comments inputstream addon needs to send another 2 POST requests to get to the actual key for the video stream, but those POST requests are not made and it fails with 'no keys' error right after first POST request itself with 1748 bytes (validated this with CURL logs from kodi.log).

Please help me understand if I am missing here? How can I play this stream in Kodi?

Thank you for all the help and contribution

Anan5a commented 3 years ago

I'm still having same problem today when tried Google’s shaka player demo and the widevine integration demo

First 2 bytes are never sent!

@peak3d can you please help? I cloned this repository just yesterday!

The debug .challenge file contains the challenge payload but the data sent to the server is a string thus resulting in invalid licence request or empty request

glennguy commented 3 years ago

Please see wiki for instructions