├── LICENSE ├── README.md ├── config.py ├── data ├── drop.png ├── firefox │ ├── extensions.json │ ├── gmp-gmpopenh264 │ │ └── 2.3.2 │ │ │ ├── gmpopenh264.dll │ │ │ └── gmpopenh264.info │ ├── gmp-widevinecdm │ │ └── 4.10.2710.0 │ │ │ ├── LICENSE.txt │ │ │ ├── manifest.json │ │ │ ├── widevinecdm.dll │ │ │ ├── widevinecdm.dll.lib │ │ │ └── widevinecdm.dll.sig │ └── prefs.js └── pywidevine │ └── L3 │ ├── cdm │ ├── cdm.py │ ├── deviceconfig.py │ ├── devices │ │ └── android_generic │ │ │ └── CDM_HERE │ ├── formats │ │ ├── wv_proto2.proto │ │ ├── wv_proto2_pb2.py │ │ ├── wv_proto3.proto │ │ ├── wv_proto3_pb2.py │ │ ├── wv_proto4.proto │ │ └── wv_proto4_pb2.py │ ├── key.py │ ├── session.py │ └── vmp.py │ ├── decrypt │ └── wvdecryptcustom.py │ └── getPSSH.py ├── icon.png ├── initext.py ├── mpdl.py ├── requirements.txt └── util.py /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | drawing 2 | 3 | # === Please do not use this anymore it's a terrbile piece of software === 4 | 5 | # mpdl 6 | GUI-Based MPD Downloader for DRM Protected content. 7 | 8 | # Requirements 9 | + Python 3.12 10 | + ffmpeg 11 | + mp4decrypt 12 | + [**Content Decryption Module**](https://forum.videohelp.com/threads/408031-Dumping-Your-own-L3-CDM-with-Android-Studio) 13 | + Firefox (Installed) 14 | 15 | # Setup 16 | + Clone the Repo 17 | + Install the required modules: `pip3 install -r requirements.txt` 18 | + Run mpdl: `python3 mpdl.py` 19 | 20 | # Usage 21 | 1. Select a CDM 22 | 2. If not already in your PATH, select an ffmpeg and mp4decrypt executable 23 | 3. Optionally, specify a download directory in the settings (default is the program's root) 24 | 4. Hit 'Apply' 25 | 5. Start the Browser 26 | + If you just want to download: 27 | 6. Open the URL Sniffer 28 | 7. Navigate to a Website utilizing DRM 29 | 8. Choose "License URLs" in the drop-down menu 30 | 9. Select a License URL from the list and hit 'Select' 31 | 10. Choose "MPD URLs" from the drop-down menu 32 | 11. Select an MPD URL from the list and hit 'Download' 33 | + If you want more control: 34 | 6. Open the advanced mode. 35 | 7. Enter everything you have. (**use the internal browser if you want to have the request headers automatically transferred to pywidevine**) 36 | 8. Hit 'Download' 37 | 38 | ![image](https://github.com/DevLARLEY/mpdl/assets/121249322/f51cf92c-cbc6-438e-a562-5b9500fed4d8) 39 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | import configparser 2 | import os.path 3 | 4 | parser = configparser.ConfigParser() 5 | 6 | 7 | def setupConfig(): 8 | if not os.path.isfile("config.ini"): 9 | fp = open("config.ini", 'x') 10 | fp.close() 11 | parser.read("config.ini") 12 | parser["MAIN"] = {"downloadpath": "", "downloadfrompath": "False", "ffmpegfrompath": "True", "ffmpegpath": "", 13 | "mp4decryptfrompath": "True", "mp4decryptpath": "", "cdmselected": "False"} 14 | parser["BROWSER"] = {"startpage": "https://duckduckgo.com/", "drmenabled": "True", "addons": ""} 15 | writeConfig() 16 | parser.read("config.ini") 17 | 18 | 19 | def resetConfig(): 20 | if os.path.isfile("config.ini"): 21 | fp = open("config.ini", 'x') 22 | fp.close() 23 | parser.read("config.ini") 24 | parser["MAIN"] = {"downloadpath": "", "downloadfrompath": "False", "ffmpegfrompath": "True", "ffmpegpath": "", 25 | "mp4decryptfrompath": "True", "mp4decryptpath": "", "cdmselected": "False"} 26 | parser["BROWSER"] = {"startpage": "https://duckduckgo.com/", "drmenabled": "True", "addons": ""} 27 | writeConfig() 28 | 29 | 30 | def writeConfig(): 31 | with open('config.ini', 'w') as configfile: 32 | parser.write(configfile) 33 | -------------------------------------------------------------------------------- /data/drop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevLARLEY/mpdl/f544a691eba49f580dae2e64e8d1bb5d3014495c/data/drop.png -------------------------------------------------------------------------------- /data/firefox/extensions.json: -------------------------------------------------------------------------------- 1 | {"schemaVersion":35,"addons":[{"id":"formautofill@mozilla.org","syncGUID":"{2b125048-bfb0-441a-bccb-1eb64bdf189f}","version":"1.0.1","type":"extension","loader":null,"updateURL":null,"installOrigins":null,"manifestVersion":2,"optionsURL":null,"optionsType":null,"optionsBrowserStyle":true,"aboutURL":null,"defaultLocale":{"name":"Form Autofill","creator":null,"developers":null,"translators":null,"contributors":null},"visible":true,"active":true,"userDisabled":false,"appDisabled":false,"embedderDisabled":false,"installDate":1695983911703,"updateDate":1695983911703,"applyBackgroundUpdates":1,"path":"C:\\Program Files\\Mozilla Firefox\\browser\\features\\formautofill@mozilla.org.xpi","skinnable":false,"sourceURI":null,"releaseNotesURI":null,"softDisabled":false,"foreignInstall":false,"strictCompatibility":true,"locales":[],"targetApplications":[{"id":"toolkit@mozilla.org","minVersion":null,"maxVersion":null}],"targetPlatforms":[],"signedDate":null,"seen":true,"dependencies":[],"incognito":"spanning","userPermissions":{"permissions":[],"origins":[]},"optionalPermissions":{"permissions":[],"origins":[]},"icons":{},"iconURL":null,"blocklistState":0,"blocklistURL":null,"startupData":null,"hidden":true,"installTelemetryInfo":null,"recommendationState":null,"rootURI":"jar:file:///C:/Program%20Files/Mozilla%20Firefox/browser/features/formautofill@mozilla.org.xpi!/","location":"app-system-defaults"},{"id":"pictureinpicture@mozilla.org","syncGUID":"{41a3d223-a353-401b-a102-a5844514f52b}","version":"1.0.0","type":"extension","loader":null,"updateURL":null,"installOrigins":null,"manifestVersion":2,"optionsURL":null,"optionsType":null,"optionsBrowserStyle":true,"aboutURL":null,"defaultLocale":{"name":"Picture-In-Picture","description":"Fixes for web compatibility with Picture-in-Picture","creator":null,"developers":null,"translators":null,"contributors":null},"visible":true,"active":true,"userDisabled":false,"appDisabled":false,"embedderDisabled":false,"installDate":1698348535931,"updateDate":1698348535931,"applyBackgroundUpdates":1,"path":"C:\\Program Files\\Mozilla Firefox\\browser\\features\\pictureinpicture@mozilla.org.xpi","skinnable":false,"sourceURI":null,"releaseNotesURI":null,"softDisabled":false,"foreignInstall":false,"strictCompatibility":true,"locales":[],"targetApplications":[{"id":"toolkit@mozilla.org","minVersion":"88.0a1","maxVersion":null}],"targetPlatforms":[],"signedDate":null,"seen":true,"dependencies":[],"incognito":"spanning","userPermissions":{"permissions":[],"origins":[]},"optionalPermissions":{"permissions":[],"origins":[]},"icons":{},"iconURL":null,"blocklistState":0,"blocklistURL":null,"startupData":null,"hidden":true,"installTelemetryInfo":null,"recommendationState":null,"rootURI":"jar:file:///C:/Program%20Files/Mozilla%20Firefox/browser/features/pictureinpicture@mozilla.org.xpi!/","location":"app-system-defaults"},{"id":"screenshots@mozilla.org","syncGUID":"{634ca17d-00ee-4498-a9e5-9e7d102c80c5}","version":"39.0.1","type":"extension","loader":null,"updateURL":null,"installOrigins":null,"manifestVersion":2,"optionsURL":null,"optionsType":null,"optionsBrowserStyle":true,"aboutURL":null,"defaultLocale":{"name":"Firefox Screenshots","description":"Take clips and screenshots from the Web and save them temporarily or permanently.","creator":"Mozilla ","homepageURL":"https://github.com/mozilla-services/screenshots","developers":null,"translators":null,"contributors":null},"visible":true,"active":true,"userDisabled":false,"appDisabled":false,"embedderDisabled":false,"installDate":1698348535930,"updateDate":1698348535930,"applyBackgroundUpdates":1,"path":"C:\\Program Files\\Mozilla Firefox\\browser\\features\\screenshots@mozilla.org.xpi","skinnable":false,"sourceURI":null,"releaseNotesURI":null,"softDisabled":false,"foreignInstall":false,"strictCompatibility":true,"locales":[],"targetApplications":[{"id":"toolkit@mozilla.org","minVersion":"57.0a1","maxVersion":null}],"targetPlatforms":[],"signedDate":null,"seen":true,"dependencies":[],"incognito":"spanning","userPermissions":{"permissions":["activeTab","downloads","tabs","storage","notifications","clipboardWrite","contextMenus","mozillaAddons","telemetry"],"origins":["","https://screenshots.firefox.com/","resource://pdf.js/","about:reader*","https://screenshots.firefox.com/*"]},"optionalPermissions":{"permissions":[],"origins":[]},"icons":{},"iconURL":null,"blocklistState":0,"blocklistURL":null,"startupData":null,"hidden":true,"installTelemetryInfo":null,"recommendationState":null,"rootURI":"jar:file:///C:/Program%20Files/Mozilla%20Firefox/browser/features/screenshots@mozilla.org.xpi!/","location":"app-system-defaults"},{"id":"webcompat-reporter@mozilla.org","syncGUID":"{03e5d13b-6382-4b07-a982-e71f3fdcf627}","version":"1.5.1","type":"extension","loader":null,"updateURL":null,"installOrigins":null,"manifestVersion":2,"optionsURL":null,"optionsType":null,"optionsBrowserStyle":true,"aboutURL":null,"defaultLocale":{"name":"WebCompat Reporter","description":"Report site compatibility issues on webcompat.com","creator":"Thomas Wisniewski ","homepageURL":"https://github.com/mozilla/webcompat-reporter","developers":null,"translators":null,"contributors":null},"visible":true,"active":false,"userDisabled":true,"appDisabled":false,"embedderDisabled":false,"installDate":1695983911702,"updateDate":1695983911702,"applyBackgroundUpdates":1,"path":"C:\\Program Files\\Mozilla Firefox\\browser\\features\\webcompat-reporter@mozilla.org.xpi","skinnable":false,"sourceURI":null,"releaseNotesURI":null,"softDisabled":false,"foreignInstall":false,"strictCompatibility":true,"locales":[],"targetApplications":[{"id":"toolkit@mozilla.org","minVersion":null,"maxVersion":null}],"targetPlatforms":[],"signedDate":null,"seen":true,"dependencies":[],"incognito":"spanning","userPermissions":{"permissions":["tabs"],"origins":[""]},"optionalPermissions":{"permissions":[],"origins":[]},"icons":{"16":"icons/lightbulb.svg","32":"icons/lightbulb.svg","48":"icons/lightbulb.svg","96":"icons/lightbulb.svg","128":"icons/lightbulb.svg"},"iconURL":null,"blocklistState":0,"blocklistURL":null,"startupData":null,"hidden":true,"installTelemetryInfo":null,"recommendationState":null,"rootURI":"jar:file:///C:/Program%20Files/Mozilla%20Firefox/browser/features/webcompat-reporter@mozilla.org.xpi!/","location":"app-system-defaults"},{"id":"webcompat@mozilla.org","syncGUID":"{9ffca57d-f608-4597-9155-34ae81c601d4}","version":"119.0.0","type":"extension","loader":null,"updateURL":null,"installOrigins":null,"manifestVersion":2,"optionsURL":null,"optionsType":null,"optionsBrowserStyle":true,"aboutURL":null,"defaultLocale":{"name":"Web Compatibility Interventions","description":"Urgent post-release fixes for web compatibility.","creator":null,"developers":null,"translators":null,"contributors":null},"visible":true,"active":true,"userDisabled":false,"appDisabled":false,"embedderDisabled":false,"installDate":1698348535928,"updateDate":1698348535928,"applyBackgroundUpdates":1,"path":"C:\\Program Files\\Mozilla Firefox\\browser\\features\\webcompat@mozilla.org.xpi","skinnable":false,"sourceURI":null,"releaseNotesURI":null,"softDisabled":false,"foreignInstall":false,"strictCompatibility":true,"locales":[],"targetApplications":[{"id":"toolkit@mozilla.org","minVersion":"102.0","maxVersion":null}],"targetPlatforms":[],"signedDate":null,"seen":true,"dependencies":[],"incognito":"spanning","userPermissions":{"permissions":["mozillaAddons","scripting","tabs","webNavigation","webRequest","webRequestBlocking"],"origins":[""]},"optionalPermissions":{"permissions":[],"origins":[]},"icons":{},"iconURL":null,"blocklistState":0,"blocklistURL":null,"startupData":null,"hidden":true,"installTelemetryInfo":null,"recommendationState":null,"rootURI":"jar:file:///C:/Program%20Files/Mozilla%20Firefox/browser/features/webcompat@mozilla.org.xpi!/","location":"app-system-defaults"},{"id":"default-theme@mozilla.org","syncGUID":"{400c9015-7d40-4886-9eeb-69b630f35df8}","version":"1.3","type":"theme","loader":null,"updateURL":null,"installOrigins":null,"manifestVersion":2,"optionsURL":null,"optionsType":null,"optionsBrowserStyle":true,"aboutURL":null,"defaultLocale":{"name":"System theme — auto","description":"Follow the operating system setting for buttons, menus, and windows.","creator":"Mozilla","developers":null,"translators":null,"contributors":null},"visible":true,"active":true,"userDisabled":false,"appDisabled":false,"embedderDisabled":false,"installDate":1699786001183,"applyBackgroundUpdates":1,"path":null,"skinnable":false,"sourceURI":null,"releaseNotesURI":null,"softDisabled":false,"foreignInstall":false,"strictCompatibility":true,"locales":[],"targetApplications":[{"id":"toolkit@mozilla.org","minVersion":null,"maxVersion":null}],"targetPlatforms":[],"signedDate":null,"seen":true,"dependencies":[],"userPermissions":null,"optionalPermissions":null,"icons":{"32":"icon.svg"},"iconURL":null,"blocklistState":0,"blocklistURL":null,"startupData":null,"hidden":false,"installTelemetryInfo":null,"recommendationState":null,"rootURI":"resource://default-theme/","location":"app-builtin"},{"id":"addons-search-detection@mozilla.com","syncGUID":"{6599ac2b-2aa7-4435-bc59-7c0145f81ba7}","version":"2.0.0","type":"extension","loader":null,"updateURL":null,"installOrigins":null,"manifestVersion":2,"optionsURL":null,"optionsType":null,"optionsBrowserStyle":true,"aboutURL":null,"defaultLocale":{"name":"Add-ons Search Detection","description":"","creator":null,"developers":null,"translators":null,"contributors":null},"visible":true,"active":true,"userDisabled":false,"appDisabled":false,"embedderDisabled":false,"installDate":1699786002039,"applyBackgroundUpdates":1,"path":null,"skinnable":false,"sourceURI":null,"releaseNotesURI":null,"softDisabled":false,"foreignInstall":false,"strictCompatibility":true,"locales":[],"targetApplications":[{"id":"toolkit@mozilla.org","minVersion":null,"maxVersion":null}],"targetPlatforms":[],"signedDate":null,"seen":true,"dependencies":[],"incognito":"spanning","userPermissions":{"permissions":["telemetry","webRequest","webRequestBlocking"],"origins":[""]},"optionalPermissions":{"permissions":[],"origins":[]},"icons":{},"iconURL":null,"blocklistState":0,"blocklistURL":null,"startupData":null,"hidden":true,"installTelemetryInfo":null,"recommendationState":null,"rootURI":"resource://builtin-addons/search-detection/","location":"app-builtin"},{"id":"google@search.mozilla.org","syncGUID":"{25b621a7-231a-49c5-90b4-bb38fed9d567}","version":"1.4","type":"extension","loader":null,"updateURL":null,"installOrigins":null,"manifestVersion":2,"optionsURL":null,"optionsType":null,"optionsBrowserStyle":true,"aboutURL":null,"defaultLocale":{"name":"Google","description":"Google Search","creator":null,"developers":null,"translators":null,"contributors":null},"visible":true,"active":true,"userDisabled":false,"appDisabled":false,"embedderDisabled":false,"installDate":1699786002117,"applyBackgroundUpdates":1,"path":null,"skinnable":false,"sourceURI":null,"releaseNotesURI":null,"softDisabled":false,"foreignInstall":false,"strictCompatibility":true,"locales":[{"name":"Google","description":"Google Search","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["en"]},{"name":"Google","description":"Google Search","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["region-ru"]},{"name":"Google","description":"Google Search","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["region-by"]},{"name":"Google","description":"Google Search","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["region-tr"]},{"name":"Google","description":"Google Search","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["region-kz"]}],"targetApplications":[{"id":"toolkit@mozilla.org","minVersion":null,"maxVersion":null}],"targetPlatforms":[],"signedDate":null,"seen":true,"dependencies":[],"incognito":"spanning","userPermissions":{"permissions":[],"origins":[]},"optionalPermissions":{"permissions":[],"origins":[]},"icons":{"16":"favicon.ico"},"iconURL":null,"blocklistState":0,"blocklistURL":null,"startupData":null,"hidden":true,"installTelemetryInfo":null,"recommendationState":null,"rootURI":"resource://search-extensions/google/","location":"app-builtin"},{"id":"wikipedia@search.mozilla.org","syncGUID":"{a20662b0-3c00-4ca5-a2da-60ae2f9ce3e7}","version":"1.3","type":"extension","loader":null,"updateURL":null,"installOrigins":null,"manifestVersion":2,"optionsURL":null,"optionsType":null,"optionsBrowserStyle":true,"aboutURL":null,"defaultLocale":{"name":"Wikipedia (en)","description":"Wikipedia, the Free Encyclopedia","creator":null,"developers":null,"translators":null,"contributors":null},"visible":true,"active":true,"userDisabled":false,"appDisabled":false,"embedderDisabled":false,"installDate":1699786002197,"applyBackgroundUpdates":1,"path":null,"skinnable":false,"sourceURI":null,"releaseNotesURI":null,"softDisabled":false,"foreignInstall":false,"strictCompatibility":true,"locales":[{"name":"Wikipedia (en)","description":"Wikipedia, the Free Encyclopedia","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["en"]},{"name":"Wikipedija (hr)","description":"Wikipedija, slobodna enciklopedija","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["hr"]},{"name":"Wikipedia (fi)","description":"Wikipedia (fi), vapaa tietosanakirja","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["fi"]},{"name":"Wikipedia (hy)","description":"Վիքիփեդիա՝ ազատ հանրագիտարան","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["hy"]},{"name":"Уикипедия (kk)","description":"Уикипедия (kk)","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["kk"]},{"name":"Вікіпедыя (be)","description":"Вікіпедыя, свабодная энцыклапедыя","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["be"]},{"name":"위키백과 (ko)","description":"Wikipedia, the free encyclopedia","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["kr"]},{"name":"Wikipedia (ro)","description":"Wikipedia, enciclopedia liberă","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["ro"]},{"name":"Wikipedia (bs)","description":"Slobodna enciklopedija","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["bs"]},{"name":"Wikipedia (pt)","description":"Wikipédia, a enciclopédia livre","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["pt"]},{"name":"Vikipetã (gn)","description":"Vikipetã, opaite tembikuaa hekosãsóva renda","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["gn"]},{"name":"વિકિપીડિયા (gu)","description":"વીકીપીડિયા, મુક્ત એનસાયક્લોપીડિયા","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["gu"]},{"name":"Wikipedia (el)","description":"Βικιπαίδεια, η ελεύθερη εγκυκλοπαίδεια","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["el"]},{"name":"Wikipedia (es)","description":"Wikipedia, la enciclopedia libre","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["es"]},{"name":"ויקיפדיה","description":"ויקיפדיה","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["he"]},{"name":"Wikipedia (da)","description":"Wikipedia, den frie encyklopædi","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["da"]},{"name":"Wikipedia (tr)","description":"Vikipedi, özgür ansiklopedi","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["tr"]},{"name":"Wikipedija (hsb)","description":"Wikipedija, swobodna encyklopedija","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["hsb"]},{"name":"Wikipedy (fy)","description":"De fergese ensyklopedy","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["fy-NL"]},{"name":"विकिपीडिया (ne)","description":"विकिपिडिया एक स्वतन्त्र विश्वकोष","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["ne"]},{"name":"Wikipedia (nl)","description":"De vrije encyclopedie","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["nl"]},{"name":"Wikipedia (ja)","description":"Wikipedia - フリー百科事典","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["ja"]},{"name":"Vikipeedia (et)","description":"Vikipeedia, vaba entsüklopeedia","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["et"]},{"name":"Wikipèdia (oc)","description":"Wikipèdia, l'enciclopèdia liura","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["oc"]},{"name":"Wicipedia (cy)","description":"Wicipedia, Y Gwyddioniadur Rhydd","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["cy"]},{"name":"వికీపీడియా (te)","description":"వికీపీడియా (te)","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["te"]},{"name":"Wikipédia (fr)","description":"Wikipédia, l'encyclopédie libre","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["fr"]},{"name":"Wikipedia (tl)","description":"Wikipedia, ang malayang ensiklopedya","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["tl"]},{"name":"维基百科","description":"维基百科,自由的百科全书","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["zh-CN"]},{"name":"Wikipedia (lij)","description":"Wikipedia, l'enciclopedia libera","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["lij"]},{"name":"វីគីភីឌា (km)","description":"វីគីភីឌា សព្វ​វចនា​ធិប្បាយ​សេរី","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["km"]},{"name":"Уикипедия (bg)","description":"Уикипедия, свободната енциклоподия","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["bg"]},{"name":"Wikipedia (id)","description":"Wikipedia, ensiklopedia bebas","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["id"]},{"name":"Wikipedia (pa)","description":"ਵਿਕਿਪੀਡਿਆ, ਮੁਫ਼ਤ/ਮੁਕਤ ਸ਼ਬਦਕੋਸ਼","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["pa"]},{"name":"উইকিপিডিয়া (bn)","description":"উইকিপিডিয়া, মুক্ত বিশ্বকোষ","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["bn"]},{"name":"Wikipedia (eu)","description":"Wikipedia, entziklopedia askea","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["eu"]},{"name":"Wikipedie (cs)","description":"Wikipedia, svobodná encyclopedie","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["cz"]},{"name":"Wikipédia (hu)","description":"Wikipedia, a szabad enciklopédia","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["hu"]},{"name":"Wikipedia (kn)","description":"Wikipedia, the free encyclopedia","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["kn"]},{"name":"Wikipedia (is)","description":"Wikipedia, the free encyclopedia","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["is"]},{"name":"Вікіпедія (uk)","description":"Вікіпедія, вільна енциклопедія","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["uk"]},{"name":"Wikipedia (kab)","description":"Wikipedia, tasanayt tilellit","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["kab"]},{"name":"Wikipedia (zh)","description":"維基百科,自由的百科全書","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["zh-TW"]},{"name":"ویکیپیڈیا (ur)","description":"ویکیپیڈیا آزاد دائرۃ المعارف","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["ur"]},{"name":"Vikipedio (eo)","description":"Vikipedio, la libera enciklopedio","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["eo"]},{"name":"Wikipedia (si)","description":"Wikipedia, the free encyclopedia","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["si"]},{"name":"ويكيبيديا (ar)","description":"ويكيبيديا (ar)","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["ar"]},{"name":"Wikipedia (vi)","description":"Wikipedia, bách khoa toàn thư mở","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["vi"]},{"name":"ვიკიპედია (ka)","description":"ვიკიპედია, თავისუფალი ენციკლოპედია","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["ka"]},{"name":"Uicipeid (gd)","description":"Wikipedia, An leabhar mòr-eòlais","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["gd"]},{"name":"Wikipedia (it)","description":"Wikipedia, l'enciclopedia libera","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["it"]},{"name":"Vikipediya (uz)","description":"Vikipediya, ochiq ensiklopediya","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["uz"]},{"name":"Wikipedia (lt)","description":"Vikipedija, laisvoji enciklopedija","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["lt"]},{"name":"Wikipedia (sq)","description":"Wikipedia, enciklopedia e lirë","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["sq"]},{"name":"Vicipéid (ga)","description":"Vicipéid, an Chiclipéid Shaor","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["ga-IE"]},{"name":"विकिपीडिया (hi)","description":"विकिपीडिया (हिन्दी)","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["hi"]},{"name":"Vikipedeja (ltg)","description":"Vikipēdija, breivuo eņciklopedeja","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["ltg"]},{"name":"Vikipediya (az)","description":"Vikipediya, açıq ensiklopediya","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["az"]},{"name":"விக்கிப்பீடியா (ta)","description":"விக்கிப்பீடியா (ta)","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["ta"]},{"name":"Википедија (mk)","description":"Википедија, слободната енциклопедија","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["mk"]},{"name":"วิกิพีเดีย","description":"วิกิพีเดีย สารานุกรมเสรี","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["th"]},{"name":"Wikipedia (de)","description":"Wikipedia, die freie Enzyklopädie","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["de"]},{"name":"Wikipedija (dsb)","description":"Wikipedija, lichotna encyklopedija","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["dsb"]},{"name":"विकिपीडिया (mr)","description":"विकिपीडिया, मोफत माहितीकोष","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["mr"]},{"name":"Wikipedia (ast)","description":"La enciclopedia llibre","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["ast"]},{"name":"Wikipedia (my)","description":"အခမဲ့လွတ်လပ်စွယ်စုံကျမ်း","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["my"]},{"name":"Wikipedia (rm)","description":"Vichipedia, l'enciclopedia libra","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["rm"]},{"name":"Wikipedia (nn)","description":"Wikipedia, det frie oppslagsverket","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["NN"]},{"name":"Wikipedia (wo)","description":"Wikipedia, Jimbulang bu Ubbeeku bi","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["wo"]},{"name":"Wikipedia (gl)","description":"Wikipedia, a enciclopedia libre","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["gl"]},{"name":"Viquipèdia (ca)","description":"L'enciclopèdia lliure","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["ca"]},{"name":"Wikipédia (sk)","description":"Wikipédia, slobodná a otvorená encyklopédia","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["sk"]},{"name":"Википедија (sr)","description":"Претрага Википедије на српском језику","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["sr"]},{"name":"Wikipedia (af)","description":"Wikipedia, die vrye ensiklopedie","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["af"]},{"name":"ویکی‌پدیا (fa)","description":"ویکی‌پدیا، دانشنامهٔ آزاد","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["fa"]},{"name":"Wikipedia (ms)","description":"Wikipedia, ensiklopedia bebas","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["ms"]},{"name":"Wikipedia (ia)","description":"Wikipedia, le encyclopedia libere","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["ia"]},{"name":"Wikipedia (no)","description":"Wikipedia, den frie encyklopedi","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["NO"]},{"name":"Википедия (ru)","description":"Википедия, свободная энциклопедия","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["ru"]},{"name":"Wikipedia (pl)","description":"Wikipedia, wolna encyklopedia","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["pl"]},{"name":"Wikipedia (br)","description":"Wikipedia, an holloueziadur digor","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["br"]},{"name":"Вікіпэдыя (be-tarask)","description":"Вікіпэдыя, вольная энцыкляпэдыя","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["be-tarask"]},{"name":"Wikipedia (sv)","description":"Wikipedia, den fria encyklopedin","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["sv-SE"]},{"name":"ວິກິພີເດຍ (lo)","description":"ວິກິພີເດຍ, ສາລານຸກົມເສລີ","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["lo"]},{"name":"Wikipedija (sl)","description":"Wikipedija, prosta enciklopedija","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["sl"]},{"name":"Vikipēdija","description":"Vikipēdija, brīvā enciklopēdija","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["lv"]},{"name":"Biquipedia (an)","description":"A enciclopedia Libre","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["an"]}],"targetApplications":[{"id":"toolkit@mozilla.org","minVersion":null,"maxVersion":null}],"targetPlatforms":[],"signedDate":null,"seen":true,"dependencies":[],"incognito":"spanning","userPermissions":{"permissions":[],"origins":[]},"optionalPermissions":{"permissions":[],"origins":[]},"icons":{"16":"favicon.ico"},"iconURL":null,"blocklistState":0,"blocklistURL":null,"startupData":null,"hidden":true,"installTelemetryInfo":null,"recommendationState":null,"rootURI":"resource://search-extensions/wikipedia/","location":"app-builtin"},{"id":"bing@search.mozilla.org","syncGUID":"{27f57cd4-db34-4d92-88ea-9dab32b62733}","version":"1.6","type":"extension","loader":null,"updateURL":null,"installOrigins":null,"manifestVersion":2,"optionsURL":null,"optionsType":null,"optionsBrowserStyle":true,"aboutURL":null,"defaultLocale":{"name":"Bing","description":"Microsoft Bing","creator":null,"developers":null,"translators":null,"contributors":null},"visible":true,"active":true,"userDisabled":false,"appDisabled":false,"embedderDisabled":false,"installDate":1699786002214,"applyBackgroundUpdates":1,"path":null,"skinnable":false,"sourceURI":null,"releaseNotesURI":null,"softDisabled":false,"foreignInstall":false,"strictCompatibility":true,"locales":[],"targetApplications":[{"id":"toolkit@mozilla.org","minVersion":null,"maxVersion":null}],"targetPlatforms":[],"signedDate":null,"seen":true,"dependencies":[],"incognito":"spanning","userPermissions":{"permissions":[],"origins":[]},"optionalPermissions":{"permissions":[],"origins":[]},"icons":{"16":"favicon.ico"},"iconURL":null,"blocklistState":0,"blocklistURL":null,"startupData":null,"hidden":true,"installTelemetryInfo":null,"recommendationState":null,"rootURI":"resource://search-extensions/bing/","location":"app-builtin"},{"id":"ddg@search.mozilla.org","syncGUID":"{af03c841-33cd-4ca9-8d5f-e3d22aee0021}","version":"1.4","type":"extension","loader":null,"updateURL":null,"installOrigins":null,"manifestVersion":2,"optionsURL":null,"optionsType":null,"optionsBrowserStyle":true,"aboutURL":null,"defaultLocale":{"name":"DuckDuckGo","description":"Search DuckDuckGo","creator":null,"developers":null,"translators":null,"contributors":null},"visible":true,"active":true,"userDisabled":false,"appDisabled":false,"embedderDisabled":false,"installDate":1699786002223,"applyBackgroundUpdates":1,"path":null,"skinnable":false,"sourceURI":null,"releaseNotesURI":null,"softDisabled":false,"foreignInstall":false,"strictCompatibility":true,"locales":[],"targetApplications":[{"id":"toolkit@mozilla.org","minVersion":null,"maxVersion":null}],"targetPlatforms":[],"signedDate":null,"seen":true,"dependencies":[],"incognito":"spanning","userPermissions":{"permissions":[],"origins":[]},"optionalPermissions":{"permissions":[],"origins":[]},"icons":{"16":"favicon.ico"},"iconURL":null,"blocklistState":0,"blocklistURL":null,"startupData":null,"hidden":true,"installTelemetryInfo":null,"recommendationState":null,"rootURI":"resource://search-extensions/ddg/","location":"app-builtin"},{"id":"firefox-compact-light@mozilla.org","syncGUID":"{153b1fa4-1bde-4227-9387-52dd475c9556}","version":"1.2","type":"theme","loader":null,"updateURL":null,"installOrigins":null,"manifestVersion":2,"optionsURL":null,"optionsType":null,"optionsBrowserStyle":true,"aboutURL":null,"defaultLocale":{"name":"Light","description":"A theme with a light color scheme.","creator":"Mozilla","developers":null,"translators":null,"contributors":null},"visible":true,"active":false,"userDisabled":true,"appDisabled":false,"embedderDisabled":false,"installDate":1699786002352,"applyBackgroundUpdates":1,"path":null,"skinnable":false,"sourceURI":null,"releaseNotesURI":null,"softDisabled":false,"foreignInstall":false,"strictCompatibility":true,"locales":[],"targetApplications":[{"id":"toolkit@mozilla.org","minVersion":null,"maxVersion":null}],"targetPlatforms":[],"signedDate":null,"seen":true,"dependencies":[],"userPermissions":null,"optionalPermissions":null,"icons":{"32":"icon.svg"},"iconURL":null,"blocklistState":0,"blocklistURL":null,"startupData":null,"hidden":false,"installTelemetryInfo":null,"recommendationState":null,"rootURI":"resource://builtin-themes/light/","location":"app-builtin"},{"id":"firefox-compact-dark@mozilla.org","syncGUID":"{65579fad-67f3-4f52-9457-b8f4fbd93eae}","version":"1.2","type":"theme","loader":null,"updateURL":null,"installOrigins":null,"manifestVersion":2,"optionsURL":null,"optionsType":null,"optionsBrowserStyle":true,"aboutURL":null,"defaultLocale":{"name":"Dark","description":"A theme with a dark color scheme.","creator":"Mozilla","developers":null,"translators":null,"contributors":null},"visible":true,"active":false,"userDisabled":true,"appDisabled":false,"embedderDisabled":false,"installDate":1699786002352,"applyBackgroundUpdates":1,"path":null,"skinnable":false,"sourceURI":null,"releaseNotesURI":null,"softDisabled":false,"foreignInstall":false,"strictCompatibility":true,"locales":[],"targetApplications":[{"id":"toolkit@mozilla.org","minVersion":null,"maxVersion":null}],"targetPlatforms":[],"signedDate":null,"seen":true,"dependencies":[],"userPermissions":null,"optionalPermissions":null,"icons":{"32":"icon.svg"},"iconURL":null,"blocklistState":0,"blocklistURL":null,"startupData":null,"hidden":false,"installTelemetryInfo":null,"recommendationState":null,"rootURI":"resource://builtin-themes/dark/","location":"app-builtin"},{"id":"firefox-alpenglow@mozilla.org","syncGUID":"{8d0273b7-8917-4534-9397-ae26db84b820}","version":"1.4","type":"theme","loader":null,"updateURL":null,"installOrigins":null,"manifestVersion":2,"optionsURL":null,"optionsType":null,"optionsBrowserStyle":true,"aboutURL":null,"defaultLocale":{"name":"Firefox Alpenglow","description":"Use a colorful appearance for buttons, menus, and windows.","creator":null,"developers":null,"translators":null,"contributors":null},"visible":true,"active":false,"userDisabled":true,"appDisabled":false,"embedderDisabled":false,"installDate":1699786002352,"applyBackgroundUpdates":1,"path":null,"skinnable":false,"sourceURI":null,"releaseNotesURI":null,"softDisabled":false,"foreignInstall":false,"strictCompatibility":true,"locales":[],"targetApplications":[{"id":"toolkit@mozilla.org","minVersion":null,"maxVersion":null}],"targetPlatforms":[],"signedDate":null,"seen":true,"dependencies":[],"userPermissions":null,"optionalPermissions":null,"icons":{"32":"icon.svg"},"iconURL":null,"blocklistState":0,"blocklistURL":null,"startupData":null,"hidden":false,"installTelemetryInfo":null,"recommendationState":null,"rootURI":"resource://builtin-themes/alpenglow/","location":"app-builtin"},{"id":"amazon@search.mozilla.org","syncGUID":"{1f52d62d-f9cc-473b-b044-5ac3c84ba5eb}","version":"1.13","type":"extension","loader":null,"updateURL":null,"installOrigins":null,"manifestVersion":2,"optionsURL":null,"optionsType":null,"optionsBrowserStyle":true,"aboutURL":null,"defaultLocale":{"name":"Amazon.com.au","description":"Amazon.com.au Search","creator":null,"developers":null,"translators":null,"contributors":null},"visible":true,"active":true,"userDisabled":false,"appDisabled":false,"embedderDisabled":false,"installDate":1699786002522,"applyBackgroundUpdates":1,"path":null,"skinnable":false,"sourceURI":null,"releaseNotesURI":null,"softDisabled":false,"foreignInstall":false,"strictCompatibility":true,"locales":[{"name":"Amazon.com.au","description":"Amazon.com.au Search","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["au"]},{"name":"Amazon.ca","description":"Amazon.ca Search","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["ca"]},{"name":"Amazon.es","description":"Amazon.es","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["spain"]},{"name":"Amazon.fr","description":"Recherche Amazon.fr","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["france"]},{"name":"Amazon.nl","description":"Amazon.nl Search","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["nl"]},{"name":"Amazon.co.uk","description":"Amazon.co.uk Search","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["en-GB"]},{"name":"Amazon.co.jp","description":"Amazon.co.jp Search","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["jp"]},{"name":"Amazon.se","description":"Amazon.se","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["sweden"]},{"name":"Amazon.it","description":"Ricerca Amazon.it","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["it"]},{"name":"Amazon.de","description":"Amazon.de Suche","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["de"]},{"name":"Amazon.in","description":"Amazon.in Search","creator":null,"developers":null,"translators":null,"contributors":null,"locales":["in"]}],"targetApplications":[{"id":"toolkit@mozilla.org","minVersion":null,"maxVersion":null}],"targetPlatforms":[],"signedDate":null,"seen":true,"dependencies":[],"incognito":"spanning","userPermissions":{"permissions":[],"origins":[]},"optionalPermissions":{"permissions":[],"origins":[]},"icons":{"16":"favicon.ico"},"iconURL":null,"blocklistState":0,"blocklistURL":null,"startupData":null,"hidden":true,"installTelemetryInfo":null,"recommendationState":null,"rootURI":"resource://search-extensions/amazon/","location":"app-builtin"}]} -------------------------------------------------------------------------------- /data/firefox/gmp-gmpopenh264/2.3.2/gmpopenh264.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevLARLEY/mpdl/f544a691eba49f580dae2e64e8d1bb5d3014495c/data/firefox/gmp-gmpopenh264/2.3.2/gmpopenh264.dll -------------------------------------------------------------------------------- /data/firefox/gmp-gmpopenh264/2.3.2/gmpopenh264.info: -------------------------------------------------------------------------------- 1 | Name: gmpopenh264 2 | Description: GMP Plugin for OpenH264. 3 | Version: 2.3.2 4 | APIs: encode-video[h264], decode-video[h264] 5 | -------------------------------------------------------------------------------- /data/firefox/gmp-widevinecdm/4.10.2710.0/LICENSE.txt: -------------------------------------------------------------------------------- 1 | "Google Inc. and its affiliates ("Google") own all legal right, title and 2 | interest in and to the content decryption module software ("Software") and 3 | related documentation, including any intellectual property rights in the 4 | Software. You may not use, modify, sell, or otherwise distribute the Software 5 | without a separate license agreement with Google. The Software is not open 6 | source software. 7 | 8 | If you are interested in licensing the Software, please contact 9 | widevine@google.com. 10 | -------------------------------------------------------------------------------- /data/firefox/gmp-widevinecdm/4.10.2710.0/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "arch": "x64", 3 | "description": "Widevine Content Decryption Module", 4 | "manifest_version": 2, 5 | "name": "WidevineCdm", 6 | "os": "win", 7 | "version": "4.10.2710.0", 8 | "x-cdm-codecs": "vp8,vp09,avc1,av01", 9 | "x-cdm-host-versions": "10", 10 | "x-cdm-interface-versions": "10", 11 | "x-cdm-module-versions": "4", 12 | "x-cdm-persistent-license-support": true 13 | } -------------------------------------------------------------------------------- /data/firefox/gmp-widevinecdm/4.10.2710.0/widevinecdm.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevLARLEY/mpdl/f544a691eba49f580dae2e64e8d1bb5d3014495c/data/firefox/gmp-widevinecdm/4.10.2710.0/widevinecdm.dll -------------------------------------------------------------------------------- /data/firefox/gmp-widevinecdm/4.10.2710.0/widevinecdm.dll.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevLARLEY/mpdl/f544a691eba49f580dae2e64e8d1bb5d3014495c/data/firefox/gmp-widevinecdm/4.10.2710.0/widevinecdm.dll.lib -------------------------------------------------------------------------------- /data/firefox/gmp-widevinecdm/4.10.2710.0/widevinecdm.dll.sig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevLARLEY/mpdl/f544a691eba49f580dae2e64e8d1bb5d3014495c/data/firefox/gmp-widevinecdm/4.10.2710.0/widevinecdm.dll.sig -------------------------------------------------------------------------------- /data/firefox/prefs.js: -------------------------------------------------------------------------------- 1 | user_pref("media.gmp-gmpopenh264.abi", "x86_64-msvc-x64"); 2 | user_pref("media.gmp-gmpopenh264.hashValue", "b667086ed49579592d435df2b486fe30ba1b62ddd169f19e700cd079239747dd3e20058c285fa9c10a533e34f22b5198ed9b1f92ae560a3067f3e3feacc724f1"); 3 | user_pref("media.gmp-gmpopenh264.version", "2.3.2"); 4 | user_pref("media.gmp-widevinecdm.abi", "x86_64-msvc-x64"); 5 | user_pref("media.gmp-widevinecdm.hashValue", "59521f8c61236641b3299ab460c58c8f5f26fa67e828de853c2cf372f9614d58b9f541aae325b1600ec4f3a47953caacb8122b0dfce7481acfec81045735947d"); 6 | user_pref("media.gmp-widevinecdm.version", "4.10.2710.0"); 7 | user_pref("browser.urlbar.placeholderName", "DuckDuckGo on MPDL"); 8 | user_pref("browser.urlbar.placeholderName.private", "DuckDuckGo on MPDL"); 9 | -------------------------------------------------------------------------------- /data/pywidevine/L3/cdm/cdm.py: -------------------------------------------------------------------------------- 1 | import base64 2 | 3 | import os 4 | import time 5 | import binascii 6 | 7 | from google.protobuf.message import DecodeError 8 | from google.protobuf import text_format 9 | 10 | from data.pywidevine.L3.cdm.formats import wv_proto4_pb2 as wv_proto4 11 | from data.pywidevine.L3.cdm.session import Session 12 | from data.pywidevine.L3.cdm.key import Key 13 | from Cryptodome.Random import get_random_bytes 14 | from Cryptodome.Random import random 15 | from Cryptodome.Cipher import PKCS1_OAEP, AES 16 | from Cryptodome.Hash import CMAC, SHA256, HMAC, SHA1 17 | from Cryptodome.PublicKey import RSA 18 | from Cryptodome.Signature import pss 19 | from Cryptodome.Util import Padding 20 | import logging 21 | 22 | 23 | class Cdm: 24 | def __init__(self): 25 | self.logger = logging.getLogger(__name__) 26 | self.sessions = {} 27 | 28 | def open_session(self, init_data_b64, device, raw_init_data=None, offline=False): 29 | self.logger.debug("open_session(init_data_b64={}, device={}".format(init_data_b64, device)) 30 | self.logger.info("opening new cdm session") 31 | if device.session_id_type == 'android': 32 | # format: 16 random hexdigits, 2 digit counter, 14 0s 33 | rand_ascii = ''.join(random.choice('ABCDEF0123456789') for _ in range(16)) 34 | counter = '01' # this resets regularly so its fine to use 01 35 | rest = '00000000000000' 36 | session_id = rand_ascii + counter + rest 37 | session_id = session_id.encode('ascii') 38 | elif device.session_id_type == 'chrome': 39 | rand_bytes = get_random_bytes(16) 40 | session_id = rand_bytes 41 | else: 42 | # other formats NYI 43 | self.logger.error("device type is unusable") 44 | return 1 45 | if raw_init_data and isinstance(raw_init_data, (bytes, bytearray)): 46 | # used for NF key exchange, where they don't provide a valid PSSH 47 | init_data = raw_init_data 48 | self.raw_pssh = True 49 | else: 50 | init_data = self._parse_init_data(init_data_b64) 51 | self.raw_pssh = False 52 | 53 | if init_data: 54 | new_session = Session(session_id, init_data, device, offline) 55 | else: 56 | self.logger.error("unable to parse init data") 57 | return 1 58 | self.sessions[session_id] = new_session 59 | self.logger.info("session opened and init data parsed successfully") 60 | return session_id 61 | 62 | def _parse_init_data(self, init_data_b64): 63 | parsed_init_data = wv_proto4.WidevineCencHeader() 64 | try: 65 | self.logger.debug("trying to parse init_data directly") 66 | parsed_init_data.ParseFromString(base64.b64decode(init_data_b64)[32:]) 67 | except DecodeError: 68 | self.logger.debug("unable to parse as-is, trying with removed pssh box header") 69 | try: 70 | id_bytes = parsed_init_data.ParseFromString(base64.b64decode(init_data_b64)[32:]) 71 | except DecodeError: 72 | self.logger.error("unable to parse, unsupported init data format") 73 | return None 74 | self.logger.debug("init_data:") 75 | for line in text_format.MessageToString(parsed_init_data).splitlines(): 76 | self.logger.debug(line) 77 | return parsed_init_data 78 | 79 | def close_session(self, session_id): 80 | self.logger.debug("close_session(session_id={})".format(session_id)) 81 | self.logger.info("closing cdm session") 82 | if session_id in self.sessions: 83 | self.sessions.pop(session_id) 84 | self.logger.info("cdm session closed") 85 | return 0 86 | else: 87 | self.logger.info("session {} not found".format(session_id)) 88 | return 1 89 | 90 | def set_service_certificate(self, session_id, cert_b64): 91 | self.logger.debug("set_service_certificate(session_id={}, cert={})".format(session_id, cert_b64)) 92 | self.logger.info("setting service certificate") 93 | 94 | if session_id not in self.sessions: 95 | self.logger.error("session id doesn't exist") 96 | return 1 97 | 98 | session = self.sessions[session_id] 99 | 100 | message = wv_proto4.SignedMessage() 101 | 102 | try: 103 | message.ParseFromString(base64.b64decode(cert_b64)) 104 | except DecodeError: 105 | self.logger.error("failed to parse cert as SignedMessage") 106 | 107 | service_certificate = wv_proto4.SignedDeviceCertificate() 108 | 109 | if message.Type: 110 | self.logger.debug("service cert provided as signedmessage") 111 | try: 112 | service_certificate.ParseFromString(message.Msg) 113 | except DecodeError: 114 | self.logger.error("failed to parse service certificate") 115 | return 1 116 | else: 117 | self.logger.debug("service cert provided as signeddevicecertificate") 118 | try: 119 | service_certificate.ParseFromString(base64.b64decode(cert_b64)) 120 | except DecodeError: 121 | self.logger.error("failed to parse service certificate") 122 | return 1 123 | 124 | self.logger.debug("service certificate:") 125 | for line in text_format.MessageToString(service_certificate).splitlines(): 126 | self.logger.debug(line) 127 | 128 | session.service_certificate = service_certificate 129 | session.privacy_mode = True 130 | 131 | return 0 132 | 133 | def get_license_request(self, session_id): 134 | self.logger.debug("get_license_request(session_id={})".format(session_id)) 135 | self.logger.info("getting license request") 136 | 137 | if session_id not in self.sessions: 138 | self.logger.error("session ID does not exist") 139 | return None 140 | 141 | session = self.sessions[session_id] 142 | 143 | # raw pssh will be treated as bytes and not parsed 144 | if self.raw_pssh: 145 | license_request = wv_proto4.SignedLicenseRequestRaw() 146 | else: 147 | license_request = wv_proto4.SignedLicenseRequest() 148 | client_id = wv_proto4.ClientIdentification() 149 | 150 | if not os.path.exists(session.device_config.device_client_id_blob_filename): 151 | self.logger.error("no client ID blob available for this device") 152 | return None 153 | 154 | with open(session.device_config.device_client_id_blob_filename, "rb") as f: 155 | try: 156 | cid_bytes = client_id.ParseFromString(f.read()) 157 | except DecodeError: 158 | self.logger.error("client id failed to parse as protobuf") 159 | return None 160 | 161 | self.logger.debug("building license request") 162 | if not self.raw_pssh: 163 | license_request.Type = wv_proto4.SignedLicenseRequest.MessageType.Value('LICENSE_REQUEST') 164 | license_request.Msg.ContentId.CencId.Pssh.CopyFrom(session.init_data) 165 | else: 166 | license_request.Type = wv_proto4.SignedLicenseRequestRaw.MessageType.Value('LICENSE_REQUEST') 167 | license_request.Msg.ContentId.CencId.Pssh = session.init_data # bytes 168 | 169 | if session.offline: 170 | license_type = wv_proto4.LicenseType.Value('OFFLINE') 171 | else: 172 | license_type = wv_proto4.LicenseType.Value('DEFAULT') 173 | license_request.Msg.ContentId.CencId.LicenseType = license_type 174 | license_request.Msg.ContentId.CencId.RequestId = session_id 175 | license_request.Msg.Type = wv_proto4.LicenseRequest.RequestType.Value('NEW') 176 | license_request.Msg.RequestTime = int(time.time()) 177 | license_request.Msg.ProtocolVersion = wv_proto4.ProtocolVersion.Value('CURRENT') 178 | if session.device_config.send_key_control_nonce: 179 | license_request.Msg.KeyControlNonce = random.randrange(1, 2 ** 31) 180 | 181 | if session.privacy_mode: 182 | if session.device_config.vmp: 183 | self.logger.debug("vmp required, adding to client_id") 184 | self.logger.debug("reading vmp hashes") 185 | vmp_hashes = wv_proto4.FileHashes() 186 | with open(session.device_config.device_vmp_blob_filename, "rb") as f: 187 | try: 188 | vmp_bytes = vmp_hashes.ParseFromString(f.read()) 189 | except DecodeError: 190 | self.logger.error("vmp hashes failed to parse as protobuf") 191 | return None 192 | client_id._FileHashes.CopyFrom(vmp_hashes) 193 | self.logger.debug("privacy mode & service certificate loaded, encrypting client id") 194 | self.logger.debug("unencrypted client id:") 195 | for line in text_format.MessageToString(client_id).splitlines(): 196 | self.logger.debug(line) 197 | cid_aes_key = get_random_bytes(16) 198 | cid_iv = get_random_bytes(16) 199 | 200 | cid_cipher = AES.new(cid_aes_key, AES.MODE_CBC, cid_iv) 201 | 202 | encrypted_client_id = cid_cipher.encrypt(Padding.pad(client_id.SerializeToString(), 16)) 203 | 204 | service_public_key = RSA.importKey(session.service_certificate._DeviceCertificate.PublicKey) 205 | 206 | service_cipher = PKCS1_OAEP.new(service_public_key) 207 | 208 | encrypted_cid_key = service_cipher.encrypt(cid_aes_key) 209 | 210 | encrypted_client_id_proto = wv_proto4.EncryptedClientIdentification() 211 | 212 | encrypted_client_id_proto.ServiceId = session.service_certificate._DeviceCertificate.ServiceId 213 | encrypted_client_id_proto.ServiceCertificateSerialNumber = session.service_certificate._DeviceCertificate.SerialNumber 214 | encrypted_client_id_proto.EncryptedClientId = encrypted_client_id 215 | encrypted_client_id_proto.EncryptedClientIdIv = cid_iv 216 | encrypted_client_id_proto.EncryptedPrivacyKey = encrypted_cid_key 217 | 218 | license_request.Msg.EncryptedClientId.CopyFrom(encrypted_client_id_proto) 219 | else: 220 | license_request.Msg.ClientId.CopyFrom(client_id) 221 | 222 | if session.device_config.private_key_available: 223 | key = RSA.importKey(open(session.device_config.device_private_key_filename).read()) 224 | session.device_key = key 225 | else: 226 | self.logger.error("need device private key, other methods unimplemented") 227 | return None 228 | 229 | self.logger.debug("signing license request") 230 | 231 | hash = SHA1.new(license_request.Msg.SerializeToString()) 232 | signature = pss.new(key).sign(hash) 233 | 234 | license_request.Signature = signature 235 | 236 | session.license_request = license_request 237 | 238 | self.logger.debug("license request:") 239 | for line in text_format.MessageToString(session.license_request).splitlines(): 240 | self.logger.debug(line) 241 | self.logger.info("license request created") 242 | self.logger.debug("license request b64: {}".format(base64.b64encode(license_request.SerializeToString()))) 243 | return license_request.SerializeToString() 244 | 245 | def provide_license(self, session_id, license_b64) -> bool: 246 | self.logger.debug("provide_license(session_id={}, license_b64={})".format(session_id, license_b64)) 247 | self.logger.info("decrypting provided license") 248 | 249 | if session_id not in self.sessions: 250 | self.logger.error("session does not exist") 251 | return False 252 | 253 | session = self.sessions[session_id] 254 | 255 | if not session.license_request: 256 | self.logger.error("generate a license request first!") 257 | return False 258 | 259 | lic = wv_proto4.SignedLicense() 260 | try: 261 | lic.ParseFromString(base64.b64decode(license_b64)) 262 | except DecodeError: 263 | self.logger.error("unable to parse license - check protobufs") 264 | return False 265 | 266 | session.license = lic 267 | 268 | self.logger.debug("license:") 269 | for line in text_format.MessageToString(lic).splitlines(): 270 | self.logger.debug(line) 271 | 272 | self.logger.debug("deriving keys from session key") 273 | 274 | oaep_cipher = PKCS1_OAEP.new(session.device_key) 275 | 276 | session.session_key = oaep_cipher.decrypt(lic.SessionKey) 277 | 278 | lic_req_msg = session.license_request.Msg.SerializeToString() 279 | 280 | enc_key_base = b"ENCRYPTION\000" + lic_req_msg + b"\0\0\0\x80" 281 | auth_key_base = b"AUTHENTICATION\0" + lic_req_msg + b"\0\0\2\0" 282 | 283 | enc_key = b"\x01" + enc_key_base 284 | auth_key_1 = b"\x01" + auth_key_base 285 | auth_key_2 = b"\x02" + auth_key_base 286 | auth_key_3 = b"\x03" + auth_key_base 287 | auth_key_4 = b"\x04" + auth_key_base 288 | 289 | cmac_obj = CMAC.new(session.session_key, ciphermod=AES) 290 | cmac_obj.update(enc_key) 291 | 292 | enc_cmac_key = cmac_obj.digest() 293 | 294 | cmac_obj = CMAC.new(session.session_key, ciphermod=AES) 295 | cmac_obj.update(auth_key_1) 296 | auth_cmac_key_1 = cmac_obj.digest() 297 | 298 | cmac_obj = CMAC.new(session.session_key, ciphermod=AES) 299 | cmac_obj.update(auth_key_2) 300 | auth_cmac_key_2 = cmac_obj.digest() 301 | 302 | cmac_obj = CMAC.new(session.session_key, ciphermod=AES) 303 | cmac_obj.update(auth_key_3) 304 | auth_cmac_key_3 = cmac_obj.digest() 305 | 306 | cmac_obj = CMAC.new(session.session_key, ciphermod=AES) 307 | cmac_obj.update(auth_key_4) 308 | auth_cmac_key_4 = cmac_obj.digest() 309 | 310 | auth_cmac_combined_1 = auth_cmac_key_1 + auth_cmac_key_2 311 | auth_cmac_combined_2 = auth_cmac_key_3 + auth_cmac_key_4 312 | 313 | session.derived_keys['enc'] = enc_cmac_key 314 | session.derived_keys['auth_1'] = auth_cmac_combined_1 315 | session.derived_keys['auth_2'] = auth_cmac_combined_2 316 | 317 | self.logger.debug('verifying license signature') 318 | 319 | lic_hmac = HMAC.new(session.derived_keys['auth_1'], digestmod=SHA256) 320 | lic_hmac.update(lic.Msg.SerializeToString()) 321 | 322 | self.logger.debug( 323 | "calculated sig: {} actual sig: {}".format(lic_hmac.hexdigest(), binascii.hexlify(lic.Signature))) 324 | 325 | if lic_hmac.digest() != lic.Signature: 326 | self.logger.info("license signature doesn't match - writing bin so they can be debugged") 327 | with open("original_lic.bin", "wb") as f: 328 | f.write(base64.b64decode(license_b64)) 329 | with open("parsed_lic.bin", "wb") as f: 330 | f.write(lic.SerializeToString()) 331 | self.logger.info("continuing anyway") 332 | 333 | self.logger.debug("key count: {}".format(len(lic.Msg.Key))) 334 | for key in lic.Msg.Key: 335 | if key.Id: 336 | key_id = key.Id 337 | else: 338 | key_id = wv_proto4.License.KeyContainer.KeyType.Name(key.Type).encode('utf-8') 339 | encrypted_key = key.Key 340 | iv = key.Iv 341 | type = wv_proto4.License.KeyContainer.KeyType.Name(key.Type) 342 | 343 | cipher = AES.new(session.derived_keys['enc'], AES.MODE_CBC, iv=iv) 344 | decrypted_key = cipher.decrypt(encrypted_key) 345 | if type == "OPERATOR_SESSION": 346 | permissions = [] 347 | perms = key._OperatorSessionKeyPermissions 348 | for (descriptor, value) in perms.ListFields(): 349 | if value == 1: 350 | permissions.append(descriptor.name) 351 | print(permissions) 352 | else: 353 | permissions = [] 354 | session.keys.append(Key(key_id, type, Padding.unpad(decrypted_key, 16), permissions)) 355 | 356 | self.logger.info("decrypted all keys") 357 | return True 358 | 359 | def get_keys(self, session_id): 360 | if session_id in self.sessions: 361 | return self.sessions[session_id].keys 362 | else: 363 | self.logger.error("session not found") 364 | return 1 365 | -------------------------------------------------------------------------------- /data/pywidevine/L3/cdm/deviceconfig.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | device_android_generic = { 4 | 'name': 'android_generic', 5 | 'description': 'android studio cdm', 6 | 'security_level': 3, 7 | 'session_id_type': 'android', 8 | 'private_key_available': True, 9 | 'vmp': False, 10 | 'send_key_control_nonce': True 11 | } 12 | 13 | devices_available = [device_android_generic] 14 | 15 | FILES_FOLDER = 'devices' 16 | 17 | 18 | class DeviceConfig: 19 | def __init__(self, device): 20 | self.device_name = device['name'] 21 | self.description = device['description'] 22 | self.security_level = device['security_level'] 23 | self.session_id_type = device['session_id_type'] 24 | self.private_key_available = device['private_key_available'] 25 | self.vmp = device['vmp'] 26 | self.send_key_control_nonce = device['send_key_control_nonce'] 27 | 28 | if 'keybox_filename' in device: 29 | self.keybox_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], 30 | device['keybox_filename']) 31 | else: 32 | self.keybox_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], 'keybox') 33 | 34 | if 'device_cert_filename' in device: 35 | self.device_cert_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], 36 | device['device_cert_filename']) 37 | else: 38 | self.device_cert_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], 39 | 'device_cert') 40 | 41 | if 'device_private_key_filename' in device: 42 | self.device_private_key_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], 43 | device['device_private_key_filename']) 44 | else: 45 | self.device_private_key_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], 46 | 'device_private_key') 47 | 48 | if 'device_client_id_blob_filename' in device: 49 | self.device_client_id_blob_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], 50 | device['device_client_id_blob_filename']) 51 | else: 52 | self.device_client_id_blob_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], 53 | 'device_client_id_blob') 54 | 55 | if 'device_vmp_blob_filename' in device: 56 | self.device_vmp_blob_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], 57 | device['device_vmp_blob_filename']) 58 | else: 59 | self.device_vmp_blob_filename = os.path.join(os.path.dirname(__file__), FILES_FOLDER, device['name'], 60 | 'device_vmp_blob') 61 | 62 | def __repr__(self): 63 | return ("DeviceConfig(name={}, description={}, security_level={}, session_id_type={}, private_key_available={" 64 | "}, vmp={})").format( 65 | self.device_name, self.description, self.security_level, self.session_id_type, self.private_key_available, 66 | self.vmp) 67 | -------------------------------------------------------------------------------- /data/pywidevine/L3/cdm/devices/android_generic/CDM_HERE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevLARLEY/mpdl/f544a691eba49f580dae2e64e8d1bb5d3014495c/data/pywidevine/L3/cdm/devices/android_generic/CDM_HERE -------------------------------------------------------------------------------- /data/pywidevine/L3/cdm/formats/wv_proto2.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto2"; 2 | 3 | // from x86 (partial), most of it from the ARM version: 4 | message ClientIdentification { 5 | enum TokenType { 6 | KEYBOX = 0; 7 | DEVICE_CERTIFICATE = 1; 8 | REMOTE_ATTESTATION_CERTIFICATE = 2; 9 | } 10 | message NameValue { 11 | required string Name = 1; 12 | required string Value = 2; 13 | } 14 | message ClientCapabilities { 15 | enum HdcpVersion { 16 | HDCP_NONE = 0; 17 | HDCP_V1 = 1; 18 | HDCP_V2 = 2; 19 | HDCP_V2_1 = 3; 20 | HDCP_V2_2 = 4; 21 | } 22 | optional uint32 ClientToken = 1; 23 | optional uint32 SessionToken = 2; 24 | optional uint32 VideoResolutionConstraints = 3; 25 | optional HdcpVersion MaxHdcpVersion = 4; 26 | optional uint32 OemCryptoApiVersion = 5; 27 | } 28 | required TokenType Type = 1; 29 | //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: 30 | optional SignedDeviceCertificate Token = 2; // use this when parsing, "bytes" when building a client id blob 31 | repeated NameValue ClientInfo = 3; 32 | optional bytes ProviderClientToken = 4; 33 | optional uint32 LicenseCounter = 5; 34 | optional ClientCapabilities _ClientCapabilities = 6; // how should we deal with duped names? will have to look at proto docs later 35 | optional FileHashes _FileHashes = 7; // vmp blob goes here 36 | } 37 | 38 | message DeviceCertificate { 39 | enum CertificateType { 40 | ROOT = 0; 41 | INTERMEDIATE = 1; 42 | USER_DEVICE = 2; 43 | SERVICE = 3; 44 | } 45 | 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) 46 | optional bytes SerialNumber = 2; 47 | optional uint32 CreationTimeSeconds = 3; 48 | optional bytes PublicKey = 4; 49 | optional uint32 SystemId = 5; 50 | optional uint32 TestDeviceDeprecated = 6; // is it bool or int? 51 | optional bytes ServiceId = 7; // service URL for service certificates 52 | } 53 | 54 | // missing some references, 55 | message DeviceCertificateStatus { 56 | enum CertificateStatus { 57 | VALID = 0; 58 | REVOKED = 1; 59 | } 60 | optional bytes SerialNumber = 1; 61 | optional CertificateStatus Status = 2; 62 | optional ProvisionedDeviceInfo DeviceInfo = 4; // where is 3? is it deprecated? 63 | } 64 | 65 | message DeviceCertificateStatusList { 66 | optional uint32 CreationTimeSeconds = 1; 67 | repeated DeviceCertificateStatus CertificateStatus = 2; 68 | } 69 | 70 | message EncryptedClientIdentification { 71 | required string ServiceId = 1; 72 | optional bytes ServiceCertificateSerialNumber = 2; 73 | required bytes EncryptedClientId = 3; 74 | required bytes EncryptedClientIdIv = 4; 75 | required bytes EncryptedPrivacyKey = 5; 76 | } 77 | 78 | // todo: fill (for this top-level type, it might be impossible/difficult) 79 | enum LicenseType { 80 | ZERO = 0; 81 | 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 82 | OFFLINE = 2; 83 | } 84 | 85 | // todo: fill (for this top-level type, it might be impossible/difficult) 86 | // this is just a guess because these globals got lost, but really, do we need more? 87 | enum ProtocolVersion { 88 | CURRENT = 21; // don't have symbols for this 89 | } 90 | 91 | 92 | message LicenseIdentification { 93 | optional bytes RequestId = 1; 94 | optional bytes SessionId = 2; 95 | optional bytes PurchaseId = 3; 96 | optional LicenseType Type = 4; 97 | optional uint32 Version = 5; 98 | optional bytes ProviderSessionToken = 6; 99 | } 100 | 101 | 102 | message License { 103 | message Policy { 104 | optional bool CanPlay = 1; // changed from uint32 to bool 105 | optional bool CanPersist = 2; 106 | optional bool CanRenew = 3; 107 | optional uint32 RentalDurationSeconds = 4; 108 | optional uint32 PlaybackDurationSeconds = 5; 109 | optional uint32 LicenseDurationSeconds = 6; 110 | optional uint32 RenewalRecoveryDurationSeconds = 7; 111 | optional string RenewalServerUrl = 8; 112 | optional uint32 RenewalDelaySeconds = 9; 113 | optional uint32 RenewalRetryIntervalSeconds = 10; 114 | optional bool RenewWithUsage = 11; // was uint32 115 | } 116 | message KeyContainer { 117 | enum KeyType { 118 | SIGNING = 1; 119 | CONTENT = 2; 120 | KEY_CONTROL = 3; 121 | OPERATOR_SESSION = 4; 122 | } 123 | enum SecurityLevel { 124 | SW_SECURE_CRYPTO = 1; 125 | SW_SECURE_DECODE = 2; 126 | HW_SECURE_CRYPTO = 3; 127 | HW_SECURE_DECODE = 4; 128 | HW_SECURE_ALL = 5; 129 | } 130 | message OutputProtection { 131 | enum CGMS { 132 | COPY_FREE = 0; 133 | COPY_ONCE = 2; 134 | COPY_NEVER = 3; 135 | CGMS_NONE = 0x2A; // PC default! 136 | } 137 | optional ClientIdentification.ClientCapabilities.HdcpVersion Hdcp = 1; // it's most likely a copy of Hdcp version available here, but compiler optimized it away 138 | optional CGMS CgmsFlags = 2; 139 | } 140 | message KeyControl { 141 | required bytes KeyControlBlock = 1; // what is this? 142 | required bytes Iv = 2; 143 | } 144 | message OperatorSessionKeyPermissions { 145 | optional uint32 AllowEncrypt = 1; 146 | optional uint32 AllowDecrypt = 2; 147 | optional uint32 AllowSign = 3; 148 | optional uint32 AllowSignatureVerify = 4; 149 | } 150 | message VideoResolutionConstraint { 151 | optional uint32 MinResolutionPixels = 1; 152 | optional uint32 MaxResolutionPixels = 2; 153 | optional OutputProtection RequiredProtection = 3; 154 | } 155 | optional bytes Id = 1; 156 | optional bytes Iv = 2; 157 | optional bytes Key = 3; 158 | optional KeyType Type = 4; 159 | optional SecurityLevel Level = 5; 160 | optional OutputProtection RequiredProtection = 6; 161 | optional OutputProtection RequestedProtection = 7; 162 | optional KeyControl _KeyControl = 8; // duped names, etc 163 | optional OperatorSessionKeyPermissions _OperatorSessionKeyPermissions = 9; // duped names, etc 164 | repeated VideoResolutionConstraint VideoResolutionConstraints = 10; 165 | } 166 | optional LicenseIdentification Id = 1; 167 | optional Policy _Policy = 2; // duped names, etc 168 | repeated KeyContainer Key = 3; 169 | optional uint32 LicenseStartTime = 4; 170 | optional uint32 RemoteAttestationVerified = 5; // bool? 171 | optional bytes ProviderClientToken = 6; 172 | // there might be more, check with newer versions (I see field 7-8 in a lic) 173 | // this appeared in latest x86: 174 | optional uint32 ProtectionScheme = 7; // type unconfirmed fully, but it's likely as WidevineCencHeader describesit (fourcc) 175 | } 176 | 177 | message LicenseError { 178 | enum Error { 179 | INVALID_DEVICE_CERTIFICATE = 1; 180 | REVOKED_DEVICE_CERTIFICATE = 2; 181 | SERVICE_UNAVAILABLE = 3; 182 | } 183 | //LicenseRequest.RequestType ErrorCode; // clang mismatch 184 | optional Error ErrorCode = 1; 185 | } 186 | 187 | message LicenseRequest { 188 | message ContentIdentification { 189 | message CENC { 190 | //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: 191 | optional WidevineCencHeader Pssh = 1; 192 | 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!) 193 | optional bytes RequestId = 3; 194 | } 195 | message WebM { 196 | optional bytes Header = 1; // identical to CENC, aside from PSSH and the parent field number used 197 | optional LicenseType LicenseType = 2; 198 | optional bytes RequestId = 3; 199 | } 200 | message ExistingLicense { 201 | optional LicenseIdentification LicenseId = 1; 202 | optional uint32 SecondsSinceStarted = 2; 203 | optional uint32 SecondsSinceLastPlayed = 3; 204 | optional bytes SessionUsageTableEntry = 4; // interesting! try to figure out the connection between the usage table blob and KCB! 205 | } 206 | optional CENC CencId = 1; 207 | optional WebM WebmId = 2; 208 | optional ExistingLicense License = 3; 209 | } 210 | enum RequestType { 211 | NEW = 1; 212 | RENEWAL = 2; 213 | RELEASE = 3; 214 | } 215 | optional ClientIdentification ClientId = 1; 216 | optional ContentIdentification ContentId = 2; 217 | optional RequestType Type = 3; 218 | optional uint32 RequestTime = 4; 219 | optional bytes KeyControlNonceDeprecated = 5; 220 | optional ProtocolVersion ProtocolVersion = 6; // lacking symbols for this 221 | optional uint32 KeyControlNonce = 7; 222 | optional EncryptedClientIdentification EncryptedClientId = 8; 223 | } 224 | 225 | // raw pssh hack 226 | message LicenseRequestRaw { 227 | message ContentIdentification { 228 | message CENC { 229 | 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: 230 | //optional WidevineCencHeader Pssh = 1; 231 | 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!) 232 | optional bytes RequestId = 3; 233 | } 234 | message WebM { 235 | optional bytes Header = 1; // identical to CENC, aside from PSSH and the parent field number used 236 | optional LicenseType LicenseType = 2; 237 | optional bytes RequestId = 3; 238 | } 239 | message ExistingLicense { 240 | optional LicenseIdentification LicenseId = 1; 241 | optional uint32 SecondsSinceStarted = 2; 242 | optional uint32 SecondsSinceLastPlayed = 3; 243 | optional bytes SessionUsageTableEntry = 4; // interesting! try to figure out the connection between the usage table blob and KCB! 244 | } 245 | optional CENC CencId = 1; 246 | optional WebM WebmId = 2; 247 | optional ExistingLicense License = 3; 248 | } 249 | enum RequestType { 250 | NEW = 1; 251 | RENEWAL = 2; 252 | RELEASE = 3; 253 | } 254 | optional ClientIdentification ClientId = 1; 255 | optional ContentIdentification ContentId = 2; 256 | optional RequestType Type = 3; 257 | optional uint32 RequestTime = 4; 258 | optional bytes KeyControlNonceDeprecated = 5; 259 | optional ProtocolVersion ProtocolVersion = 6; // lacking symbols for this 260 | optional uint32 KeyControlNonce = 7; 261 | optional EncryptedClientIdentification EncryptedClientId = 8; 262 | } 263 | 264 | 265 | message ProvisionedDeviceInfo { 266 | enum WvSecurityLevel { 267 | LEVEL_UNSPECIFIED = 0; 268 | LEVEL_1 = 1; 269 | LEVEL_2 = 2; 270 | LEVEL_3 = 3; 271 | } 272 | optional uint32 SystemId = 1; 273 | optional string Soc = 2; 274 | optional string Manufacturer = 3; 275 | optional string Model = 4; 276 | optional string DeviceType = 5; 277 | optional uint32 ModelYear = 6; 278 | optional WvSecurityLevel SecurityLevel = 7; 279 | optional uint32 TestDevice = 8; // bool? 280 | } 281 | 282 | 283 | // todo: fill 284 | message ProvisioningOptions { 285 | } 286 | 287 | // todo: fill 288 | message ProvisioningRequest { 289 | } 290 | 291 | // todo: fill 292 | message ProvisioningResponse { 293 | } 294 | 295 | message RemoteAttestation { 296 | optional EncryptedClientIdentification Certificate = 1; 297 | optional string Salt = 2; 298 | optional string Signature = 3; 299 | } 300 | 301 | // todo: fill 302 | message SessionInit { 303 | } 304 | 305 | // todo: fill 306 | message SessionState { 307 | } 308 | 309 | // todo: fill 310 | message SignedCertificateStatusList { 311 | } 312 | 313 | message SignedDeviceCertificate { 314 | 315 | //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: 316 | optional DeviceCertificate _DeviceCertificate = 1; // how should we deal with duped names? will have to look at proto docs later 317 | optional bytes Signature = 2; 318 | optional SignedDeviceCertificate Signer = 3; 319 | } 320 | 321 | 322 | // todo: fill 323 | message SignedProvisioningMessage { 324 | } 325 | 326 | // the root of all messages, from either server or client 327 | message SignedMessage { 328 | enum MessageType { 329 | LICENSE_REQUEST = 1; 330 | LICENSE = 2; 331 | ERROR_RESPONSE = 3; 332 | SERVICE_CERTIFICATE_REQUEST = 4; 333 | SERVICE_CERTIFICATE = 5; 334 | } 335 | optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel 336 | 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 337 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE 338 | optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now) 339 | optional bytes SessionKey = 4; // often RSA wrapped for licenses 340 | optional RemoteAttestation RemoteAttestation = 5; 341 | } 342 | 343 | 344 | 345 | // This message is copied from google's docs, not reversed: 346 | message WidevineCencHeader { 347 | enum Algorithm { 348 | UNENCRYPTED = 0; 349 | AESCTR = 1; 350 | }; 351 | optional Algorithm algorithm = 1; 352 | repeated bytes key_id = 2; 353 | 354 | // Content provider name. 355 | optional string provider = 3; 356 | 357 | // A content identifier, specified by content provider. 358 | optional bytes content_id = 4; 359 | 360 | // Track type. Acceptable values are SD, HD and AUDIO. Used to 361 | // differentiate content keys used by an asset. 362 | optional string track_type_deprecated = 5; 363 | 364 | // The name of a registered policy to be used for this asset. 365 | optional string policy = 6; 366 | 367 | // Crypto period index, for media using key rotation. 368 | optional uint32 crypto_period_index = 7; 369 | 370 | // Optional protected context for group content. The grouped_license is a 371 | // serialized SignedMessage. 372 | optional bytes grouped_license = 8; 373 | 374 | // Protection scheme identifying the encryption algorithm. 375 | // Represented as one of the following 4CC values: 376 | // 'cenc' (AESCTR), 'cbc1' (AESCBC), 377 | // 'cens' (AESCTR subsample), 'cbcs' (AESCBC subsample). 378 | optional uint32 protection_scheme = 9; 379 | 380 | // Optional. For media using key rotation, this represents the duration 381 | // of each crypto period in seconds. 382 | optional uint32 crypto_period_seconds = 10; 383 | } 384 | 385 | 386 | // remove these when using it outside of protoc: 387 | 388 | // 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 389 | message SignedLicenseRequest { 390 | enum MessageType { 391 | LICENSE_REQUEST = 1; 392 | LICENSE = 2; 393 | ERROR_RESPONSE = 3; 394 | SERVICE_CERTIFICATE_REQUEST = 4; 395 | SERVICE_CERTIFICATE = 5; 396 | } 397 | optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel 398 | 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 399 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE 400 | optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now) 401 | optional bytes SessionKey = 4; // often RSA wrapped for licenses 402 | optional RemoteAttestation RemoteAttestation = 5; 403 | } 404 | 405 | // hack 406 | message SignedLicenseRequestRaw { 407 | enum MessageType { 408 | LICENSE_REQUEST = 1; 409 | LICENSE = 2; 410 | ERROR_RESPONSE = 3; 411 | SERVICE_CERTIFICATE_REQUEST = 4; 412 | SERVICE_CERTIFICATE = 5; 413 | } 414 | optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel 415 | 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 416 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE 417 | optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now) 418 | optional bytes SessionKey = 4; // often RSA wrapped for licenses 419 | optional RemoteAttestation RemoteAttestation = 5; 420 | } 421 | 422 | 423 | message SignedLicense { 424 | enum MessageType { 425 | LICENSE_REQUEST = 1; 426 | LICENSE = 2; 427 | ERROR_RESPONSE = 3; 428 | SERVICE_CERTIFICATE_REQUEST = 4; 429 | SERVICE_CERTIFICATE = 5; 430 | } 431 | optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel 432 | 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 433 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE 434 | optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now) 435 | optional bytes SessionKey = 4; // often RSA wrapped for licenses 436 | optional RemoteAttestation RemoteAttestation = 5; 437 | } 438 | 439 | message SignedServiceCertificate { 440 | enum MessageType { 441 | LICENSE_REQUEST = 1; 442 | LICENSE = 2; 443 | ERROR_RESPONSE = 3; 444 | SERVICE_CERTIFICATE_REQUEST = 4; 445 | SERVICE_CERTIFICATE = 5; 446 | } 447 | optional MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel 448 | 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 449 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE 450 | optional bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now) 451 | optional bytes SessionKey = 4; // often RSA wrapped for licenses 452 | optional RemoteAttestation RemoteAttestation = 5; 453 | } 454 | 455 | //vmp support 456 | message FileHashes { 457 | message Signature { 458 | optional string filename = 1; 459 | optional bool test_signing = 2; //0 - release, 1 - testing 460 | optional bytes SHA512Hash = 3; 461 | optional bool main_exe = 4; //0 for dlls, 1 for exe, this is field 3 in file 462 | optional bytes signature = 5; 463 | } 464 | optional bytes signer = 1; 465 | repeated Signature signatures = 2; 466 | } 467 | -------------------------------------------------------------------------------- /data/pywidevine/L3/cdm/formats/wv_proto3.proto: -------------------------------------------------------------------------------- 1 | // beware proto3 won't show missing fields it seems, need to change to "proto2" and add "optional" before every field, and remove all the dummy enum members I added: 2 | syntax = "proto3"; 3 | 4 | // from x86 (partial), most of it from the ARM version: 5 | message ClientIdentification { 6 | enum TokenType { 7 | KEYBOX = 0; 8 | DEVICE_CERTIFICATE = 1; 9 | REMOTE_ATTESTATION_CERTIFICATE = 2; 10 | } 11 | message NameValue { 12 | string Name = 1; 13 | string Value = 2; 14 | } 15 | message ClientCapabilities { 16 | enum HdcpVersion { 17 | HDCP_NONE = 0; 18 | HDCP_V1 = 1; 19 | HDCP_V2 = 2; 20 | HDCP_V2_1 = 3; 21 | HDCP_V2_2 = 4; 22 | } 23 | uint32 ClientToken = 1; 24 | uint32 SessionToken = 2; 25 | uint32 VideoResolutionConstraints = 3; 26 | HdcpVersion MaxHdcpVersion = 4; 27 | uint32 OemCryptoApiVersion = 5; 28 | } 29 | TokenType Type = 1; 30 | //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: 31 | SignedDeviceCertificate Token = 2; 32 | repeated NameValue ClientInfo = 3; 33 | bytes ProviderClientToken = 4; 34 | uint32 LicenseCounter = 5; 35 | ClientCapabilities _ClientCapabilities = 6; // how should we deal with duped names? will have to look at proto docs later 36 | } 37 | 38 | message DeviceCertificate { 39 | enum CertificateType { 40 | ROOT = 0; 41 | INTERMEDIATE = 1; 42 | USER_DEVICE = 2; 43 | SERVICE = 3; 44 | } 45 | //ProvisionedDeviceInfo.WvSecurityLevel Type = 1; // is this how one is supposed to call it? (it's an enum) there might be a bug here, with CertificateType getting confused with WvSecurityLevel, for now renaming it (verify against other binaries) 46 | CertificateType Type = 1; 47 | bytes SerialNumber = 2; 48 | uint32 CreationTimeSeconds = 3; 49 | bytes PublicKey = 4; 50 | uint32 SystemId = 5; 51 | uint32 TestDeviceDeprecated = 6; // is it bool or int? 52 | bytes ServiceId = 7; // service URL for service certificates 53 | } 54 | 55 | // missing some references, 56 | message DeviceCertificateStatus { 57 | enum CertificateStatus { 58 | VALID = 0; 59 | REVOKED = 1; 60 | } 61 | bytes SerialNumber = 1; 62 | CertificateStatus Status = 2; 63 | ProvisionedDeviceInfo DeviceInfo = 4; // where is 3? is it deprecated? 64 | } 65 | 66 | message DeviceCertificateStatusList { 67 | uint32 CreationTimeSeconds = 1; 68 | repeated DeviceCertificateStatus CertificateStatus = 2; 69 | } 70 | 71 | message EncryptedClientIdentification { 72 | string ServiceId = 1; 73 | bytes ServiceCertificateSerialNumber = 2; 74 | bytes EncryptedClientId = 3; 75 | bytes EncryptedClientIdIv = 4; 76 | bytes EncryptedPrivacyKey = 5; 77 | } 78 | 79 | // todo: fill (for this top-level type, it might be impossible/difficult) 80 | enum LicenseType { 81 | ZERO = 0; 82 | DEFAULT = 1; // do not know what this is either, but should be 1; on recent versions may go up to 3 (latest x86) 83 | } 84 | 85 | // todo: fill (for this top-level type, it might be impossible/difficult) 86 | // this is just a guess because these globals got lost, but really, do we need more? 87 | enum ProtocolVersion { 88 | DUMMY = 0; 89 | CURRENT = 21; // don't have symbols for this 90 | } 91 | 92 | 93 | message LicenseIdentification { 94 | bytes RequestId = 1; 95 | bytes SessionId = 2; 96 | bytes PurchaseId = 3; 97 | LicenseType Type = 4; 98 | uint32 Version = 5; 99 | bytes ProviderSessionToken = 6; 100 | } 101 | 102 | 103 | message License { 104 | message Policy { 105 | uint32 CanPlay = 1; 106 | uint32 CanPersist = 2; 107 | uint32 CanRenew = 3; 108 | uint32 RentalDurationSeconds = 4; 109 | uint32 PlaybackDurationSeconds = 5; 110 | uint32 LicenseDurationSeconds = 6; 111 | uint32 RenewalRecoveryDurationSeconds = 7; 112 | string RenewalServerUrl = 8; 113 | uint32 RenewalDelaySeconds = 9; 114 | uint32 RenewalRetryIntervalSeconds = 10; 115 | uint32 RenewWithUsage = 11; 116 | uint32 UnknownPolicy12 = 12; 117 | } 118 | message KeyContainer { 119 | enum KeyType { 120 | _NOKEYTYPE = 0; // dummy, added to satisfy proto3, not present in original 121 | SIGNING = 1; 122 | CONTENT = 2; 123 | KEY_CONTROL = 3; 124 | OPERATOR_SESSION = 4; 125 | } 126 | enum SecurityLevel { 127 | _NOSECLEVEL = 0; // dummy, added to satisfy proto3, not present in original 128 | SW_SECURE_CRYPTO = 1; 129 | SW_SECURE_DECODE = 2; 130 | HW_SECURE_CRYPTO = 3; 131 | HW_SECURE_DECODE = 4; 132 | HW_SECURE_ALL = 5; 133 | } 134 | message OutputProtection { 135 | enum CGMS { 136 | COPY_FREE = 0; 137 | COPY_ONCE = 2; 138 | COPY_NEVER = 3; 139 | CGMS_NONE = 0x2A; // PC default! 140 | } 141 | ClientIdentification.ClientCapabilities.HdcpVersion Hdcp = 1; // it's most likely a copy of Hdcp version available here, but compiler optimized it away 142 | CGMS CgmsFlags = 2; 143 | } 144 | message KeyControl { 145 | bytes KeyControlBlock = 1; // what is this? 146 | bytes Iv = 2; 147 | } 148 | message OperatorSessionKeyPermissions { 149 | uint32 AllowEncrypt = 1; 150 | uint32 AllowDecrypt = 2; 151 | uint32 AllowSign = 3; 152 | uint32 AllowSignatureVerify = 4; 153 | } 154 | message VideoResolutionConstraint { 155 | uint32 MinResolutionPixels = 1; 156 | uint32 MaxResolutionPixels = 2; 157 | OutputProtection RequiredProtection = 3; 158 | } 159 | bytes Id = 1; 160 | bytes Iv = 2; 161 | bytes Key = 3; 162 | KeyType Type = 4; 163 | SecurityLevel Level = 5; 164 | OutputProtection RequiredProtection = 6; 165 | OutputProtection RequestedProtection = 7; 166 | KeyControl _KeyControl = 8; // duped names, etc 167 | OperatorSessionKeyPermissions _OperatorSessionKeyPermissions = 9; // duped names, etc 168 | repeated VideoResolutionConstraint VideoResolutionConstraints = 10; 169 | } 170 | LicenseIdentification Id = 1; 171 | Policy _Policy = 2; // duped names, etc 172 | repeated KeyContainer Key = 3; 173 | uint32 LicenseStartTime = 4; 174 | uint32 RemoteAttestationVerified = 5; // bool? 175 | bytes ProviderClientToken = 6; 176 | // there might be more, check with newer versions (I see field 7-8 in a lic) 177 | // this appeared in latest x86: 178 | uint32 ProtectionScheme = 7; // type unconfirmed fully, but it's likely as WidevineCencHeader describesit (fourcc) 179 | bytes UnknownHdcpDataField = 8; 180 | } 181 | 182 | message LicenseError { 183 | enum Error { 184 | DUMMY_NO_ERROR = 0; // dummy, added to satisfy proto3 185 | INVALID_DEVICE_CERTIFICATE = 1; 186 | REVOKED_DEVICE_CERTIFICATE = 2; 187 | SERVICE_UNAVAILABLE = 3; 188 | } 189 | //LicenseRequest.RequestType ErrorCode; // clang mismatch 190 | Error ErrorCode = 1; 191 | } 192 | 193 | message LicenseRequest { 194 | message ContentIdentification { 195 | message CENC { 196 | // 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: 197 | WidevineCencHeader Pssh = 1; 198 | LicenseType LicenseType = 2; // unfortunately the LicenseType symbols are not present, acceptable value seems to only be 1 199 | bytes RequestId = 3; 200 | } 201 | message WebM { 202 | bytes Header = 1; // identical to CENC, aside from PSSH and the parent field number used 203 | LicenseType LicenseType = 2; 204 | bytes RequestId = 3; 205 | } 206 | message ExistingLicense { 207 | LicenseIdentification LicenseId = 1; 208 | uint32 SecondsSinceStarted = 2; 209 | uint32 SecondsSinceLastPlayed = 3; 210 | bytes SessionUsageTableEntry = 4; 211 | } 212 | CENC CencId = 1; 213 | WebM WebmId = 2; 214 | ExistingLicense License = 3; 215 | } 216 | enum RequestType { 217 | DUMMY_REQ_TYPE = 0; // dummy, added to satisfy proto3 218 | NEW = 1; 219 | RENEWAL = 2; 220 | RELEASE = 3; 221 | } 222 | ClientIdentification ClientId = 1; 223 | ContentIdentification ContentId = 2; 224 | RequestType Type = 3; 225 | uint32 RequestTime = 4; 226 | bytes KeyControlNonceDeprecated = 5; 227 | ProtocolVersion ProtocolVersion = 6; // lacking symbols for this 228 | uint32 KeyControlNonce = 7; 229 | EncryptedClientIdentification EncryptedClientId = 8; 230 | } 231 | 232 | message ProvisionedDeviceInfo { 233 | enum WvSecurityLevel { 234 | LEVEL_UNSPECIFIED = 0; 235 | LEVEL_1 = 1; 236 | LEVEL_2 = 2; 237 | LEVEL_3 = 3; 238 | } 239 | uint32 SystemId = 1; 240 | string Soc = 2; 241 | string Manufacturer = 3; 242 | string Model = 4; 243 | string DeviceType = 5; 244 | uint32 ModelYear = 6; 245 | WvSecurityLevel SecurityLevel = 7; 246 | uint32 TestDevice = 8; // bool? 247 | } 248 | 249 | 250 | // todo: fill 251 | message ProvisioningOptions { 252 | } 253 | 254 | // todo: fill 255 | message ProvisioningRequest { 256 | } 257 | 258 | // todo: fill 259 | message ProvisioningResponse { 260 | } 261 | 262 | message RemoteAttestation { 263 | EncryptedClientIdentification Certificate = 1; 264 | string Salt = 2; 265 | string Signature = 3; 266 | } 267 | 268 | // todo: fill 269 | message SessionInit { 270 | } 271 | 272 | // todo: fill 273 | message SessionState { 274 | } 275 | 276 | // todo: fill 277 | message SignedCertificateStatusList { 278 | } 279 | 280 | message SignedDeviceCertificate { 281 | 282 | //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: 283 | DeviceCertificate _DeviceCertificate = 1; // how should we deal with duped names? will have to look at proto docs later 284 | bytes Signature = 2; 285 | SignedDeviceCertificate Signer = 3; 286 | } 287 | 288 | 289 | // todo: fill 290 | message SignedProvisioningMessage { 291 | } 292 | 293 | // the root of all messages, from either server or client 294 | message SignedMessage { 295 | enum MessageType { 296 | DUMMY_MSG_TYPE = 0; // dummy, added to satisfy proto3 297 | LICENSE_REQUEST = 1; 298 | LICENSE = 2; 299 | ERROR_RESPONSE = 3; 300 | SERVICE_CERTIFICATE_REQUEST = 4; 301 | SERVICE_CERTIFICATE = 5; 302 | } 303 | MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel 304 | 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 305 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE 306 | bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now) 307 | bytes SessionKey = 4; // often RSA wrapped for licenses 308 | RemoteAttestation RemoteAttestation = 5; 309 | } 310 | 311 | 312 | 313 | // This message is copied from google's docs, not reversed: 314 | message WidevineCencHeader { 315 | enum Algorithm { 316 | UNENCRYPTED = 0; 317 | AESCTR = 1; 318 | }; 319 | Algorithm algorithm = 1; 320 | repeated bytes key_id = 2; 321 | 322 | // Content provider name. 323 | string provider = 3; 324 | 325 | // A content identifier, specified by content provider. 326 | bytes content_id = 4; 327 | 328 | // Track type. Acceptable values are SD, HD and AUDIO. Used to 329 | // differentiate content keys used by an asset. 330 | string track_type_deprecated = 5; 331 | 332 | // The name of a registered policy to be used for this asset. 333 | string policy = 6; 334 | 335 | // Crypto period index, for media using key rotation. 336 | uint32 crypto_period_index = 7; 337 | 338 | // Optional protected context for group content. The grouped_license is a 339 | // serialized SignedMessage. 340 | bytes grouped_license = 8; 341 | 342 | // Protection scheme identifying the encryption algorithm. 343 | // Represented as one of the following 4CC values: 344 | // 'cenc' (AESCTR), 'cbc1' (AESCBC), 345 | // 'cens' (AESCTR subsample), 'cbcs' (AESCBC subsample). 346 | uint32 protection_scheme = 9; 347 | 348 | // Optional. For media using key rotation, this represents the duration 349 | // of each crypto period in seconds. 350 | uint32 crypto_period_seconds = 10; 351 | } 352 | 353 | 354 | 355 | 356 | // 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 357 | message SignedLicenseRequest { 358 | enum MessageType { 359 | DUMMY_MSG_TYPE = 0; // dummy, added to satisfy proto3 360 | LICENSE_REQUEST = 1; 361 | LICENSE = 2; 362 | ERROR_RESPONSE = 3; 363 | SERVICE_CERTIFICATE_REQUEST = 4; 364 | SERVICE_CERTIFICATE = 5; 365 | } 366 | MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel 367 | 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 368 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE 369 | bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now) 370 | bytes SessionKey = 4; // often RSA wrapped for licenses 371 | RemoteAttestation RemoteAttestation = 5; 372 | } 373 | 374 | message SignedLicense { 375 | enum MessageType { 376 | DUMMY_MSG_TYPE = 0; // dummy, added to satisfy proto3 377 | LICENSE_REQUEST = 1; 378 | LICENSE = 2; 379 | ERROR_RESPONSE = 3; 380 | SERVICE_CERTIFICATE_REQUEST = 4; 381 | SERVICE_CERTIFICATE = 5; 382 | } 383 | MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel 384 | 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 385 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE 386 | bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now) 387 | bytes SessionKey = 4; // often RSA wrapped for licenses 388 | RemoteAttestation RemoteAttestation = 5; 389 | } -------------------------------------------------------------------------------- /data/pywidevine/L3/cdm/formats/wv_proto4.proto: -------------------------------------------------------------------------------- 1 | // beware proto3 won't show missing fields it seems, need to change to "proto2" and add "optional" before every field, and remove all the dummy enum members I added: 2 | syntax = "proto3"; 3 | 4 | // from x86 (partial), most of it from the ARM version: 5 | message ClientIdentification { 6 | enum TokenType { 7 | KEYBOX = 0; 8 | DEVICE_CERTIFICATE = 1; 9 | REMOTE_ATTESTATION_CERTIFICATE = 2; 10 | } 11 | message NameValue { 12 | string Name = 1; 13 | string Value = 2; 14 | } 15 | message ClientCapabilities { 16 | enum HdcpVersion { 17 | HDCP_NONE = 0; 18 | HDCP_V1 = 1; 19 | HDCP_V2 = 2; 20 | HDCP_V2_1 = 3; 21 | HDCP_V2_2 = 4; 22 | } 23 | uint32 ClientToken = 1; 24 | uint32 SessionToken = 2; 25 | uint32 VideoResolutionConstraints = 3; 26 | HdcpVersion MaxHdcpVersion = 4; 27 | uint32 OemCryptoApiVersion = 5; 28 | } 29 | TokenType Type = 1; 30 | //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: 31 | SignedDeviceCertificate Token = 2; 32 | repeated NameValue ClientInfo = 3; 33 | bytes ProviderClientToken = 4; 34 | uint32 LicenseCounter = 5; 35 | ClientCapabilities _ClientCapabilities = 6; // how should we deal with duped names? will have to look at proto docs later 36 | } 37 | 38 | message DeviceCertificate { 39 | enum CertificateType { 40 | ROOT = 0; 41 | INTERMEDIATE = 1; 42 | USER_DEVICE = 2; 43 | SERVICE = 3; 44 | } 45 | //ProvisionedDeviceInfo.WvSecurityLevel Type = 1; // is this how one is supposed to call it? (it's an enum) there might be a bug here, with CertificateType getting confused with WvSecurityLevel, for now renaming it (verify against other binaries) 46 | CertificateType Type = 1; 47 | bytes SerialNumber = 2; 48 | uint32 CreationTimeSeconds = 3; 49 | bytes PublicKey = 4; 50 | uint32 SystemId = 5; 51 | uint32 TestDeviceDeprecated = 6; // is it bool or int? 52 | bytes ServiceId = 7; // service URL for service certificates 53 | } 54 | 55 | // missing some references, 56 | message DeviceCertificateStatus { 57 | enum CertificateStatus { 58 | VALID = 0; 59 | REVOKED = 1; 60 | } 61 | bytes SerialNumber = 1; 62 | CertificateStatus Status = 2; 63 | ProvisionedDeviceInfo DeviceInfo = 4; // where is 3? is it deprecated? 64 | } 65 | 66 | message DeviceCertificateStatusList { 67 | uint32 CreationTimeSeconds = 1; 68 | repeated DeviceCertificateStatus CertificateStatus = 2; 69 | } 70 | 71 | message EncryptedClientIdentification { 72 | string ServiceId = 1; 73 | bytes ServiceCertificateSerialNumber = 2; 74 | bytes EncryptedClientId = 3; 75 | bytes EncryptedClientIdIv = 4; 76 | bytes EncryptedPrivacyKey = 5; 77 | } 78 | 79 | // todo: fill (for this top-level type, it might be impossible/difficult) 80 | enum LicenseType { 81 | ZERO = 0; 82 | DEFAULT = 1; // do not know what this is either, but should be 1; on recent versions may go up to 3 (latest x86) 83 | } 84 | 85 | // todo: fill (for this top-level type, it might be impossible/difficult) 86 | // this is just a guess because these globals got lost, but really, do we need more? 87 | enum ProtocolVersion { 88 | DUMMY = 0; 89 | CURRENT = 21; // don't have symbols for this 90 | } 91 | 92 | 93 | message LicenseIdentification { 94 | bytes RequestId = 1; 95 | bytes SessionId = 2; 96 | bytes PurchaseId = 3; 97 | LicenseType Type = 4; 98 | uint32 Version = 5; 99 | bytes ProviderSessionToken = 6; 100 | } 101 | 102 | 103 | message License { 104 | message Policy { 105 | uint32 CanPlay = 1; 106 | uint32 CanPersist = 2; 107 | uint32 CanRenew = 3; 108 | uint32 RentalDurationSeconds = 4; 109 | uint32 PlaybackDurationSeconds = 5; 110 | uint32 LicenseDurationSeconds = 6; 111 | uint32 RenewalRecoveryDurationSeconds = 7; 112 | string RenewalServerUrl = 8; 113 | uint32 RenewalDelaySeconds = 9; 114 | uint32 RenewalRetryIntervalSeconds = 10; 115 | uint32 RenewWithUsage = 11; 116 | uint32 UnknownPolicy12 = 12; 117 | } 118 | message KeyContainer { 119 | enum KeyType { 120 | _NOKEYTYPE = 0; // dummy, added to satisfy proto3, not present in original 121 | SIGNING = 1; 122 | CONTENT = 2; 123 | KEY_CONTROL = 3; 124 | OPERATOR_SESSION = 4; 125 | } 126 | enum SecurityLevel { 127 | _NOSECLEVEL = 0; // dummy, added to satisfy proto3, not present in original 128 | SW_SECURE_CRYPTO = 1; 129 | SW_SECURE_DECODE = 2; 130 | HW_SECURE_CRYPTO = 3; 131 | HW_SECURE_DECODE = 4; 132 | HW_SECURE_ALL = 5; 133 | } 134 | message OutputProtection { 135 | enum CGMS { 136 | COPY_FREE = 0; 137 | COPY_ONCE = 2; 138 | COPY_NEVER = 3; 139 | CGMS_NONE = 0x2A; // PC default! 140 | } 141 | ClientIdentification.ClientCapabilities.HdcpVersion Hdcp = 1; // it's most likely a copy of Hdcp version available here, but compiler optimized it away 142 | CGMS CgmsFlags = 2; 143 | } 144 | message KeyControl { 145 | bytes KeyControlBlock = 1; // what is this? 146 | bytes Iv = 2; 147 | } 148 | message OperatorSessionKeyPermissions { 149 | uint32 AllowEncrypt = 1; 150 | uint32 AllowDecrypt = 2; 151 | uint32 AllowSign = 3; 152 | uint32 AllowSignatureVerify = 4; 153 | } 154 | message VideoResolutionConstraint { 155 | uint32 MinResolutionPixels = 1; 156 | uint32 MaxResolutionPixels = 2; 157 | OutputProtection RequiredProtection = 3; 158 | } 159 | bytes Id = 1; 160 | bytes Iv = 2; 161 | bytes Key = 3; 162 | KeyType Type = 4; 163 | SecurityLevel Level = 5; 164 | OutputProtection RequiredProtection = 6; 165 | OutputProtection RequestedProtection = 7; 166 | KeyControl _KeyControl = 8; // duped names, etc 167 | OperatorSessionKeyPermissions _OperatorSessionKeyPermissions = 9; // duped names, etc 168 | repeated VideoResolutionConstraint VideoResolutionConstraints = 10; 169 | } 170 | LicenseIdentification Id = 1; 171 | Policy _Policy = 2; // duped names, etc 172 | repeated KeyContainer Key = 3; 173 | uint32 LicenseStartTime = 4; 174 | uint32 RemoteAttestationVerified = 5; // bool? 175 | bytes ProviderClientToken = 6; 176 | // there might be more, check with newer versions (I see field 7-8 in a lic) 177 | // this appeared in latest x86: 178 | uint32 ProtectionScheme = 7; // type unconfirmed fully, but it's likely as WidevineCencHeader describesit (fourcc) 179 | bytes UnknownHdcpDataField = 8; 180 | } 181 | 182 | message LicenseError { 183 | enum Error { 184 | DUMMY_NO_ERROR = 0; // dummy, added to satisfy proto3 185 | INVALID_DEVICE_CERTIFICATE = 1; 186 | REVOKED_DEVICE_CERTIFICATE = 2; 187 | SERVICE_UNAVAILABLE = 3; 188 | } 189 | //LicenseRequest.RequestType ErrorCode; // clang mismatch 190 | Error ErrorCode = 1; 191 | } 192 | 193 | message LicenseRequest { 194 | message ContentIdentification { 195 | message CENC { 196 | // 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: 197 | WidevineCencHeader Pssh = 1; 198 | LicenseType LicenseType = 2; // unfortunately the LicenseType symbols are not present, acceptable value seems to only be 1 199 | bytes RequestId = 3; 200 | } 201 | message WebM { 202 | bytes Header = 1; // identical to CENC, aside from PSSH and the parent field number used 203 | LicenseType LicenseType = 2; 204 | bytes RequestId = 3; 205 | } 206 | message ExistingLicense { 207 | LicenseIdentification LicenseId = 1; 208 | uint32 SecondsSinceStarted = 2; 209 | uint32 SecondsSinceLastPlayed = 3; 210 | bytes SessionUsageTableEntry = 4; 211 | } 212 | CENC CencId = 1; 213 | WebM WebmId = 2; 214 | ExistingLicense License = 3; 215 | } 216 | enum RequestType { 217 | DUMMY_REQ_TYPE = 0; // dummy, added to satisfy proto3 218 | NEW = 1; 219 | RENEWAL = 2; 220 | RELEASE = 3; 221 | } 222 | ClientIdentification ClientId = 1; 223 | ContentIdentification ContentId = 2; 224 | RequestType Type = 3; 225 | uint32 RequestTime = 4; 226 | bytes KeyControlNonceDeprecated = 5; 227 | ProtocolVersion ProtocolVersion = 6; // lacking symbols for this 228 | uint32 KeyControlNonce = 7; 229 | EncryptedClientIdentification EncryptedClientId = 8; 230 | } 231 | 232 | message ProvisionedDeviceInfo { 233 | enum WvSecurityLevel { 234 | LEVEL_UNSPECIFIED = 0; 235 | LEVEL_1 = 1; 236 | LEVEL_2 = 2; 237 | LEVEL_3 = 3; 238 | } 239 | uint32 SystemId = 1; 240 | string Soc = 2; 241 | string Manufacturer = 3; 242 | string Model = 4; 243 | string DeviceType = 5; 244 | uint32 ModelYear = 6; 245 | WvSecurityLevel SecurityLevel = 7; 246 | uint32 TestDevice = 8; // bool? 247 | } 248 | 249 | 250 | // todo: fill 251 | message ProvisioningOptions { 252 | } 253 | 254 | // todo: fill 255 | message ProvisioningRequest { 256 | } 257 | 258 | // todo: fill 259 | message ProvisioningResponse { 260 | } 261 | 262 | message RemoteAttestation { 263 | EncryptedClientIdentification Certificate = 1; 264 | string Salt = 2; 265 | string Signature = 3; 266 | } 267 | 268 | // todo: fill 269 | message SessionInit { 270 | } 271 | 272 | // todo: fill 273 | message SessionState { 274 | } 275 | 276 | // todo: fill 277 | message SignedCertificateStatusList { 278 | } 279 | 280 | message SignedDeviceCertificate { 281 | 282 | //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: 283 | DeviceCertificate _DeviceCertificate = 1; // how should we deal with duped names? will have to look at proto docs later 284 | bytes Signature = 2; 285 | SignedDeviceCertificate Signer = 3; 286 | } 287 | 288 | 289 | // todo: fill 290 | message SignedProvisioningMessage { 291 | } 292 | 293 | // the root of all messages, from either server or client 294 | message SignedMessage { 295 | enum MessageType { 296 | DUMMY_MSG_TYPE = 0; // dummy, added to satisfy proto3 297 | LICENSE_REQUEST = 1; 298 | LICENSE = 2; 299 | ERROR_RESPONSE = 3; 300 | SERVICE_CERTIFICATE_REQUEST = 4; 301 | SERVICE_CERTIFICATE = 5; 302 | } 303 | MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel 304 | 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 305 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE 306 | bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now) 307 | bytes SessionKey = 4; // often RSA wrapped for licenses 308 | RemoteAttestation RemoteAttestation = 5; 309 | } 310 | 311 | 312 | 313 | // This message is copied from google's docs, not reversed: 314 | message WidevineCencHeader { 315 | enum Algorithm { 316 | UNENCRYPTED = 0; 317 | AESCTR = 1; 318 | }; 319 | Algorithm algorithm = 1; 320 | repeated bytes key_id = 2; 321 | 322 | // Content provider name. 323 | string provider = 3; 324 | 325 | // A content identifier, specified by content provider. 326 | bytes content_id = 4; 327 | 328 | // Track type. Acceptable values are SD, HD and AUDIO. Used to 329 | // differentiate content keys used by an asset. 330 | string track_type_deprecated = 5; 331 | 332 | // The name of a registered policy to be used for this asset. 333 | string policy = 6; 334 | 335 | // Crypto period index, for media using key rotation. 336 | uint32 crypto_period_index = 7; 337 | 338 | // Optional protected context for group content. The grouped_license is a 339 | // serialized SignedMessage. 340 | bytes grouped_license = 8; 341 | 342 | // Protection scheme identifying the encryption algorithm. 343 | // Represented as one of the following 4CC values: 344 | // 'cenc' (AESCTR), 'cbc1' (AESCBC), 345 | // 'cens' (AESCTR subsample), 'cbcs' (AESCBC subsample). 346 | uint32 protection_scheme = 9; 347 | 348 | // Optional. For media using key rotation, this represents the duration 349 | // of each crypto period in seconds. 350 | uint32 crypto_period_seconds = 10; 351 | } 352 | 353 | 354 | 355 | 356 | // 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 357 | message SignedLicenseRequest { 358 | enum MessageType { 359 | DUMMY_MSG_TYPE = 0; // dummy, added to satisfy proto3 360 | LICENSE_REQUEST = 1; 361 | LICENSE = 2; 362 | ERROR_RESPONSE = 3; 363 | SERVICE_CERTIFICATE_REQUEST = 4; 364 | SERVICE_CERTIFICATE = 5; 365 | } 366 | MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel 367 | 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 368 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE 369 | bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now) 370 | bytes SessionKey = 4; // often RSA wrapped for licenses 371 | RemoteAttestation RemoteAttestation = 5; 372 | } 373 | 374 | message SignedLicense { 375 | enum MessageType { 376 | DUMMY_MSG_TYPE = 0; // dummy, added to satisfy proto3 377 | LICENSE_REQUEST = 1; 378 | LICENSE = 2; 379 | ERROR_RESPONSE = 3; 380 | SERVICE_CERTIFICATE_REQUEST = 4; 381 | SERVICE_CERTIFICATE = 5; 382 | } 383 | MessageType Type = 1; // has in incorrect overlap with License_KeyContainer_SecurityLevel 384 | 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 385 | // for SERVICE_CERTIFICATE, only Type and Msg are present, and it's just a DeviceCertificate with CertificateType set to SERVICE 386 | bytes Signature = 3; // might be different type of signatures (ex. RSA vs AES CMAC(??), unconfirmed for now) 387 | bytes SessionKey = 4; // often RSA wrapped for licenses 388 | RemoteAttestation RemoteAttestation = 5; 389 | } -------------------------------------------------------------------------------- /data/pywidevine/L3/cdm/formats/wv_proto4_pb2.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by the protocol buffer compiler. DO NOT EDIT! 3 | # source: wv_proto4.proto 4 | """Generated protocol buffer code.""" 5 | from google.protobuf.internal import enum_type_wrapper 6 | from google.protobuf import descriptor as _descriptor 7 | from google.protobuf import descriptor_pool as _descriptor_pool 8 | from google.protobuf import message as _message 9 | from google.protobuf import reflection as _reflection 10 | from google.protobuf import symbol_database as _symbol_database 11 | 12 | # @@protoc_insertion_point(imports) 13 | 14 | _sym_db = _symbol_database.Default() 15 | 16 | DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( 17 | b'\n\x0fwv_proto4.proto\"\xc5\x05\n\x14\x43lientIdentification\x12-\n\x04Type\x18\x01 \x01(\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\x1a(\n\tNameValue\x12\x0c\n\x04Name\x18\x01 \x01(\t\x12\r\n\x05Value\x18\x02 \x01(\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 \x01(\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 \x01(\t\x12&\n\x1eServiceCertificateSerialNumber\x18\x02 \x01(\x0c\x12\x19\n\x11\x45ncryptedClientId\x18\x03 \x01(\x0c\x12\x1b\n\x13\x45ncryptedClientIdIv\x18\x04 \x01(\x0c\x12\x1b\n\x13\x45ncryptedPrivacyKey\x18\x05 \x01(\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\"\xfa\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\x12\x1c\n\x14UnknownHdcpDataField\x18\x08 \x01(\x0c\x1a\xd4\x02\n\x06Policy\x12\x0f\n\x07\x43\x61nPlay\x18\x01 \x01(\r\x12\x12\n\nCanPersist\x18\x02 \x01(\r\x12\x10\n\x08\x43\x61nRenew\x18\x03 \x01(\r\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(\r\x12\x17\n\x0fUnknownPolicy12\x18\x0c \x01(\r\x1a\x9b\n\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 \x01(\x0c\x12\n\n\x02Iv\x18\x02 \x01(\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\"Z\n\x07KeyType\x12\x0e\n\n_NOKEYTYPE\x10\x00\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\"\x8b\x01\n\rSecurityLevel\x12\x0f\n\x0b_NOSECLEVEL\x10\x00\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\"\xac\x01\n\x0cLicenseError\x12&\n\tErrorCode\x18\x01 \x01(\x0e\x32\x13.LicenseError.Error\"t\n\x05\x45rror\x12\x12\n\x0e\x44UMMY_NO_ERROR\x10\x00\x12\x1e\n\x1aINVALID_DEVICE_CERTIFICATE\x10\x01\x12\x1e\n\x1aREVOKED_DEVICE_CERTIFICATE\x10\x02\x12\x17\n\x13SERVICE_UNAVAILABLE\x10\x03\"\xc0\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\"D\n\x0bRequestType\x12\x12\n\x0e\x44UMMY_REQ_TYPE\x10\x00\x12\x07\n\x03NEW\x10\x01\x12\x0b\n\x07RENEWAL\x10\x02\x12\x0b\n\x07RELEASE\x10\x03\"\xa6\x02\n\x15ProvisionedDeviceInfo\x12\x10\n\x08SystemId\x18\x01 \x01(\r\x12\x0b\n\x03Soc\x18\x02 \x01(\t\x12\x14\n\x0cManufacturer\x18\x03 \x01(\t\x12\r\n\x05Model\x18\x04 \x01(\t\x12\x12\n\nDeviceType\x18\x05 \x01(\t\x12\x11\n\tModelYear\x18\x06 \x01(\r\x12=\n\rSecurityLevel\x18\x07 \x01(\x0e\x32&.ProvisionedDeviceInfo.WvSecurityLevel\x12\x12\n\nTestDevice\x18\x08 \x01(\r\"O\n\x0fWvSecurityLevel\x12\x15\n\x11LEVEL_UNSPECIFIED\x10\x00\x12\x0b\n\x07LEVEL_1\x10\x01\x12\x0b\n\x07LEVEL_2\x10\x02\x12\x0b\n\x07LEVEL_3\x10\x03\"\x15\n\x13ProvisioningOptions\"\x15\n\x13ProvisioningRequest\"\x16\n\x14ProvisioningResponse\"i\n\x11RemoteAttestation\x12\x33\n\x0b\x43\x65rtificate\x18\x01 \x01(\x0b\x32\x1e.EncryptedClientIdentification\x12\x0c\n\x04Salt\x18\x02 \x01(\t\x12\x11\n\tSignature\x18\x03 \x01(\t\"\r\n\x0bSessionInit\"\x0e\n\x0cSessionState\"\x1d\n\x1bSignedCertificateStatusList\"\x86\x01\n\x17SignedDeviceCertificate\x12.\n\x12_DeviceCertificate\x18\x01 \x01(\x0b\x32\x12.DeviceCertificate\x12\x11\n\tSignature\x18\x02 \x01(\x0c\x12(\n\x06Signer\x18\x03 \x01(\x0b\x32\x18.SignedDeviceCertificate\"\x1b\n\x19SignedProvisioningMessage\"\xb0\x02\n\rSignedMessage\x12(\n\x04Type\x18\x01 \x01(\x0e\x32\x1a.SignedMessage.MessageType\x12\x0b\n\x03Msg\x18\x02 \x01(\x0c\x12\x11\n\tSignature\x18\x03 \x01(\x0c\x12\x12\n\nSessionKey\x18\x04 \x01(\x0c\x12-\n\x11RemoteAttestation\x18\x05 \x01(\x0b\x32\x12.RemoteAttestation\"\x91\x01\n\x0bMessageType\x12\x12\n\x0e\x44UMMY_MSG_TYPE\x10\x00\x12\x13\n\x0fLICENSE_REQUEST\x10\x01\x12\x0b\n\x07LICENSE\x10\x02\x12\x12\n\x0e\x45RROR_RESPONSE\x10\x03\x12\x1f\n\x1bSERVICE_CERTIFICATE_REQUEST\x10\x04\x12\x17\n\x13SERVICE_CERTIFICATE\x10\x05\"\xc5\x02\n\x12WidevineCencHeader\x12\x30\n\talgorithm\x18\x01 \x01(\x0e\x32\x1d.WidevineCencHeader.Algorithm\x12\x0e\n\x06key_id\x18\x02 \x03(\x0c\x12\x10\n\x08provider\x18\x03 \x01(\t\x12\x12\n\ncontent_id\x18\x04 \x01(\x0c\x12\x1d\n\x15track_type_deprecated\x18\x05 \x01(\t\x12\x0e\n\x06policy\x18\x06 \x01(\t\x12\x1b\n\x13\x63rypto_period_index\x18\x07 \x01(\r\x12\x17\n\x0fgrouped_license\x18\x08 \x01(\x0c\x12\x19\n\x11protection_scheme\x18\t \x01(\r\x12\x1d\n\x15\x63rypto_period_seconds\x18\n \x01(\r\"(\n\tAlgorithm\x12\x0f\n\x0bUNENCRYPTED\x10\x00\x12\n\n\x06\x41\x45SCTR\x10\x01\"\xcf\x02\n\x14SignedLicenseRequest\x12/\n\x04Type\x18\x01 \x01(\x0e\x32!.SignedLicenseRequest.MessageType\x12\x1c\n\x03Msg\x18\x02 \x01(\x0b\x32\x0f.LicenseRequest\x12\x11\n\tSignature\x18\x03 \x01(\x0c\x12\x12\n\nSessionKey\x18\x04 \x01(\x0c\x12-\n\x11RemoteAttestation\x18\x05 \x01(\x0b\x32\x12.RemoteAttestation\"\x91\x01\n\x0bMessageType\x12\x12\n\x0e\x44UMMY_MSG_TYPE\x10\x00\x12\x13\n\x0fLICENSE_REQUEST\x10\x01\x12\x0b\n\x07LICENSE\x10\x02\x12\x12\n\x0e\x45RROR_RESPONSE\x10\x03\x12\x1f\n\x1bSERVICE_CERTIFICATE_REQUEST\x10\x04\x12\x17\n\x13SERVICE_CERTIFICATE\x10\x05\"\xba\x02\n\rSignedLicense\x12(\n\x04Type\x18\x01 \x01(\x0e\x32\x1a.SignedLicense.MessageType\x12\x15\n\x03Msg\x18\x02 \x01(\x0b\x32\x08.License\x12\x11\n\tSignature\x18\x03 \x01(\x0c\x12\x12\n\nSessionKey\x18\x04 \x01(\x0c\x12-\n\x11RemoteAttestation\x18\x05 \x01(\x0b\x32\x12.RemoteAttestation\"\x91\x01\n\x0bMessageType\x12\x12\n\x0e\x44UMMY_MSG_TYPE\x10\x00\x12\x13\n\x0fLICENSE_REQUEST\x10\x01\x12\x0b\n\x07LICENSE\x10\x02\x12\x12\n\x0e\x45RROR_RESPONSE\x10\x03\x12\x1f\n\x1bSERVICE_CERTIFICATE_REQUEST\x10\x04\x12\x17\n\x13SERVICE_CERTIFICATE\x10\x05*$\n\x0bLicenseType\x12\x08\n\x04ZERO\x10\x00\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x01*)\n\x0fProtocolVersion\x12\t\n\x05\x44UMMY\x10\x00\x12\x0b\n\x07\x43URRENT\x10\x15\x62\x06proto3') 18 | 19 | _LICENSETYPE = DESCRIPTOR.enum_types_by_name['LicenseType'] 20 | LicenseType = enum_type_wrapper.EnumTypeWrapper(_LICENSETYPE) 21 | _PROTOCOLVERSION = DESCRIPTOR.enum_types_by_name['ProtocolVersion'] 22 | ProtocolVersion = enum_type_wrapper.EnumTypeWrapper(_PROTOCOLVERSION) 23 | ZERO = 0 24 | DEFAULT = 1 25 | DUMMY = 0 26 | CURRENT = 21 27 | 28 | _CLIENTIDENTIFICATION = DESCRIPTOR.message_types_by_name['ClientIdentification'] 29 | _CLIENTIDENTIFICATION_NAMEVALUE = _CLIENTIDENTIFICATION.nested_types_by_name['NameValue'] 30 | _CLIENTIDENTIFICATION_CLIENTCAPABILITIES = _CLIENTIDENTIFICATION.nested_types_by_name['ClientCapabilities'] 31 | _DEVICECERTIFICATE = DESCRIPTOR.message_types_by_name['DeviceCertificate'] 32 | _DEVICECERTIFICATESTATUS = DESCRIPTOR.message_types_by_name['DeviceCertificateStatus'] 33 | _DEVICECERTIFICATESTATUSLIST = DESCRIPTOR.message_types_by_name['DeviceCertificateStatusList'] 34 | _ENCRYPTEDCLIENTIDENTIFICATION = DESCRIPTOR.message_types_by_name['EncryptedClientIdentification'] 35 | _LICENSEIDENTIFICATION = DESCRIPTOR.message_types_by_name['LicenseIdentification'] 36 | _LICENSE = DESCRIPTOR.message_types_by_name['License'] 37 | _LICENSE_POLICY = _LICENSE.nested_types_by_name['Policy'] 38 | _LICENSE_KEYCONTAINER = _LICENSE.nested_types_by_name['KeyContainer'] 39 | _LICENSE_KEYCONTAINER_OUTPUTPROTECTION = _LICENSE_KEYCONTAINER.nested_types_by_name['OutputProtection'] 40 | _LICENSE_KEYCONTAINER_KEYCONTROL = _LICENSE_KEYCONTAINER.nested_types_by_name['KeyControl'] 41 | _LICENSE_KEYCONTAINER_OPERATORSESSIONKEYPERMISSIONS = _LICENSE_KEYCONTAINER.nested_types_by_name[ 42 | 'OperatorSessionKeyPermissions'] 43 | _LICENSE_KEYCONTAINER_VIDEORESOLUTIONCONSTRAINT = _LICENSE_KEYCONTAINER.nested_types_by_name[ 44 | 'VideoResolutionConstraint'] 45 | _LICENSEERROR = DESCRIPTOR.message_types_by_name['LicenseError'] 46 | _LICENSEREQUEST = DESCRIPTOR.message_types_by_name['LicenseRequest'] 47 | _LICENSEREQUEST_CONTENTIDENTIFICATION = _LICENSEREQUEST.nested_types_by_name['ContentIdentification'] 48 | _LICENSEREQUEST_CONTENTIDENTIFICATION_CENC = _LICENSEREQUEST_CONTENTIDENTIFICATION.nested_types_by_name['CENC'] 49 | _LICENSEREQUEST_CONTENTIDENTIFICATION_WEBM = _LICENSEREQUEST_CONTENTIDENTIFICATION.nested_types_by_name['WebM'] 50 | _LICENSEREQUEST_CONTENTIDENTIFICATION_EXISTINGLICENSE = _LICENSEREQUEST_CONTENTIDENTIFICATION.nested_types_by_name[ 51 | 'ExistingLicense'] 52 | _PROVISIONEDDEVICEINFO = DESCRIPTOR.message_types_by_name['ProvisionedDeviceInfo'] 53 | _PROVISIONINGOPTIONS = DESCRIPTOR.message_types_by_name['ProvisioningOptions'] 54 | _PROVISIONINGREQUEST = DESCRIPTOR.message_types_by_name['ProvisioningRequest'] 55 | _PROVISIONINGRESPONSE = DESCRIPTOR.message_types_by_name['ProvisioningResponse'] 56 | _REMOTEATTESTATION = DESCRIPTOR.message_types_by_name['RemoteAttestation'] 57 | _SESSIONINIT = DESCRIPTOR.message_types_by_name['SessionInit'] 58 | _SESSIONSTATE = DESCRIPTOR.message_types_by_name['SessionState'] 59 | _SIGNEDCERTIFICATESTATUSLIST = DESCRIPTOR.message_types_by_name['SignedCertificateStatusList'] 60 | _SIGNEDDEVICECERTIFICATE = DESCRIPTOR.message_types_by_name['SignedDeviceCertificate'] 61 | _SIGNEDPROVISIONINGMESSAGE = DESCRIPTOR.message_types_by_name['SignedProvisioningMessage'] 62 | _SIGNEDMESSAGE = DESCRIPTOR.message_types_by_name['SignedMessage'] 63 | _WIDEVINECENCHEADER = DESCRIPTOR.message_types_by_name['WidevineCencHeader'] 64 | _SIGNEDLICENSEREQUEST = DESCRIPTOR.message_types_by_name['SignedLicenseRequest'] 65 | _SIGNEDLICENSE = DESCRIPTOR.message_types_by_name['SignedLicense'] 66 | _CLIENTIDENTIFICATION_CLIENTCAPABILITIES_HDCPVERSION = _CLIENTIDENTIFICATION_CLIENTCAPABILITIES.enum_types_by_name[ 67 | 'HdcpVersion'] 68 | _CLIENTIDENTIFICATION_TOKENTYPE = _CLIENTIDENTIFICATION.enum_types_by_name['TokenType'] 69 | _DEVICECERTIFICATE_CERTIFICATETYPE = _DEVICECERTIFICATE.enum_types_by_name['CertificateType'] 70 | _DEVICECERTIFICATESTATUS_CERTIFICATESTATUS = _DEVICECERTIFICATESTATUS.enum_types_by_name['CertificateStatus'] 71 | _LICENSE_KEYCONTAINER_OUTPUTPROTECTION_CGMS = _LICENSE_KEYCONTAINER_OUTPUTPROTECTION.enum_types_by_name['CGMS'] 72 | _LICENSE_KEYCONTAINER_KEYTYPE = _LICENSE_KEYCONTAINER.enum_types_by_name['KeyType'] 73 | _LICENSE_KEYCONTAINER_SECURITYLEVEL = _LICENSE_KEYCONTAINER.enum_types_by_name['SecurityLevel'] 74 | _LICENSEERROR_ERROR = _LICENSEERROR.enum_types_by_name['Error'] 75 | _LICENSEREQUEST_REQUESTTYPE = _LICENSEREQUEST.enum_types_by_name['RequestType'] 76 | _PROVISIONEDDEVICEINFO_WVSECURITYLEVEL = _PROVISIONEDDEVICEINFO.enum_types_by_name['WvSecurityLevel'] 77 | _SIGNEDMESSAGE_MESSAGETYPE = _SIGNEDMESSAGE.enum_types_by_name['MessageType'] 78 | _WIDEVINECENCHEADER_ALGORITHM = _WIDEVINECENCHEADER.enum_types_by_name['Algorithm'] 79 | _SIGNEDLICENSEREQUEST_MESSAGETYPE = _SIGNEDLICENSEREQUEST.enum_types_by_name['MessageType'] 80 | _SIGNEDLICENSE_MESSAGETYPE = _SIGNEDLICENSE.enum_types_by_name['MessageType'] 81 | ClientIdentification = _reflection.GeneratedProtocolMessageType('ClientIdentification', (_message.Message,), { 82 | 83 | 'NameValue': _reflection.GeneratedProtocolMessageType('NameValue', (_message.Message,), { 84 | 'DESCRIPTOR': _CLIENTIDENTIFICATION_NAMEVALUE, 85 | '__module__': 'wv_proto4_pb2' 86 | # @@protoc_insertion_point(class_scope:ClientIdentification.NameValue) 87 | }) 88 | , 89 | 90 | 'ClientCapabilities': _reflection.GeneratedProtocolMessageType('ClientCapabilities', (_message.Message,), { 91 | 'DESCRIPTOR': _CLIENTIDENTIFICATION_CLIENTCAPABILITIES, 92 | '__module__': 'wv_proto4_pb2' 93 | # @@protoc_insertion_point(class_scope:ClientIdentification.ClientCapabilities) 94 | }) 95 | , 96 | 'DESCRIPTOR': _CLIENTIDENTIFICATION, 97 | '__module__': 'wv_proto4_pb2' 98 | # @@protoc_insertion_point(class_scope:ClientIdentification) 99 | }) 100 | _sym_db.RegisterMessage(ClientIdentification) 101 | _sym_db.RegisterMessage(ClientIdentification.NameValue) 102 | _sym_db.RegisterMessage(ClientIdentification.ClientCapabilities) 103 | 104 | DeviceCertificate = _reflection.GeneratedProtocolMessageType('DeviceCertificate', (_message.Message,), { 105 | 'DESCRIPTOR': _DEVICECERTIFICATE, 106 | '__module__': 'wv_proto4_pb2' 107 | # @@protoc_insertion_point(class_scope:DeviceCertificate) 108 | }) 109 | _sym_db.RegisterMessage(DeviceCertificate) 110 | 111 | DeviceCertificateStatus = _reflection.GeneratedProtocolMessageType('DeviceCertificateStatus', (_message.Message,), { 112 | 'DESCRIPTOR': _DEVICECERTIFICATESTATUS, 113 | '__module__': 'wv_proto4_pb2' 114 | # @@protoc_insertion_point(class_scope:DeviceCertificateStatus) 115 | }) 116 | _sym_db.RegisterMessage(DeviceCertificateStatus) 117 | 118 | DeviceCertificateStatusList = _reflection.GeneratedProtocolMessageType('DeviceCertificateStatusList', 119 | (_message.Message,), { 120 | 'DESCRIPTOR': _DEVICECERTIFICATESTATUSLIST, 121 | '__module__': 'wv_proto4_pb2' 122 | # @@protoc_insertion_point(class_scope:DeviceCertificateStatusList) 123 | }) 124 | _sym_db.RegisterMessage(DeviceCertificateStatusList) 125 | 126 | EncryptedClientIdentification = _reflection.GeneratedProtocolMessageType('EncryptedClientIdentification', 127 | (_message.Message,), { 128 | 'DESCRIPTOR': _ENCRYPTEDCLIENTIDENTIFICATION, 129 | '__module__': 'wv_proto4_pb2' 130 | # @@protoc_insertion_point(class_scope:EncryptedClientIdentification) 131 | }) 132 | _sym_db.RegisterMessage(EncryptedClientIdentification) 133 | 134 | LicenseIdentification = _reflection.GeneratedProtocolMessageType('LicenseIdentification', (_message.Message,), { 135 | 'DESCRIPTOR': _LICENSEIDENTIFICATION, 136 | '__module__': 'wv_proto4_pb2' 137 | # @@protoc_insertion_point(class_scope:LicenseIdentification) 138 | }) 139 | _sym_db.RegisterMessage(LicenseIdentification) 140 | 141 | License = _reflection.GeneratedProtocolMessageType('License', (_message.Message,), { 142 | 143 | 'Policy': _reflection.GeneratedProtocolMessageType('Policy', (_message.Message,), { 144 | 'DESCRIPTOR': _LICENSE_POLICY, 145 | '__module__': 'wv_proto4_pb2' 146 | # @@protoc_insertion_point(class_scope:License.Policy) 147 | }) 148 | , 149 | 150 | 'KeyContainer': _reflection.GeneratedProtocolMessageType('KeyContainer', (_message.Message,), { 151 | 152 | 'OutputProtection': _reflection.GeneratedProtocolMessageType('OutputProtection', (_message.Message,), { 153 | 'DESCRIPTOR': _LICENSE_KEYCONTAINER_OUTPUTPROTECTION, 154 | '__module__': 'wv_proto4_pb2' 155 | # @@protoc_insertion_point(class_scope:License.KeyContainer.OutputProtection) 156 | }) 157 | , 158 | 159 | 'KeyControl': _reflection.GeneratedProtocolMessageType('KeyControl', (_message.Message,), { 160 | 'DESCRIPTOR': _LICENSE_KEYCONTAINER_KEYCONTROL, 161 | '__module__': 'wv_proto4_pb2' 162 | # @@protoc_insertion_point(class_scope:License.KeyContainer.KeyControl) 163 | }) 164 | , 165 | 166 | 'OperatorSessionKeyPermissions': _reflection.GeneratedProtocolMessageType('OperatorSessionKeyPermissions', 167 | (_message.Message,), { 168 | 'DESCRIPTOR': _LICENSE_KEYCONTAINER_OPERATORSESSIONKEYPERMISSIONS, 169 | '__module__': 'wv_proto4_pb2' 170 | # @@protoc_insertion_point(class_scope:License.KeyContainer.OperatorSessionKeyPermissions) 171 | }) 172 | , 173 | 174 | 'VideoResolutionConstraint': _reflection.GeneratedProtocolMessageType('VideoResolutionConstraint', 175 | (_message.Message,), { 176 | 'DESCRIPTOR': _LICENSE_KEYCONTAINER_VIDEORESOLUTIONCONSTRAINT, 177 | '__module__': 'wv_proto4_pb2' 178 | # @@protoc_insertion_point(class_scope:License.KeyContainer.VideoResolutionConstraint) 179 | }) 180 | , 181 | 'DESCRIPTOR': _LICENSE_KEYCONTAINER, 182 | '__module__': 'wv_proto4_pb2' 183 | # @@protoc_insertion_point(class_scope:License.KeyContainer) 184 | }) 185 | , 186 | 'DESCRIPTOR': _LICENSE, 187 | '__module__': 'wv_proto4_pb2' 188 | # @@protoc_insertion_point(class_scope:License) 189 | }) 190 | _sym_db.RegisterMessage(License) 191 | _sym_db.RegisterMessage(License.Policy) 192 | _sym_db.RegisterMessage(License.KeyContainer) 193 | _sym_db.RegisterMessage(License.KeyContainer.OutputProtection) 194 | _sym_db.RegisterMessage(License.KeyContainer.KeyControl) 195 | _sym_db.RegisterMessage(License.KeyContainer.OperatorSessionKeyPermissions) 196 | _sym_db.RegisterMessage(License.KeyContainer.VideoResolutionConstraint) 197 | 198 | LicenseError = _reflection.GeneratedProtocolMessageType('LicenseError', (_message.Message,), { 199 | 'DESCRIPTOR': _LICENSEERROR, 200 | '__module__': 'wv_proto4_pb2' 201 | # @@protoc_insertion_point(class_scope:LicenseError) 202 | }) 203 | _sym_db.RegisterMessage(LicenseError) 204 | 205 | LicenseRequest = _reflection.GeneratedProtocolMessageType('LicenseRequest', (_message.Message,), { 206 | 207 | 'ContentIdentification': _reflection.GeneratedProtocolMessageType('ContentIdentification', (_message.Message,), { 208 | 209 | 'CENC': _reflection.GeneratedProtocolMessageType('CENC', (_message.Message,), { 210 | 'DESCRIPTOR': _LICENSEREQUEST_CONTENTIDENTIFICATION_CENC, 211 | '__module__': 'wv_proto4_pb2' 212 | # @@protoc_insertion_point(class_scope:LicenseRequest.ContentIdentification.CENC) 213 | }) 214 | , 215 | 216 | 'WebM': _reflection.GeneratedProtocolMessageType('WebM', (_message.Message,), { 217 | 'DESCRIPTOR': _LICENSEREQUEST_CONTENTIDENTIFICATION_WEBM, 218 | '__module__': 'wv_proto4_pb2' 219 | # @@protoc_insertion_point(class_scope:LicenseRequest.ContentIdentification.WebM) 220 | }) 221 | , 222 | 223 | 'ExistingLicense': _reflection.GeneratedProtocolMessageType('ExistingLicense', (_message.Message,), { 224 | 'DESCRIPTOR': _LICENSEREQUEST_CONTENTIDENTIFICATION_EXISTINGLICENSE, 225 | '__module__': 'wv_proto4_pb2' 226 | # @@protoc_insertion_point(class_scope:LicenseRequest.ContentIdentification.ExistingLicense) 227 | }) 228 | , 229 | 'DESCRIPTOR': _LICENSEREQUEST_CONTENTIDENTIFICATION, 230 | '__module__': 'wv_proto4_pb2' 231 | # @@protoc_insertion_point(class_scope:LicenseRequest.ContentIdentification) 232 | }) 233 | , 234 | 'DESCRIPTOR': _LICENSEREQUEST, 235 | '__module__': 'wv_proto4_pb2' 236 | # @@protoc_insertion_point(class_scope:LicenseRequest) 237 | }) 238 | _sym_db.RegisterMessage(LicenseRequest) 239 | _sym_db.RegisterMessage(LicenseRequest.ContentIdentification) 240 | _sym_db.RegisterMessage(LicenseRequest.ContentIdentification.CENC) 241 | _sym_db.RegisterMessage(LicenseRequest.ContentIdentification.WebM) 242 | _sym_db.RegisterMessage(LicenseRequest.ContentIdentification.ExistingLicense) 243 | 244 | ProvisionedDeviceInfo = _reflection.GeneratedProtocolMessageType('ProvisionedDeviceInfo', (_message.Message,), { 245 | 'DESCRIPTOR': _PROVISIONEDDEVICEINFO, 246 | '__module__': 'wv_proto4_pb2' 247 | # @@protoc_insertion_point(class_scope:ProvisionedDeviceInfo) 248 | }) 249 | _sym_db.RegisterMessage(ProvisionedDeviceInfo) 250 | 251 | ProvisioningOptions = _reflection.GeneratedProtocolMessageType('ProvisioningOptions', (_message.Message,), { 252 | 'DESCRIPTOR': _PROVISIONINGOPTIONS, 253 | '__module__': 'wv_proto4_pb2' 254 | # @@protoc_insertion_point(class_scope:ProvisioningOptions) 255 | }) 256 | _sym_db.RegisterMessage(ProvisioningOptions) 257 | 258 | ProvisioningRequest = _reflection.GeneratedProtocolMessageType('ProvisioningRequest', (_message.Message,), { 259 | 'DESCRIPTOR': _PROVISIONINGREQUEST, 260 | '__module__': 'wv_proto4_pb2' 261 | # @@protoc_insertion_point(class_scope:ProvisioningRequest) 262 | }) 263 | _sym_db.RegisterMessage(ProvisioningRequest) 264 | 265 | ProvisioningResponse = _reflection.GeneratedProtocolMessageType('ProvisioningResponse', (_message.Message,), { 266 | 'DESCRIPTOR': _PROVISIONINGRESPONSE, 267 | '__module__': 'wv_proto4_pb2' 268 | # @@protoc_insertion_point(class_scope:ProvisioningResponse) 269 | }) 270 | _sym_db.RegisterMessage(ProvisioningResponse) 271 | 272 | RemoteAttestation = _reflection.GeneratedProtocolMessageType('RemoteAttestation', (_message.Message,), { 273 | 'DESCRIPTOR': _REMOTEATTESTATION, 274 | '__module__': 'wv_proto4_pb2' 275 | # @@protoc_insertion_point(class_scope:RemoteAttestation) 276 | }) 277 | _sym_db.RegisterMessage(RemoteAttestation) 278 | 279 | SessionInit = _reflection.GeneratedProtocolMessageType('SessionInit', (_message.Message,), { 280 | 'DESCRIPTOR': _SESSIONINIT, 281 | '__module__': 'wv_proto4_pb2' 282 | # @@protoc_insertion_point(class_scope:SessionInit) 283 | }) 284 | _sym_db.RegisterMessage(SessionInit) 285 | 286 | SessionState = _reflection.GeneratedProtocolMessageType('SessionState', (_message.Message,), { 287 | 'DESCRIPTOR': _SESSIONSTATE, 288 | '__module__': 'wv_proto4_pb2' 289 | # @@protoc_insertion_point(class_scope:SessionState) 290 | }) 291 | _sym_db.RegisterMessage(SessionState) 292 | 293 | SignedCertificateStatusList = _reflection.GeneratedProtocolMessageType('SignedCertificateStatusList', 294 | (_message.Message,), { 295 | 'DESCRIPTOR': _SIGNEDCERTIFICATESTATUSLIST, 296 | '__module__': 'wv_proto4_pb2' 297 | # @@protoc_insertion_point(class_scope:SignedCertificateStatusList) 298 | }) 299 | _sym_db.RegisterMessage(SignedCertificateStatusList) 300 | 301 | SignedDeviceCertificate = _reflection.GeneratedProtocolMessageType('SignedDeviceCertificate', (_message.Message,), { 302 | 'DESCRIPTOR': _SIGNEDDEVICECERTIFICATE, 303 | '__module__': 'wv_proto4_pb2' 304 | # @@protoc_insertion_point(class_scope:SignedDeviceCertificate) 305 | }) 306 | _sym_db.RegisterMessage(SignedDeviceCertificate) 307 | 308 | SignedProvisioningMessage = _reflection.GeneratedProtocolMessageType('SignedProvisioningMessage', (_message.Message,), { 309 | 'DESCRIPTOR': _SIGNEDPROVISIONINGMESSAGE, 310 | '__module__': 'wv_proto4_pb2' 311 | # @@protoc_insertion_point(class_scope:SignedProvisioningMessage) 312 | }) 313 | _sym_db.RegisterMessage(SignedProvisioningMessage) 314 | 315 | SignedMessage = _reflection.GeneratedProtocolMessageType('SignedMessage', (_message.Message,), { 316 | 'DESCRIPTOR': _SIGNEDMESSAGE, 317 | '__module__': 'wv_proto4_pb2' 318 | # @@protoc_insertion_point(class_scope:SignedMessage) 319 | }) 320 | _sym_db.RegisterMessage(SignedMessage) 321 | 322 | WidevineCencHeader = _reflection.GeneratedProtocolMessageType('WidevineCencHeader', (_message.Message,), { 323 | 'DESCRIPTOR': _WIDEVINECENCHEADER, 324 | '__module__': 'wv_proto4_pb2' 325 | # @@protoc_insertion_point(class_scope:WidevineCencHeader) 326 | }) 327 | _sym_db.RegisterMessage(WidevineCencHeader) 328 | 329 | SignedLicenseRequest = _reflection.GeneratedProtocolMessageType('SignedLicenseRequest', (_message.Message,), { 330 | 'DESCRIPTOR': _SIGNEDLICENSEREQUEST, 331 | '__module__': 'wv_proto4_pb2' 332 | # @@protoc_insertion_point(class_scope:SignedLicenseRequest) 333 | }) 334 | _sym_db.RegisterMessage(SignedLicenseRequest) 335 | 336 | SignedLicense = _reflection.GeneratedProtocolMessageType('SignedLicense', (_message.Message,), { 337 | 'DESCRIPTOR': _SIGNEDLICENSE, 338 | '__module__': 'wv_proto4_pb2' 339 | # @@protoc_insertion_point(class_scope:SignedLicense) 340 | }) 341 | _sym_db.RegisterMessage(SignedLicense) 342 | 343 | if _descriptor._USE_C_DESCRIPTORS == False: 344 | DESCRIPTOR._options = None 345 | _LICENSETYPE._serialized_start = 6713 346 | _LICENSETYPE._serialized_end = 6749 347 | _PROTOCOLVERSION._serialized_start = 6751 348 | _PROTOCOLVERSION._serialized_end = 6792 349 | _CLIENTIDENTIFICATION._serialized_start = 20 350 | _CLIENTIDENTIFICATION._serialized_end = 729 351 | _CLIENTIDENTIFICATION_NAMEVALUE._serialized_start = 309 352 | _CLIENTIDENTIFICATION_NAMEVALUE._serialized_end = 349 353 | _CLIENTIDENTIFICATION_CLIENTCAPABILITIES._serialized_start = 352 354 | _CLIENTIDENTIFICATION_CLIENTCAPABILITIES._serialized_end = 644 355 | _CLIENTIDENTIFICATION_CLIENTCAPABILITIES_HDCPVERSION._serialized_start = 560 356 | _CLIENTIDENTIFICATION_CLIENTCAPABILITIES_HDCPVERSION._serialized_end = 644 357 | _CLIENTIDENTIFICATION_TOKENTYPE._serialized_start = 646 358 | _CLIENTIDENTIFICATION_TOKENTYPE._serialized_end = 729 359 | _DEVICECERTIFICATE._serialized_start = 732 360 | _DEVICECERTIFICATE._serialized_end = 1015 361 | _DEVICECERTIFICATE_CERTIFICATETYPE._serialized_start = 940 362 | _DEVICECERTIFICATE_CERTIFICATETYPE._serialized_end = 1015 363 | _DEVICECERTIFICATESTATUS._serialized_start = 1018 364 | _DEVICECERTIFICATESTATUS._serialized_end = 1214 365 | _DEVICECERTIFICATESTATUS_CERTIFICATESTATUS._serialized_start = 1171 366 | _DEVICECERTIFICATESTATUS_CERTIFICATESTATUS._serialized_end = 1214 367 | _DEVICECERTIFICATESTATUSLIST._serialized_start = 1216 368 | _DEVICECERTIFICATESTATUSLIST._serialized_end = 1327 369 | _ENCRYPTEDCLIENTIDENTIFICATION._serialized_start = 1330 370 | _ENCRYPTEDCLIENTIDENTIFICATION._serialized_end = 1505 371 | _LICENSEIDENTIFICATION._serialized_start = 1508 372 | _LICENSEIDENTIFICATION._serialized_end = 1664 373 | _LICENSE._serialized_start = 1667 374 | _LICENSE._serialized_end = 3581 375 | _LICENSE_POLICY._serialized_start = 1931 376 | _LICENSE_POLICY._serialized_end = 2271 377 | _LICENSE_KEYCONTAINER._serialized_start = 2274 378 | _LICENSE_KEYCONTAINER._serialized_end = 3581 379 | _LICENSE_KEYCONTAINER_OUTPUTPROTECTION._serialized_start = 2795 380 | _LICENSE_KEYCONTAINER_OUTPUTPROTECTION._serialized_end = 3014 381 | _LICENSE_KEYCONTAINER_OUTPUTPROTECTION_CGMS._serialized_start = 2947 382 | _LICENSE_KEYCONTAINER_OUTPUTPROTECTION_CGMS._serialized_end = 3014 383 | _LICENSE_KEYCONTAINER_KEYCONTROL._serialized_start = 3016 384 | _LICENSE_KEYCONTAINER_KEYCONTROL._serialized_end = 3065 385 | _LICENSE_KEYCONTAINER_OPERATORSESSIONKEYPERMISSIONS._serialized_start = 3067 386 | _LICENSE_KEYCONTAINER_OPERATORSESSIONKEYPERMISSIONS._serialized_end = 3191 387 | _LICENSE_KEYCONTAINER_VIDEORESOLUTIONCONSTRAINT._serialized_start = 3194 388 | _LICENSE_KEYCONTAINER_VIDEORESOLUTIONCONSTRAINT._serialized_end = 3347 389 | _LICENSE_KEYCONTAINER_KEYTYPE._serialized_start = 3349 390 | _LICENSE_KEYCONTAINER_KEYTYPE._serialized_end = 3439 391 | _LICENSE_KEYCONTAINER_SECURITYLEVEL._serialized_start = 3442 392 | _LICENSE_KEYCONTAINER_SECURITYLEVEL._serialized_end = 3581 393 | _LICENSEERROR._serialized_start = 3584 394 | _LICENSEERROR._serialized_end = 3756 395 | _LICENSEERROR_ERROR._serialized_start = 3640 396 | _LICENSEERROR_ERROR._serialized_end = 3756 397 | _LICENSEREQUEST._serialized_start = 3759 398 | _LICENSEREQUEST._serialized_end = 4719 399 | _LICENSEREQUEST_CONTENTIDENTIFICATION._serialized_start = 4103 400 | _LICENSEREQUEST_CONTENTIDENTIFICATION._serialized_end = 4649 401 | _LICENSEREQUEST_CONTENTIDENTIFICATION_CENC._serialized_start = 4320 402 | _LICENSEREQUEST_CONTENTIDENTIFICATION_CENC._serialized_end = 4415 403 | _LICENSEREQUEST_CONTENTIDENTIFICATION_WEBM._serialized_start = 4417 404 | _LICENSEREQUEST_CONTENTIDENTIFICATION_WEBM._serialized_end = 4493 405 | _LICENSEREQUEST_CONTENTIDENTIFICATION_EXISTINGLICENSE._serialized_start = 4496 406 | _LICENSEREQUEST_CONTENTIDENTIFICATION_EXISTINGLICENSE._serialized_end = 4649 407 | _LICENSEREQUEST_REQUESTTYPE._serialized_start = 4651 408 | _LICENSEREQUEST_REQUESTTYPE._serialized_end = 4719 409 | _PROVISIONEDDEVICEINFO._serialized_start = 4722 410 | _PROVISIONEDDEVICEINFO._serialized_end = 5016 411 | _PROVISIONEDDEVICEINFO_WVSECURITYLEVEL._serialized_start = 4937 412 | _PROVISIONEDDEVICEINFO_WVSECURITYLEVEL._serialized_end = 5016 413 | _PROVISIONINGOPTIONS._serialized_start = 5018 414 | _PROVISIONINGOPTIONS._serialized_end = 5039 415 | _PROVISIONINGREQUEST._serialized_start = 5041 416 | _PROVISIONINGREQUEST._serialized_end = 5062 417 | _PROVISIONINGRESPONSE._serialized_start = 5064 418 | _PROVISIONINGRESPONSE._serialized_end = 5086 419 | _REMOTEATTESTATION._serialized_start = 5088 420 | _REMOTEATTESTATION._serialized_end = 5193 421 | _SESSIONINIT._serialized_start = 5195 422 | _SESSIONINIT._serialized_end = 5208 423 | _SESSIONSTATE._serialized_start = 5210 424 | _SESSIONSTATE._serialized_end = 5224 425 | _SIGNEDCERTIFICATESTATUSLIST._serialized_start = 5226 426 | _SIGNEDCERTIFICATESTATUSLIST._serialized_end = 5255 427 | _SIGNEDDEVICECERTIFICATE._serialized_start = 5258 428 | _SIGNEDDEVICECERTIFICATE._serialized_end = 5392 429 | _SIGNEDPROVISIONINGMESSAGE._serialized_start = 5394 430 | _SIGNEDPROVISIONINGMESSAGE._serialized_end = 5421 431 | _SIGNEDMESSAGE._serialized_start = 5424 432 | _SIGNEDMESSAGE._serialized_end = 5728 433 | _SIGNEDMESSAGE_MESSAGETYPE._serialized_start = 5583 434 | _SIGNEDMESSAGE_MESSAGETYPE._serialized_end = 5728 435 | _WIDEVINECENCHEADER._serialized_start = 5731 436 | _WIDEVINECENCHEADER._serialized_end = 6056 437 | _WIDEVINECENCHEADER_ALGORITHM._serialized_start = 6016 438 | _WIDEVINECENCHEADER_ALGORITHM._serialized_end = 6056 439 | _SIGNEDLICENSEREQUEST._serialized_start = 6059 440 | _SIGNEDLICENSEREQUEST._serialized_end = 6394 441 | _SIGNEDLICENSEREQUEST_MESSAGETYPE._serialized_start = 5583 442 | _SIGNEDLICENSEREQUEST_MESSAGETYPE._serialized_end = 5728 443 | _SIGNEDLICENSE._serialized_start = 6397 444 | _SIGNEDLICENSE._serialized_end = 6711 445 | _SIGNEDLICENSE_MESSAGETYPE._serialized_start = 5583 446 | _SIGNEDLICENSE_MESSAGETYPE._serialized_end = 5728 447 | # @@protoc_insertion_point(module_scope) 448 | -------------------------------------------------------------------------------- /data/pywidevine/L3/cdm/key.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | 3 | 4 | class Key: 5 | def __init__(self, kid, type, key, permissions=[]): 6 | self.kid = kid 7 | self.type = type 8 | self.key = key 9 | self.permissions = permissions 10 | 11 | def __repr__(self): 12 | if self.type == "OPERATOR_SESSION": 13 | return "key(kid={}, type={}, key={}, permissions={})".format(self.kid, self.type, 14 | binascii.hexlify(self.key), self.permissions) 15 | else: 16 | return "key(kid={}, type={}, key={})".format(self.kid, self.type, binascii.hexlify(self.key)) 17 | -------------------------------------------------------------------------------- /data/pywidevine/L3/cdm/session.py: -------------------------------------------------------------------------------- 1 | class Session: 2 | def __init__(self, session_id, init_data, device_config, offline): 3 | self.session_id = session_id 4 | self.init_data = init_data 5 | self.offline = offline 6 | self.device_config = device_config 7 | self.device_key = None 8 | self.session_key = None 9 | self.derived_keys = { 10 | 'enc': None, 11 | 'auth_1': None, 12 | 'auth_2': None 13 | } 14 | self.license_request = None 15 | self.license = None 16 | self.service_certificate = None 17 | self.privacy_mode = False 18 | self.keys = [] 19 | -------------------------------------------------------------------------------- /data/pywidevine/L3/cdm/vmp.py: -------------------------------------------------------------------------------- 1 | try: 2 | from google.protobuf.internal.decoder import \ 3 | _DecodeVarint as _di # this was tested to work with protobuf 3, but it's an internal API (any varint decoder might work) 4 | except ImportError: 5 | # this is generic and does not depend on pb internals, however it will decode "larger" possible numbers than pb decoder which has them fixed 6 | def LEB128_decode(buffer, pos, limit=64): 7 | result = 0 8 | shift = 0 9 | while True: 10 | b = buffer[pos] 11 | pos += 1 12 | result |= ((b & 0x7F) << shift) 13 | if not (b & 0x80): 14 | return (result, pos) 15 | shift += 7 16 | if shift > limit: 17 | raise Exception("integer too large, shift: {}".format(shift)) 18 | 19 | 20 | _di = LEB128_decode 21 | 22 | 23 | class FromFileMixin: 24 | @classmethod 25 | def from_file(cls, filename): 26 | """Load given a filename""" 27 | with open(filename, "rb") as f: 28 | return cls(f.read()) 29 | 30 | 31 | # the signatures use a format internally similar to 32 | # protobuf's encoding, but without wire types 33 | class VariableReader(FromFileMixin): 34 | """Protobuf-like encoding reader""" 35 | 36 | def __init__(self, buf): 37 | self.buf = buf 38 | self.pos = 0 39 | self.size = len(buf) 40 | 41 | def read_int(self): 42 | """Read a variable length integer""" 43 | # _DecodeVarint will take care of out of range errors 44 | (val, nextpos) = _di(self.buf, self.pos) 45 | self.pos = nextpos 46 | return val 47 | 48 | def read_bytes_raw(self, size): 49 | """Read size bytes""" 50 | b = self.buf[self.pos:self.pos + size] 51 | self.pos += size 52 | return b 53 | 54 | def read_bytes(self): 55 | """Read a bytes object""" 56 | size = self.read_int() 57 | return self.read_bytes_raw(size) 58 | 59 | def is_end(self): 60 | return (self.size == self.pos) 61 | 62 | 63 | class TaggedReader(VariableReader): 64 | """Tagged reader, needed for implementing a WideVine signature reader""" 65 | 66 | def read_tag(self): 67 | """Read a tagged buffer""" 68 | return (self.read_int(), self.read_bytes()) 69 | 70 | def read_all_tags(self, max_tag=3): 71 | tags = {} 72 | while (not self.is_end()): 73 | (tag, bytes) = self.read_tag() 74 | if (tag > max_tag): 75 | raise IndexError("tag out of bound: got {}, max {}".format(tag, max_tag)) 76 | 77 | tags[tag] = bytes 78 | return tags 79 | 80 | 81 | class WideVineSignatureReader(FromFileMixin): 82 | """Parses a widevine .sig signature file.""" 83 | 84 | SIGNER_TAG = 1 85 | SIGNATURE_TAG = 2 86 | ISMAINEXE_TAG = 3 87 | 88 | def __init__(self, buf): 89 | reader = TaggedReader(buf) 90 | self.version = reader.read_int() 91 | if (self.version != 0): 92 | raise Exception("Unsupported signature format version {}".format(self.version)) 93 | self.tags = reader.read_all_tags() 94 | 95 | self.signer = self.tags[self.SIGNER_TAG] 96 | self.signature = self.tags[self.SIGNATURE_TAG] 97 | 98 | extra = self.tags[self.ISMAINEXE_TAG] 99 | if (len(extra) != 1 or (extra[0] > 1)): 100 | raise Exception( 101 | "Unexpected 'ismainexe' field value (not '\\x00' or '\\x01'), please check: {0}".format(extra)) 102 | 103 | self.mainexe = bool(extra[0]) 104 | 105 | @classmethod 106 | def get_tags(cls, filename): 107 | """Return a dictionary of each tag in the signature file""" 108 | return cls.from_file(filename).tags 109 | -------------------------------------------------------------------------------- /data/pywidevine/L3/decrypt/wvdecryptcustom.py: -------------------------------------------------------------------------------- 1 | # uncompyle6 version 3.7.3 2 | # Python bytecode 3.6 (3379) 3 | # Decompiled from: Python 3.7.8 (tags/v3.7.8:4b47a5b6ba, Jun 28 2020, 08:53:46) [MSC v.1916 64 bit (AMD64)] 4 | # Embedded file name: pywidevine\decrypt\wvdecryptcustom.py 5 | import base64 6 | from data.pywidevine.L3.cdm import cdm, deviceconfig 7 | 8 | 9 | class WvDecrypt(object): 10 | WV_SYSTEM_ID = [ 11 | 237, 239, 139, 169, 121, 214, 74, 206, 163, 200, 39, 220, 213, 29, 33, 237] 12 | 13 | def __init__(self, init_data_b64, cert_data_b64, device): 14 | self.init_data_b64 = init_data_b64 15 | self.cert_data_b64 = cert_data_b64 16 | self.device = device 17 | self.cdm = cdm.Cdm() 18 | 19 | def check_pssh(pssh_b64): 20 | pssh = base64.b64decode(pssh_b64) 21 | if not pssh[12:28] == bytes(self.WV_SYSTEM_ID): 22 | new_pssh = bytearray([0, 0, 0]) 23 | new_pssh.append(32 + len(pssh)) 24 | new_pssh[4:] = bytearray(b'pssh') 25 | new_pssh[8:] = [0, 0, 0, 0] 26 | new_pssh[13:] = self.WV_SYSTEM_ID 27 | new_pssh[29:] = [0, 0, 0, 0] 28 | new_pssh[31] = len(pssh) 29 | new_pssh[32:] = pssh 30 | return base64.b64encode(new_pssh) 31 | else: 32 | return pssh_b64 33 | 34 | self.session = self.cdm.open_session(check_pssh(self.init_data_b64), deviceconfig.DeviceConfig(self.device)) 35 | if self.cert_data_b64: 36 | self.cdm.set_service_certificate(self.session, self.cert_data_b64) 37 | 38 | def log_message(self, msg): 39 | return '{}'.format(msg) 40 | 41 | def start_process(self): 42 | keys = [] 43 | try: 44 | for key in self.cdm.get_keys(self.session): 45 | if key.type == 'CONTENT': 46 | keys.append(self.log_message('{}:{}'.format(key.kid.hex(), key.key.hex()))) 47 | except Exception: 48 | return None 49 | return keys if keys else None 50 | 51 | def get_challenge(self): 52 | return self.cdm.get_license_request(self.session) 53 | 54 | def update_license(self, license_b64): 55 | return self.cdm.provide_license(self.session, license_b64) -------------------------------------------------------------------------------- /data/pywidevine/L3/getPSSH.py: -------------------------------------------------------------------------------- 1 | import requests, xmltodict, json 2 | 3 | 4 | def get_pssh(mpd_url): 5 | r = requests.get(url=mpd_url) 6 | r.raise_for_status() 7 | xml = xmltodict.parse(r.text) 8 | mpd = json.loads(json.dumps(xml)) 9 | tracks = mpd['MPD']['Period']['AdaptationSet'] 10 | for video_tracks in tracks: 11 | if video_tracks['@mimeType'] == 'video/mp4': 12 | for t in video_tracks["ContentProtection"]: 13 | if t['@schemeIdUri'].lower() == "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed": 14 | pssh = t["cenc:pssh"] 15 | return pssh 16 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevLARLEY/mpdl/f544a691eba49f580dae2e64e8d1bb5d3014495c/icon.png -------------------------------------------------------------------------------- /initext.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import binascii 3 | import re 4 | from itertools import zip_longest 5 | 6 | 7 | # Extract pssh/key ids from init.mp4 8 | 9 | def swap_endian(x): 10 | return ''.join(str(x) for x in list(reversed([b + j for b, j in zip_longest(x[::2], x[1::2], fillvalue='0')]))) 11 | 12 | 13 | def hex_to_ascii(x): 14 | return bytes.fromhex(x.replace("00", "")).decode("utf-8") 15 | 16 | 17 | def ext(file) -> list: 18 | with open(file, "rb") as f: 19 | c = f.read() 20 | heX = binascii.hexlify(c).decode("utf-8") 21 | pssh = [m.start() for m in re.finditer("70737368", heX)] 22 | res = [] 23 | for r in pssh: 24 | sysid = heX[r + 16:r + 48] 25 | if sysid == "edef8ba979d64acea3c827dcd51d21ed": 26 | s = int(heX[r - 8:r], 16) * 2 27 | if s < 100: 28 | continue 29 | f = heX[r + 56:r + 56 + s - 64] 30 | p = 0 31 | while p < len(f): 32 | h = f[p:p + 2] 33 | if h == "08": 34 | p += 4 35 | elif h == "48": 36 | p += 12 37 | else: 38 | s2 = int(f[p + 2:p + 4], 16) * 2 39 | if h == "12": 40 | res.append(base64.b64encode(bytes.fromhex(heX[r - 8:r - 8 + s])).decode()) 41 | break 42 | p += s2 + 4 43 | return res 44 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ffmpeg-python==0.2.0 2 | requests==2.31.0 3 | yt-dlp==2023.10.13 4 | PyQt5==5.15.10 5 | selenium-wire==5.1.0 6 | urllib3==1.26.18 7 | xmltodict==0.13.0 8 | packaging==23.2 9 | setuptools==68.2.2 10 | google-api-python-client==2.118.0 11 | -------------------------------------------------------------------------------- /util.py: -------------------------------------------------------------------------------- 1 | import re 2 | from PyQt5 import QtGui 3 | 4 | 5 | def getFont(size, italic, bold): 6 | font = QtGui.QFont() 7 | font.setFamily("Cascadia Code") 8 | font.setPointSize(size) 9 | font.setBold(bold) 10 | font.setItalic(italic) 11 | return font 12 | 13 | 14 | def setAddons(lw) -> str: 15 | items = [lw.item(x).text() for x in range(lw.count())] 16 | return "|".join(items) 17 | 18 | 19 | def getAddons(c) -> list: 20 | if len(c["BROWSER"]["addons"]) <= 1: 21 | return [] 22 | else: 23 | return c["BROWSER"]["addons"].split("|") 24 | 25 | 26 | def getIcon(): 27 | icon = QtGui.QIcon() 28 | icon.addPixmap(QtGui.QPixmap("icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) 29 | icon.addPixmap(QtGui.QPixmap("icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.On) 30 | icon.addPixmap(QtGui.QPixmap("icon.png"), QtGui.QIcon.Disabled, QtGui.QIcon.Off) 31 | icon.addPixmap(QtGui.QPixmap("icon.png"), QtGui.QIcon.Disabled, QtGui.QIcon.On) 32 | icon.addPixmap(QtGui.QPixmap("icon.png"), QtGui.QIcon.Active, QtGui.QIcon.Off) 33 | icon.addPixmap(QtGui.QPixmap("icon.png"), QtGui.QIcon.Active, QtGui.QIcon.On) 34 | icon.addPixmap(QtGui.QPixmap("icon.png"), QtGui.QIcon.Selected, QtGui.QIcon.Off) 35 | icon.addPixmap(QtGui.QPixmap("icon.png"), QtGui.QIcon.Selected, QtGui.QIcon.On) 36 | return icon 37 | 38 | 39 | curlexclude = ["te", "accept-encoding"] 40 | 41 | 42 | def formatCURL(header): 43 | st = header.split("\n") 44 | r = '' 45 | for s in st: 46 | if s == '' or s == ' ' or s.split(": ")[0].lower() in curlexclude: 47 | continue 48 | r += " '" 49 | r += s.replace(": ", "': '") 50 | r += "',\n" 51 | return r.rsplit(",", 1)[0] 52 | 53 | 54 | def clearHeaders(): 55 | file = open("headers.py", 'w') 56 | file.write("headers = {}\n") 57 | file.close() 58 | 59 | 60 | def getPSSH(file): 61 | f = open(file, "r").read() 62 | res = re.findall('.*<.*/cenc:pssh>', f) 63 | return str(min([x[11:-12] for x in res], key=len)).split(">")[-1].split("<")[-1] if res else None 64 | --------------------------------------------------------------------------------