Showing preview only (1,449K chars total). Download the full file or copy to clipboard to get everything.
Repository: apprenticeharper/DeDRM_tools
Branch: master
Commit: 776f146ca00d
Files: 109
Total size: 1.4 MB
Directory structure:
gitextract_cqmmgc0y/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ └── QUESTION.md
│ └── workflows/
│ ├── Format.yaml
│ └── Lint.yaml
├── .gitignore
├── CALIBRE_CLI_INSTRUCTIONS.md
├── DeDRM_plugin/
│ ├── DeDRM_Adobe Digital Editions Key_Help.htm
│ ├── DeDRM_Barnes and Noble Key_Help.htm
│ ├── DeDRM_EInk Kindle Serial Number_Help.htm
│ ├── DeDRM_Help.htm
│ ├── DeDRM_Kindle for Android Key_Help.htm
│ ├── DeDRM_Kindle for Mac and PC Key_Help.htm
│ ├── DeDRM_Mobipocket PID_Help.htm
│ ├── DeDRM_eReader Key_Help.htm
│ ├── __init__.py
│ ├── activitybar.py
│ ├── adobekey.py
│ ├── aescbc.py
│ ├── alfcrypto.py
│ ├── androidkindlekey.py
│ ├── argv_utils.py
│ ├── askfolder_ed.py
│ ├── config.py
│ ├── convert2xml.py
│ ├── epubtest.py
│ ├── erdr2pml.py
│ ├── flatxml2html.py
│ ├── flatxml2svg.py
│ ├── genbook.py
│ ├── ignobleepub.py
│ ├── ignoblekey.py
│ ├── ignoblekeyfetch.py
│ ├── ignoblekeygen.py
│ ├── ignoblepdf.py
│ ├── ineptepub.py
│ ├── ineptpdf.py
│ ├── ion.py
│ ├── k4mobidedrm.py
│ ├── kfxdedrm.py
│ ├── kgenpids.py
│ ├── kindlekey.py
│ ├── kindlepid.py
│ ├── mobidedrm.py
│ ├── openssl_des.py
│ ├── plugin-import-name-dedrm.txt
│ ├── prefs.py
│ ├── pycrypto_des.py
│ ├── python_des.py
│ ├── scriptinterface.py
│ ├── scrolltextwidget.py
│ ├── simpleprefs.py
│ ├── stylexml2css.py
│ ├── subasyncio.py
│ ├── topazextract.py
│ ├── utilities.py
│ ├── wineutils.py
│ ├── zipfilerugged.py
│ └── zipfix.py
├── DeDRM_plugin_ReadMe.txt
├── FAQs.md
├── Obok_plugin/
│ ├── __init__.py
│ ├── action.py
│ ├── common_utils.py
│ ├── config.py
│ ├── dialogs.py
│ ├── obok/
│ │ ├── __init__.py
│ │ ├── legacy_obok.py
│ │ └── obok.py
│ ├── obok_dedrm_Help.htm
│ ├── plugin-import-name-obok_dedrm.txt
│ ├── translations/
│ │ ├── ar.mo
│ │ ├── ar.po
│ │ ├── de.mo
│ │ ├── de.po
│ │ ├── default.po
│ │ ├── es.mo
│ │ ├── es.po
│ │ ├── nl.mo
│ │ ├── nl.po
│ │ ├── pt.mo
│ │ ├── pt.po
│ │ ├── sv.mo
│ │ └── sv.po
│ └── utilities.py
├── Other_Tools/
│ ├── B_and_N_Download_Helper/
│ │ ├── BN-Dload.user.js
│ │ └── BN-Dload.user_ReadMe.txt
│ ├── DRM_Key_Scripts/
│ │ ├── Adobe_Digital_Editions/
│ │ │ └── adobekey.pyw
│ │ ├── Barnes_and_Noble_ePubs/
│ │ │ ├── ignoblekey.pyw
│ │ │ ├── ignoblekeyfetch.pyw
│ │ │ └── ignoblekeygen.pyw
│ │ ├── Kindle_for_Android/
│ │ │ └── androidkindlekey.pyw
│ │ ├── Kindle_for_Mac_and_PC/
│ │ │ └── kindlekey.pyw
│ │ └── Kindle_for_iOS/
│ │ └── kindleiospidgen.pyw
│ ├── Kindle_for_Android_Patches/
│ │ ├── A_Patching_Experience.txt
│ │ ├── kindle_version_3.0.1.70/
│ │ │ ├── ReadMe_K4Android.txt
│ │ │ └── kindle3.0.1.70.patch
│ │ ├── kindle_version_3.7.0.108/
│ │ │ ├── ReadMe_K4Android.txt
│ │ │ └── kindle3.7.0.108.patch
│ │ ├── kindle_version_4.0.2.1/
│ │ │ └── kindle4.0.2.1.patch
│ │ └── kindle_version_4.8.1.10/
│ │ ├── Notes on the Patch.txt
│ │ └── kindle4.8.1.10.patch
│ ├── Kobo/
│ │ └── obok.py
│ ├── Rocket_ebooks/
│ │ └── rebhack_ReadMe.txt
│ ├── Scuolabook_DRM/
│ │ └── Scuolabook_ReadMe.txt
│ └── Tetrachroma_FileOpen_ineptpdf/
│ ├── ineptpdf_8.4.51.pyw
│ └── ineptpdf_8.4.51_ReadMe.txt
├── README.md
├── ReadMe_Overview.txt
├── make_release.py
└── obok_plugin_ReadMe.txt
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/QUESTION.md
================================================
---
name: Question
about: Questions for DeDRM Project
title: "[QUESTION] Title"
labels: Question
---
## CheckList
<!-- Check with `[x]` -->
- [ ] `The Title` and The `Log Title` are setted correctly.
- [ ] Clarified about `my environment`.
- [ ] Code block is used for `the log`.
<!-- If you don't know the version, please specify 'Unknown'. -->
<!-- In case of markdown To use the code block, enclose it in ```. -->
<!-- If you don't need Log, please delete the log section. -->
---
## Title
<!-- content -->
## My Environment
### Calibre: `Version`
### Kindle: `Version`
### DeDRM: `Version`
## Log
<details><summary>Log Title</summary>
```log
PUT YOUR LOG
```
</details>
================================================
FILE: .github/workflows/Format.yaml
================================================
name: Python code format
on:
push:
branches: master
jobs:
Format:
if: "contains(github.event.head_commit.message, '!format')"
runs-on: ubuntu-20.04
strategy:
fail-fast: false
steps:
- uses: actions/checkout@main
- name: Set up Python
uses: actions/setup-python@main
with:
python-version: 3.x
- uses: actions/cache@main
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-format
restore-keys: |
${{ runner.os }}-pip-format
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install autopep8 pycodestyle
- name: Format by autopep8 then Push
env:
GIT_EMAIL: github-actions[bot]@users.noreply.github.com
GIT_ACTOR: github-actions[bot]
run: |
export HASH_SHORT=$(git rev-parse --short HEAD)
git checkout -b format--${HASH_SHORT}
git config --global user.email $GIT_EMAIL
git config --global user.name $GIT_ACTOR
python -m autopep8 --in-place --aggressive --aggressive --experimental -r ./
git add -A
git commit -m 'Format by autopep8' -m From: -m $(git rev-parse HEAD)
git push --set-upstream origin format--${HASH_SHORT}
================================================
FILE: .github/workflows/Lint.yaml
================================================
name: Python code review
on: [push, pull_request]
jobs:
Test:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
steps:
- uses: actions/checkout@main
- name: Set up Python
uses: actions/setup-python@main
with:
python-version: 3.x
- uses: actions/cache@main
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-lint
restore-keys: |
${{ runner.os }}-pip-lint
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8
- name: Lint with flake8
run: |
python -m flake8 . --builtins=_,I --ignore=E501 --count --benchmark --show-source --statistics
================================================
FILE: .gitignore
================================================
.DS_Store
================================================
FILE: CALIBRE_CLI_INSTRUCTIONS.md
================================================
# Using the DeDRM plugin with the Calibre command line interface
If you prefer the Calibre CLI instead of the GUI, follow this guide to
install and use the DeDRM plugin.
This guide assumes you are on Linux, but it may very well work on other
platforms.
## Step-by-step Tutorial
#### Install Calibre
- Follow [Calibre's installation instructions](https://calibre-ebook.com/download_linux)
#### Install plugins
- Download the DeDRM `.zip` archive from DeDRM_tools'
[latest release](https://github.com/apprenticeharper/DeDRM_tools/releases/latest).
Then unzip it.
- Add the DeDRM plugin to Calibre:
```
cd *the unzipped DeDRM_tools folder*
calibre-customize --add DeDRM_calibre_plugin/DeDRM_plugin.zip
```
- Add the Obok plugin:
```
calibre-customize --add Obok_calibre_plugin/obok_plugin.zip
```
#### Enter your keys
- Figure out what format DeDRM wants your key in by looking in
[the code that handles that](DeDRM_plugin/prefs.py).
- For Kindle eInk devices, DeDRM expects you to put a list of serial
numbers in the `serials` field: `"serials": ["012345689abcdef"]` or
`"serials": ["1111111111111111", "2222222222222222"]`.
- Now add your keys to `$CALIBRE_CONFIG_DIRECTORY/plugins/dedrm.json`.
#### Import your books
- Make a library folder
```
mkdir library
```
- Add your book(s) with this command:
```
calibredb add /path/to/book.format --with-library=library
```
The DRM should be removed from your book, which you can find in the `library`
folder.
================================================
FILE: DeDRM_plugin/DeDRM_Adobe Digital Editions Key_Help.htm
================================================
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Managing Adobe Digital Editions Keys</title>
<style type="text/css">
span.version {font-size: 50%}
span.bold {font-weight: bold}
h3 {margin-bottom: 0}
p {margin-top: 0}
li {margin-top: 0.5em}
</style>
</head>
<body>
<h1>Managing Adobe Digital Editions Keys</h1>
<p>If you have upgraded from an earlier version of the plugin, any existing Adobe Digital Editions keys will have been automatically imported, so you might not need to do any more configuration. In addition, on Windows and Mac, the default Adobe Digital Editions key is added the first time the plugin is run. Continue reading for key generation and management instructions.</p>
<h3>Creating New Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog prompting you to enter a key name for the default Adobe Digital Editions key. </p>
<ul>
<li><span class="bold">Unique Key Name:</span> this is a unique name you choose to help you identify the key. This name will show in the list of configured keys.</li>
</ul>
<p>Click the OK button to create and store the Adobe Digital Editions key for the current installation of Adobe Digital Editions. Or Cancel if you don’t want to create the key.</p>
<p>New keys are checked against the current list of keys before being added, and duplicates are discarded.</p>
<h3>Deleting Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a red "X". Clicking this button will delete the highlighted key in the list. You will be prompted once to be sure that’s what you truly mean to do. Once gone, it’s permanently gone.</p>
<h3>Renaming Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a sheet of paper. Clicking this button will prompt you to enter a new name for the highlighted key in the list. Enter the new name for the encryption key and click the OK button to use the new name, or Cancel to revert to the old name..</p>
<h3>Exporting Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a computer’s hard-drive. Use this button to export the highlighted key to a file (with a ‘.der’ file name extension). Used for backup purposes or to migrate key data to other computers/calibre installations. The dialog will prompt you for a place to save the file.</p>
<h3>Linux Users: WINEPREFIX</h3>
<p>Under the list of keys, Linux users will see a text field labeled "WINEPREFIX". If you are use Adobe Digital Editions under Wine, and your wine installation containing Adobe Digital Editions isn't the default Wine installation, you may enter the full path to the correct Wine installation here. Leave blank if you are unsure.</p>
<h3>Importing Existing Keyfiles:</h3>
<p>At the bottom-left of the plugin’s customization dialog, you will see a button labeled "Import Existing Keyfiles". Use this button to import existing ‘.der’ key files. Key files might come from being exported from this or older plugins, or may have been generated using the adobekey.pyw script running under Wine on Linux systems.</p>
<p>Once done creating/deleting/renaming/importing decryption keys, click Close to exit the customization dialogue. Your changes will only be saved permanently when you click OK in the main configuration dialog.</p>
</body>
</html>
================================================
FILE: DeDRM_plugin/DeDRM_Barnes and Noble Key_Help.htm
================================================
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Managing Barnes and Noble Keys</title>
<style type="text/css">
span.version {font-size: 50%}
span.bold {font-weight: bold}
h3 {margin-bottom: 0}
p {margin-top: 0}
li {margin-top: 0.5em}
</style>
</head>
<body>
<h1>Managing Barnes and Noble Keys</h1>
<p>If you have upgraded from an earlier version of the plugin, any existing Barnes and Noble keys will have been automatically imported, so you might not need to do any more configuration. Continue reading for key generation and management instructions.</p>
<h3>Changes at Barnes & Noble</h3>
<p>In mid-2014, Barnes & Noble changed the way they generated encryption keys. Instead of deriving the key from the user's name and credit card number, they started generating a random key themselves, sending that key through to devices when they connected to the Barnes & Noble servers. This means that most users will now find that no combination of their name and CC# will work in decrypting their recently downloaded ebooks.</p>
<p>Someone commenting at Apprentice Alf's blog detailed a way to retrieve a new account key using the account's email address and password. This method has now been incorporated into the plugin.
<h3>Creating New Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog for entering the necessary data to generate a new key.</p>
<ul>
<li><span class="bold">Unique Key Name:</span> this is a unique name you choose to help you identify the key. This name will show in the list of configured keys. Choose something that will help you remember the data (account email address) it was created with.</li>
<li><span class="bold">B&N/nook account email address:</span> This is the default email address for your Barnes and Noble/nook account. This email will not be stored anywhere on your computer or in calibre. It will only be used to fetch the account key that from the B&N server, and it is that key that will be stored in the preferences.</li>
<li><span class="bold">B&N/nook account password:</span> this is the password for your Barnes and Noble/nook account. As with the email address, this will not be stored anywhere on your computer or in calibre. It will only be used to fetch the key from the B&N server.</li>
</ul>
<p>Click the OK button to create and store the generated key. Or Cancel if you don’t want to create a key.</p>
<p>New keys are checked against the current list of keys before being added, and duplicates are discarded.</p>
<h3>Deleting Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a red "X". Clicking this button will delete the highlighted key in the list. You will be prompted once to be sure that’s what you truly mean to do. Once gone, it’s permanently gone.</p>
<h3>Renaming Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a sheet of paper. Clicking this button will prompt you to enter a new name for the highlighted key in the list. Enter the new name for the encryption key and click the OK button to use the new name, or Cancel to revert to the old name..</p>
<h3>Exporting Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a computer’s hard-drive. Use this button to export the highlighted key to a file (with a ‘.b64’ file name extension). Used for backup purposes or to migrate key data to other computers/calibre installations. The dialog will prompt you for a place to save the file.</p>
<h3>Importing Existing Keyfiles:</h3>
<p>At the bottom-left of the plugin’s customization dialog, you will see a button labeled "Import Existing Keyfiles". Use this button to import existing ‘.b64’ key files. Key files might come from being exported from this or older plugins, or may have been generated using the original i♥cabbages script, or you may have made it by following the instructions above.</p>
<p>Once done creating/deleting/renaming/importing decryption keys, click Close to exit the customization dialogue. Your changes will only be saved permanently when you click OK in the main configuration dialog.</p>
<h3>NOOK Study</h3>
<p>Books downloaded through NOOK Study may or may not use the key found using the above method. If a book is not decrypted successfully with any of the keys, the plugin will attempt to recover keys from the NOOK Study log file and use them.</p>
</body>
</html>
================================================
FILE: DeDRM_plugin/DeDRM_EInk Kindle Serial Number_Help.htm
================================================
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Managing eInk Kindle serial numbers</title>
<style type="text/css">
span.version {font-size: 50%}
span.bold {font-weight: bold}
h3 {margin-bottom: 0}
p {margin-top: 0}
li {margin-top: 0.5em}
</style>
</head>
<body>
<h1>Managing eInk Kindle serial numbers</h1>
<p>If you have upgraded from an earlier version of the plugin, any existing eInk Kindle serial numbers will have been automatically imported, so you might not need to do any more configuration.</p>
<p>Please note that Kindle serial numbers are only valid keys for eInk Kindles like the Kindle Touch and PaperWhite. The Kindle Fire and Fire HD do not use their serial number for DRM and it is useless to enter those serial numbers.</p>
<h3>Creating New Kindle serial numbers:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog for entering a new Kindle serial number.</p>
<ul>
<li><span class="bold">Eink Kindle Serial Number:</span> this is the unique serial number of your device. It usually starts with a ‘B’ or a ‘9’ and is sixteen characters long. For a reference of where to find serial numbers and their ranges, please refer to this <a href="http://wiki.mobileread.com/wiki/Kindle_serial_numbers">mobileread wiki page.</a></li>
</ul>
<p>Click the OK button to save the serial number. Or Cancel if you didn’t want to enter a serial number.</p>
<h3>Deleting Kindle serial numbers:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a red "X". Clicking this button will delete the highlighted Kindle serial number from the list. You will be prompted once to be sure that’s what you truly mean to do. Once gone, it’s permanently gone.</p>
<p>Once done creating/deleting serial numbers, click Close to exit the customization dialogue. Your changes will only be saved permanently when you click OK in the main configuration dialog.</p>
</body>
</html>
================================================
FILE: DeDRM_plugin/DeDRM_Help.htm
================================================
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>DeDRM Plugin Configuration</title>
<style type="text/css">
span.version {font-size: 50%}
span.bold {font-weight: bold}
h3 {margin-bottom: 0}
p {margin-top: 0}
</style>
</head>
<body>
<h1>DeDRM Plugin <span class="version">(v6.7.0)</span></h1>
<p>This plugin removes DRM from ebooks when they are imported into calibre. If you already have DRMed ebooks in your calibre library, you will need to remove them and import them again.</p>
<h3>Installation</h3>
<p>You have obviously managed to install the plugin, as otherwise you wouldn’t be reading this help file. However, you should also delete any older DRM removal plugins, as this DeDRM plugin replaces the five older plugins: Kindle and Mobipocket DeDRM (K4MobiDeDRM), Ignoble Epub DeDRM (ignobleepub), Inept Epub DeDRM (ineptepub), Inept PDF DeDRM (ineptepub) and eReader PDB 2 PML (eReaderPDB2PML).</p>
<h3>Configuration</h3>
<p>On Windows and Mac, the keys for ebooks downloaded for Kindle for Mac/PC and Adobe Digital Editions are automatically generated. If all your DRMed ebooks can be opened and read in Kindle for Mac/PC and/or Adobe Digital Editions on the same computer on which you are running calibre, you do not need to do any configuration of this plugin. On Linux, keys for Kindle for PC and Adobe Digital Editions need to be generated separately (see the Linux section below)</p>
<p>If you have other DRMed ebooks, you will need to enter extra configuration information. The buttons in this dialog will open individual configuration dialogs that will allow you to enter the needed information, depending on the type and source of your DRMed eBooks. Additional help on the information required is available in each of the the dialogs.</p>
<p>If you have used previous versions of the various DeDRM plugins on this machine, you may find that some of the configuration dialogs already contain the information you entered through those previous plugins.</p>
<p>When you have finished entering your configuration information, you <em>must</em> click the OK button to save it. If you click the Cancel button, all your changes in all the configuration dialogs will be lost.</p>
<h3>Troubleshooting:</h3>
<p >If you find that it’s not working for you , you can save a lot of time by trying to add the ebook to Calibre in debug mode. This will print out a lot of helpful info that can be copied into any online help requests.</p>
<p>Open a command prompt (terminal window) and type "calibre-debug -g" (without the quotes). Calibre will launch, and you can can add the problem ebook the usual way. The debug info will be output to the original command prompt (terminal window). Copy the resulting output and paste it into the comment you make at my blog.</p>
<p><span class="bold">Note:</span> The Mac version of Calibre doesn’t install the command line tools by default. If you go to the ‘Preferences’ page and click on the miscellaneous button, you’ll find the option to install the command line tools.</p>
<h3>Credits:</h3>
<ul>
<li>The Dark Reverser for the Mobipocket and eReader scripts</li>
<li>i♥cabbages for the Adobe Digital Editions scripts</li>
<li>Skindle aka Bart Simpson for the Amazon Kindle for PC script</li>
<li>CMBDTC for Amazon Topaz DRM removal script</li>
<li>some_updates, clarknova and Bart Simpson for Amazon Topaz conversion scripts</li>
<li>DiapDealer for the first calibre plugin versions of the tools</li>
<li>some_updates, DiapDealer, Apprentice Alf and mdlnx for Amazon Kindle/Mobipocket tools</li>
<li>some_updates for the DeDRM all-in-one Python tool</li>
<li>Apprentice Alf for the DeDRM all-in-one AppleScript tool</li>
<li>Apprentice Alf for the DeDRM all-in-one calibre plugin</li>
<li>And probably many more.</li>
</ul>
<h3> For additional help read the <a href="https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md">FAQs</a> at <a href="https://github.com/apprenticeharper/DeDRM_tools/">Apprentice Harpers’s GitHub repository</a>. You can ask questions in the comments section of the <a href="http://apprenticealf.wordpress.com/2012/09/10/drm-removal-tools-for-ebooks/">first post</a> at <a href="http://wordpress.com/apprenticealf/">Apprentice Alf's blog</a> or <a href="https://github.com/apprenticeharper/DeDRM_tools/issues">raise an issue</a>. </h3>
<h2>Linux Systems Only</h2>
<h3>Generating decryption keys for Adobe Digital Editions and Kindle for PC</h3>
<p>If you install Kindle for PC and/or Adobe Digital Editions in Wine, you will be able to download DRMed ebooks to them under Wine. To be able to remove the DRM, you will need to generate key files and add them in the plugin's customisation dialogs.</p>
<p>To generate the key files you will need to install Python and PyCrypto under the same Wine setup as your Kindle for PC and/or Adobe Digital Editions installations. (Kindle for PC, Python and Pycrypto installation instructions in the ReadMe.)</p>
<p>Once everything's installed under Wine, you'll need to run the adobekey.pyw script (for Adobe Digital Editions) and kindlekey.pyw (For Kindle for PC) using the python installation in your Wine system. The scripts can be found in Other_Tools/Key_Retrieval_Scripts.</p>
<p>Each script will create a key file in the same folder as the script. Copy the key files to your Linux system and then load the key files using the Adobe Digital Editions ebooks dialog and the Kindle for Mac/PC ebooks dialog.</p>
</body>
</html>
================================================
FILE: DeDRM_plugin/DeDRM_Kindle for Android Key_Help.htm
================================================
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Managing Kindle for Android Keys</title>
<style type="text/css">
span.version {font-size: 50%}
span.bold {font-weight: bold}
h3 {margin-bottom: 0}
p {margin-top: 0}
li {margin-top: 0.5em}
</style>
</head>
<body>
<h1>Managing Kindle for Android Keys</h1>
<p>Amazon's Kindle for Android application uses an internal key equivalent to an eInk Kindle's serial number. Extracting that key is a little tricky, but worth it, as it then allows the DRM to be removed from any Kindle ebooks that have been downloaded to that Android device.</p>
<p>Please note that it is not currently known whether the same applies to the Kindle application on the Kindle Fire and Fire HD.</p>
<h3>Getting the Kindle for Android backup file</h3>
<p>Obtain and install adb (Android Debug Bridge) on your computer. Details of how to do this are beyond the scope of this help file, but there are plenty of on-line guides.</p>
<p>Enable developer mode on your Android device. Again, look for an on-line guide for your device.</p>
<p>Once you have adb installed and your device in developer mode, connect your device to your computer with a USB cable and then open up a command line (Terminal on Mac OS X and cmd.exe on Windows) and enter "adb backup com.amazon.kindle" (without the quotation marks!) and press return. A file "backup.ab" should be created in your home directory.
<h3>Adding a Kindle for Android Key</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog with two main controls.
<ul>
<li><span class="bold">Choose backup file:</span> click this button and you will be prompted to find the backup.ab file you created earlier. Once selected the file will be processed to extract the decryption key, and if successful the file name will be displayed to the right of the button.</li>
<li><span class="bold">Unique Key Name:</span> this is a unique name you choose to help you identify the key. This name will show in the list of Kindle for Android keys. Enter a name that will help you remember which device this key came from.</li>
</ul>
<p>Click the OK button to store the Kindle for Android key for the current list of Kindle for Android keys. Or click Cancel if you don’t want to store the key.</p>
<p>New keys are checked against the current list of keys before being added, and duplicates are discarded.</p>
<h3>Deleting Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a red "X". Clicking this button will delete the highlighted key in the list. You will be prompted once to be sure that’s what you truly mean to do. Once gone, it’s permanently gone.</p>
<h3>Renaming Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a sheet of paper. Clicking this button will prompt you to enter a new name for the highlighted key in the list. Enter the new name for the key and click the OK button to use the new name, or Cancel to revert to the old name.</p>
<h3>Exporting Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a computer’s hard-drive. Use this button to export the highlighted key to a file (with a ‘.k4a' file name extension). Used for backup purposes or to migrate key data to other computers/calibre installations. The dialog will prompt you for a place to save the file.</p>
<h3>Importing Existing Keyfiles:</h3>
<p>At the bottom-left of the plugin’s customization dialog, you will see a button labeled "Import Existing Keyfiles". Use this button to import any ‘.k4a’ file you obtained by using the androidkindlekey.py script manually, or by exporting from another copy of calibre.</p>
</body>
</html>
================================================
FILE: DeDRM_plugin/DeDRM_Kindle for Mac and PC Key_Help.htm
================================================
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Managing Kindle for Mac/PC Keys</title>
<style type="text/css">
span.version {font-size: 50%}
span.bold {font-weight: bold}
h3 {margin-bottom: 0}
p {margin-top: 0}
li {margin-top: 0.5em}
</style>
</head>
<body>
<h1>Managing Kindle for Mac/PC Keys</h1>
<p>If you have upgraded from an earlier version of the plugin, any existing Kindle for Mac/PC keys will have been automatically imported, so you might not need to do any more configuration. In addition, on Windows and Mac, the default Kindle for Mac/PC key is added the first time the plugin is run. Continue reading for key generation and management instructions.</p>
<h3>Creating New Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog prompting you to enter a key name for the default Kindle for Mac/PC key. </p>
<ul>
<li><span class="bold">Unique Key Name:</span> this is a unique name you choose to help you identify the key. This name will show in the list of configured keys.</li>
</ul>
<p>Click the OK button to create and store the Kindle for Mac/PC key for the current installation of Kindle for Mac/PC. Or Cancel if you don’t want to create the key.</p>
<p>New keys are checked against the current list of keys before being added, and duplicates are discarded.</p>
<h3>Deleting Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a red "X". Clicking this button will delete the highlighted key in the list. You will be prompted once to be sure that’s what you truly mean to do. Once gone, it’s permanently gone.</p>
<h3>Renaming Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a sheet of paper. Clicking this button will prompt you to enter a new name for the highlighted key in the list. Enter the new name for the encryption key and click the OK button to use the new name, or Cancel to revert to the old name..</p>
<h3>Exporting Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a computer’s hard-drive. Use this button to export the highlighted key to a file (with a ‘.der’ file name extension). Used for backup purposes or to migrate key data to other computers/calibre installations. The dialog will prompt you for a place to save the file.</p>
<h3>Linux Users: WINEPREFIX</h3>
<p>Under the list of keys, Linux users will see a text field labeled "WINEPREFIX". If you are using the Kindle for PC under Wine, and your wine installation containing Kindle for PC isn't the default Wine installation, you may enter the full path to the correct Wine installation here. Leave blank if you are unsure.</p>
<h3>Importing Existing Keyfiles:</h3>
<p>At the bottom-left of the plugin’s customization dialog, you will see a button labeled "Import Existing Keyfiles". Use this button to import existing ‘.k4i’ key files. Key files might come from being exported from this plugin, or may have been generated using the kindlekey.pyw script running under Wine on Linux systems.</p>
<p>Once done creating/deleting/renaming/importing decryption keys, click Close to exit the customization dialogue. Your changes wil only be saved permanently when you click OK in the main configuration dialog.</p>
</body>
</html>
================================================
FILE: DeDRM_plugin/DeDRM_Mobipocket PID_Help.htm
================================================
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Managing Mobipocket PIDs</title>
<style type="text/css">
span.version {font-size: 50%}
span.bold {font-weight: bold}
h3 {margin-bottom: 0}
p {margin-top: 0}
li {margin-top: 0.5em}
</style>
</head>
<body>
<h1>Managing Mobipocket PIDs</h1>
<p>If you have upgraded from an earlier version of the plugin, any existing Mobipocket PIDs will have been automatically imported, so you might not need to do any more configuration.</p>
<h3>Creating New Mobipocket PIDs:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog for entering a new Mobipocket PID.</p>
<ul>
<li><span class="bold">PID:</span> this is a PID used to decrypt your Mobipocket ebooks. It is eight or ten characters long. Mobipocket PIDs are usualy displayed in the About screen of your Mobipocket device.</li>
</ul>
<p>Click the OK button to save the PID. Or Cancel if you didn’t want to enter a PID.</p>
<h3>Deleting Mobipocket PIDs:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a red "X". Clicking this button will delete the highlighted Mobipocket PID from the list. You will be prompted once to be sure that’s what you truly mean to do. Once gone, it’s permanently gone.</p>
<p>Once done creating/deleting PIDs, click Close to exit the customization dialogue. Your changes wil only be saved permanently when you click OK in the main configuration dialog.</p>
</body>
</html>
================================================
FILE: DeDRM_plugin/DeDRM_eReader Key_Help.htm
================================================
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Managing eReader Keys</title>
<style type="text/css">
span.version {font-size: 50%}
span.bold {font-weight: bold}
h3 {margin-bottom: 0}
p {margin-top: 0}
li {margin-top: 0.5em}
</style>
</head>
<body>
<h1>Managing eReader Keys</h1>
<p>If you have upgraded from an earlier version of the plugin, any existing eReader (Fictionwise ‘.pdb’) keys will have been automatically imported, so you might not need to do any more configuration. Continue reading for key generation and management instructions.</p>
<h3>Creating New Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a green plus sign (+). Clicking this button will open a new dialog for entering the necessary data to generate a new key.</p>
<ul>
<li><span class="bold">Unique Key Name:</span> this is a unique name you choose to help you identify the key. This name will show in the list of configured keys. Choose something that will help you remember the data (name, cc#) it was created with.</li>
<li><span class="bold">Your Name:</span> This is the name used by Fictionwise to generate your encryption key. Since Fictionwise has now closed down, you might not have easy access to this. It was often the name on the Credit Card used at Fictionwise.</li>
<li><span class="bold">Credit Card#:</span> this is the default credit card number that was on file with Fictionwise at the time of download of the ebook to be de-DRMed. Just enter the last 8 digits of the number. As with the name, this number will not be stored anywhere on your computer or in calibre. It will only be used in the creation of the one-way hash/key that’s stored in the preferences.</li>
</ul>
<p>Click the OK button to create and store the generated key. Or Cancel if you don’t want to create a key.</p>
<p>New keys are checked against the current list of keys before being added, and duplicates are discarded.</p>
<h3>Deleting Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a red "X". Clicking this button will delete the highlighted key in the list. You will be prompted once to be sure that’s what you truly mean to do. Once gone, it’s permanently gone.</p>
<h3>Renaming Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a sheet of paper. Clicking this button will promt you to enter a new name for the highlighted key in the list. Enter the new name for the encryption key and click the OK button to use the new name, or Cancel to revert to the old name..</p>
<h3>Exporting Keys:</h3>
<p>On the right-hand side of the plugin’s customization dialog, you will see a button with an icon that looks like a computer’s hard-drive. Use this button to export the highlighted key to a file (with a ‘.b63’ file name extension). Used for backup purposes or to migrate key data to other computers/calibre installations. The dialog will prompt you for a place to save the file.</p>
<h3>Importing Existing Keyfiles:</h3>
<p>At the bottom-left of the plugin’s customization dialog, you will see a button labeled "Import Existing Keyfiles". Use this button to import existing ‘.b63’ key files that have previously been exported.</p>
<p>Once done creating/deleting/renaming/importing decryption keys, click Close to exit the customization dialogue. Your changes wil only be saved permanently when you click OK in the main configuration dialog.</p>
</body>
</html>
================================================
FILE: DeDRM_plugin/__init__.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# __init__.py for DeDRM_plugin
# Copyright © 2008-2020 Apprentice Harper et al.
__license__ = 'GPL v3'
__version__ = '7.2.1'
__docformat__ = 'restructuredtext en'
# Released under the terms of the GNU General Public Licence, version 3
# <http://www.gnu.org/licenses/>
#
# All credit given to i♥cabbages and The Dark Reverser for the original standalone scripts.
# We had the much easier job of converting them to a calibre plugin.
#
# This plugin is meant to decrypt eReader PDBs, Adobe Adept ePubs, Barnes & Noble ePubs,
# Adobe Adept PDFs, Amazon Kindle and Mobipocket files without having
# to install any dependencies... other than having calibre installed, of course.
#
# Configuration:
# Check out the plugin's configuration settings by clicking the "Customize plugin"
# button when you have the "DeDRM" plugin highlighted (under Preferences->
# Plugins->File type plugins). Once you have the configuration dialog open, you'll
# see a Help link on the top right-hand side.
#
# Revision history:
# 6.0.0 - Initial release
# 6.0.1 - Bug Fixes for Windows App, Kindle for Mac and Windows Adobe Digital Editions
# 6.0.2 - Restored call to Wine to get Kindle for PC keys, added for ADE
# 6.0.3 - Fixes for Kindle for Mac and Windows non-ascii user names
# 6.0.4 - Fixes for stand-alone scripts and applications
# and pdb files in plugin and initial conversion of prefs.
# 6.0.5 - Fix a key issue
# 6.0.6 - Fix up an incorrect function call
# 6.0.7 - Error handling for incomplete PDF metadata
# 6.0.8 - Fixes a Wine key issue and topaz support
# 6.0.9 - Ported to work with newer versions of Calibre (moved to Qt5). Still supports older Qt4 versions.
# 6.1.0 - Fixed multiple books import problem and PDF import with no key problem
# 6.2.0 - Support for getting B&N key from nook Study log. Fix for UTF-8 filenames in Adobe ePubs.
# Fix for not copying needed files. Fix for getting default Adobe key for PDFs
# 6.2.1 - Fix for non-ascii Windows user names
# 6.2.2 - Added URL method for B&N/nook books
# 6.3.0 - Added in Kindle for Android serial number solution
# 6.3.1 - Version number bump for clarity
# 6.3.2 - Fixed Kindle for Android help file
# 6.3.3 - Bug fix for Kindle for PC support
# 6.3.4 - Fixes for Kindle for Android, Linux, and Kobo 3.17
# 6.3.5 - Fixes for Linux, and Kobo 3.19 and more logging
# 6.3.6 - Fixes for ADE ePub and PDF introduced in 6.3.5
# 6.4.0 - Updated for new Kindle for PC encryption
# 6.4.1 - Fix for some new tags in Topaz ebooks.
# 6.4.2 - Fix for more new tags in Topaz ebooks and very small Topaz ebooks
# 6.4.3 - Fix for error that only appears when not in debug mode
# Also includes fix for Macs with bonded ethernet ports
# 6.5.0 - Big update to Macintosh app
# Fix for some more 'new' tags in Topaz ebooks.
# Fix an error in wineutils.py
# 6.5.1 - Updated version number, added PDF check for DRM-free documents
# 6.5.2 - Another Topaz fix
# 6.5.3 - Warn about KFX files explicitly
# 6.5.4 - Mac App Fix, improve PDF decryption, handle latest tcl changes in ActivePython
# 6.5.5 - Finally a fix for the Windows non-ASCII user names.
# 6.6.0 - Add kfx and kfx-zip as supported file types (also invoke this plugin if the original
# imported format was azw8 since that may be converted to kfx)
# 6.6.1 - Thanks to wzyboy for a fix for stand-alone tools, and the new folder structure.
# 6.6.2 - revamp of folders to get Mac OS X app working. Updated to 64-bit app. Various fixes.
# 6.6.3 - More cleanup of kindle book names and start of support for .kinf2018
# 6.7.0 - Handle new library in calibre.
# 6.8.0 - Full support for .kinf2018 and new KFX encryption (Kindle for PC/Mac 2.5+)
# 6.8.1 - Kindle key fix for Mac OS X Big Sur
# 7.0.0 - Switched to Python 3 for calibre 5.0. Thanks to all who contributed
# 7.0.1 - More Python 3 changes. Adobe PDF decryption should now work in some cases
# 7.0.2 - More Python 3 changes. Adobe PDF decryption should now work on PC too.
# 7.0.3 - More Python 3 changes. Integer division in ineptpdf.py
# 7.1.0 - Full release for calibre 5.x
# 7.2.0 - Update for latest KFX changes, and Python 3 Obok fixes.
# 7.2.1 - Whitespace!
"""
Decrypt DRMed ebooks.
"""
PLUGIN_NAME = "DeDRM"
PLUGIN_VERSION_TUPLE = tuple([int(x) for x in __version__.split(".")])
PLUGIN_VERSION = ".".join([str(x)for x in PLUGIN_VERSION_TUPLE])
# Include an html helpfile in the plugin's zipfile with the following name.
RESOURCE_NAME = PLUGIN_NAME + '_Help.htm'
import codecs
import sys, os, re
import time
import zipfile
import traceback
from zipfile import ZipFile
class DeDRMError(Exception):
pass
from calibre.customize import FileTypePlugin
from calibre.constants import iswindows, isosx
from calibre.gui2 import is_ok_to_use_qt
from calibre.utils.config import config_dir
# Wrap a stream so that output gets flushed immediately
# and also make sure that any unicode strings get safely
# encoded using "replace" before writing them.
class SafeUnbuffered:
def __init__(self, stream):
self.stream = stream
self.encoding = stream.encoding
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
if isinstance(data,str):
data = data.encode(self.encoding,"replace")
try:
self.stream.buffer.write(data)
self.stream.buffer.flush()
except:
# We can do nothing if a write fails
pass
def __getattr__(self, attr):
return getattr(self.stream, attr)
class DeDRM(FileTypePlugin):
name = PLUGIN_NAME
description = "Removes DRM from Amazon Kindle, Adobe Adept (including Kobo), Barnes & Noble, Mobipocket and eReader ebooks. Credit given to i♥cabbages and The Dark Reverser for the original stand-alone scripts."
supported_platforms = ['linux', 'osx', 'windows']
author = "Apprentice Alf, Aprentice Harper, The Dark Reverser and i♥cabbages"
version = PLUGIN_VERSION_TUPLE
minimum_calibre_version = (5, 0, 0) # Python 3.
file_types = set(['epub','pdf','pdb','prc','mobi','pobi','azw','azw1','azw3','azw4','azw8','tpz','kfx','kfx-zip'])
on_import = True
on_preprocess = True
priority = 600
def initialize(self):
"""
Dynamic modules can't be imported/loaded from a zipfile.
So this routine will extract the appropriate
library for the target OS and copy it to the 'alfcrypto' subdirectory of
calibre's configuration directory. That 'alfcrypto' directory is then
inserted into the syspath (as the very first entry) in the run function
so the CDLL stuff will work in the alfcrypto.py script.
The extraction only happens once per version of the plugin
Also perform upgrade of preferences once per version
"""
try:
self.pluginsdir = os.path.join(config_dir,"plugins")
if not os.path.exists(self.pluginsdir):
os.mkdir(self.pluginsdir)
self.maindir = os.path.join(self.pluginsdir,"DeDRM")
if not os.path.exists(self.maindir):
os.mkdir(self.maindir)
self.helpdir = os.path.join(self.maindir,"help")
if not os.path.exists(self.helpdir):
os.mkdir(self.helpdir)
self.alfdir = os.path.join(self.maindir,"libraryfiles")
if not os.path.exists(self.alfdir):
os.mkdir(self.alfdir)
# only continue if we've never run this version of the plugin before
self.verdir = os.path.join(self.maindir,PLUGIN_VERSION)
if not os.path.exists(self.verdir):
if iswindows:
names = ["alfcrypto.dll","alfcrypto64.dll"]
elif isosx:
names = ["libalfcrypto.dylib"]
else:
names = ["libalfcrypto32.so","libalfcrypto64.so","kindlekey.py","adobekey.py","subasyncio.py"]
lib_dict = self.load_resources(names)
print("{0} v{1}: Copying needed library files from plugin's zip".format(PLUGIN_NAME, PLUGIN_VERSION))
for entry, data in lib_dict.items():
file_path = os.path.join(self.alfdir, entry)
try:
os.remove(file_path)
except:
pass
try:
open(file_path,'wb').write(data)
except:
print("{0} v{1}: Exception when copying needed library files".format(PLUGIN_NAME, PLUGIN_VERSION))
traceback.print_exc()
pass
# convert old preferences, if necessary.
from calibre_plugins.dedrm.prefs import convertprefs
convertprefs()
# mark that this version has been initialized
os.mkdir(self.verdir)
except Exception as e:
traceback.print_exc()
raise
def ePubDecrypt(self,path_to_ebook):
# Create a TemporaryPersistent file to work with.
# Check original epub archive for zip errors.
import calibre_plugins.dedrm.zipfix
inf = self.temporary_file(".epub")
try:
print("{0} v{1}: Verifying zip archive integrity".format(PLUGIN_NAME, PLUGIN_VERSION))
fr = zipfix.fixZip(path_to_ebook, inf.name)
fr.fix()
except Exception as e:
print("{0} v{1}: Error \'{2}\' when checking zip archive".format(PLUGIN_NAME, PLUGIN_VERSION, e.args[0]))
raise Exception(e)
# import the decryption keys
import calibre_plugins.dedrm.prefs as prefs
dedrmprefs = prefs.DeDRM_Prefs()
# import the Barnes & Noble ePub handler
import calibre_plugins.dedrm.ignobleepub as ignobleepub
#check the book
if ignobleepub.ignobleBook(inf.name):
print("{0} v{1}: “{2}” is a secure Barnes & Noble ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
# Attempt to decrypt epub with each encryption key (generated or provided).
for keyname, userkey in dedrmprefs['bandnkeys'].items():
keyname_masked = "".join(("X" if (x.isdigit()) else x) for x in keyname)
print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked))
of = self.temporary_file(".epub")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
result = ignobleepub.decryptBook(userkey, inf.name, of.name)
except:
print("{0} v{1}: Exception when trying to decrypt after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
result = 1
of.close()
if result == 0:
# Decryption was successful.
# Return the modified PersistentTemporary file to calibre.
return of.name
print("{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime))
# perhaps we should see if we can get a key from a log file
print("{0} v{1}: Looking for new NOOK Study Keys after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
# get the default NOOK Study keys
defaultkeys = []
try:
if iswindows or isosx:
from calibre_plugins.dedrm.ignoblekey import nookkeys
defaultkeys = nookkeys()
else: # linux
from .wineutils import WineGetKeys
scriptpath = os.path.join(self.alfdir,"ignoblekey.py")
defaultkeys = WineGetKeys(scriptpath, ".b64",dedrmprefs['adobewineprefix'])
except:
print("{0} v{1}: Exception when getting default NOOK Study Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
newkeys = []
for keyvalue in defaultkeys:
if keyvalue not in dedrmprefs['bandnkeys'].values():
newkeys.append(keyvalue)
if len(newkeys) > 0:
try:
for i,userkey in enumerate(newkeys):
print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
of = self.temporary_file(".epub")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
result = ignobleepub.decryptBook(userkey, inf.name, of.name)
except:
print("{0} v{1}: Exception when trying to decrypt after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
result = 1
of.close()
if result == 0:
# Decryption was a success
# Store the new successful key in the defaults
print("{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
try:
dedrmprefs.addnamedvaluetoprefs('bandnkeys','nook_Study_key',keyvalue)
dedrmprefs.writeprefs()
print("{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
except:
print("{0} v{1}: Exception saving a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
# Return the modified PersistentTemporary file to calibre.
return of.name
print("{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
except Exception as e:
pass
print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
raise DeDRMError("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
# import the Adobe Adept ePub handler
import calibre_plugins.dedrm.ineptepub as ineptepub
if ineptepub.adeptBook(inf.name):
print("{0} v{1}: {2} is a secure Adobe Adept ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
# Attempt to decrypt epub with each encryption key (generated or provided).
for keyname, userkeyhex in dedrmprefs['adeptkeys'].items():
userkey = codecs.decode(userkeyhex, 'hex')
print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
of = self.temporary_file(".epub")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
result = ineptepub.decryptBook(userkey, inf.name, of.name)
except:
print("{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
result = 1
try:
of.close()
except:
print("{0} v{1}: Exception closing temporary file after {2:.1f} seconds. Ignored.".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
if result == 0:
# Decryption was successful.
# Return the modified PersistentTemporary file to calibre.
print("{0} v{1}: Decrypted with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname,time.time()-self.starttime))
return of.name
print("{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname,time.time()-self.starttime))
# perhaps we need to get a new default ADE key
print("{0} v{1}: Looking for new default Adobe Digital Editions Keys after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
# get the default Adobe keys
defaultkeys = []
try:
if iswindows or isosx:
from calibre_plugins.dedrm.adobekey import adeptkeys
defaultkeys = adeptkeys()
else: # linux
from .wineutils import WineGetKeys
scriptpath = os.path.join(self.alfdir,"adobekey.py")
defaultkeys = WineGetKeys(scriptpath, ".der",dedrmprefs['adobewineprefix'])
self.default_key = defaultkeys[0]
except:
print("{0} v{1}: Exception when getting default Adobe Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
self.default_key = ""
newkeys = []
for keyvalue in defaultkeys:
if codecs.encode(keyvalue, 'hex').decode('ascii') not in dedrmprefs['adeptkeys'].values():
newkeys.append(keyvalue)
if len(newkeys) > 0:
try:
for i,userkey in enumerate(newkeys):
print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
of = self.temporary_file(".epub")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
result = ineptepub.decryptBook(userkey, inf.name, of.name)
except:
print("{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
result = 1
of.close()
if result == 0:
# Decryption was a success
# Store the new successful key in the defaults
print("{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
try:
dedrmprefs.addnamedvaluetoprefs('adeptkeys','default_key',codecs.encode(keyvalue, 'hex').decode('ascii'))
dedrmprefs.writeprefs()
print("{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
except:
print("{0} v{1}: Exception when saving a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
print("{0} v{1}: Decrypted with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
# Return the modified PersistentTemporary file to calibre.
return of.name
print("{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
except Exception as e:
print("{0} v{1}: Unexpected Exception trying a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
pass
# Something went wrong with decryption.
print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
raise DeDRMError("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
# Not a Barnes & Noble nor an Adobe Adept
# Import the fixed epub.
print("{0} v{1}: “{2}” is neither an Adobe Adept nor a Barnes & Noble encrypted ePub".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
raise DeDRMError("{0} v{1}: Couldn't decrypt after {2:.1f} seconds. DRM free perhaps?".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
def PDFDecrypt(self,path_to_ebook):
import calibre_plugins.dedrm.prefs as prefs
import calibre_plugins.dedrm.ineptpdf
dedrmprefs = prefs.DeDRM_Prefs()
# Attempt to decrypt epub with each encryption key (generated or provided).
print("{0} v{1}: {2} is a PDF ebook".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
for keyname, userkeyhex in dedrmprefs['adeptkeys'].items():
userkey = codecs.decode(userkeyhex,'hex')
print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
of = self.temporary_file(".pdf")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
result = ineptpdf.decryptBook(userkey, path_to_ebook, of.name)
except:
print("{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
result = 1
of.close()
if result == 0:
# Decryption was successful.
# Return the modified PersistentTemporary file to calibre.
return of.name
print("{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname,time.time()-self.starttime))
# perhaps we need to get a new default ADE key
print("{0} v{1}: Looking for new default Adobe Digital Editions Keys after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
# get the default Adobe keys
defaultkeys = []
try:
if iswindows or isosx:
from calibre_plugins.dedrm.adobekey import adeptkeys
defaultkeys = adeptkeys()
else: # linux
from .wineutils import WineGetKeys
scriptpath = os.path.join(self.alfdir,"adobekey.py")
defaultkeys = WineGetKeys(scriptpath, ".der",dedrmprefs['adobewineprefix'])
self.default_key = defaultkeys[0]
except:
print("{0} v{1}: Exception when getting default Adobe Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
self.default_key = ""
newkeys = []
for keyvalue in defaultkeys:
if codecs.encode(keyvalue,'hex') not in dedrmprefs['adeptkeys'].values():
newkeys.append(keyvalue)
if len(newkeys) > 0:
try:
for i,userkey in enumerate(newkeys):
print("{0} v{1}: Trying a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
of = self.temporary_file(".pdf")
# Give the user key, ebook and TemporaryPersistent file to the decryption function.
try:
result = ineptpdf.decryptBook(userkey, path_to_ebook, of.name)
except:
print("{0} v{1}: Exception when decrypting after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
result = 1
of.close()
if result == 0:
# Decryption was a success
# Store the new successful key in the defaults
print("{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
try:
dedrmprefs.addnamedvaluetoprefs('adeptkeys','default_key',codecs.encode(keyvalue,'hex'))
dedrmprefs.writeprefs()
print("{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
except:
print("{0} v{1}: Exception when saving a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
# Return the modified PersistentTemporary file to calibre.
return of.name
print("{0} v{1}: Failed to decrypt with new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
except Exception as e:
pass
# Something went wrong with decryption.
print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
raise DeDRMError("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
def KindleMobiDecrypt(self,path_to_ebook):
# add the alfcrypto directory to sys.path so alfcrypto.py
# will be able to locate the custom lib(s) for CDLL import.
sys.path.insert(0, self.alfdir)
# Had to move this import here so the custom libs can be
# extracted to the appropriate places beforehand these routines
# look for them.
import calibre_plugins.dedrm.prefs as prefs
import calibre_plugins.dedrm.k4mobidedrm
dedrmprefs = prefs.DeDRM_Prefs()
pids = dedrmprefs['pids']
serials = dedrmprefs['serials']
for android_serials_list in dedrmprefs['androidkeys'].values():
#print android_serials_list
serials.extend(android_serials_list)
#print serials
androidFiles = []
kindleDatabases = list(dedrmprefs['kindlekeys'].items())
try:
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,kindleDatabases,androidFiles,serials,pids,self.starttime)
except Exception as e:
decoded = False
# perhaps we need to get a new default Kindle for Mac/PC key
defaultkeys = []
print("{0} v{1}: Failed to decrypt with error: {2}".format(PLUGIN_NAME, PLUGIN_VERSION,e.args[0]))
print("{0} v{1}: Looking for new default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
try:
if iswindows or isosx:
from calibre_plugins.dedrm.kindlekey import kindlekeys
defaultkeys = kindlekeys()
else: # linux
from .wineutils import WineGetKeys
scriptpath = os.path.join(self.alfdir,"kindlekey.py")
defaultkeys = WineGetKeys(scriptpath, ".k4i",dedrmprefs['kindlewineprefix'])
except:
print("{0} v{1}: Exception when getting default Kindle Key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
traceback.print_exc()
pass
newkeys = {}
for i,keyvalue in enumerate(defaultkeys):
keyname = "default_key_{0:d}".format(i+1)
if keyvalue not in dedrmprefs['kindlekeys'].values():
newkeys[keyname] = keyvalue
if len(newkeys) > 0:
print("{0} v{1}: Found {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), "key" if len(newkeys)==1 else "keys"))
try:
book = k4mobidedrm.GetDecryptedBook(path_to_ebook,list(newkeys.items()),[],[],[],self.starttime)
decoded = True
# store the new successful keys in the defaults
print("{0} v{1}: Saving {2} new {3}".format(PLUGIN_NAME, PLUGIN_VERSION, len(newkeys), "key" if len(newkeys)==1 else "keys"))
for keyvalue in newkeys.values():
dedrmprefs.addnamedvaluetoprefs('kindlekeys','default_key',keyvalue)
dedrmprefs.writeprefs()
except Exception as e:
pass
if not decoded:
#if you reached here then no luck raise and exception
print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
raise DeDRMError("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
of = self.temporary_file(book.getBookExtension())
book.getFile(of.name)
of.close()
book.cleanup()
return of.name
def eReaderDecrypt(self,path_to_ebook):
import calibre_plugins.dedrm.prefs as prefs
import calibre_plugins.dedrm.erdr2pml
dedrmprefs = prefs.DeDRM_Prefs()
# Attempt to decrypt epub with each encryption key (generated or provided).
for keyname, userkey in dedrmprefs['ereaderkeys'].items():
keyname_masked = "".join(("X" if (x.isdigit()) else x) for x in keyname)
print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname_masked))
of = self.temporary_file(".pmlz")
# Give the userkey, ebook and TemporaryPersistent file to the decryption function.
result = erdr2pml.decryptBook(path_to_ebook, of.name, True, codecs.decode(userkey,'hex'))
of.close()
# Decryption was successful return the modified PersistentTemporary
# file to Calibre's import process.
if result == 0:
print("{0} v{1}: Successfully decrypted with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime))
return of.name
print("{0} v{1}: Failed to decrypt with key {2:s} after {3:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,keyname_masked,time.time()-self.starttime))
print("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
raise DeDRMError("{0} v{1}: Ultimately failed to decrypt after {2:.1f} seconds. Read the FAQs at Harper's repository: https://github.com/apprenticeharper/DeDRM_tools/blob/master/FAQs.md".format(PLUGIN_NAME, PLUGIN_VERSION, time.time()-self.starttime))
def run(self, path_to_ebook):
# make sure any unicode output gets converted safely with 'replace'
sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr)
print("{0} v{1}: Trying to decrypt {2}".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
self.starttime = time.time()
booktype = os.path.splitext(path_to_ebook)[1].lower()[1:]
if booktype in ['prc','mobi','pobi','azw','azw1','azw3','azw4','tpz','kfx-zip']:
# Kindle/Mobipocket
decrypted_ebook = self.KindleMobiDecrypt(path_to_ebook)
elif booktype == 'pdb':
# eReader
decrypted_ebook = self.eReaderDecrypt(path_to_ebook)
pass
elif booktype == 'pdf':
# Adobe Adept PDF (hopefully)
decrypted_ebook = self.PDFDecrypt(path_to_ebook)
pass
elif booktype == 'epub':
# Adobe Adept or B&N ePub
decrypted_ebook = self.ePubDecrypt(path_to_ebook)
else:
print("Unknown booktype {0}. Passing back to calibre unchanged".format(booktype))
return path_to_ebook
print("{0} v{1}: Finished after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
return decrypted_ebook
def is_customizable(self):
# return true to allow customization via the Plugin->Preferences.
return True
def config_widget(self):
import calibre_plugins.dedrm.config as config
return config.ConfigWidget(self.plugin_path, self.alfdir)
def save_settings(self, config_widget):
config_widget.save_settings()
================================================
FILE: DeDRM_plugin/activitybar.py
================================================
import sys
import tkinter
import tkinter.constants
class ActivityBar(tkinter.Frame):
def __init__(self, master, length=300, height=20, barwidth=15, interval=50, bg='white', fillcolor='orchid1',\
bd=2, relief=tkinter.constants.GROOVE, *args, **kw):
tkinter.Frame.__init__(self, master, bg=bg, width=length, height=height, *args, **kw)
self._master = master
self._interval = interval
self._maximum = length
self._startx = 0
self._barwidth = barwidth
self._bardiv = length / barwidth
if self._bardiv < 10:
self._bardiv = 10
stopx = self._startx + self._barwidth
if stopx > self._maximum:
stopx = self._maximum
# self._canv = Tkinter.Canvas(self, bg=self['bg'], width=self['width'], height=self['height'],\
# highlightthickness=0, relief='flat', bd=0)
self._canv = tkinter.Canvas(self, bg=self['bg'], width=self['width'], height=self['height'],\
highlightthickness=0, relief=relief, bd=bd)
self._canv.pack(fill='both', expand=1)
self._rect = self._canv.create_rectangle(0, 0, self._canv.winfo_reqwidth(), self._canv.winfo_reqheight(), fill=fillcolor, width=0)
self._set()
self.bind('<Configure>', self._update_coords)
self._running = False
def _update_coords(self, event):
'''Updates the position of the rectangle inside the canvas when the size of
the widget gets changed.'''
# looks like we have to call update_idletasks() twice to make sure
# to get the results we expect
self._canv.update_idletasks()
self._maximum = self._canv.winfo_width()
self._startx = 0
self._barwidth = self._maximum / self._bardiv
if self._barwidth < 2:
self._barwidth = 2
stopx = self._startx + self._barwidth
if stopx > self._maximum:
stopx = self._maximum
self._canv.coords(self._rect, 0, 0, stopx, self._canv.winfo_height())
self._canv.update_idletasks()
def _set(self):
if self._startx < 0:
self._startx = 0
if self._startx > self._maximum:
self._startx = self._startx % self._maximum
stopx = self._startx + self._barwidth
if stopx > self._maximum:
stopx = self._maximum
self._canv.coords(self._rect, self._startx, 0, stopx, self._canv.winfo_height())
self._canv.update_idletasks()
def start(self):
self._running = True
self.after(self._interval, self._step)
def stop(self):
self._running = False
self._set()
def _step(self):
if self._running:
stepsize = self._barwidth / 4
if stepsize < 2:
stepsize = 2
self._startx += stepsize
self._set()
self.after(self._interval, self._step)
================================================
FILE: DeDRM_plugin/adobekey.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# adobekey.pyw, version 6.0
# Copyright © 2009-2020 i♥cabbages, Apprentice Harper et al.
# Released under the terms of the GNU General Public Licence, version 3
# <http://www.gnu.org/licenses/>
# Revision history:
# 1 - Initial release, for Adobe Digital Editions 1.7
# 2 - Better algorithm for finding pLK; improved error handling
# 3 - Rename to INEPT
# 4 - Series of changes by joblack (and others?) --
# 4.1 - quick beta fix for ADE 1.7.2 (anon)
# 4.2 - added old 1.7.1 processing
# 4.3 - better key search
# 4.4 - Make it working on 64-bit Python
# 5 - Clean up and improve 4.x changes;
# Clean up and merge OS X support by unknown
# 5.1 - add support for using OpenSSL on Windows in place of PyCrypto
# 5.2 - added support for output of key to a particular file
# 5.3 - On Windows try PyCrypto first, OpenSSL next
# 5.4 - Modify interface to allow use of import
# 5.5 - Fix for potential problem with PyCrypto
# 5.6 - Revised to allow use in Plugins to eliminate need for duplicate code
# 5.7 - Unicode support added, renamed adobekey from ineptkey
# 5.8 - Added getkey interface for Windows DeDRM application
# 5.9 - moved unicode_argv call inside main for Windows DeDRM compatibility
# 6.0 - Work if TkInter is missing
# 7.0 - Python 3 for calibre 5
"""
Retrieve Adobe ADEPT user key.
"""
__license__ = 'GPL v3'
__version__ = '7.0'
import sys, os, struct, getopt
from base64 import b64decode
# Wrap a stream so that output gets flushed immediately
# and also make sure that any unicode strings get
# encoded using "replace" before writing them.
class SafeUnbuffered:
def __init__(self, stream):
self.stream = stream
self.encoding = stream.encoding
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
if isinstance(data, str):
data = data.encode(self.encoding,"replace")
self.stream.buffer.write(data)
self.stream.buffer.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
try:
from calibre.constants import iswindows, isosx
except:
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
def unicode_argv():
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
# as a list of Unicode strings and encode them as utf-8
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return ["adobekey.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
class ADEPTError(Exception):
pass
if iswindows:
from ctypes import windll, c_char_p, c_wchar_p, c_uint, POINTER, byref, \
create_unicode_buffer, create_string_buffer, CFUNCTYPE, addressof, \
string_at, Structure, c_void_p, cast, c_size_t, memmove, CDLL, c_int, \
c_long, c_ulong
from ctypes.wintypes import LPVOID, DWORD, BOOL
import winreg
def _load_crypto_libcrypto():
from ctypes.util import find_library
libcrypto = find_library('libcrypto-1_1')
if libcrypto is None:
libcrypto = find_library('libeay32')
if libcrypto is None:
raise ADEPTError('libcrypto not found')
libcrypto = CDLL(libcrypto)
AES_MAXNR = 14
c_char_pp = POINTER(c_char_p)
c_int_p = POINTER(c_int)
class AES_KEY(Structure):
_fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))),
('rounds', c_int)]
AES_KEY_p = POINTER(AES_KEY)
def F(restype, name, argtypes):
func = getattr(libcrypto, name)
func.restype = restype
func.argtypes = argtypes
return func
AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',
[c_char_p, c_int, AES_KEY_p])
AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',
[c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p,
c_int])
class AES(object):
def __init__(self, userkey):
self._blocksize = len(userkey)
if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) :
raise ADEPTError('AES improper key used')
key = self._key = AES_KEY()
rv = AES_set_decrypt_key(userkey, len(userkey) * 8, key)
if rv < 0:
raise ADEPTError('Failed to initialize AES key')
def decrypt(self, data):
out = create_string_buffer(len(data))
iv = (b"\x00" * self._blocksize)
rv = AES_cbc_encrypt(data, out, len(data), self._key, iv, 0)
if rv == 0:
raise ADEPTError('AES decryption failed')
return out.raw
return AES
def _load_crypto_pycrypto():
from Crypto.Cipher import AES as _AES
class AES(object):
def __init__(self, key):
self._aes = _AES.new(key, _AES.MODE_CBC, b'\x00'*16)
def decrypt(self, data):
return self._aes.decrypt(data)
return AES
def _load_crypto():
AES = None
for loader in (_load_crypto_pycrypto, _load_crypto_libcrypto):
try:
AES = loader()
break
except (ImportError, ADEPTError):
pass
return AES
AES = _load_crypto()
DEVICE_KEY_PATH = r'Software\Adobe\Adept\Device'
PRIVATE_LICENCE_KEY_PATH = r'Software\Adobe\Adept\Activation'
MAX_PATH = 255
kernel32 = windll.kernel32
advapi32 = windll.advapi32
crypt32 = windll.crypt32
def GetSystemDirectory():
GetSystemDirectoryW = kernel32.GetSystemDirectoryW
GetSystemDirectoryW.argtypes = [c_wchar_p, c_uint]
GetSystemDirectoryW.restype = c_uint
def GetSystemDirectory():
buffer = create_unicode_buffer(MAX_PATH + 1)
GetSystemDirectoryW(buffer, len(buffer))
return buffer.value
return GetSystemDirectory
GetSystemDirectory = GetSystemDirectory()
def GetVolumeSerialNumber():
GetVolumeInformationW = kernel32.GetVolumeInformationW
GetVolumeInformationW.argtypes = [c_wchar_p, c_wchar_p, c_uint,
POINTER(c_uint), POINTER(c_uint),
POINTER(c_uint), c_wchar_p, c_uint]
GetVolumeInformationW.restype = c_uint
def GetVolumeSerialNumber(path):
vsn = c_uint(0)
GetVolumeInformationW(
path, None, 0, byref(vsn), None, None, None, 0)
return vsn.value
return GetVolumeSerialNumber
GetVolumeSerialNumber = GetVolumeSerialNumber()
def GetUserName():
GetUserNameW = advapi32.GetUserNameW
GetUserNameW.argtypes = [c_wchar_p, POINTER(c_uint)]
GetUserNameW.restype = c_uint
def GetUserName():
buffer = create_unicode_buffer(32)
size = c_uint(len(buffer))
while not GetUserNameW(buffer, byref(size)):
buffer = create_unicode_buffer(len(buffer) * 2)
size.value = len(buffer)
return buffer.value.encode('utf-16-le')[::2]
return GetUserName
GetUserName = GetUserName()
PAGE_EXECUTE_READWRITE = 0x40
MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
def VirtualAlloc():
_VirtualAlloc = kernel32.VirtualAlloc
_VirtualAlloc.argtypes = [LPVOID, c_size_t, DWORD, DWORD]
_VirtualAlloc.restype = LPVOID
def VirtualAlloc(addr, size, alloctype=(MEM_COMMIT | MEM_RESERVE),
protect=PAGE_EXECUTE_READWRITE):
return _VirtualAlloc(addr, size, alloctype, protect)
return VirtualAlloc
VirtualAlloc = VirtualAlloc()
MEM_RELEASE = 0x8000
def VirtualFree():
_VirtualFree = kernel32.VirtualFree
_VirtualFree.argtypes = [LPVOID, c_size_t, DWORD]
_VirtualFree.restype = BOOL
def VirtualFree(addr, size=0, freetype=MEM_RELEASE):
return _VirtualFree(addr, size, freetype)
return VirtualFree
VirtualFree = VirtualFree()
class NativeFunction(object):
def __init__(self, restype, argtypes, insns):
self._buf = buf = VirtualAlloc(None, len(insns))
memmove(buf, insns, len(insns))
ftype = CFUNCTYPE(restype, *argtypes)
self._native = ftype(buf)
def __call__(self, *args):
return self._native(*args)
def __del__(self):
if self._buf is not None:
VirtualFree(self._buf)
self._buf = None
if struct.calcsize("P") == 4:
CPUID0_INSNS = (
b"\x53" # push %ebx
b"\x31\xc0" # xor %eax,%eax
b"\x0f\xa2" # cpuid
b"\x8b\x44\x24\x08" # mov 0x8(%esp),%eax
b"\x89\x18" # mov %ebx,0x0(%eax)
b"\x89\x50\x04" # mov %edx,0x4(%eax)
b"\x89\x48\x08" # mov %ecx,0x8(%eax)
b"\x5b" # pop %ebx
b"\xc3" # ret
)
CPUID1_INSNS = (
b"\x53" # push %ebx
b"\x31\xc0" # xor %eax,%eax
b"\x40" # inc %eax
b"\x0f\xa2" # cpuid
b"\x5b" # pop %ebx
b"\xc3" # ret
)
else:
CPUID0_INSNS = (
b"\x49\x89\xd8" # mov %rbx,%r8
b"\x49\x89\xc9" # mov %rcx,%r9
b"\x48\x31\xc0" # xor %rax,%rax
b"\x0f\xa2" # cpuid
b"\x4c\x89\xc8" # mov %r9,%rax
b"\x89\x18" # mov %ebx,0x0(%rax)
b"\x89\x50\x04" # mov %edx,0x4(%rax)
b"\x89\x48\x08" # mov %ecx,0x8(%rax)
b"\x4c\x89\xc3" # mov %r8,%rbx
b"\xc3" # retq
)
CPUID1_INSNS = (
b"\x53" # push %rbx
b"\x48\x31\xc0" # xor %rax,%rax
b"\x48\xff\xc0" # inc %rax
b"\x0f\xa2" # cpuid
b"\x5b" # pop %rbx
b"\xc3" # retq
)
def cpuid0():
_cpuid0 = NativeFunction(None, [c_char_p], CPUID0_INSNS)
buf = create_string_buffer(12)
def cpuid0():
_cpuid0(buf)
return buf.raw
return cpuid0
cpuid0 = cpuid0()
cpuid1 = NativeFunction(c_uint, [], CPUID1_INSNS)
class DataBlob(Structure):
_fields_ = [('cbData', c_uint),
('pbData', c_void_p)]
DataBlob_p = POINTER(DataBlob)
def CryptUnprotectData():
_CryptUnprotectData = crypt32.CryptUnprotectData
_CryptUnprotectData.argtypes = [DataBlob_p, c_wchar_p, DataBlob_p,
c_void_p, c_void_p, c_uint, DataBlob_p]
_CryptUnprotectData.restype = c_uint
def CryptUnprotectData(indata, entropy):
indatab = create_string_buffer(indata)
indata = DataBlob(len(indata), cast(indatab, c_void_p))
entropyb = create_string_buffer(entropy)
entropy = DataBlob(len(entropy), cast(entropyb, c_void_p))
outdata = DataBlob()
if not _CryptUnprotectData(byref(indata), None, byref(entropy),
None, None, 0, byref(outdata)):
raise ADEPTError("Failed to decrypt user key key (sic)")
return string_at(outdata.pbData, outdata.cbData)
return CryptUnprotectData
CryptUnprotectData = CryptUnprotectData()
def adeptkeys():
if AES is None:
raise ADEPTError("PyCrypto or OpenSSL must be installed")
root = GetSystemDirectory().split('\\')[0] + '\\'
serial = GetVolumeSerialNumber(root)
vendor = cpuid0()
signature = struct.pack('>I', cpuid1())[1:]
user = GetUserName()
entropy = struct.pack('>I12s3s13s', serial, vendor, signature, user)
cuser = winreg.HKEY_CURRENT_USER
try:
regkey = winreg.OpenKey(cuser, DEVICE_KEY_PATH)
device = winreg.QueryValueEx(regkey, 'key')[0]
except WindowsError:
raise ADEPTError("Adobe Digital Editions not activated")
keykey = CryptUnprotectData(device, entropy)
userkey = None
keys = []
try:
plkroot = winreg.OpenKey(cuser, PRIVATE_LICENCE_KEY_PATH)
except WindowsError:
raise ADEPTError("Could not locate ADE activation")
for i in range(0, 16):
try:
plkparent = winreg.OpenKey(plkroot, "%04d" % (i,))
except WindowsError:
break
ktype = winreg.QueryValueEx(plkparent, None)[0]
if ktype != 'credentials':
continue
for j in range(0, 16):
try:
plkkey = winreg.OpenKey(plkparent, "%04d" % (j,))
except WindowsError:
break
ktype = winreg.QueryValueEx(plkkey, None)[0]
if ktype != 'privateLicenseKey':
continue
userkey = winreg.QueryValueEx(plkkey, 'value')[0]
userkey = b64decode(userkey)
aes = AES(keykey)
userkey = aes.decrypt(userkey)
userkey = userkey[26:-ord(userkey[-1:])]
#print "found key:",userkey.encode('hex')
keys.append(userkey)
if len(keys) == 0:
raise ADEPTError('Could not locate privateLicenseKey')
print("Found {0:d} keys".format(len(keys)))
return keys
elif isosx:
import xml.etree.ElementTree as etree
import subprocess
NSMAP = {'adept': 'http://ns.adobe.com/adept',
'enc': 'http://www.w3.org/2001/04/xmlenc#'}
def findActivationDat():
import warnings
warnings.filterwarnings('ignore', category=FutureWarning)
home = os.getenv('HOME')
cmdline = 'find "' + home + '/Library/Application Support/Adobe/Digital Editions" -name "activation.dat"'
cmdline = cmdline.encode(sys.getfilesystemencoding())
p2 = subprocess.Popen(cmdline, shell=True, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=False)
out1, out2 = p2.communicate()
reslst = out1.split(b'\n')
cnt = len(reslst)
ActDatPath = b"activation.dat"
for j in range(cnt):
resline = reslst[j]
pp = resline.find(b'activation.dat')
if pp >= 0:
ActDatPath = resline
break
if os.path.exists(ActDatPath):
return ActDatPath
return None
def adeptkeys():
actpath = findActivationDat()
if actpath is None:
raise ADEPTError("Could not find ADE activation.dat file.")
tree = etree.parse(actpath)
adept = lambda tag: '{%s}%s' % (NSMAP['adept'], tag)
expr = '//%s/%s' % (adept('credentials'), adept('privateLicenseKey'))
userkey = tree.findtext(expr)
userkey = b64decode(userkey)
userkey = userkey[26:]
return [userkey]
else:
def adeptkeys():
raise ADEPTError("This script only supports Windows and Mac OS X.")
return []
# interface for Python DeDRM
def getkey(outpath):
keys = adeptkeys()
if len(keys) > 0:
if not os.path.isdir(outpath):
outfile = outpath
with open(outfile, 'wb') as keyfileout:
keyfileout.write(keys[0])
print("Saved a key to {0}".format(outfile))
else:
keycount = 0
for key in keys:
while True:
keycount += 1
outfile = os.path.join(outpath,"adobekey_{0:d}.der".format(keycount))
if not os.path.exists(outfile):
break
with open(outfile, 'wb') as keyfileout:
keyfileout.write(key)
print("Saved a key to {0}".format(outfile))
return True
return False
def usage(progname):
print("Finds, decrypts and saves the default Adobe Adept encryption key(s).")
print("Keys are saved to the current directory, or a specified output directory.")
print("If a file name is passed instead of a directory, only the first key is saved, in that file.")
print("Usage:")
print(" {0:s} [-h] [<outpath>]".format(progname))
def cli_main():
sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv()
progname = os.path.basename(argv[0])
print("{0} v{1}\nCopyright © 2009-2020 i♥cabbages, Apprentice Harper et al.".format(progname,__version__))
try:
opts, args = getopt.getopt(argv[1:], "h")
except getopt.GetoptError as err:
print("Error in options or arguments: {0}".format(err.args[0]))
usage(progname)
sys.exit(2)
for o, a in opts:
if o == "-h":
usage(progname)
sys.exit(0)
if len(args) > 1:
usage(progname)
sys.exit(2)
if len(args) == 1:
# save to the specified file or directory
outpath = args[0]
if not os.path.isabs(outpath):
outpath = os.path.abspath(outpath)
else:
# save to the same directory as the script
outpath = os.path.dirname(argv[0])
# make sure the outpath is the
outpath = os.path.realpath(os.path.normpath(outpath))
keys = adeptkeys()
if len(keys) > 0:
if not os.path.isdir(outpath):
outfile = outpath
with open(outfile, 'wb') as keyfileout:
keyfileout.write(keys[0])
print("Saved a key to {0}".format(outfile))
else:
keycount = 0
for key in keys:
while True:
keycount += 1
outfile = os.path.join(outpath,"adobekey_{0:d}.der".format(keycount))
if not os.path.exists(outfile):
break
with open(outfile, 'wb') as keyfileout:
keyfileout.write(key)
print("Saved a key to {0}".format(outfile))
else:
print("Could not retrieve Adobe Adept key.")
return 0
def gui_main():
try:
import tkinter
import tkinter.constants
import tkinter.messagebox
import traceback
except:
return cli_main()
class ExceptionDialog(tkinter.Frame):
def __init__(self, root, text):
tkinter.Frame.__init__(self, root, border=5)
label = tkinter.Label(self, text="Unexpected error:",
anchor=tkinter.constants.W, justify=tkinter.constants.LEFT)
label.pack(fill=tkinter.constants.X, expand=0)
self.text = tkinter.Text(self)
self.text.pack(fill=tkinter.constants.BOTH, expand=1)
self.text.insert(tkinter.constants.END, text)
argv=unicode_argv()
root = tkinter.Tk()
root.withdraw()
progpath, progname = os.path.split(argv[0])
success = False
try:
keys = adeptkeys()
keycount = 0
for key in keys:
while True:
keycount += 1
outfile = os.path.join(progpath,"adobekey_{0:d}.der".format(keycount))
if not os.path.exists(outfile):
break
with open(outfile, 'wb') as keyfileout:
keyfileout.write(key)
success = True
tkinter.messagebox.showinfo(progname, "Key successfully retrieved to {0}".format(outfile))
except ADEPTError as e:
tkinter.messagebox.showerror(progname, "Error: {0}".format(str(e)))
except Exception:
root.wm_state('normal')
root.title(progname)
text = traceback.format_exc()
ExceptionDialog(root, text).pack(fill=tkinter.constants.BOTH, expand=1)
root.mainloop()
if not success:
return 1
return 0
if __name__ == '__main__':
if len(sys.argv) > 1:
sys.exit(cli_main())
sys.exit(gui_main())
================================================
FILE: DeDRM_plugin/aescbc.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Routines for doing AES CBC in one file
Modified by some_updates to extract
and combine only those parts needed for AES CBC
into one simple to add python file
Original Version
Copyright (c) 2002 by Paul A. Lambert
Under:
CryptoPy Artisitic License Version 1.0
See the wonderful pure python package cryptopy-1.2.5
and read its LICENSE.txt for complete license details.
Adjusted for Python 3, September 2020
"""
class CryptoError(Exception):
""" Base class for crypto exceptions """
def __init__(self,errorMessage='Error!'):
self.message = errorMessage
def __str__(self):
return self.message
class InitCryptoError(CryptoError):
""" Crypto errors during algorithm initialization """
class BadKeySizeError(InitCryptoError):
""" Bad key size error """
class EncryptError(CryptoError):
""" Error in encryption processing """
class DecryptError(CryptoError):
""" Error in decryption processing """
class DecryptNotBlockAlignedError(DecryptError):
""" Error in decryption processing """
def xorS(a,b):
""" XOR two strings """
assert len(a)==len(b)
x = []
for i in range(len(a)):
x.append( chr(ord(a[i])^ord(b[i])))
return ''.join(x)
def xor(a,b):
""" XOR two strings """
x = []
for i in range(min(len(a),len(b))):
x.append( chr(ord(a[i])^ord(b[i])))
return ''.join(x)
"""
Base 'BlockCipher' and Pad classes for cipher instances.
BlockCipher supports automatic padding and type conversion. The BlockCipher
class was written to make the actual algorithm code more readable and
not for performance.
"""
class BlockCipher:
""" Block ciphers """
def __init__(self):
self.reset()
def reset(self):
self.resetEncrypt()
self.resetDecrypt()
def resetEncrypt(self):
self.encryptBlockCount = 0
self.bytesToEncrypt = ''
def resetDecrypt(self):
self.decryptBlockCount = 0
self.bytesToDecrypt = ''
def encrypt(self, plainText, more = None):
""" Encrypt a string and return a binary string """
self.bytesToEncrypt += plainText # append plainText to any bytes from prior encrypt
numBlocks, numExtraBytes = divmod(len(self.bytesToEncrypt), self.blockSize)
cipherText = ''
for i in range(numBlocks):
bStart = i*self.blockSize
ctBlock = self.encryptBlock(self.bytesToEncrypt[bStart:bStart+self.blockSize])
self.encryptBlockCount += 1
cipherText += ctBlock
if numExtraBytes > 0: # save any bytes that are not block aligned
self.bytesToEncrypt = self.bytesToEncrypt[-numExtraBytes:]
else:
self.bytesToEncrypt = ''
if more == None: # no more data expected from caller
finalBytes = self.padding.addPad(self.bytesToEncrypt,self.blockSize)
if len(finalBytes) > 0:
ctBlock = self.encryptBlock(finalBytes)
self.encryptBlockCount += 1
cipherText += ctBlock
self.resetEncrypt()
return cipherText
def decrypt(self, cipherText, more = None):
""" Decrypt a string and return a string """
self.bytesToDecrypt += cipherText # append to any bytes from prior decrypt
numBlocks, numExtraBytes = divmod(len(self.bytesToDecrypt), self.blockSize)
if more == None: # no more calls to decrypt, should have all the data
if numExtraBytes != 0:
raise DecryptNotBlockAlignedError('Data not block aligned on decrypt')
# hold back some bytes in case last decrypt has zero len
if (more != None) and (numExtraBytes == 0) and (numBlocks >0) :
numBlocks -= 1
numExtraBytes = self.blockSize
plainText = ''
for i in range(numBlocks):
bStart = i*self.blockSize
ptBlock = self.decryptBlock(self.bytesToDecrypt[bStart : bStart+self.blockSize])
self.decryptBlockCount += 1
plainText += ptBlock
if numExtraBytes > 0: # save any bytes that are not block aligned
self.bytesToEncrypt = self.bytesToEncrypt[-numExtraBytes:]
else:
self.bytesToEncrypt = ''
if more == None: # last decrypt remove padding
plainText = self.padding.removePad(plainText, self.blockSize)
self.resetDecrypt()
return plainText
class Pad:
def __init__(self):
pass # eventually could put in calculation of min and max size extension
class padWithPadLen(Pad):
""" Pad a binary string with the length of the padding """
def addPad(self, extraBytes, blockSize):
""" Add padding to a binary string to make it an even multiple
of the block size """
blocks, numExtraBytes = divmod(len(extraBytes), blockSize)
padLength = blockSize - numExtraBytes
return extraBytes + padLength*chr(padLength)
def removePad(self, paddedBinaryString, blockSize):
""" Remove padding from a binary string """
if not(0<len(paddedBinaryString)):
raise DecryptNotBlockAlignedError('Expected More Data')
return paddedBinaryString[:-ord(paddedBinaryString[-1])]
class noPadding(Pad):
""" No padding. Use this to get ECB behavior from encrypt/decrypt """
def addPad(self, extraBytes, blockSize):
""" Add no padding """
return extraBytes
def removePad(self, paddedBinaryString, blockSize):
""" Remove no padding """
return paddedBinaryString
"""
Rijndael encryption algorithm
This byte oriented implementation is intended to closely
match FIPS specification for readability. It is not implemented
for performance.
"""
class Rijndael(BlockCipher):
""" Rijndael encryption algorithm """
def __init__(self, key = None, padding = padWithPadLen(), keySize=16, blockSize=16 ):
self.name = 'RIJNDAEL'
self.keySize = keySize
self.strength = keySize*8
self.blockSize = blockSize # blockSize is in bytes
self.padding = padding # change default to noPadding() to get normal ECB behavior
assert( keySize%4==0 and keySize/4 in NrTable[4]),'key size must be 16,20,24,29 or 32 bytes'
assert( blockSize%4==0 and blockSize/4 in NrTable), 'block size must be 16,20,24,29 or 32 bytes'
self.Nb = self.blockSize/4 # Nb is number of columns of 32 bit words
self.Nk = keySize/4 # Nk is the key length in 32-bit words
self.Nr = NrTable[self.Nb][self.Nk] # The number of rounds (Nr) is a function of
# the block (Nb) and key (Nk) sizes.
if key != None:
self.setKey(key)
def setKey(self, key):
""" Set a key and generate the expanded key """
assert( len(key) == (self.Nk*4) ), 'Key length must be same as keySize parameter'
self.__expandedKey = keyExpansion(self, key)
self.reset() # BlockCipher.reset()
def encryptBlock(self, plainTextBlock):
""" Encrypt a block, plainTextBlock must be a array of bytes [Nb by 4] """
self.state = self._toBlock(plainTextBlock)
AddRoundKey(self, self.__expandedKey[0:self.Nb])
for round in range(1,self.Nr): #for round = 1 step 1 to Nr
SubBytes(self)
ShiftRows(self)
MixColumns(self)
AddRoundKey(self, self.__expandedKey[round*self.Nb:(round+1)*self.Nb])
SubBytes(self)
ShiftRows(self)
AddRoundKey(self, self.__expandedKey[self.Nr*self.Nb:(self.Nr+1)*self.Nb])
return self._toBString(self.state)
def decryptBlock(self, encryptedBlock):
""" decrypt a block (array of bytes) """
self.state = self._toBlock(encryptedBlock)
AddRoundKey(self, self.__expandedKey[self.Nr*self.Nb:(self.Nr+1)*self.Nb])
for round in range(self.Nr-1,0,-1):
InvShiftRows(self)
InvSubBytes(self)
AddRoundKey(self, self.__expandedKey[round*self.Nb:(round+1)*self.Nb])
InvMixColumns(self)
InvShiftRows(self)
InvSubBytes(self)
AddRoundKey(self, self.__expandedKey[0:self.Nb])
return self._toBString(self.state)
def _toBlock(self, bs):
""" Convert binary string to array of bytes, state[col][row]"""
assert ( len(bs) == 4*self.Nb ), 'Rijndarl blocks must be of size blockSize'
return [[ord(bs[4*i]),ord(bs[4*i+1]),ord(bs[4*i+2]),ord(bs[4*i+3])] for i in range(self.Nb)]
def _toBString(self, block):
""" Convert block (array of bytes) to binary string """
l = []
for col in block:
for rowElement in col:
l.append(chr(rowElement))
return ''.join(l)
#-------------------------------------
""" Number of rounds Nr = NrTable[Nb][Nk]
Nb Nk=4 Nk=5 Nk=6 Nk=7 Nk=8
------------------------------------- """
NrTable = {4: {4:10, 5:11, 6:12, 7:13, 8:14},
5: {4:11, 5:11, 6:12, 7:13, 8:14},
6: {4:12, 5:12, 6:12, 7:13, 8:14},
7: {4:13, 5:13, 6:13, 7:13, 8:14},
8: {4:14, 5:14, 6:14, 7:14, 8:14}}
#-------------------------------------
def keyExpansion(algInstance, keyString):
""" Expand a string of size keySize into a larger array """
Nk, Nb, Nr = algInstance.Nk, algInstance.Nb, algInstance.Nr # for readability
key = [ord(byte) for byte in keyString] # convert string to list
w = [[key[4*i],key[4*i+1],key[4*i+2],key[4*i+3]] for i in range(Nk)]
for i in range(Nk,Nb*(Nr+1)):
temp = w[i-1] # a four byte column
if (i%Nk) == 0 :
temp = temp[1:]+[temp[0]] # RotWord(temp)
temp = [ Sbox[byte] for byte in temp ]
temp[0] ^= Rcon[i/Nk]
elif Nk > 6 and i%Nk == 4 :
temp = [ Sbox[byte] for byte in temp ] # SubWord(temp)
w.append( [ w[i-Nk][byte]^temp[byte] for byte in range(4) ] )
return w
Rcon = (0,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x1b,0x36, # note extra '0' !!!
0x6c,0xd8,0xab,0x4d,0x9a,0x2f,0x5e,0xbc,0x63,0xc6,
0x97,0x35,0x6a,0xd4,0xb3,0x7d,0xfa,0xef,0xc5,0x91)
#-------------------------------------
def AddRoundKey(algInstance, keyBlock):
""" XOR the algorithm state with a block of key material """
for column in range(algInstance.Nb):
for row in range(4):
algInstance.state[column][row] ^= keyBlock[column][row]
#-------------------------------------
def SubBytes(algInstance):
for column in range(algInstance.Nb):
for row in range(4):
algInstance.state[column][row] = Sbox[algInstance.state[column][row]]
def InvSubBytes(algInstance):
for column in range(algInstance.Nb):
for row in range(4):
algInstance.state[column][row] = InvSbox[algInstance.state[column][row]]
Sbox = (0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,
0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,
0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,
0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,
0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,
0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,
0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,
0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,
0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,
0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,
0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,
0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,
0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,
0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,
0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,
0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,
0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16)
InvSbox = (0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38,
0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb,
0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87,
0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb,
0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d,
0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e,
0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2,
0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25,
0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16,
0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92,
0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda,
0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84,
0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a,
0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06,
0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02,
0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b,
0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea,
0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73,
0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85,
0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e,
0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89,
0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b,
0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20,
0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4,
0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31,
0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f,
0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d,
0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef,
0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0,
0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61,
0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26,
0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d)
#-------------------------------------
""" For each block size (Nb), the ShiftRow operation shifts row i
by the amount Ci. Note that row 0 is not shifted.
Nb C1 C2 C3
------------------- """
shiftOffset = { 4 : ( 0, 1, 2, 3),
5 : ( 0, 1, 2, 3),
6 : ( 0, 1, 2, 3),
7 : ( 0, 1, 2, 4),
8 : ( 0, 1, 3, 4) }
def ShiftRows(algInstance):
tmp = [0]*algInstance.Nb # list of size Nb
for r in range(1,4): # row 0 reamains unchanged and can be skipped
for c in range(algInstance.Nb):
tmp[c] = algInstance.state[(c+shiftOffset[algInstance.Nb][r]) % algInstance.Nb][r]
for c in range(algInstance.Nb):
algInstance.state[c][r] = tmp[c]
def InvShiftRows(algInstance):
tmp = [0]*algInstance.Nb # list of size Nb
for r in range(1,4): # row 0 reamains unchanged and can be skipped
for c in range(algInstance.Nb):
tmp[c] = algInstance.state[(c+algInstance.Nb-shiftOffset[algInstance.Nb][r]) % algInstance.Nb][r]
for c in range(algInstance.Nb):
algInstance.state[c][r] = tmp[c]
#-------------------------------------
def MixColumns(a):
Sprime = [0,0,0,0]
for j in range(a.Nb): # for each column
Sprime[0] = mul(2,a.state[j][0])^mul(3,a.state[j][1])^mul(1,a.state[j][2])^mul(1,a.state[j][3])
Sprime[1] = mul(1,a.state[j][0])^mul(2,a.state[j][1])^mul(3,a.state[j][2])^mul(1,a.state[j][3])
Sprime[2] = mul(1,a.state[j][0])^mul(1,a.state[j][1])^mul(2,a.state[j][2])^mul(3,a.state[j][3])
Sprime[3] = mul(3,a.state[j][0])^mul(1,a.state[j][1])^mul(1,a.state[j][2])^mul(2,a.state[j][3])
for i in range(4):
a.state[j][i] = Sprime[i]
def InvMixColumns(a):
""" Mix the four bytes of every column in a linear way
This is the opposite operation of Mixcolumn """
Sprime = [0,0,0,0]
for j in range(a.Nb): # for each column
Sprime[0] = mul(0x0E,a.state[j][0])^mul(0x0B,a.state[j][1])^mul(0x0D,a.state[j][2])^mul(0x09,a.state[j][3])
Sprime[1] = mul(0x09,a.state[j][0])^mul(0x0E,a.state[j][1])^mul(0x0B,a.state[j][2])^mul(0x0D,a.state[j][3])
Sprime[2] = mul(0x0D,a.state[j][0])^mul(0x09,a.state[j][1])^mul(0x0E,a.state[j][2])^mul(0x0B,a.state[j][3])
Sprime[3] = mul(0x0B,a.state[j][0])^mul(0x0D,a.state[j][1])^mul(0x09,a.state[j][2])^mul(0x0E,a.state[j][3])
for i in range(4):
a.state[j][i] = Sprime[i]
#-------------------------------------
def mul(a, b):
""" Multiply two elements of GF(2^m)
needed for MixColumn and InvMixColumn """
if (a !=0 and b!=0):
return Alogtable[(Logtable[a] + Logtable[b])%255]
else:
return 0
Logtable = ( 0, 0, 25, 1, 50, 2, 26, 198, 75, 199, 27, 104, 51, 238, 223, 3,
100, 4, 224, 14, 52, 141, 129, 239, 76, 113, 8, 200, 248, 105, 28, 193,
125, 194, 29, 181, 249, 185, 39, 106, 77, 228, 166, 114, 154, 201, 9, 120,
101, 47, 138, 5, 33, 15, 225, 36, 18, 240, 130, 69, 53, 147, 218, 142,
150, 143, 219, 189, 54, 208, 206, 148, 19, 92, 210, 241, 64, 70, 131, 56,
102, 221, 253, 48, 191, 6, 139, 98, 179, 37, 226, 152, 34, 136, 145, 16,
126, 110, 72, 195, 163, 182, 30, 66, 58, 107, 40, 84, 250, 133, 61, 186,
43, 121, 10, 21, 155, 159, 94, 202, 78, 212, 172, 229, 243, 115, 167, 87,
175, 88, 168, 80, 244, 234, 214, 116, 79, 174, 233, 213, 231, 230, 173, 232,
44, 215, 117, 122, 235, 22, 11, 245, 89, 203, 95, 176, 156, 169, 81, 160,
127, 12, 246, 111, 23, 196, 73, 236, 216, 67, 31, 45, 164, 118, 123, 183,
204, 187, 62, 90, 251, 96, 177, 134, 59, 82, 161, 108, 170, 85, 41, 157,
151, 178, 135, 144, 97, 190, 220, 252, 188, 149, 207, 205, 55, 63, 91, 209,
83, 57, 132, 60, 65, 162, 109, 71, 20, 42, 158, 93, 86, 242, 211, 171,
68, 17, 146, 217, 35, 32, 46, 137, 180, 124, 184, 38, 119, 153, 227, 165,
103, 74, 237, 222, 197, 49, 254, 24, 13, 99, 140, 128, 192, 247, 112, 7)
Alogtable= ( 1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19, 53,
95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34, 102, 170,
229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, 112, 144, 171, 230, 49,
83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184, 211, 110, 178, 205,
76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241, 8, 24, 40, 120, 136,
131, 158, 185, 208, 107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154,
181, 196, 87, 249, 16, 48, 80, 240, 11, 29, 39, 105, 187, 214, 97, 163,
254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174, 233, 32, 96, 160,
251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65,
195, 94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218, 117,
159, 186, 213, 100, 172, 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128,
155, 182, 193, 88, 232, 35, 101, 175, 234, 37, 111, 177, 200, 67, 197, 84,
252, 31, 33, 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, 203, 70, 202,
69, 207, 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14,
18, 54, 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23,
57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246, 1)
"""
AES Encryption Algorithm
The AES algorithm is just Rijndael algorithm restricted to the default
blockSize of 128 bits.
"""
class AES(Rijndael):
""" The AES algorithm is the Rijndael block cipher restricted to block
sizes of 128 bits and key sizes of 128, 192 or 256 bits
"""
def __init__(self, key = None, padding = padWithPadLen(), keySize=16):
""" Initialize AES, keySize is in bytes """
if not (keySize == 16 or keySize == 24 or keySize == 32) :
raise BadKeySizeError('Illegal AES key size, must be 16, 24, or 32 bytes')
Rijndael.__init__( self, key, padding=padding, keySize=keySize, blockSize=16 )
self.name = 'AES'
"""
CBC mode of encryption for block ciphers.
This algorithm mode wraps any BlockCipher to make a
Cipher Block Chaining mode.
"""
from random import Random # should change to crypto.random!!!
class CBC(BlockCipher):
""" The CBC class wraps block ciphers to make cipher block chaining (CBC) mode
algorithms. The initialization (IV) is automatic if set to None. Padding
is also automatic based on the Pad class used to initialize the algorithm
"""
def __init__(self, blockCipherInstance, padding = padWithPadLen()):
""" CBC algorithms are created by initializing with a BlockCipher instance """
self.baseCipher = blockCipherInstance
self.name = self.baseCipher.name + '_CBC'
self.blockSize = self.baseCipher.blockSize
self.keySize = self.baseCipher.keySize
self.padding = padding
self.baseCipher.padding = noPadding() # baseCipher should NOT pad!!
self.r = Random() # for IV generation, currently uses
# mediocre standard distro version <----------------
import time
newSeed = time.ctime()+str(self.r) # seed with instance location
self.r.seed(newSeed) # to make unique
self.reset()
def setKey(self, key):
self.baseCipher.setKey(key)
# Overload to reset both CBC state and the wrapped baseCipher
def resetEncrypt(self):
BlockCipher.resetEncrypt(self) # reset CBC encrypt state (super class)
self.baseCipher.resetEncrypt() # reset base cipher encrypt state
def resetDecrypt(self):
BlockCipher.resetDecrypt(self) # reset CBC state (super class)
self.baseCipher.resetDecrypt() # reset base cipher decrypt state
def encrypt(self, plainText, iv=None, more=None):
""" CBC encryption - overloads baseCipher to allow optional explicit IV
when iv=None, iv is auto generated!
"""
if self.encryptBlockCount == 0:
self.iv = iv
else:
assert(iv==None), 'IV used only on first call to encrypt'
return BlockCipher.encrypt(self,plainText, more=more)
def decrypt(self, cipherText, iv=None, more=None):
""" CBC decryption - overloads baseCipher to allow optional explicit IV
when iv=None, iv is auto generated!
"""
if self.decryptBlockCount == 0:
self.iv = iv
else:
assert(iv==None), 'IV used only on first call to decrypt'
return BlockCipher.decrypt(self, cipherText, more=more)
def encryptBlock(self, plainTextBlock):
""" CBC block encryption, IV is set with 'encrypt' """
auto_IV = ''
if self.encryptBlockCount == 0:
if self.iv == None:
# generate IV and use
self.iv = ''.join([chr(self.r.randrange(256)) for i in range(self.blockSize)])
self.prior_encr_CT_block = self.iv
auto_IV = self.prior_encr_CT_block # prepend IV if it's automatic
else: # application provided IV
assert(len(self.iv) == self.blockSize ),'IV must be same length as block'
self.prior_encr_CT_block = self.iv
""" encrypt the prior CT XORed with the PT """
ct = self.baseCipher.encryptBlock( xor(self.prior_encr_CT_block, plainTextBlock) )
self.prior_encr_CT_block = ct
return auto_IV+ct
def decryptBlock(self, encryptedBlock):
""" Decrypt a single block """
if self.decryptBlockCount == 0: # first call, process IV
if self.iv == None: # auto decrypt IV?
self.prior_CT_block = encryptedBlock
return ''
else:
assert(len(self.iv)==self.blockSize),"Bad IV size on CBC decryption"
self.prior_CT_block = self.iv
dct = self.baseCipher.decryptBlock(encryptedBlock)
""" XOR the prior decrypted CT with the prior CT """
dct_XOR_priorCT = xor( self.prior_CT_block, dct )
self.prior_CT_block = encryptedBlock
return dct_XOR_priorCT
"""
AES_CBC Encryption Algorithm
"""
class AES_CBC(CBC):
""" AES encryption in CBC feedback mode """
def __init__(self, key=None, padding=padWithPadLen(), keySize=16):
CBC.__init__( self, AES(key, noPadding(), keySize), padding)
self.name = 'AES_CBC'
================================================
FILE: DeDRM_plugin/alfcrypto.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# crypto library mainly by some_updates
# pbkdf2.py pbkdf2 code taken from pbkdf2.py
# pbkdf2.py Copyright © 2004 Matt Johnston <matt @ ucc asn au>
# pbkdf2.py Copyright © 2009 Daniel Holth <dholth@fastmail.fm>
# pbkdf2.py This code may be freely used and modified for any purpose.
import sys, os
import hmac
from struct import pack
import hashlib
# interface to needed routines libalfcrypto
def _load_libalfcrypto():
import ctypes
from ctypes import CDLL, byref, POINTER, c_void_p, c_char_p, c_int, c_long, \
Structure, c_ulong, create_string_buffer, addressof, string_at, cast, sizeof
pointer_size = ctypes.sizeof(ctypes.c_voidp)
name_of_lib = None
if sys.platform.startswith('darwin'):
name_of_lib = 'libalfcrypto.dylib'
elif sys.platform.startswith('win'):
if pointer_size == 4:
name_of_lib = 'alfcrypto.dll'
else:
name_of_lib = 'alfcrypto64.dll'
else:
if pointer_size == 4:
name_of_lib = 'libalfcrypto32.so'
else:
name_of_lib = 'libalfcrypto64.so'
# hard code to local location for libalfcrypto
libalfcrypto = os.path.join(sys.path[0],name_of_lib)
if not os.path.isfile(libalfcrypto):
libalfcrypto = os.path.join(sys.path[0], 'lib', name_of_lib)
if not os.path.isfile(libalfcrypto):
libalfcrypto = os.path.join('.',name_of_lib)
if not os.path.isfile(libalfcrypto):
raise Exception('libalfcrypto not found at %s' % libalfcrypto)
libalfcrypto = CDLL(libalfcrypto)
c_char_pp = POINTER(c_char_p)
c_int_p = POINTER(c_int)
def F(restype, name, argtypes):
func = getattr(libalfcrypto, name)
func.restype = restype
func.argtypes = argtypes
return func
# aes cbc decryption
#
# struct aes_key_st {
# unsigned long rd_key[4 *(AES_MAXNR + 1)];
# int rounds;
# };
#
# typedef struct aes_key_st AES_KEY;
#
# int AES_set_decrypt_key(const unsigned char *userKey, const int bits, AES_KEY *key);
#
#
# void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,
# const unsigned long length, const AES_KEY *key,
# unsigned char *ivec, const int enc);
AES_MAXNR = 14
class AES_KEY(Structure):
_fields_ = [('rd_key', c_long * (4 * (AES_MAXNR + 1))), ('rounds', c_int)]
AES_KEY_p = POINTER(AES_KEY)
AES_cbc_encrypt = F(None, 'AES_cbc_encrypt',[c_char_p, c_char_p, c_ulong, AES_KEY_p, c_char_p, c_int])
AES_set_decrypt_key = F(c_int, 'AES_set_decrypt_key',[c_char_p, c_int, AES_KEY_p])
# Pukall 1 Cipher
# unsigned char *PC1(const unsigned char *key, unsigned int klen, const unsigned char *src,
# unsigned char *dest, unsigned int len, int decryption);
PC1 = F(c_char_p, 'PC1', [c_char_p, c_ulong, c_char_p, c_char_p, c_ulong, c_ulong])
# Topaz Encryption
# typedef struct _TpzCtx {
# unsigned int v[2];
# } TpzCtx;
#
# void topazCryptoInit(TpzCtx *ctx, const unsigned char *key, int klen);
# void topazCryptoDecrypt(const TpzCtx *ctx, const unsigned char *in, unsigned char *out, int len);
class TPZ_CTX(Structure):
_fields_ = [('v', c_long * 2)]
TPZ_CTX_p = POINTER(TPZ_CTX)
topazCryptoInit = F(None, 'topazCryptoInit', [TPZ_CTX_p, c_char_p, c_ulong])
topazCryptoDecrypt = F(None, 'topazCryptoDecrypt', [TPZ_CTX_p, c_char_p, c_char_p, c_ulong])
class AES_CBC(object):
def __init__(self):
self._blocksize = 0
self._keyctx = None
self._iv = 0
def set_decrypt_key(self, userkey, iv):
self._blocksize = len(userkey)
if (self._blocksize != 16) and (self._blocksize != 24) and (self._blocksize != 32) :
raise Exception('AES CBC improper key used')
return
keyctx = self._keyctx = AES_KEY()
self._iv = iv
rv = AES_set_decrypt_key(userkey, len(userkey) * 8, keyctx)
if rv < 0:
raise Exception('Failed to initialize AES CBC key')
def decrypt(self, data):
out = create_string_buffer(len(data))
mutable_iv = create_string_buffer(self._iv, len(self._iv))
rv = AES_cbc_encrypt(data, out, len(data), self._keyctx, mutable_iv, 0)
if rv == 0:
raise Exception('AES CBC decryption failed')
return out.raw
class Pukall_Cipher(object):
def __init__(self):
self.key = None
def PC1(self, key, src, decryption=True):
self.key = key
out = create_string_buffer(len(src))
de = 0
if decryption:
de = 1
rv = PC1(key, len(key), src, out, len(src), de)
return out.raw
class Topaz_Cipher(object):
def __init__(self):
self._ctx = None
def ctx_init(self, key):
tpz_ctx = self._ctx = TPZ_CTX()
topazCryptoInit(tpz_ctx, key, len(key))
return tpz_ctx
def decrypt(self, data, ctx=None):
if ctx == None:
ctx = self._ctx
out = create_string_buffer(len(data))
topazCryptoDecrypt(ctx, data, out, len(data))
return out.raw
print("Using Library AlfCrypto DLL/DYLIB/SO")
return (AES_CBC, Pukall_Cipher, Topaz_Cipher)
def _load_python_alfcrypto():
import aescbc
class Pukall_Cipher(object):
def __init__(self):
self.key = None
def PC1(self, key, src, decryption=True):
sum1 = 0;
sum2 = 0;
keyXorVal = 0;
if len(key)!=16:
raise Exception('Pukall_Cipher: Bad key length.')
wkey = []
for i in range(8):
wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
dst = ""
for i in range(len(src)):
temp1 = 0;
byteXorVal = 0;
for j in range(8):
temp1 ^= wkey[j]
sum2 = (sum2+j)*20021 + sum1
sum1 = (temp1*346)&0xFFFF
sum2 = (sum2+sum1)&0xFFFF
temp1 = (temp1*20021+1)&0xFFFF
byteXorVal ^= temp1 ^ sum2
curByte = ord(src[i])
if not decryption:
keyXorVal = curByte * 257;
curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
if decryption:
keyXorVal = curByte * 257;
for j in range(8):
wkey[j] ^= keyXorVal;
dst+=chr(curByte)
return dst
class Topaz_Cipher(object):
def __init__(self):
self._ctx = None
def ctx_init(self, key):
ctx1 = 0x0CAFFE19E
for keyChar in key:
keyByte = ord(keyChar)
ctx2 = ctx1
ctx1 = ((((ctx1 >>2) * (ctx1 >>7))&0xFFFFFFFF) ^ (keyByte * keyByte * 0x0F902007)& 0xFFFFFFFF )
self._ctx = [ctx1, ctx2]
return [ctx1,ctx2]
def decrypt(self, data, ctx=None):
if ctx == None:
ctx = self._ctx
ctx1 = ctx[0]
ctx2 = ctx[1]
plainText = ""
for dataChar in data:
dataByte = ord(dataChar)
m = (dataByte ^ ((ctx1 >> 3) &0xFF) ^ ((ctx2<<3) & 0xFF)) &0xFF
ctx2 = ctx1
ctx1 = (((ctx1 >> 2) * (ctx1 >> 7)) &0xFFFFFFFF) ^((m * m * 0x0F902007) &0xFFFFFFFF)
plainText += chr(m)
return plainText
class AES_CBC(object):
def __init__(self):
self._key = None
self._iv = None
self.aes = None
def set_decrypt_key(self, userkey, iv):
self._key = userkey
self._iv = iv
self.aes = aescbc.AES_CBC(userkey, aescbc.noPadding(), len(userkey))
def decrypt(self, data):
iv = self._iv
cleartext = self.aes.decrypt(iv + data)
return cleartext
print("Using Library AlfCrypto Python")
return (AES_CBC, Pukall_Cipher, Topaz_Cipher)
def _load_crypto():
AES_CBC = Pukall_Cipher = Topaz_Cipher = None
cryptolist = (_load_libalfcrypto, _load_python_alfcrypto)
for loader in cryptolist:
try:
AES_CBC, Pukall_Cipher, Topaz_Cipher = loader()
break
except (ImportError, Exception):
pass
return AES_CBC, Pukall_Cipher, Topaz_Cipher
AES_CBC, Pukall_Cipher, Topaz_Cipher = _load_crypto()
class KeyIVGen(object):
# this only exists in openssl so we will use pure python implementation instead
# PKCS5_PBKDF2_HMAC_SHA1 = F(c_int, 'PKCS5_PBKDF2_HMAC_SHA1',
# [c_char_p, c_ulong, c_char_p, c_ulong, c_ulong, c_ulong, c_char_p])
def pbkdf2(self, passwd, salt, iter, keylen):
def xorbytes( a, b ):
if len(a) != len(b):
raise Exception("xorbytes(): lengths differ")
return bytes([x ^ y for x, y in zip(a, b)])
def prf( h, data ):
hm = h.copy()
hm.update( data )
return hm.digest()
def pbkdf2_F( h, salt, itercount, blocknum ):
U = prf( h, salt + pack('>i',blocknum ) )
T = U
for i in range(2, itercount+1):
U = prf( h, U )
T = xorbytes( T, U )
return T
sha = hashlib.sha1
digest_size = sha().digest_size
# l - number of output blocks to produce
l = keylen // digest_size
if keylen % digest_size != 0:
l += 1
h = hmac.new( passwd, None, sha )
T = b""
for i in range(1, l+1):
T += pbkdf2_F( h, salt, iter, i )
return T[0: keylen]
================================================
FILE: DeDRM_plugin/androidkindlekey.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# androidkindlekey.py
# Copyright © 2010-20 by Thom, Apprentice Harper et al.
# Revision history:
# 1.0 - AmazonSecureStorage.xml decryption to serial number
# 1.1 - map_data_storage.db decryption to serial number
# 1.2 - Changed to be callable from AppleScript by returning only serial number
# - and changed name to androidkindlekey.py
# - and added in unicode command line support
# 1.3 - added in TkInter interface, output to a file
# 1.4 - Fix some problems identified by Aldo Bleeker
# 1.5 - Fix another problem identified by Aldo Bleeker
# 2.0 - Python 3 compatibility
"""
Retrieve Kindle for Android Serial Number.
"""
__license__ = 'GPL v3'
__version__ = '2.0'
import os
import sys
import traceback
import getopt
import tempfile
import zlib
import tarfile
from hashlib import md5
from io import BytesIO
from binascii import a2b_hex, b2a_hex
# Routines common to Mac and PC
# Wrap a stream so that output gets flushed immediately
# and also make sure that any unicode strings get
# encoded using "replace" before writing them.
class SafeUnbuffered:
def __init__(self, stream):
self.stream = stream
self.encoding = stream.encoding
if self.encoding == None:
self.encoding = "utf-8"
def write(self, data):
if isinstance(data,str):
data = data.encode(self.encoding,"replace")
self.stream.buffer.write(data)
self.stream.buffer.flush()
def __getattr__(self, attr):
return getattr(self.stream, attr)
try:
from calibre.constants import iswindows, isosx
except:
iswindows = sys.platform.startswith('win')
isosx = sys.platform.startswith('darwin')
def unicode_argv():
if iswindows:
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'. So use shell32.GetCommandLineArgvW to get sys.argv
# as a list of Unicode strings and encode them as utf-8
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return ["kindlekey.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
class DrmException(Exception):
pass
STORAGE = "backup.ab"
STORAGE1 = "AmazonSecureStorage.xml"
STORAGE2 = "map_data_storage.db"
class AndroidObfuscation(object):
'''AndroidObfuscation
For the key, it's written in java, and run in android dalvikvm
'''
key = a2b_hex('0176e04c9408b1702d90be333fd53523')
def encrypt(self, plaintext):
cipher = self._get_cipher()
padding = len(self.key) - len(plaintext) % len(self.key)
plaintext += chr(padding) * padding
return b2a_hex(cipher.encrypt(plaintext.encode('utf-8')))
def decrypt(self, ciphertext):
cipher = self._get_cipher()
plaintext = cipher.decrypt(a2b_hex(ciphertext))
return plaintext[:-ord(plaintext[-1])]
def _get_cipher(self):
try:
from Crypto.Cipher import AES
return AES.new(self.key)
except ImportError:
from aescbc import AES, noPadding
return AES(self.key, padding=noPadding())
class AndroidObfuscationV2(AndroidObfuscation):
'''AndroidObfuscationV2
'''
count = 503
password = b'Thomsun was here!'
def __init__(self, salt):
key = self.password + salt
for _ in range(self.count):
key = md5(key).digest()
self.key = key[:8]
self.iv = key[8:16]
def _get_cipher(self):
try :
from Crypto.Cipher import DES
return DES.new(self.key, DES.MODE_CBC, self.iv)
except ImportError:
from python_des import Des, CBC
return Des(self.key, CBC, self.iv)
def parse_preference(path):
''' parse android's shared preference xml '''
storage = {}
read = open(path)
for line in read:
line = line.strip()
# <string name="key">value</string>
if line.startswith('<string name="'):
index = line.find('"', 14)
key = line[14:index]
value = line[index+2:-9]
storage[key] = value
read.close()
return storage
def get_serials1(path=STORAGE1):
''' get serials from android's shared preference xml '''
if not os.path.isfile(path):
return []
storage = parse_preference(path)
salt = storage.get('AmazonSaltKey')
if salt and len(salt) == 16:
obfuscation = AndroidObfuscationV2(a2b_hex(salt))
else:
obfuscation = AndroidObfuscation()
def get_value(key):
encrypted_key = obfuscation.encrypt(key)
encrypted_value = storage.get(encrypted_key)
if encrypted_value:
return obfuscation.decrypt(encrypted_value)
return ''
# also see getK4Pids in kgenpids.py
try:
dsnid = get_value('DsnId')
except:
sys.stderr.write('cannot get DsnId\n')
return []
try:
tokens = set(get_value('kindle.account.tokens').split(','))
except:
sys.stderr.write('cannot get kindle account tokens\n')
return []
serials = []
if dsnid:
serials.append(dsnid)
for token in tokens:
if token:
serials.append('%s%s' % (dsnid, token))
serials.append(token)
return serials
def get_serials2(path=STORAGE2):
''' get serials from android's sql database '''
if not os.path.isfile(path):
return []
import sqlite3
connection = sqlite3.connect(path)
cursor = connection.cursor()
cursor.execute('''select device_data_value from device_data where device_data_key like '%serial.number%' ''')
device_data_keys = cursor.fetchall()
dsns = []
for device_data_row in device_data_keys:
try:
if device_data_row and device_data_row[0]:
if len(device_data_row[0]) > 0:
dsns.append(device_data_row[0])
except:
print("Error getting one of the device serial name keys")
traceback.print_exc()
pass
dsns = list(set(dsns))
cursor.execute('''select userdata_value from userdata where userdata_key like '%/%kindle.account.tokens%' ''')
userdata_keys = cursor.fetchall()
tokens = []
for userdata_row in userdata_keys:
try:
if userdata_row and userdata_row[0]:
if len(userdata_row[0]) > 0:
if ',' in userdata_row[0]:
splits = userdata_row[0].split(',')
for split in splits:
tokens.append(split)
tokens.append(userdata_row[0])
except:
print("Error getting one of the account token keys")
traceback.print_exc()
pass
tokens = list(set(tokens))
serials = []
for x in dsns:
serials.append(x)
for y in tokens:
serials.append(y)
serials.append(x+y)
return serials
def get_serials(path=STORAGE):
'''get serials from files in from android backup.ab
backup.ab can be get using adb command:
shell> adb backup com.amazon.kindle
or from individual files if they're passed.
'''
if not os.path.isfile(path):
return []
basename = os.path.basename(path)
if basename == STORAGE1:
return get_serials1(path)
elif basename == STORAGE2:
return get_serials2(path)
output = None
try :
read = open(path, 'rb')
head = read.read(24)
if head[:14] == b'ANDROID BACKUP':
output = BytesIO(zlib.decompress(read.read()))
except Exception:
pass
finally:
read.close()
if not output:
return []
serials = []
tar = tarfile.open(fileobj=output)
for member in tar.getmembers():
if member.name.strip().endswith(STORAGE1):
write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
write.write(tar.extractfile(member).read())
write.close()
write_path = os.path.abspath(write.name)
serials.extend(get_serials1(write_path))
os.remove(write_path)
elif member.name.strip().endswith(STORAGE2):
write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
write.write(tar.extractfile(member).read())
write.close()
write_path = os.path.abspath(write.name)
serials.extend(get_serials2(write_path))
os.remove(write_path)
return list(set(serials))
__all__ = [ 'get_serials', 'getkey']
# procedure for CLI and GUI interfaces
# returns single or multiple keys (one per line) in the specified file
def getkey(outfile, inpath):
keys = get_serials(inpath)
if len(keys) > 0:
with open(outfile, 'w') as keyfileout:
for key in keys:
keyfileout.write(key)
keyfileout.write("\n")
return True
return False
def usage(progname):
print("Decrypts the serial number(s) of Kindle For Android from Android backup or file")
print("Get backup.ab file using adb backup com.amazon.kindle for Android 4.0+.")
print("Otherwise extract AmazonSecureStorage.xml from /data/data/com.amazon.kindle/shared_prefs/AmazonSecureStorage.xml")
print("Or map_data_storage.db from /data/data/com.amazon.kindle/databases/map_data_storage.db")
print("")
print("Usage:")
print(" {0:s} [-h] [-b <backup.ab>] [<outfile.k4a>]".format(progname))
def cli_main():
sys.stdout=SafeUnbuffered(sys.stdout)
sys.stderr=SafeUnbuffered(sys.stderr)
argv=unicode_argv()
progname = os.path.basename(argv[0])
print("{0} v{1}\nCopyright © 2010-2020 Thom, Apprentice Harper et al.".format(progname,__version__))
try:
opts, args = getopt.getopt(argv[1:], "hb:")
except getopt.GetoptError as err:
usage(progname)
print("\nError in options or arguments: {0}".format(err.args[0]))
return 2
inpath = ""
for o, a in opts:
if o == "-h":
usage(progname)
return 0
if o == "-b":
inpath = a
if len(args) > 1:
usage(progname)
return 2
if len(args) == 1:
# save to the specified file or directory
outfile = args[0]
if not os.path.isabs(outfile):
outfile = os.path.join(os.path.dirname(argv[0]),outfile)
outfile = os.path.abspath(outfile)
if os.path.isdir(outfile):
outfile = os.path.join(os.path.dirname(argv[0]),"androidkindlekey.k4a")
else:
# save to the same directory as the script
outfile = os.path.join(os.path.dirname(argv[0]),"androidkindlekey.k4a")
# make sure the outpath is OK
outfile = os.path.realpath(os.path.normpath(outfile))
if not os.path.isfile(inpath):
usage(progname)
print("\n{0:s} file not found".format(inpath))
return 2
if getkey(outfile, inpath):
print("\nSaved Kindle for Android key to {0}".format(outfile))
else:
print("\nCould not retrieve Kindle for Android key.")
return 0
def gui_main():
try:
import tkinter
import tkinter.constants
import tkinter.messagebox
import tkinter.filedialog
except:
print("tkinter not installed")
return 0
class DecryptionDialog(tkinter.Frame):
def __init__(self, root):
tkinter.Frame.__init__(self, root, border=5)
self.status = tkinter.Label(self, text="Select backup.ab file")
self.status.pack(fill=tkinter.constants.X, expand=1)
body = tkinter.Frame(self)
body.pack(fill=tkinter.constants.X, expand=1)
sticky = tkinter.constants.E + tkinter.constants.W
body.grid_columnconfigure(1, weight=2)
tkinter.Label(body, text="Backup file").grid(row=0, column=0)
self.keypath = tkinter.Entry(body, width=40)
self.keypath.grid(row=0, column=1, sticky=sticky)
self.keypath.insert(2, "backup.ab")
button = tkinter.Button(body, text="...", command=self.get_keypath)
button.grid(row=0, column=2)
buttons = tkinter.Frame(self)
buttons.pack()
button2 = tkinter.Button(
buttons, text="Extract", width=10, command=self.generate)
button2.pack(side=tkinter.constants.LEFT)
tkinter.Frame(buttons, width=10).pack(side=tkinter.constants.LEFT)
button3 = tkinter.Button(
buttons, text="Quit", width=10, command=self.quit)
button3.pack(side=tkinter.constants.RIGHT)
def get_keypath(self):
keypath = tkinter.filedialog.askopenfilename(
parent=None, title="Select backup.ab file",
defaultextension=".ab",
filetypes=[('adb backup com.amazon.kindle', '.ab'),
('All Files', '.*')])
if keypath:
keypath = os.path.normpath(keypath)
self.keypath.delete(0, tkinter.constants.END)
self.keypath.insert(0, keypath)
return
def generate(self):
inpath = self.keypath.get()
self.status['text'] = "Getting key..."
try:
keys = get_serials(inpath)
keycount = 0
for key in keys:
while True:
keycount += 1
outfile = os.path.join(progpath,"kindlekey{0:d}.k4a".format(keycount))
if not os.path.exists(outfile):
break
with open(outfile, 'w') as keyfileout:
keyfileout.write(key)
success = True
tkinter.messagebox.showinfo(progname, "Key successfully retrieved to {0}".format(outfile))
except Exception as e:
self.status['text'] = "Error: {0}".format(e.args[0])
return
self.status['text'] = "Select backup.ab file"
argv=unicode_argv()
progpath, progname = os.path.split(argv[0])
root = tkinter.Tk()
root.title("Kindle for Android Key Extraction v.{0}".format(__version__))
root.resizable(True, False)
root.minsize(300, 0)
DecryptionDialog(root).pack(fill=tkinter.constants.X, expand=1)
root.mainloop()
return 0
if __name__ == '__main__':
if len(sys.argv) > 1:
sys.exit(cli_main())
sys.exit(gui_main())
================================================
FILE: DeDRM_plugin/argv_utils.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys, os
import locale
import codecs
import importlib
# get sys.argv arguments and encode them into utf-8
def unicode_argv():
if sys.platform.startswith('win'):
# Uses shell32.GetCommandLineArgvW to get sys.argv as a list of Unicode
# strings.
# Versions 2.x of Python don't support Unicode in sys.argv on
# Windows, with the underlying Windows API instead replacing multi-byte
# characters with '?'.
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
GetCommandLineW = cdll.kernel32.GetCommandLineW
GetCommandLineW.argtypes = []
GetCommandLineW.restype = LPCWSTR
CommandLineToArgvW = windll.shell32.CommandLineToArgvW
CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(c_int)]
CommandLineToArgvW.restype = POINTER(LPWSTR)
cmd = GetCommandLineW()
argc = c_int(0)
argv = CommandLineToArgvW(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in
range(start, argc.value)]
# if we don't have any arguments at all, just pass back script name
# this should never happen
return ["DeDRM.py"]
else:
argvencoding = sys.stdin.encoding or "utf-8"
return [arg if isinstance(arg, str) else str(arg, argvencoding) for arg in sys.argv]
def add_cp65001_codec():
try:
codecs.lookup('cp65001')
except LookupError:
codecs.register(
lambda name: name == 'cp65001' and codecs.lookup('utf-8') or None)
return
def set_utf8_default_encoding():
if sys.getdefaultencoding() == 'utf-8':
return
# Regenerate setdefaultencoding.
importlib.reload(sys)
sys.setdefaultencoding('utf-8')
for attr in dir(locale):
if attr[0:3] != 'LC_':
continue
aref = getattr(locale, attr)
try:
locale.setlocale(aref, '')
except locale.Error:
continue
try:
lang = locale.getlocale(aref)[0]
except (TypeError, ValueError):
continue
if lang:
try:
locale.setlocale(aref, (lang, 'UTF-8'))
except locale.Error:
os.environ[attr] = lang + '.UTF-8'
try:
locale.setlocale(locale.LC_ALL, '')
except locale.Error:
pass
return
================================================
FILE: DeDRM_plugin/askfolder_ed.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab
# to work around tk_chooseDirectory not properly returning unicode paths on Windows
# need to use a dialog that can be hacked up to actually return full unicode paths
# originally based on AskFolder from EasyDialogs for Windows but modified to fix it
# to actually use unicode for path
# The original license for EasyDialogs is as follows
#
# Copyright (c) 2003-2005 Jimmy Retzlaff
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
# Adjusted for Python 3, September 2020
"""
AskFolder(...) -- Ask the user to select a folder Windows specific
"""
import os
import ctypes
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
import ctypes.wintypes as wintypes
__all__ = ['AskFolder']
# Load required Windows DLLs
ole32 = ctypes.windll.ole32
shell32 = ctypes.windll.shell32
user32 = ctypes.windll.user32
# Windows Constants
BFFM_INITIALIZED = 1
BFFM_SETOKTEXT = 1129
BFFM_SETSELECTIONA = 1126
BFFM_SETSELECTIONW = 1127
BIF_EDITBOX = 16
BS_DEFPUSHBUTTON = 1
CB_ADDSTRING = 323
CB_GETCURSEL = 327
CB_SETCURSEL = 334
CDM_SETCONTROLTEXT = 1128
EM_GETLINECOUNT = 186
EM_GETMARGINS = 212
EM_POSFROMCHAR = 214
EM_SETSEL = 177
GWL_STYLE = -16
IDC_STATIC = -1
IDCANCEL = 2
IDNO = 7
IDOK = 1
IDYES = 6
MAX_PATH = 260
OFN_ALLOWMULTISELECT = 512
OFN_ENABLEHOOK = 32
OFN_ENABLESIZING = 8388608
OFN_ENABLETEMPLATEHANDLE = 128
OFN_EXPLORER = 524288
OFN_OVERWRITEPROMPT = 2
OPENFILENAME_SIZE_VERSION_400 = 76
PBM_GETPOS = 1032
PBM_SETMARQUEE = 1034
PBM_SETPOS = 1026
PBM_SETRANGE = 1025
PBM_SETRANGE32 = 1030
PBS_MARQUEE = 8
PM_REMOVE = 1
SW_HIDE = 0
SW_SHOW = 5
SW_SHOWNORMAL = 1
SWP_NOACTIVATE = 16
SWP_NOMOVE = 2
SWP_NOSIZE = 1
SWP_NOZORDER = 4
VER_PLATFORM_WIN32_NT = 2
WM_COMMAND = 273
WM_GETTEXT = 13
WM_GETTEXTLENGTH = 14
WM_INITDIALOG = 272
WM_NOTIFY = 78
# Windows function prototypes
BrowseCallbackProc = ctypes.WINFUNCTYPE(ctypes.c_int, wintypes.HWND, ctypes.c_uint, wintypes.LPARAM, wintypes.LPARAM)
# Windows types
LPCTSTR = ctypes.c_char_p
LPTSTR = ctypes.c_char_p
LPVOID = ctypes.c_voidp
TCHAR = ctypes.c_char
class BROWSEINFO(ctypes.Structure):
_fields_ = [
("hwndOwner", wintypes.HWND),
("pidlRoot", LPVOID),
("pszDisplayName", LPTSTR),
("lpszTitle", LPCTSTR),
("ulFlags", ctypes.c_uint),
("lpfn", BrowseCallbackProc),
("lParam", wintypes.LPARAM),
("iImage", ctypes.c_int)
]
# Utilities
def CenterWindow(hwnd):
desktopRect = GetWindowRect(user32.GetDesktopWindow())
myRect = GetWindowRect(hwnd)
x = width(desktopRect) // 2 - width(myRect) // 2
y = height(desktopRect) // 2 - height(myRect) // 2
user32.SetWindowPos(hwnd, 0,
desktopRect.left + x,
desktopRect.top + y,
0, 0,
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER
)
def GetWindowRect(hwnd):
rect = wintypes.RECT()
user32.GetWindowRect(hwnd, ctypes.byref(rect))
return rect
def width(rect):
return rect.right-rect.left
def height(rect):
return rect.bottom-rect.top
def AskFolder(
message=None,
version=None,
defaultLocation=None,
location=None,
windowTitle=None,
actionButtonLabel=None,
cancelButtonLabel=None,
multiple=None):
"""Display a dialog asking the user for select a folder.
modified to use unicode strings as much as possible
returns unicode path
"""
def BrowseCallback(hwnd, uMsg, lParam, lpData):
if uMsg == BFFM_INITIALIZED:
if actionButtonLabel:
label = str(actionButtonLabel, errors='replace')
user32.SendMessageW(hwnd, BFFM_SETOKTEXT, 0, label)
if cancelButtonLabel:
label = str(cancelButtonLabel, errors='replace')
cancelButton = user32.GetDlgItem(hwnd, IDCANCEL)
if cancelButton:
user32.SetWindowTextW(cancelButton, label)
if windowTitle:
title = str(windowTitle, errors='replace')
user32.SetWindowTextW(hwnd, title)
if defaultLocation:
user32.SendMessageW(hwnd, BFFM_SETSELECTIONW, 1, defaultLocation.replace('/', '\\'))
if location:
x, y = location
desktopRect = wintypes.RECT()
user32.GetWindowRect(0, ctypes.byref(desktopRect))
user32.SetWindowPos(hwnd, 0,
desktopRect.left + x,
desktopRect.top + y, 0, 0,
SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER)
else:
CenterWindow(hwnd)
return 0
# This next line is needed to prevent gc of the callback
callback = BrowseCallbackProc(BrowseCallback)
browseInfo = BROWSEINFO()
browseInfo.pszDisplayName = ctypes.c_char_p('\0' * (MAX_PATH+1))
browseInfo.lpszTitle = message
browseInfo.lpfn = callback
pidl = shell32.SHBrowseForFolder(ctypes.byref(browseInfo))
if not pidl:
result = None
else:
path = LPCWSTR(" " * (MAX_PATH+1))
shell32.SHGetPathFromIDListW(pidl, path)
ole32.CoTaskMemFree(pidl)
result = path.value
return result
================================================
FILE: DeDRM_plugin/config.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
__license__ = 'GPL v3'
# Python 3, September 2020
# Standard Python modules.
import os, traceback, json, codecs
from PyQt5.Qt import (Qt, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QLineEdit,
QGroupBox, QPushButton, QListWidget, QListWidgetItem,
QAbstractItemView, QIcon, QDialog, QDialogButtonBox, QUrl)
from PyQt5 import Qt as QtGui
from zipfile import ZipFile
# calibre modules and constants.
from calibre.gui2 import (error_dialog, question_dialog, info_dialog, open_url,
choose_dir, choose_files, choose_save_file)
from calibre.utils.config import dynamic, config_dir, JSONConfig
from calibre.constants import iswindows, isosx
# modules from this plugin's zipfile.
from calibre_plugins.dedrm.__init__ import PLUGIN_NAME, PLUGIN_VERSION
from calibre_plugins.dedrm.__init__ import RESOURCE_NAME as help_file_name
from calibre_plugins.dedrm.utilities import uStrCmp
import calibre_plugins.dedrm.prefs as prefs
import calibre_plugins.dedrm.androidkindlekey as androidkindlekey
class ConfigWidget(QWidget):
def __init__(self, plugin_path, alfdir):
QWidget.__init__(self)
self.plugin_path = plugin_path
self.alfdir = alfdir
# get the prefs
self.dedrmprefs = prefs.DeDRM_Prefs()
# make a local copy
self.tempdedrmprefs = {}
self.tempdedrmprefs['bandnkeys'] = self.dedrmprefs['bandnkeys'].copy()
self.tempdedrmprefs['adeptkeys'] = self.dedrmprefs['adeptkeys'].copy()
self.tempdedrmprefs['ereaderkeys'] = self.dedrmprefs['ereaderkeys'].copy()
self.tempdedrmprefs['kindlekeys'] = self.dedrmprefs['kindlekeys'].copy()
self.tempdedrmprefs['androidkeys'] = self.dedrmprefs['androidkeys'].copy()
self.tempdedrmprefs['pids'] = list(self.dedrmprefs['pids'])
self.tempdedrmprefs['serials'] = list(self.dedrmprefs['serials'])
self.tempdedrmprefs['adobewineprefix'] = self.dedrmprefs['adobewineprefix']
self.tempdedrmprefs['kindlewineprefix'] = self.dedrmprefs['kindlewineprefix']
# Start Qt Gui dialog layout
layout = QVBoxLayout(self)
self.setLayout(layout)
help_layout = QHBoxLayout()
layout.addLayout(help_layout)
# Add hyperlink to a help file at the right. We will replace the correct name when it is clicked.
help_label = QLabel('<a href="http://www.foo.com/">Plugin Help</a>', self)
help_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse | Qt.LinksAccessibleByKeyboard)
help_label.setAlignment(Qt.AlignRight)
help_label.linkActivated.connect(self.help_link_activated)
help_layout.addWidget(help_label)
keys_group_box = QGroupBox(_('Configuration:'), self)
layout.addWidget(keys_group_box)
keys_group_box_layout = QHBoxLayout()
keys_group_box.setLayout(keys_group_box_layout)
button_layout = QVBoxLayout()
keys_group_box_layout.addLayout(button_layout)
self.bandn_button = QtGui.QPushButton(self)
self.bandn_button.setToolTip(_("Click to manage keys for Barnes and Noble ebooks"))
self.bandn_button.setText("Barnes and Noble ebooks")
self.bandn_button.clicked.connect(self.bandn_keys)
self.kindle_android_button = QtGui.QPushButton(self)
self.kindle_android_button.setToolTip(_("Click to manage keys for Kindle for Android ebooks"))
self.kindle_android_button.setText("Kindle for Android ebooks")
self.kindle_android_button.clicked.connect(self.kindle_android)
self.kindle_serial_button = QtGui.QPushButton(self)
self.kindle_serial_button.setToolTip(_("Click to manage eInk Kindle serial numbers for Kindle ebooks"))
self.kindle_serial_button.setText("eInk Kindle ebooks")
self.kindle_serial_button.clicked.connect(self.kindle_serials)
self.kindle_key_button = QtGui.QPushButton(self)
self.kindle_key_button.setToolTip(_("Click to manage keys for Kindle for Mac/PC ebooks"))
self.kindle_key_button.setText("Kindle for Mac/PC ebooks")
self.kindle_key_button.clicked.connect(self.kindle_keys)
self.adept_button = QtGui.QPushButton(self)
self.adept_button.setToolTip(_("Click to manage keys for Adobe Digital Editions ebooks"))
self.adept_button.setText("Adobe Digital Editions ebooks")
self.adept_button.clicked.connect(self.adept_keys)
self.mobi_button = QtGui.QPushButton(self)
self.mobi_button.setToolTip(_("Click to manage PIDs for Mobipocket ebooks"))
self.mobi_button.setText("Mobipocket ebooks")
self.mobi_button.clicked.connect(self.mobi_keys)
self.ereader_button = QtGui.QPushButton(self)
self.ereader_button.setToolTip(_("Click to manage keys for eReader ebooks"))
self.ereader_button.setText("eReader ebooks")
self.ereader_button.clicked.connect(self.ereader_keys)
button_layout.addWidget(self.kindle_serial_button)
button_layout.addWidget(self.kindle_android_button)
button_layout.addWidget(self.bandn_button)
button_layout.addWidget(self.mobi_button)
button_layout.addWidget(self.ereader_button)
button_layout.addWidget(self.adept_button)
button_layout.addWidget(self.kindle_key_button)
self.resize(self.sizeHint())
def kindle_serials(self):
d = ManageKeysDialog(self,"EInk Kindle Serial Number",self.tempdedrmprefs['serials'], AddSerialDialog)
d.exec_()
def kindle_android(self):
d = ManageKeysDialog(self,"Kindle for Android Key",self.tempdedrmprefs['androidkeys'], AddAndroidDialog, 'k4a')
d.exec_()
def kindle_keys(self):
if isosx or iswindows:
d = ManageKeysDialog(self,"Kindle for Mac and PC Key",self.tempdedrmprefs['kindlekeys'], AddKindleDialog, 'k4i')
else:
# linux
d = ManageKeysDialog(self,"Kindle for Mac and PC Key",self.tempdedrmprefs['kindlekeys'], AddKindleDialog, 'k4i', self.tempdedrmprefs['kindlewineprefix'])
d.exec_()
self.tempdedrmprefs['kindlewineprefix'] = d.getwineprefix()
def adept_keys(self):
if isosx or iswindows:
d = ManageKeysDialog(self,"Adobe Digital Editions Key",self.tempdedrmprefs['adeptkeys'], AddAdeptDialog, 'der')
else:
# linux
d = ManageKeysDialog(self,"Adobe Digital Editions Key",self.tempdedrmprefs['adeptkeys'], AddAdeptDialog, 'der', self.tempdedrmprefs['adobewineprefix'])
d.exec_()
self.tempdedrmprefs['adobewineprefix'] = d.getwineprefix()
def mobi_keys(self):
d = ManageKeysDialog(self,"Mobipocket PID",self.tempdedrmprefs['pids'], AddPIDDialog)
d.exec_()
def bandn_keys(self):
d = ManageKeysDialog(self,"Barnes and Noble Key",self.tempdedrmprefs['bandnkeys'], AddBandNKeyDialog, 'b64')
d.exec_()
def ereader_keys(self):
d = ManageKeysDialog(self,"eReader Key",self.tempdedrmprefs['ereaderkeys'], AddEReaderDialog, 'b63')
d.exec_()
def help_link_activated(self, url):
def get_help_file_resource():
# Copy the HTML helpfile to the plugin directory each time the
# link is clicked in case the helpfile is updated in newer plugins.
file_path = os.path.join(config_dir, "plugins", "DeDRM", "help", help_file_name)
with open(file_path,'w') as f:
f.write(self.load_resource(help_file_name))
return file_path
url = 'file:///' + get_help_file_resource()
open_url(QUrl(url))
def save_settings(self):
self.dedrmprefs.set('bandnkeys', self.tempdedrmprefs['bandnkeys'])
self.dedrmprefs.set('adeptkeys', self.tempdedrmprefs['adeptkeys'])
self.dedrmprefs.set('ereaderkeys', self.tempdedrmprefs['ereaderkeys'])
self.dedrmprefs.set('kindlekeys', self.tempdedrmprefs['kindlekeys'])
self.dedrmprefs.set('androidkeys', self.tempdedrmprefs['androidkeys'])
self.dedrmprefs.set('pids', self.tempdedrmprefs['pids'])
self.dedrmprefs.set('serials', self.tempdedrmprefs['serials'])
self.dedrmprefs.set('adobewineprefix', self.tempdedrmprefs['adobewineprefix'])
self.dedrmprefs.set('kindlewineprefix', self.tempdedrmprefs['kindlewineprefix'])
self.dedrmprefs.set('configured', True)
self.dedrmprefs.writeprefs()
def load_resource(self, name):
with ZipFile(self.plugin_path, 'r') as zf:
if name in zf.namelist():
return zf.read(name).decode('utf-8')
return ""
class ManageKeysDialog(QDialog):
def __init__(self, parent, key_type_name, plugin_keys, create_key, keyfile_ext = "", wineprefix = None):
QDialog.__init__(self,parent)
self.parent = parent
self.key_type_name = key_type_name
self.plugin_keys = plugin_keys
self.create_key = create_key
self.keyfile_ext = keyfile_ext
self.import_key = (keyfile_ext != "")
self.binary_file = (keyfile_ext == "der")
self.json_file = (keyfile_ext == "k4i")
self.android_file = (keyfile_ext == "k4a")
self.wineprefix = wineprefix
self.setWindowTitle("{0} {1}: Manage {2}s".format(PLUGIN_NAME, PLUGIN_VERSION, self.key_type_name))
# Start Qt Gui dialog layout
layout = QVBoxLayout(self)
self.setLayout(layout)
help_layout = QHBoxLayout()
layout.addLayout(help_layout)
# Add hyperlink to a help file at the right. We will replace the correct name when it is clicked.
help_label = QLabel('<a href="http://www.foo.com/">Help</a>', self)
help_label.setTextInteractionFlags(Qt.LinksAccessibleByMouse | Qt.LinksAccessibleByKeyboard)
help_label.setAlignment(Qt.AlignRight)
help_label.linkActivated.connect(self.help_link_activated)
help_layout.addWidget(help_label)
keys_group_box = QGroupBox(_("{0}s".format(self.key_type_name)), self)
layout.addWidget(keys_group_box)
keys_group_box_layout = QHBoxLayout()
keys_group_box.setLayout(keys_group_box_layout)
self.listy = QListWidget(self)
self.listy.setToolTip("{0}s that will be used to decrypt ebooks".format(self.key_type_name))
self.listy.setSelectionMode(QAbstractItemView.SingleSelection)
self.populate_list()
keys_group_box_layout.addWidget(self.listy)
button_layout = QVBoxLayout()
keys_group_box_layout.addLayout(button_layout)
self._add_key_button = QtGui.QToolButton(self)
self._add_key_button.setIcon(QIcon(I('plus.png')))
self._add_key_button.setToolTip("Create new {0}".format(self.key_type_name))
self._add_key_button.clicked.connect(self.add_key)
button_layout.addWidget(self._add_key_button)
self._delete_key_button = QtGui.QToolButton(self)
self._delete_key_button.setToolTip(_("Delete highlighted key"))
self._delete_key_button.setIcon(QIcon(I('list_remove.png')))
self._delete_key_button.clicked.connect(self.delete_key)
button_layout.addWidget(self._delete_key_button)
if type(self.plugin_keys) == dict and self.import_key:
self._rename_key_button = QtGui.QToolButton(self)
self._rename_key_button.setToolTip(_("Rename highlighted key"))
self._rename_key_button.setIcon(QIcon(I('edit-select-all.png')))
self._rename_key_button.clicked.connect(self.rename_key)
button_layout.addWidget(self._rename_key_button)
self.export_key_button = QtGui.QToolButton(self)
self.export_key_button.setToolTip("Save highlighted key to a .{0} file".format(self.keyfile_ext))
self.export_key_button.setIcon(QIcon(I('save.png')))
self.export_key_button.clicked.connect(self.export_key)
button_layout.addWidget(self.export_key_button)
spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding)
button_layout.addItem(spacerItem)
if self.wineprefix is not None:
layout.addSpacing(5)
wineprefix_layout = QHBoxLayout()
layout.addLayout(wineprefix_layout)
wineprefix_layout.setAlignment(Qt.AlignCenter)
self.wp_label = QLabel("WINEPREFIX:")
wineprefix_layout.addWidget(self.wp_label)
self.wp_lineedit = QLineEdit(self)
wineprefix_layout.addWidget(self.wp_lineedit)
self.wp_label.setBuddy(self.wp_lineedit)
self.wp_lineedit.setText(self.wineprefix)
layout.addSpacing(5)
migrate_layout = QHBoxLayout()
layout.addLayout(migrate_layout)
if self.import_key:
migrate_layout.setAlignment(Qt.AlignJustify)
self.migrate_btn = QPushButton("Import Existing Keyfiles", self)
self.migrate_btn.setToolTip("Import *.{0} files (created using other tools).".format(self.keyfile_ext))
self.migrate_btn.clicked.connect(self.migrate_wrapper)
migrate_layout.addWidget(self.migrate_btn)
migrate_layout.addStretch()
self.button_box = QDialogButtonBox(QDialogButtonBox.Close)
self.button_box.rejected.connect(self.close)
migrate_layout.addWidget(self.button_box)
self.resize(self.sizeHint())
def getwineprefix(self):
if self.wineprefix is not None:
return str(self.wp_lineedit.text()).strip()
return ""
def populate_list(self):
if type(self.plugin_keys) == dict:
for key in self.plugin_keys.keys():
self.listy.addItem(QListWidgetItem(key))
else:
for key in self.plugin_keys:
self.listy.addItem(QListWidgetItem(key))
def add_key(self):
d = self.create_key(self)
d.exec_()
if d.result() != d.Accepted:
# New key generation cancelled.
return
new_key_value = d.key_value
if type(self.plugin_keys) == dict:
if new_key_value in self.plugin_keys.values():
old_key_name = [name for name, value in self.plugin_keys.items() if value == new_key_value][0]
info_dialog(None, "{0} {1}: Duplicate {2}".format(PLUGIN_NAME, PLUGIN_VERSION,self.key_type_name),
"The new {1} is the same as the existing {1} named <strong>{0}</strong> and has not been added.".format(old_key_name,self.key_type_name), show=True)
return
self.plugin_keys[d.key_name] = new_key_value
else:
if new_key_value in self.plugin_keys:
info_dialog(None, "{0} {1}: Duplicate {2}".format(PLUGIN_NAME, PLUGIN_VERSION,self.key_type_name),
"This {0} is already in the list of {0}s has not been added.".format(self.key_type_name), show=True)
return
self.plugin_keys.append(d.key_value)
self.listy.clear()
self.populate_list()
def rename_key(self):
if not self.listy.currentItem():
errmsg = "No {0} selected to rename. Highlight a keyfile first.".format(self.key_type_name)
r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
return
d = RenameKeyDialog(self)
d.exec_()
if d.result() != d.Accepted:
# rename cancelled or moot.
return
keyname = str(self.listy.currentItem().text())
if not question_dialog(self, "{0} {1}: Confirm Rename".format(PLUGIN_NAME, PLUGIN_VERSION), "Do you really want to rename the {2} named <strong>{0}</strong> to <strong>{1}</strong>?".format(keyname,d.key_name,self.key_type_name), show_copy_button=False, default_yes=False):
return
self.plugin_keys[d.key_name] = self.plugin_keys[keyname]
del self.plugin_keys[keyname]
self.listy.clear()
self.populate_list()
def delete_key(self):
if not self.listy.currentItem():
return
keyname = str(self.listy.currentItem().text())
if not question_dialog(self, "{0} {1}: Confirm Delete".format(PLUGIN_NAME, PLUGIN_VERSION), "Do you really want to delete the {1} <strong>{0}</strong>?".format(keyname, self.key_type_name), show_copy_button=False, default_yes=False):
return
if type(self.plugin_keys) == dict:
del self.plugin_keys[keyname]
else:
self.plugin_keys.remove(keyname)
self.listy.clear()
self.populate_list()
def help_link_activated(self, url):
def get_help_file_resource():
# Copy the HTML helpfile to the plugin directory each time the
# link is clicked in case the helpfile is updated in newer plugins.
help_file_name = "{0}_{1}_Help.htm".format(PLUGIN_NAME, self.key_type_name)
file_path = os.path.join(config_dir, "plugins", "DeDRM", "help", help_file_name)
with open(file_path,'w') as f:
f.write(self.parent.load_resource(help_file_name))
return file_path
url = 'file:///' + get_help_file_resource()
open_url(QUrl(url))
def migrate_files(self):
unique_dlg_name = PLUGIN_NAME + "import {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory
caption = "Select {0} files to import".format(self.key_type_name)
filters = [("{0} files".format(self.key_type_name), [self.keyfile_ext])]
files = choose_files(self, unique_dlg_name, caption, filters, all_files=False)
counter = 0
skipped = 0
if files:
for filename in files:
fpath = os.path.join(config_dir, filename)
filename = os.path.basename(filename)
new_key_name = os.path.splitext(os.path.basename(filename))[0]
with open(fpath,'rb') as keyfile:
new_key_value = keyfile.read()
if self.binary_file:
new_key_value = codecs.encode(new_key_value,'hex')
elif self.json_file:
new_key_value = json.loads(new_key_value)
elif self.android_file:
# convert to list of the keys in the string
new_key_value = new_key_value.splitlines()
match = False
for key in self.plugin_keys.keys():
if uStrCmp(new_key_name, key, True):
skipped += 1
msg = "A key with the name <strong>{0}</strong> already exists!\nSkipping key file <strong>{1}</strong>.\nRename the existing key and import again".format(new_key_name,filename)
inf = info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(msg), show_copy_button=False, show=True)
match = True
break
if not match:
if new_key_value in self.plugin_keys.values():
old_key_name = [name for name, value in self.plugin_keys.items() if value == new_key_value][0]
skipped += 1
info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
"The key in file {0} is the same as the existing key <strong>{1}</strong> and has been skipped.".format(filename,old_key_name), show_copy_button=False, show=True)
else:
counter += 1
self.plugin_keys[new_key_name] = new_key_value
msg = ""
if counter+skipped > 1:
if counter > 0:
msg += "Imported <strong>{0:d}</strong> key {1}. ".format(counter, "file" if counter == 1 else "files")
if skipped > 0:
msg += "Skipped <strong>{0:d}</strong> key {1}.".format(skipped, "file" if counter == 1 else "files")
inf = info_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(msg), show_copy_button=False, show=True)
return counter > 0
def migrate_wrapper(self):
if self.migrate_files():
self.listy.clear()
self.populate_list()
def export_key(self):
if not self.listy.currentItem():
errmsg = "No keyfile selected to export. Highlight a keyfile first."
r = error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
return
keyname = str(self.listy.currentItem().text())
unique_dlg_name = PLUGIN_NAME + "export {0} keys".format(self.key_type_name).replace(' ', '_') #takes care of automatically remembering last directory
caption = "Save {0} File as...".format(self.key_type_name)
filters = [("{0} Files".format(self.key_type_name), ["{0}".format(self.keyfile_ext)])]
defaultname = "{0}.{1}".format(keyname, self.keyfile_ext)
filename = choose_save_file(self, unique_dlg_name, caption, filters, all_files=False, initial_filename=defaultname)
if filename:
if self.binary_file:
with open(filename, 'wb') as fname:
fname.write(codecs.decode(self.plugin_keys[keyname],'hex'))
elif self.json_file:
with open(filename, 'w') as fname:
fname.write(json.dumps(self.plugin_keys[keyname]))
elif self.android_file:
with open(filename, 'w') as fname:
for key in self.plugin_keys[keyname]:
fname.write(key)
fname.write('\n')
else:
with open(filename, 'w') as fname:
fname.write(self.plugin_keys[keyname])
class RenameKeyDialog(QDialog):
def __init__(self, parent=None,):
print(repr(self), repr(parent))
QDialog.__init__(self, parent)
self.parent = parent
self.setWindowTitle("{0} {1}: Rename {0}".format(PLUGIN_NAME, PLUGIN_VERSION, parent.key_type_name))
layout = QVBoxLayout(self)
self.setLayout(layout)
data_group_box = QGroupBox('', self)
layout.addWidget(data_group_box)
data_group_box_layout = QVBoxLayout()
data_group_box.setLayout(data_group_box_layout)
data_group_box_layout.addWidget(QLabel('New Key Name:', self))
self.key_ledit = QLineEdit(self.parent.listy.currentItem().text(), self)
self.key_ledit.setToolTip("Enter a new name for this existing {0}.".format(parent.key_type_name))
data_group_box_layout.addWidget(self.key_ledit)
layout.addSpacing(20)
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.button_box.accepted.connect(self.accept)
self.button_box.rejected.connect(self.reject)
layout.addWidget(self.button_box)
self.resize(self.sizeHint())
def accept(self):
if not str(self.key_ledit.text()) or str(self.key_ledit.text()).isspace():
errmsg = "Key name field cannot be empty!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
if len(self.key_ledit.text()) < 4:
errmsg = "Key name must be at <i>least</i> 4 characters long!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
if uStrCmp(self.key_ledit.text(), self.parent.listy.currentItem().text()):
# Same exact name ... do nothing.
return QDialog.reject(self)
for k in self.parent.plugin_keys.keys():
if (uStrCmp(self.key_ledit.text(), k, True) and
not uStrCmp(k, self.parent.listy.currentItem().text(), True)):
errmsg = "The key name <strong>{0}</strong> is already being used.".format(self.key_ledit.text())
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION),
_(errmsg), show=True, show_copy_button=False)
QDialog.accept(self)
@property
def key_name(self):
return str(self.key_ledit.text()).strip()
class AddBandNKeyDialog(QDialog):
def __init__(self, parent=None,):
QDialog.__init__(self, parent)
self.parent = parent
self.setWindowTitle("{0} {1}: Create New Barnes & Noble Key".format(PLUGIN_NAME, PLUGIN_VERSION))
layout = QVBoxLayout(self)
self.setLayout(layout)
data_group_box = QGroupBox("", self)
layout.addWidget(data_group_box)
data_group_box_layout = QVBoxLayout()
data_group_box.setLayout(data_group_box_layout)
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
key_group.addWidget(QLabel("Unique Key Name:", self))
self.key_ledit = QLineEdit("", self)
self.key_ledit.setToolTip(_("<p>Enter an identifying name for this new key.</p>" +
"<p>It should be something that will help you remember " +
"what personal information was used to create it."))
key_group.addWidget(self.key_ledit)
name_group = QHBoxLayout()
data_group_box_layout.addLayout(name_group)
name_group.addWidget(QLabel("B&N/nook account email address:", self))
self.name_ledit = QLineEdit("", self)
self.name_ledit.setToolTip(_("<p>Enter your email address as it appears in your B&N " +
"account.</p>" +
"<p>It will only be used to generate this " +
"key and won\'t be stored anywhere " +
"in calibre or on your computer.</p>" +
"<p>eg: apprenticeharper@gmail.com</p>"))
name_group.addWidget(self.name_ledit)
name_disclaimer_label = QLabel(_("(Will not be saved in configuration data)"), self)
name_disclaimer_label.setAlignment(Qt.AlignHCenter)
data_group_box_layout.addWidget(name_disclaimer_label)
ccn_group = QHBoxLayout()
data_group_box_layout.addLayout(ccn_group)
ccn_group.addWidget(QLabel("B&N/nook account password:", self))
self.cc_ledit = QLineEdit("", self)
self.cc_ledit.setToolTip(_("<p>Enter the password " +
"for your B&N account.</p>" +
"<p>The password will only be used to generate this " +
"key and won\'t be stored anywhere in " +
"calibre or on your computer."))
ccn_group.addWidget(self.cc_ledit)
ccn_disclaimer_label = QLabel(_('(Will not be saved in configuration data)'), self)
ccn_disclaimer_label.setAlignment(Qt.AlignHCenter)
data_group_box_layout.addWidget(ccn_disclaimer_label)
layout.addSpacing(10)
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
key_group.addWidget(QLabel("Retrieved key:", self))
self.key_display = QLabel("", self)
self.key_display.setToolTip(_("Click the Retrieve Key button to fetch your B&N encryption key from the B&N servers"))
key_group.addWidget(self.key_display)
self.retrieve_button = QtGui.QPushButton(self)
self.retrieve_button.setToolTip(_("Click to retrieve your B&N encryption key from the B&N servers"))
self.retrieve_button.setText("Retrieve Key")
self.retrieve_button.clicked.connect(self.retrieve_key)
key_group.addWidget(self.retrieve_button)
layout.addSpacing(10)
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.button_box.accepted.connect(self.accept)
self.button_box.rejected.connect(self.reject)
layout.addWidget(self.button_box)
self.resize(self.sizeHint())
@property
def key_name(self):
return str(self.key_ledit.text()).strip()
@property
def key_value(self):
return str(self.key_display.text()).strip()
@property
def user_name(self):
return str(self.name_ledit.text()).strip().lower().replace(' ','')
@property
def cc_number(self):
return str(self.cc_ledit.text()).strip()
def retrieve_key(self):
from calibre_plugins.dedrm.ignoblekeyfetch import fetch_key as fetch_bandn_key
fetched_key = fetch_bandn_key(self.user_name,self.cc_number)
if fetched_key == "":
errmsg = "Could not retrieve key. Check username, password and intenet connectivity and try again."
error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
else:
self.key_display.setText(fetched_key)
def accept(self):
if len(self.key_name) == 0 or len(self.user_name) == 0 or len(self.cc_number) == 0 or self.key_name.isspace() or self.user_name.isspace() or self.cc_number.isspace():
errmsg = "All fields are required!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_name) < 4:
errmsg = "Key name must be at <i>least</i> 4 characters long!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_value) == 0:
self.retrieve_key()
if len(self.key_value) == 0:
return
QDialog.accept(self)
class AddEReaderDialog(QDialog):
def __init__(self, parent=None,):
QDialog.__init__(self, parent)
self.parent = parent
self.setWindowTitle("{0} {1}: Create New eReader Key".format(PLUGIN_NAME, PLUGIN_VERSION))
layout = QVBoxLayout(self)
self.setLayout(layout)
data_group_box = QGroupBox("", self)
layout.addWidget(data_group_box)
data_group_box_layout = QVBoxLayout()
data_group_box.setLayout(data_group_box_layout)
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
key_group.addWidget(QLabel("Unique Key Name:", self))
self.key_ledit = QLineEdit("", self)
self.key_ledit.setToolTip("<p>Enter an identifying name for this new key.\nIt should be something that will help you remember what personal information was used to create it.")
key_group.addWidget(self.key_ledit)
name_group = QHBoxLayout()
data_group_box_layout.addLayout(name_group)
name_group.addWidget(QLabel("Your Name:", self))
self.name_ledit = QLineEdit("", self)
self.name_ledit.setToolTip("Enter the name for this eReader key, usually the name on your credit card.\nIt will only be used to generate this one-time key and won\'t be stored anywhere in calibre or on your computer.\n(ex: Mr Jonathan Q Smith)")
name_group.addWidget(self.name_ledit)
name_disclaimer_label = QLabel(_("(Will not be saved in configuration data)"), self)
name_disclaimer_label.setAlignment(Qt.AlignHCenter)
data_group_box_layout.addWidget(name_disclaimer_label)
ccn_group = QHBoxLayout()
data_group_box_layout.addLayout(ccn_group)
ccn_group.addWidget(QLabel("Credit Card#:", self))
self.cc_ledit = QLineEdit("", self)
self.cc_ledit.setToolTip("<p>Enter the last 8 digits of credit card number for this eReader key.\nThey will only be used to generate this one-time key and won\'t be stored anywhere in calibre or on your computer.")
ccn_group.addWidget(self.cc_ledit)
ccn_disclaimer_label = QLabel(_('(Will not be saved in configuration data)'), self)
ccn_disclaimer_label.setAlignment(Qt.AlignHCenter)
data_group_box_layout.addWidget(ccn_disclaimer_label)
layout.addSpacing(10)
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.button_box.accepted.connect(self.accept)
self.button_box.rejected.connect(self.reject)
layout.addWidget(self.button_box)
self.resize(self.sizeHint())
@property
def key_name(self):
return str(self.key_ledit.text()).strip()
@property
def key_value(self):
from calibre_plugins.dedrm.erdr2pml import getuser_key as generate_ereader_key
return codecs.encode(generate_ereader_key(self.user_name, self.cc_number),'hex')
@property
def user_name(self):
return str(self.name_ledit.text()).strip().lower().replace(' ','')
@property
def cc_number(self):
return str(self.cc_ledit.text()).strip().replace(' ', '').replace('-','')
def accept(self):
if len(self.key_name) == 0 or len(self.user_name) == 0 or len(self.cc_number) == 0 or self.key_name.isspace() or self.user_name.isspace() or self.cc_number.isspace():
errmsg = "All fields are required!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if not self.cc_number.isdigit():
errmsg = "Numbers only in the credit card number field!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_name) < 4:
errmsg = "Key name must be at <i>least</i> 4 characters long!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
QDialog.accept(self)
class AddAdeptDialog(QDialog):
def __init__(self, parent=None,):
QDialog.__init__(self, parent)
self.parent = parent
self.setWindowTitle("{0} {1}: Getting Default Adobe Digital Editions Key".format(PLUGIN_NAME, PLUGIN_VERSION))
layout = QVBoxLayout(self)
self.setLayout(layout)
try:
if iswindows or isosx:
from calibre_plugins.dedrm.adobekey import adeptkeys
defaultkeys = adeptkeys()
else: # linux
from .wineutils import WineGetKeys
scriptpath = os.path.join(parent.parent.alfdir,"adobekey.py")
defaultkeys = WineGetKeys(scriptpath, ".der",parent.getwineprefix())
self.default_key = defaultkeys[0]
except:
traceback.print_exc()
self.default_key = ""
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
if len(self.default_key)>0:
data_group_box = QGroupBox("", self)
layout.addWidget(data_group_box)
data_group_box_layout = QVBoxLayout()
data_group_box.setLayout(data_group_box_layout)
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
key_group.addWidget(QLabel("Unique Key Name:", self))
self.key_ledit = QLineEdit("default_key", self)
self.key_ledit.setToolTip("<p>Enter an identifying name for the current default Adobe Digital Editions key.")
key_group.addWidget(self.key_ledit)
self.button_box.accepted.connect(self.accept)
else:
default_key_error = QLabel("The default encryption key for Adobe Digital Editions could not be found.", self)
default_key_error.setAlignment(Qt.AlignHCenter)
layout.addWidget(default_key_error)
# if no default, bot buttons do the same
self.button_box.accepted.connect(self.reject)
self.button_box.rejected.connect(self.reject)
layout.addWidget(self.button_box)
self.resize(self.sizeHint())
@property
def key_name(self):
return str(self.key_ledit.text()).strip()
@property
def key_value(self):
return codecs.encode(self.default_key,'hex')
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
errmsg = "All fields are required!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_name) < 4:
errmsg = "Key name must be at <i>least</i> 4 characters long!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
QDialog.accept(self)
class AddKindleDialog(QDialog):
def __init__(self, parent=None,):
QDialog.__init__(self, parent)
self.parent = parent
self.setWindowTitle("{0} {1}: Getting Default Kindle for Mac/PC Key".format(PLUGIN_NAME, PLUGIN_VERSION))
layout = QVBoxLayout(self)
self.setLayout(layout)
try:
if iswindows or isosx:
from calibre_plugins.dedrm.kindlekey import kindlekeys
defaultkeys = kindlekeys()
else: # linux
from .wineutils import WineGetKeys
scriptpath = os.path.join(parent.parent.alfdir,"kindlekey.py")
defaultkeys = WineGetKeys(scriptpath, ".k4i",parent.getwineprefix())
self.default_key = defaultkeys[0]
except:
traceback.print_exc()
self.default_key = ""
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
if len(self.default_key)>0:
data_group_box = QGroupBox("", self)
layout.addWidget(data_group_box)
data_group_box_layout = QVBoxLayout()
data_group_box.setLayout(data_group_box_layout)
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
key_group.addWidget(QLabel("Unique Key Name:", self))
self.key_ledit = QLineEdit("default_key", self)
self.key_ledit.setToolTip("<p>Enter an identifying name for the current default Kindle for Mac/PC key.")
key_group.addWidget(self.key_ledit)
self.button_box.accepted.connect(self.accept)
else:
default_key_error = QLabel("The default encryption key for Kindle for Mac/PC could not be found.", self)
default_key_error.setAlignment(Qt.AlignHCenter)
layout.addWidget(default_key_error)
# if no default, both buttons do the same
self.button_box.accepted.connect(self.reject)
self.button_box.rejected.connect(self.reject)
layout.addWidget(self.button_box)
self.resize(self.sizeHint())
@property
def key_name(self):
return str(self.key_ledit.text()).strip()
@property
def key_value(self):
return self.default_key
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
errmsg = "All fields are required!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_name) < 4:
errmsg = "Key name must be at <i>least</i> 4 characters long!"
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
QDialog.accept(self)
class AddSerialDialog(QDialog):
def __init__(self, parent=None,):
QDialog.__init__(self, parent)
self.parent = parent
self.setWindowTitle("{0} {1}: Add New EInk Kindle Serial Number".format(PLUGIN_NAME, PLUGIN_VERSION))
layout = QVBoxLayout(self)
self.setLayout(layout)
data_group_box = QGroupBox("", self)
layout.addWidget(data_group_box)
data_group_box_layout = QVBoxLayout()
data_group_box.setLayout(data_group_box_layout)
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
key_group.addWidget(QLabel("EInk Kindle Serial Number:", self))
self.key_ledit = QLineEdit("", self)
self.key_ledit.setToolTip("Enter an eInk Kindle serial number. EInk Kindle serial numbers are 16 characters long and usually start with a 'B' or a '9'. Kindle Serial Numbers are case-sensitive, so be sure to enter the upper and lower case letters unchanged.")
key_group.addWidget(self.key_ledit)
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.button_box.accepted.connect(self.accept)
self.button_box.rejected.connect(self.reject)
layout.addWidget(self.button_box)
self.resize(self.sizeHint())
@property
def key_name(self):
return str(self.key_ledit.text()).strip()
@property
def key_value(self):
return str(self.key_ledit.text()).replace(' ', '')
def accept(self):
if len(self.key_name) == 0 or self.key_name.isspace():
errmsg = "Please enter an eInk Kindle Serial Number or click Cancel in the dialog."
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
if len(self.key_name) != 16:
errmsg = "EInk Kindle Serial Numbers must be 16 characters long. This is {0:d} characters long.".format(len(self.key_name))
return error_dialog(None, "{0} {1}".format(PLUGIN_NAME, PLUGIN_VERSION), errmsg, show=True, show_copy_button=False)
QDialog.accept(self)
class AddAndroidDialog(QDialog):
def __init__(self, parent=None,):
QDialog.__init__(self, parent)
self.parent = parent
self.setWindowTitle("{0} {1}: Add new Kindle for Android Key".format(PLUGIN_NAME, PLUGIN_VERSION))
layout = QVBoxLayout(self)
self.setLayout(layout)
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
data_group_box = QGroupBox("", self)
layout.addWidget(data_group_box)
data_group_box_layout = QVBoxLayout()
data_group_box.setLayout(data_group_box_layout)
file_group = QHBoxLayout()
data_group_box_layout.addLayout(file_group)
add_btn = QPushButton("Choose Backup File", self)
add_btn.setToolTip("Import Kindle for Android backup file.")
add_btn.clicked.connect(self.get_android_file)
file_group.addWidget(add_btn)
self.selected_file_name = QLabel("",self)
self.selected_file_name.setAlignment(Qt.AlignHCenter)
file_group.addWidget(self.selected_file_name)
key_group = QHBoxLayout()
data_group_box_layout.addLayout(key_group)
key_group.addWidget(QLabel("Unique Key Name:", self))
self.key_ledit = QLineEdit("", self)
self.key_ledit.setToolTip("<p>Enter an identifying name for the Android for Kindle key.")
key_group.addWidget(self.key_ledit)
#key_label = QLabel(_(''), self)
#key_label.setAlignment(Qt.AlignHCenter)
#data_group_box_layout.addWidget(key_label)
self.button_box.accepted.connect(self.accept)
self.button_box.rejected.connect(self.reject)
layout.addWidget(self.button_box)
self.resize(self.sizeHint())
@property
def key_name(self):
return str(self.key_ledit.text()).strip()
@property
def file_name(self):
return str(self.selected_file_name.text()).strip()
@property
def key_value(self):
return self.seri
gitextract_cqmmgc0y/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ └── QUESTION.md │ └── workflows/ │ ├── Format.yaml │ └── Lint.yaml ├── .gitignore ├── CALIBRE_CLI_INSTRUCTIONS.md ├── DeDRM_plugin/ │ ├── DeDRM_Adobe Digital Editions Key_Help.htm │ ├── DeDRM_Barnes and Noble Key_Help.htm │ ├── DeDRM_EInk Kindle Serial Number_Help.htm │ ├── DeDRM_Help.htm │ ├── DeDRM_Kindle for Android Key_Help.htm │ ├── DeDRM_Kindle for Mac and PC Key_Help.htm │ ├── DeDRM_Mobipocket PID_Help.htm │ ├── DeDRM_eReader Key_Help.htm │ ├── __init__.py │ ├── activitybar.py │ ├── adobekey.py │ ├── aescbc.py │ ├── alfcrypto.py │ ├── androidkindlekey.py │ ├── argv_utils.py │ ├── askfolder_ed.py │ ├── config.py │ ├── convert2xml.py │ ├── epubtest.py │ ├── erdr2pml.py │ ├── flatxml2html.py │ ├── flatxml2svg.py │ ├── genbook.py │ ├── ignobleepub.py │ ├── ignoblekey.py │ ├── ignoblekeyfetch.py │ ├── ignoblekeygen.py │ ├── ignoblepdf.py │ ├── ineptepub.py │ ├── ineptpdf.py │ ├── ion.py │ ├── k4mobidedrm.py │ ├── kfxdedrm.py │ ├── kgenpids.py │ ├── kindlekey.py │ ├── kindlepid.py │ ├── mobidedrm.py │ ├── openssl_des.py │ ├── plugin-import-name-dedrm.txt │ ├── prefs.py │ ├── pycrypto_des.py │ ├── python_des.py │ ├── scriptinterface.py │ ├── scrolltextwidget.py │ ├── simpleprefs.py │ ├── stylexml2css.py │ ├── subasyncio.py │ ├── topazextract.py │ ├── utilities.py │ ├── wineutils.py │ ├── zipfilerugged.py │ └── zipfix.py ├── DeDRM_plugin_ReadMe.txt ├── FAQs.md ├── Obok_plugin/ │ ├── __init__.py │ ├── action.py │ ├── common_utils.py │ ├── config.py │ ├── dialogs.py │ ├── obok/ │ │ ├── __init__.py │ │ ├── legacy_obok.py │ │ └── obok.py │ ├── obok_dedrm_Help.htm │ ├── plugin-import-name-obok_dedrm.txt │ ├── translations/ │ │ ├── ar.mo │ │ ├── ar.po │ │ ├── de.mo │ │ ├── de.po │ │ ├── default.po │ │ ├── es.mo │ │ ├── es.po │ │ ├── nl.mo │ │ ├── nl.po │ │ ├── pt.mo │ │ ├── pt.po │ │ ├── sv.mo │ │ └── sv.po │ └── utilities.py ├── Other_Tools/ │ ├── B_and_N_Download_Helper/ │ │ ├── BN-Dload.user.js │ │ └── BN-Dload.user_ReadMe.txt │ ├── DRM_Key_Scripts/ │ │ ├── Adobe_Digital_Editions/ │ │ │ └── adobekey.pyw │ │ ├── Barnes_and_Noble_ePubs/ │ │ │ ├── ignoblekey.pyw │ │ │ ├── ignoblekeyfetch.pyw │ │ │ └── ignoblekeygen.pyw │ │ ├── Kindle_for_Android/ │ │ │ └── androidkindlekey.pyw │ │ ├── Kindle_for_Mac_and_PC/ │ │ │ └── kindlekey.pyw │ │ └── Kindle_for_iOS/ │ │ └── kindleiospidgen.pyw │ ├── Kindle_for_Android_Patches/ │ │ ├── A_Patching_Experience.txt │ │ ├── kindle_version_3.0.1.70/ │ │ │ ├── ReadMe_K4Android.txt │ │ │ └── kindle3.0.1.70.patch │ │ ├── kindle_version_3.7.0.108/ │ │ │ ├── ReadMe_K4Android.txt │ │ │ └── kindle3.7.0.108.patch │ │ ├── kindle_version_4.0.2.1/ │ │ │ └── kindle4.0.2.1.patch │ │ └── kindle_version_4.8.1.10/ │ │ ├── Notes on the Patch.txt │ │ └── kindle4.8.1.10.patch │ ├── Kobo/ │ │ └── obok.py │ ├── Rocket_ebooks/ │ │ └── rebhack_ReadMe.txt │ ├── Scuolabook_DRM/ │ │ └── Scuolabook_ReadMe.txt │ └── Tetrachroma_FileOpen_ineptpdf/ │ ├── ineptpdf_8.4.51.pyw │ └── ineptpdf_8.4.51_ReadMe.txt ├── README.md ├── ReadMe_Overview.txt ├── make_release.py └── obok_plugin_ReadMe.txt
SYMBOL INDEX (1339 symbols across 54 files)
FILE: DeDRM_plugin/__init__.py
class DeDRMError (line 98) | class DeDRMError(Exception):
class SafeUnbuffered (line 110) | class SafeUnbuffered:
method __init__ (line 111) | def __init__(self, stream):
method write (line 116) | def write(self, data):
method __getattr__ (line 125) | def __getattr__(self, attr):
class DeDRM (line 128) | class DeDRM(FileTypePlugin):
method initialize (line 141) | def initialize(self):
method ePubDecrypt (line 202) | def ePubDecrypt(self,path_to_ebook):
method PDFDecrypt (line 422) | def PDFDecrypt(self,path_to_ebook):
method KindleMobiDecrypt (line 518) | def KindleMobiDecrypt(self,path_to_ebook):
method eReaderDecrypt (line 592) | def eReaderDecrypt(self,path_to_ebook):
method run (line 621) | def run(self, path_to_ebook):
method is_customizable (line 651) | def is_customizable(self):
method config_widget (line 655) | def config_widget(self):
method save_settings (line 659) | def save_settings(self, config_widget):
FILE: DeDRM_plugin/activitybar.py
class ActivityBar (line 5) | class ActivityBar(tkinter.Frame):
method __init__ (line 7) | def __init__(self, master, length=300, height=20, barwidth=15, interva...
method _update_coords (line 32) | def _update_coords(self, event):
method _set (line 49) | def _set(self):
method start (line 60) | def start(self):
method stop (line 64) | def stop(self):
method _step (line 68) | def _step(self):
FILE: DeDRM_plugin/adobekey.py
class SafeUnbuffered (line 47) | class SafeUnbuffered:
method __init__ (line 48) | def __init__(self, stream):
method write (line 53) | def write(self, data):
method __getattr__ (line 59) | def __getattr__(self, attr):
function unicode_argv (line 68) | def unicode_argv():
class ADEPTError (line 104) | class ADEPTError(Exception):
function _load_crypto_libcrypto (line 116) | def _load_crypto_libcrypto():
function _load_crypto_pycrypto (line 161) | def _load_crypto_pycrypto():
function _load_crypto (line 170) | def _load_crypto():
function GetSystemDirectory (line 192) | def GetSystemDirectory():
function GetVolumeSerialNumber (line 203) | def GetVolumeSerialNumber():
function GetUserName (line 217) | def GetUserName():
function VirtualAlloc (line 235) | def VirtualAlloc():
function VirtualFree (line 247) | def VirtualFree():
class NativeFunction (line 256) | class NativeFunction(object):
method __init__ (line 257) | def __init__(self, restype, argtypes, insns):
method __call__ (line 263) | def __call__(self, *args):
method __del__ (line 266) | def __del__(self):
function cpuid0 (line 313) | def cpuid0():
class DataBlob (line 324) | class DataBlob(Structure):
function CryptUnprotectData (line 329) | def CryptUnprotectData():
function adeptkeys (line 347) | def adeptkeys():
function findActivationDat (line 405) | def findActivationDat():
function adeptkeys (line 427) | def adeptkeys():
function adeptkeys (line 440) | def adeptkeys():
function getkey (line 445) | def getkey(outpath):
function usage (line 467) | def usage(progname):
function cli_main (line 474) | def cli_main():
function gui_main (line 532) | def gui_main():
FILE: DeDRM_plugin/aescbc.py
class CryptoError (line 21) | class CryptoError(Exception):
method __init__ (line 23) | def __init__(self,errorMessage='Error!'):
method __str__ (line 25) | def __str__(self):
class InitCryptoError (line 28) | class InitCryptoError(CryptoError):
class BadKeySizeError (line 30) | class BadKeySizeError(InitCryptoError):
class EncryptError (line 32) | class EncryptError(CryptoError):
class DecryptError (line 34) | class DecryptError(CryptoError):
class DecryptNotBlockAlignedError (line 36) | class DecryptNotBlockAlignedError(DecryptError):
function xorS (line 39) | def xorS(a,b):
function xor (line 47) | def xor(a,b):
class BlockCipher (line 61) | class BlockCipher:
method __init__ (line 63) | def __init__(self):
method reset (line 66) | def reset(self):
method resetEncrypt (line 69) | def resetEncrypt(self):
method resetDecrypt (line 72) | def resetDecrypt(self):
method encrypt (line 76) | def encrypt(self, plainText, more = None):
method decrypt (line 100) | def decrypt(self, cipherText, more = None):
class Pad (line 132) | class Pad:
method __init__ (line 133) | def __init__(self):
class padWithPadLen (line 136) | class padWithPadLen(Pad):
method addPad (line 139) | def addPad(self, extraBytes, blockSize):
method removePad (line 146) | def removePad(self, paddedBinaryString, blockSize):
class noPadding (line 152) | class noPadding(Pad):
method addPad (line 155) | def addPad(self, extraBytes, blockSize):
method removePad (line 159) | def removePad(self, paddedBinaryString, blockSize):
class Rijndael (line 170) | class Rijndael(BlockCipher):
method __init__ (line 172) | def __init__(self, key = None, padding = padWithPadLen(), keySize=16, ...
method setKey (line 189) | def setKey(self, key):
method encryptBlock (line 195) | def encryptBlock(self, plainTextBlock):
method decryptBlock (line 210) | def decryptBlock(self, encryptedBlock):
method _toBlock (line 224) | def _toBlock(self, bs):
method _toBString (line 229) | def _toBString(self, block):
function keyExpansion (line 247) | def keyExpansion(algInstance, keyString):
function AddRoundKey (line 268) | def AddRoundKey(algInstance, keyBlock):
function SubBytes (line 275) | def SubBytes(algInstance):
function InvSubBytes (line 280) | def InvSubBytes(algInstance):
function ShiftRows (line 361) | def ShiftRows(algInstance):
function InvShiftRows (line 368) | def InvShiftRows(algInstance):
function MixColumns (line 376) | def MixColumns(a):
function InvMixColumns (line 386) | def InvMixColumns(a):
function mul (line 399) | def mul(a, b):
class AES (line 450) | class AES(Rijndael):
method __init__ (line 454) | def __init__(self, key = None, padding = padWithPadLen(), keySize=16):
class CBC (line 472) | class CBC(BlockCipher):
method __init__ (line 477) | def __init__(self, blockCipherInstance, padding = padWithPadLen()):
method setKey (line 492) | def setKey(self, key):
method resetEncrypt (line 496) | def resetEncrypt(self):
method resetDecrypt (line 500) | def resetDecrypt(self):
method encrypt (line 504) | def encrypt(self, plainText, iv=None, more=None):
method decrypt (line 515) | def decrypt(self, cipherText, iv=None, more=None):
method encryptBlock (line 526) | def encryptBlock(self, plainTextBlock):
method decryptBlock (line 543) | def decryptBlock(self, encryptedBlock):
class AES_CBC (line 567) | class AES_CBC(CBC):
method __init__ (line 569) | def __init__(self, key=None, padding=padWithPadLen(), keySize=16):
FILE: DeDRM_plugin/alfcrypto.py
function _load_libalfcrypto (line 17) | def _load_libalfcrypto():
function _load_python_alfcrypto (line 165) | def _load_python_alfcrypto():
function _load_crypto (line 251) | def _load_crypto():
class KeyIVGen (line 265) | class KeyIVGen(object):
method pbkdf2 (line 269) | def pbkdf2(self, passwd, salt, iter, keylen):
FILE: DeDRM_plugin/androidkindlekey.py
class SafeUnbuffered (line 41) | class SafeUnbuffered:
method __init__ (line 42) | def __init__(self, stream):
method write (line 47) | def write(self, data):
method __getattr__ (line 53) | def __getattr__(self, attr):
function unicode_argv (line 62) | def unicode_argv():
class DrmException (line 98) | class DrmException(Exception):
class AndroidObfuscation (line 105) | class AndroidObfuscation(object):
method encrypt (line 112) | def encrypt(self, plaintext):
method decrypt (line 118) | def decrypt(self, ciphertext):
method _get_cipher (line 123) | def _get_cipher(self):
class AndroidObfuscationV2 (line 131) | class AndroidObfuscationV2(AndroidObfuscation):
method __init__ (line 138) | def __init__(self, salt):
method _get_cipher (line 145) | def _get_cipher(self):
function parse_preference (line 153) | def parse_preference(path):
function get_serials1 (line 168) | def get_serials1(path=STORAGE1):
function get_serials2 (line 210) | def get_serials2(path=STORAGE2):
function get_serials (line 258) | def get_serials(path=STORAGE):
function getkey (line 310) | def getkey(outfile, inpath):
function usage (line 321) | def usage(progname):
function cli_main (line 331) | def cli_main():
function gui_main (line 384) | def gui_main():
FILE: DeDRM_plugin/argv_utils.py
function unicode_argv (line 10) | def unicode_argv():
function add_cp65001_codec (line 47) | def add_cp65001_codec():
function set_utf8_default_encoding (line 56) | def set_utf8_default_encoding():
FILE: DeDRM_plugin/askfolder_ed.py
class BROWSEINFO (line 113) | class BROWSEINFO(ctypes.Structure):
function CenterWindow (line 127) | def CenterWindow(hwnd):
function GetWindowRect (line 140) | def GetWindowRect(hwnd):
function width (line 145) | def width(rect):
function height (line 148) | def height(rect):
function AskFolder (line 152) | def AskFolder(
FILE: DeDRM_plugin/config.py
class ConfigWidget (line 32) | class ConfigWidget(QWidget):
method __init__ (line 33) | def __init__(self, plugin_path, alfdir):
method kindle_serials (line 113) | def kindle_serials(self):
method kindle_android (line 117) | def kindle_android(self):
method kindle_keys (line 121) | def kindle_keys(self):
method adept_keys (line 130) | def adept_keys(self):
method mobi_keys (line 139) | def mobi_keys(self):
method bandn_keys (line 143) | def bandn_keys(self):
method ereader_keys (line 147) | def ereader_keys(self):
method help_link_activated (line 151) | def help_link_activated(self, url):
method save_settings (line 162) | def save_settings(self):
method load_resource (line 175) | def load_resource(self, name):
class ManageKeysDialog (line 183) | class ManageKeysDialog(QDialog):
method __init__ (line 184) | def __init__(self, parent, key_type_name, plugin_keys, create_key, key...
method getwineprefix (line 280) | def getwineprefix(self):
method populate_list (line 285) | def populate_list(self):
method add_key (line 293) | def add_key(self):
method rename_key (line 318) | def rename_key(self):
method delete_key (line 340) | def delete_key(self):
method help_link_activated (line 354) | def help_link_activated(self, url):
method migrate_files (line 366) | def migrate_files(self):
method migrate_wrapper (line 416) | def migrate_wrapper(self):
method export_key (line 421) | def export_key(self):
class RenameKeyDialog (line 452) | class RenameKeyDialog(QDialog):
method __init__ (line 453) | def __init__(self, parent=None,):
method accept (line 480) | def accept(self):
method key_name (line 501) | def key_name(self):
class AddBandNKeyDialog (line 511) | class AddBandNKeyDialog(QDialog):
method __init__ (line 512) | def __init__(self, parent=None,):
method key_name (line 584) | def key_name(self):
method key_value (line 588) | def key_value(self):
method user_name (line 592) | def user_name(self):
method cc_number (line 596) | def cc_number(self):
method retrieve_key (line 599) | def retrieve_key(self):
method accept (line 608) | def accept(self):
class AddEReaderDialog (line 621) | class AddEReaderDialog(QDialog):
method __init__ (line 622) | def __init__(self, parent=None,):
method key_name (line 670) | def key_name(self):
method key_value (line 674) | def key_value(self):
method user_name (line 679) | def user_name(self):
method cc_number (line 683) | def cc_number(self):
method accept (line 687) | def accept(self):
class AddAdeptDialog (line 700) | class AddAdeptDialog(QDialog):
method __init__ (line 701) | def __init__(self, parent=None,):
method key_name (line 753) | def key_name(self):
method key_value (line 757) | def key_value(self):
method accept (line 761) | def accept(self):
class AddKindleDialog (line 771) | class AddKindleDialog(QDialog):
method __init__ (line 772) | def __init__(self, parent=None,):
method key_name (line 825) | def key_name(self):
method key_value (line 829) | def key_value(self):
method accept (line 833) | def accept(self):
class AddSerialDialog (line 843) | class AddSerialDialog(QDialog):
method __init__ (line 844) | def __init__(self, parent=None,):
method key_name (line 871) | def key_name(self):
method key_value (line 875) | def key_value(self):
method accept (line 878) | def accept(self):
class AddAndroidDialog (line 888) | class AddAndroidDialog(QDialog):
method __init__ (line 889) | def __init__(self, parent=None,):
method key_name (line 929) | def key_name(self):
method file_name (line 933) | def file_name(self):
method key_value (line 937) | def key_value(self):
method get_android_file (line 940) | def get_android_file(self):
method accept (line 959) | def accept(self):
class AddPIDDialog (line 971) | class AddPIDDialog(QDialog):
method __init__ (line 972) | def __init__(self, parent=None,):
method key_name (line 999) | def key_name(self):
method key_value (line 1003) | def key_value(self):
method accept (line 1006) | def accept(self):
FILE: DeDRM_plugin/convert2xml.py
class SafeUnbuffered (line 11) | class SafeUnbuffered:
method __init__ (line 12) | def __init__(self, stream):
method write (line 17) | def write(self, data):
method __getattr__ (line 23) | def __getattr__(self, attr):
class TpzDRMError (line 33) | class TpzDRMError(Exception):
function readEncodedNumber (line 39) | def readEncodedNumber(file):
function encodeNumber (line 71) | def encodeNumber(number):
function lengthPrefixString (line 100) | def lengthPrefixString(data):
function readString (line 103) | def readString(file):
function convert (line 116) | def convert(i):
class Dictionary (line 129) | class Dictionary(object):
method __init__ (line 130) | def __init__(self, dictFile):
method escapestr (line 140) | def escapestr(self, str):
method lookup (line 147) | def lookup(self,val):
method getSize (line 156) | def getSize(self):
method getPos (line 159) | def getPos(self):
method dumpDict (line 162) | def dumpDict(self):
class PageParser (line 171) | class PageParser(object):
method __init__ (line 172) | def __init__(self, filename, dict, debug, flat_xml):
method tag_push (line 422) | def tag_push(self, token):
method tag_pop (line 424) | def tag_pop(self):
method tagpath_len (line 427) | def tagpath_len(self):
method get_tagpath (line 429) | def get_tagpath(self, i):
method peek (line 443) | def peek(self, aheadi):
method getNext (line 453) | def getNext(self):
method formatArg (line 462) | def formatArg(self, arg, argtype):
method procToken (line 477) | def procToken(self, token):
method doLoop72 (line 551) | def doLoop72(self, argtype):
method doLoop76Mode (line 570) | def doLoop76Mode(self, argtype, cnt, mode):
method decodeCMD (line 594) | def decodeCMD(self, cmd, argtype):
method updateName (line 611) | def updateName(self, tag, prefix):
method injectSnippets (line 630) | def injectSnippets(self, snippet):
method formatTag (line 660) | def formatTag(self, node):
method flattenTag (line 696) | def flattenTag(self, node):
method formatDoc (line 724) | def formatDoc(self, flat_xml):
method process (line 746) | def process(self):
function fromData (line 810) | def fromData(dict, fname):
function getXML (line 817) | def getXML(dict, fname):
function usage (line 824) | def usage():
function main (line 843) | def main(argv):
FILE: DeDRM_plugin/epubtest.py
class SafeUnbuffered (line 62) | class SafeUnbuffered:
method __init__ (line 63) | def __init__(self, stream):
method write (line 68) | def write(self, data):
method __getattr__ (line 74) | def __getattr__(self, attr):
function unicode_argv (line 83) | def unicode_argv():
function uncompress (line 125) | def uncompress(cmpdata):
function getfiledata (line 144) | def getfiledata(file, zi):
function encryption (line 170) | def encryption(infile):
function main (line 199) | def main():
FILE: DeDRM_plugin/erdr2pml.py
class SafeUnbuffered (line 81) | class SafeUnbuffered:
method __init__ (line 82) | def __init__(self, stream):
method write (line 87) | def write(self, data):
method __getattr__ (line 92) | def __getattr__(self, attr):
function unicode_argv (line 98) | def unicode_argv():
class Sectionizer (line 194) | class Sectionizer(object):
method __init__ (line 197) | def __init__(self, filename, ident):
method loadSection (line 212) | def loadSection(self, section):
function sanitizeFileName (line 225) | def sanitizeFileName(name):
function fixKey (line 240) | def fixKey(key):
function deXOR (line 245) | def deXOR(text, sp, table):
class EreaderProcessor (line 255) | class EreaderProcessor(object):
method __init__ (line 256) | def __init__(self, sect, user_key):
method getNumImages (line 352) | def getNumImages(self):
method getImage (line 355) | def getImage(self, i):
method getText (line 405) | def getText(self):
function cleanPML (line 455) | def cleanPML(pml):
function decryptBook (line 462) | def decryptBook(infile, outpath, make_pmlz, user_key):
function usage (line 526) | def usage():
function getuser_key (line 544) | def getuser_key(name,cc):
function cli_main (line 549) | def cli_main():
FILE: DeDRM_plugin/flatxml2html.py
class DocParser (line 15) | class DocParser(object):
method __init__ (line 16) | def __init__(self, flatxml, classlst, fileid, bookDir, gdict, fixedima...
method getGlyph (line 42) | def getGlyph(self, gid):
method glyphs_to_image (line 47) | def glyphs_to_image(self, glyphList):
method lineinDoc (line 123) | def lineinDoc(self, pos) :
method findinDoc (line 135) | def findinDoc(self, tagpath, pos, end) :
method posinDoc (line 159) | def posinDoc(self, tagpath):
method getData (line 172) | def getData(self, tagpath, pos, end):
method getClass (line 182) | def getClass(self, pclass):
method PageDescription (line 219) | def PageDescription(self):
method getParaDescription (line 268) | def getParaDescription(self, start, end, regtype):
method buildParagraph (line 437) | def buildParagraph(self, pclass, pdesc, type, regtype) :
method buildTOCEntry (line 540) | def buildTOCEntry(self, pdesc) :
method process (line 610) | def process(self):
function convert2HTML (line 803) | def convert2HTML(flatxml, classlst, fileid, bookDir, gdict, fixedimage):
FILE: DeDRM_plugin/flatxml2svg.py
class PParser (line 12) | class PParser(object):
method __init__ (line 13) | def __init__(self, gd, flatxml, meta_array):
method lineinDoc (line 58) | def lineinDoc(self, pos) :
method findinDoc (line 69) | def findinDoc(self, tagpath, pos, end) :
method posinDoc (line 92) | def posinDoc(self, tagpath):
method getData (line 103) | def getData(self, path):
method getDataatPos (line 122) | def getDataatPos(self, path, pos):
method getDataTemp (line 140) | def getDataTemp(self, path):
method getImages (line 162) | def getImages(self):
method getGlyphs (line 174) | def getGlyphs(self):
function convert2SVG (line 189) | def convert2SVG(gdict, flat_xml, pageid, previd, nextid, svgDir, raw, me...
FILE: DeDRM_plugin/genbook.py
class SafeUnbuffered (line 10) | class SafeUnbuffered:
method __init__ (line 11) | def __init__(self, stream):
method write (line 16) | def write(self, data):
method __getattr__ (line 22) | def __getattr__(self, attr):
class TpzDRMError (line 32) | class TpzDRMError(Exception):
function readEncodedNumber (line 56) | def readEncodedNumber(file):
function lengthPrefixString (line 82) | def lengthPrefixString(data):
function readString (line 85) | def readString(file):
function getMetaArray (line 94) | def getMetaArray(metaFile):
class Dictionary (line 109) | class Dictionary(object):
method __init__ (line 110) | def __init__(self, dictFile):
method escapestr (line 119) | def escapestr(self, str):
method lookup (line 125) | def lookup(self,val):
method getSize (line 133) | def getSize(self):
method getPos (line 135) | def getPos(self):
class PageDimParser (line 139) | class PageDimParser(object):
method __init__ (line 140) | def __init__(self, flatxml):
method findinDoc (line 143) | def findinDoc(self, tagpath, pos, end) :
method process (line 164) | def process(self):
function getPageDim (line 171) | def getPageDim(flatxml):
class GParser (line 177) | class GParser(object):
method __init__ (line 178) | def __init__(self, flatxml):
method getData (line 202) | def getData(self, path):
method getGlyphDim (line 220) | def getGlyphDim(self, gly):
method getPath (line 226) | def getPath(self, gly):
class GlyphDict (line 265) | class GlyphDict(object):
method __init__ (line 266) | def __init__(self):
method lookup (line 268) | def lookup(self, id):
method addGlyph (line 273) | def addGlyph(self, val, path):
function generateBook (line 278) | def generateBook(bookDir, raw, fixedimage):
function usage (line 684) | def usage():
function main (line 696) | def main(argv):
FILE: DeDRM_plugin/ignobleepub.py
class SafeUnbuffered (line 48) | class SafeUnbuffered:
method __init__ (line 49) | def __init__(self, stream):
method write (line 54) | def write(self, data):
method __getattr__ (line 59) | def __getattr__(self, attr):
function unicode_argv (line 68) | def unicode_argv():
class IGNOBLEError (line 103) | class IGNOBLEError(Exception):
function _load_crypto_libcrypto (line 106) | def _load_crypto_libcrypto():
function _load_crypto_pycrypto (line 163) | def _load_crypto_pycrypto():
function _load_crypto (line 175) | def _load_crypto():
class Decryptor (line 194) | class Decryptor(object):
method __init__ (line 195) | def __init__(self, bookkey, encryption):
method decompress (line 208) | def decompress(self, bytes):
method decrypt (line 216) | def decrypt(self, path, data):
function ignobleBook (line 224) | def ignobleBook(inpath):
function decryptBook (line 242) | def decryptBook(keyb64, inpath, outpath):
function cli_main (line 309) | def cli_main():
function gui_main (line 324) | def gui_main():
FILE: DeDRM_plugin/ignoblekey.py
class SafeUnbuffered (line 33) | class SafeUnbuffered:
method __init__ (line 34) | def __init__(self, stream):
method write (line 39) | def write(self, data):
method __getattr__ (line 45) | def __getattr__(self, attr):
function unicode_argv (line 54) | def unicode_argv():
class DrmException (line 90) | class DrmException(Exception):
function getNookLogFiles (line 94) | def getNookLogFiles():
function getKeysFromLog (line 183) | def getKeysFromLog(kLogFile):
function nookkeys (line 192) | def nookkeys(files = []):
function getkey (line 205) | def getkey(outpath, files=[]):
function usage (line 227) | def usage(progname):
function cli_main (line 235) | def cli_main():
function gui_main (line 278) | def gui_main():
FILE: DeDRM_plugin/ignoblekeyfetch.py
class SafeUnbuffered (line 40) | class SafeUnbuffered:
method __init__ (line 41) | def __init__(self, stream):
method write (line 46) | def write(self, data):
method __getattr__ (line 52) | def __getattr__(self, attr):
function unicode_argv (line 61) | def unicode_argv():
class IGNOBLEError (line 98) | class IGNOBLEError(Exception):
function fetch_key (line 101) | def fetch_key(email, password):
function cli_main (line 148) | def cli_main():
function gui_main (line 165) | def gui_main():
FILE: DeDRM_plugin/ignoblekeygen.py
class SafeUnbuffered (line 50) | class SafeUnbuffered:
method __init__ (line 51) | def __init__(self, stream):
method write (line 56) | def write(self, data):
method __getattr__ (line 62) | def __getattr__(self, attr):
function unicode_argv (line 71) | def unicode_argv():
class IGNOBLEError (line 108) | class IGNOBLEError(Exception):
function _load_crypto_libcrypto (line 111) | def _load_crypto_libcrypto():
function _load_crypto_pycrypto (line 165) | def _load_crypto_pycrypto():
function _load_crypto (line 177) | def _load_crypto():
function normalize_name (line 192) | def normalize_name(name):
function generate_key (line 196) | def generate_key(name, ccn):
function cli_main (line 218) | def cli_main():
function gui_main (line 237) | def gui_main():
FILE: DeDRM_plugin/ignoblepdf.py
class SafeUnbuffered (line 39) | class SafeUnbuffered:
method __init__ (line 40) | def __init__(self, stream):
method write (line 45) | def write(self, data):
method __getattr__ (line 51) | def __getattr__(self, attr):
function unicode_argv (line 57) | def unicode_argv():
class IGNOBLEError (line 92) | class IGNOBLEError(Exception):
function SHA256 (line 98) | def SHA256(message):
function _load_crypto_libcrypto (line 104) | def _load_crypto_libcrypto():
function _load_crypto_pycrypto (line 195) | def _load_crypto_pycrypto():
function _load_crypto (line 224) | def _load_crypto():
function choplist (line 256) | def choplist(n, seq):
function nunpack (line 266) | def nunpack(s, default=0):
class PSException (line 288) | class PSException(Exception): pass
class PSEOF (line 289) | class PSEOF(PSException): pass
class PSSyntaxError (line 290) | class PSSyntaxError(PSException): pass
class PSTypeError (line 291) | class PSTypeError(PSException): pass
class PSValueError (line 292) | class PSValueError(PSException): pass
class PSObject (line 299) | class PSObject(object): pass
class PSLiteral (line 301) | class PSLiteral(PSObject):
method __init__ (line 307) | def __init__(self, name):
method __repr__ (line 311) | def __repr__(self):
class PSKeyword (line 320) | class PSKeyword(PSObject):
method __init__ (line 326) | def __init__(self, name):
method __repr__ (line 330) | def __repr__(self):
class PSSymbolTable (line 334) | class PSSymbolTable(object):
method __init__ (line 340) | def __init__(self, classe):
method intern (line 345) | def intern(self, name):
function literal_name (line 365) | def literal_name(x):
function keyword_name (line 373) | def keyword_name(x):
class PSBaseParser (line 397) | class PSBaseParser(object):
method __init__ (line 404) | def __init__(self, fp):
method __repr__ (line 409) | def __repr__(self):
method flush (line 412) | def flush(self):
method close (line 415) | def close(self):
method tell (line 419) | def tell(self):
method poll (line 422) | def poll(self, pos=None, n=80):
method seek (line 431) | def seek(self, pos):
method fillbuf (line 445) | def fillbuf(self):
method parse_main (line 455) | def parse_main(self, s, i):
method add_token (line 490) | def add_token(self, obj):
method parse_comment (line 494) | def parse_comment(self, s, i):
method parse_literal (line 505) | def parse_literal(self, s, i):
method parse_literal_hex (line 519) | def parse_literal_hex(self, s, i):
method parse_number (line 528) | def parse_number(self, s, i):
method parse_decimal (line 545) | def parse_decimal(self, s, i):
method parse_keyword (line 555) | def parse_keyword(self, s, i):
method parse_string (line 571) | def parse_string(self, s, i):
method parse_string_1 (line 593) | def parse_string_1(self, s, i):
method parse_wopen (line 605) | def parse_wopen(self, s, i):
method parse_wclose (line 614) | def parse_wclose(self, s, i):
method parse_hexstring (line 621) | def parse_hexstring(self, s, i):
method nexttoken (line 633) | def nexttoken(self):
method nextline (line 640) | def nextline(self):
method revreadlines (line 669) | def revreadlines(self):
class PSStackParser (line 696) | class PSStackParser(PSBaseParser):
method __init__ (line 698) | def __init__(self, fp):
method reset (line 703) | def reset(self):
method seek (line 710) | def seek(self, pos):
method push (line 715) | def push(self, *objs):
method pop (line 718) | def pop(self, n):
method popall (line 722) | def popall(self):
method add_results (line 726) | def add_results(self, *objs):
method start_type (line 730) | def start_type(self, pos, type):
method end_type (line 734) | def end_type(self, type):
method do_keyword (line 741) | def do_keyword(self, pos, token):
method nextobject (line 744) | def nextobject(self, direct=False):
class PDFObject (line 807) | class PDFObject(PSObject): pass
class PDFException (line 809) | class PDFException(PSException): pass
class PDFTypeError (line 810) | class PDFTypeError(PDFException): pass
class PDFValueError (line 811) | class PDFValueError(PDFException): pass
class PDFNotImplementedError (line 812) | class PDFNotImplementedError(PSException): pass
class PDFObjRef (line 817) | class PDFObjRef(PDFObject):
method __init__ (line 819) | def __init__(self, doc, objid, genno):
method __repr__ (line 828) | def __repr__(self):
method resolve (line 831) | def resolve(self):
function resolve1 (line 836) | def resolve1(x):
function resolve_all (line 845) | def resolve_all(x):
function decipher_all (line 860) | def decipher_all(decipher, objid, genno, x):
function int_value (line 875) | def int_value(x):
function decimal_value (line 883) | def decimal_value(x):
function num_value (line 891) | def num_value(x):
function str_value (line 899) | def str_value(x):
function list_value (line 907) | def list_value(x):
function dict_value (line 915) | def dict_value(x):
function stream_value (line 923) | def stream_value(x):
function ascii85decode (line 932) | def ascii85decode(data):
class PDFStream (line 955) | class PDFStream(PDFObject):
method __init__ (line 956) | def __init__(self, dic, rawdata, decipher=None):
method set_objid (line 978) | def set_objid(self, objid, genno):
method __repr__ (line 983) | def __repr__(self):
method decode (line 991) | def decode(self):
method get_data (line 1049) | def get_data(self):
method get_rawdata (line 1054) | def get_rawdata(self):
method get_decdata (line 1057) | def get_decdata(self):
class PDFSyntaxError (line 1069) | class PDFSyntaxError(PDFException): pass
class PDFNoValidXRef (line 1070) | class PDFNoValidXRef(PDFSyntaxError): pass
class PDFEncryptionError (line 1071) | class PDFEncryptionError(PDFException): pass
class PDFPasswordIncorrect (line 1072) | class PDFPasswordIncorrect(PDFEncryptionError): pass
class PDFXRef (line 1087) | class PDFXRef(object):
method __init__ (line 1089) | def __init__(self):
method __repr__ (line 1093) | def __repr__(self):
method objids (line 1096) | def objids(self):
method load (line 1099) | def load(self, parser):
method load_trailer (line 1133) | def load_trailer(self, parser):
method getpos (line 1146) | def getpos(self, objid):
class PDFXRefStream (line 1156) | class PDFXRefStream(object):
method __init__ (line 1158) | def __init__(self):
method __repr__ (line 1165) | def __repr__(self):
method objids (line 1168) | def objids(self):
method load (line 1173) | def load(self, parser, debug=0):
method getpos (line 1191) | def getpos(self, objid):
class PDFDocument (line 1221) | class PDFDocument(object):
method __init__ (line 1223) | def __init__(self):
method set_parser (line 1236) | def set_parser(self, parser):
method set_root (line 1275) | def set_root(self, root):
method initialize (line 1286) | def initialize(self, password=''):
method initialize_adobe_ps (line 1302) | def initialize_adobe_ps(self, password, docid, param):
method genkey_adobe_ps (line 1310) | def genkey_adobe_ps(self, param):
method initialize_standard (line 1344) | def initialize_standard(self, password, docid, param):
method initialize_ebx (line 1420) | def initialize_ebx(self, keyb64, docid, param):
method genkey_v2 (line 1466) | def genkey_v2(self, objid, genno):
method genkey_v3 (line 1474) | def genkey_v3(self, objid, genno):
method genkey_v4 (line 1484) | def genkey_v4(self, objid, genno):
method decrypt_aes (line 1492) | def decrypt_aes(self, objid, genno, data):
method decrypt_aes256 (line 1503) | def decrypt_aes256(self, objid, genno, data):
method decrypt_rc4 (line 1514) | def decrypt_rc4(self, objid, genno, data):
method getobj (line 1521) | def getobj(self, objid):
class PDFObjStmRef (line 1603) | class PDFObjStmRef(object):
method __init__ (line 1605) | def __init__(self, objid, stmid, index):
class PDFParser (line 1615) | class PDFParser(PSStackParser):
method __init__ (line 1617) | def __init__(self, doc, fp):
method __repr__ (line 1623) | def __repr__(self):
method do_keyword (line 1631) | def do_keyword(self, pos, token):
method find_xref (line 1694) | def find_xref(self):
method read_xref_from (line 1707) | def read_xref_from(self, start, xrefs):
method read_xref (line 1742) | def read_xref(self):
class PDFObjStrmParser (line 1775) | class PDFObjStrmParser(PDFParser):
method __init__ (line 1777) | def __init__(self, data, doc):
method flush (line 1782) | def flush(self):
method do_keyword (line 1787) | def do_keyword(self, pos, token):
class PDFSerializer (line 1805) | class PDFSerializer(object):
method __init__ (line 1806) | def __init__(self, inf, userkey):
method dump (line 1826) | def dump(self, outf):
method write (line 1921) | def write(self, data):
method tell (line 1925) | def tell(self):
method escape_string (line 1928) | def escape_string(self, string):
method serialize_object (line 1938) | def serialize_object(self, obj):
method serialize_indirect (line 1992) | def serialize_indirect(self, objid, obj):
function decryptBook (line 2002) | def decryptBook(userkey, inpath, outpath):
function cli_main (line 2022) | def cli_main():
function gui_main (line 2038) | def gui_main():
FILE: DeDRM_plugin/ineptepub.py
class SafeUnbuffered (line 54) | class SafeUnbuffered:
method __init__ (line 55) | def __init__(self, stream):
method write (line 60) | def write(self, data):
method __getattr__ (line 66) | def __getattr__(self, attr):
function unicode_argv (line 75) | def unicode_argv():
class ADEPTError (line 110) | class ADEPTError(Exception):
function _load_crypto_libcrypto (line 113) | def _load_crypto_libcrypto():
function _load_crypto_pycrypto (line 203) | def _load_crypto_pycrypto():
function _load_crypto (line 321) | def _load_crypto():
class Decryptor (line 340) | class Decryptor(object):
method __init__ (line 341) | def __init__(self, bookkey, encryption):
method decompress (line 354) | def decompress(self, bytes):
method decrypt (line 366) | def decrypt(self, path, data):
function adeptBook (line 378) | def adeptBook(inpath):
function decryptBook (line 396) | def decryptBook(userkey, inpath, outpath):
function cli_main (line 468) | def cli_main():
function gui_main (line 483) | def gui_main():
FILE: DeDRM_plugin/ineptpdf.py
class SafeUnbuffered (line 72) | class SafeUnbuffered:
method __init__ (line 73) | def __init__(self, stream):
method write (line 78) | def write(self, data):
method __getattr__ (line 84) | def __getattr__(self, attr):
function unicode_argv (line 90) | def unicode_argv():
class ADEPTError (line 125) | class ADEPTError(Exception):
function SHA256 (line 131) | def SHA256(message):
function _load_crypto_libcrypto (line 137) | def _load_crypto_libcrypto():
function _load_crypto_pycrypto (line 259) | def _load_crypto_pycrypto():
function _load_crypto (line 394) | def _load_crypto():
function choplist (line 425) | def choplist(n, seq):
function nunpack (line 435) | def nunpack(s, default=0):
class PSException (line 457) | class PSException(Exception): pass
class PSEOF (line 458) | class PSEOF(PSException): pass
class PSSyntaxError (line 459) | class PSSyntaxError(PSException): pass
class PSTypeError (line 460) | class PSTypeError(PSException): pass
class PSValueError (line 461) | class PSValueError(PSException): pass
class PSObject (line 468) | class PSObject(object): pass
class PSLiteral (line 470) | class PSLiteral(PSObject):
method __init__ (line 476) | def __init__(self, name):
method __repr__ (line 480) | def __repr__(self):
class PSKeyword (line 489) | class PSKeyword(PSObject):
method __init__ (line 495) | def __init__(self, name):
method __repr__ (line 499) | def __repr__(self):
class PSSymbolTable (line 503) | class PSSymbolTable(object):
method __init__ (line 509) | def __init__(self, classe):
method intern (line 514) | def intern(self, name):
function literal_name (line 534) | def literal_name(x):
function keyword_name (line 542) | def keyword_name(x):
class PSBaseParser (line 566) | class PSBaseParser(object):
method __init__ (line 573) | def __init__(self, fp):
method __repr__ (line 578) | def __repr__(self):
method flush (line 581) | def flush(self):
method close (line 584) | def close(self):
method tell (line 588) | def tell(self):
method poll (line 591) | def poll(self, pos=None, n=80):
method seek (line 599) | def seek(self, pos):
method fillbuf (line 613) | def fillbuf(self):
method parse_main (line 623) | def parse_main(self, s, i):
method add_token (line 658) | def add_token(self, obj):
method parse_comment (line 662) | def parse_comment(self, s, i):
method parse_literal (line 673) | def parse_literal(self, s, i):
method parse_literal_hex (line 687) | def parse_literal_hex(self, s, i):
method parse_number (line 696) | def parse_number(self, s, i):
method parse_decimal (line 713) | def parse_decimal(self, s, i):
method parse_keyword (line 723) | def parse_keyword(self, s, i):
method parse_string (line 739) | def parse_string(self, s, i):
method parse_string_1 (line 762) | def parse_string_1(self, s, i):
method parse_wopen (line 774) | def parse_wopen(self, s, i):
method parse_wclose (line 783) | def parse_wclose(self, s, i):
method parse_hexstring (line 790) | def parse_hexstring(self, s, i):
method nexttoken (line 802) | def nexttoken(self):
method nextline (line 809) | def nextline(self):
method revreadlines (line 838) | def revreadlines(self):
class PSStackParser (line 865) | class PSStackParser(PSBaseParser):
method __init__ (line 867) | def __init__(self, fp):
method reset (line 872) | def reset(self):
method seek (line 879) | def seek(self, pos):
method push (line 884) | def push(self, *objs):
method pop (line 887) | def pop(self, n):
method popall (line 891) | def popall(self):
method add_results (line 895) | def add_results(self, *objs):
method start_type (line 899) | def start_type(self, pos, type):
method end_type (line 903) | def end_type(self, type):
method do_keyword (line 910) | def do_keyword(self, pos, token):
method nextobject (line 913) | def nextobject(self, direct=False):
class PDFObject (line 976) | class PDFObject(PSObject): pass
class PDFException (line 978) | class PDFException(PSException): pass
class PDFTypeError (line 979) | class PDFTypeError(PDFException): pass
class PDFValueError (line 980) | class PDFValueError(PDFException): pass
class PDFNotImplementedError (line 981) | class PDFNotImplementedError(PSException): pass
class PDFObjRef (line 986) | class PDFObjRef(PDFObject):
method __init__ (line 988) | def __init__(self, doc, objid, genno):
method __repr__ (line 997) | def __repr__(self):
method resolve (line 1000) | def resolve(self):
function resolve1 (line 1005) | def resolve1(x):
function resolve_all (line 1014) | def resolve_all(x):
function decipher_all (line 1029) | def decipher_all(decipher, objid, genno, x):
function int_value (line 1044) | def int_value(x):
function decimal_value (line 1052) | def decimal_value(x):
function num_value (line 1060) | def num_value(x):
function str_value (line 1068) | def str_value(x):
function list_value (line 1076) | def list_value(x):
function dict_value (line 1084) | def dict_value(x):
function stream_value (line 1092) | def stream_value(x):
function ascii85decode (line 1101) | def ascii85decode(data):
class PDFStream (line 1124) | class PDFStream(PDFObject):
method __init__ (line 1125) | def __init__(self, dic, rawdata, decipher=None):
method set_objid (line 1147) | def set_objid(self, objid, genno):
method __repr__ (line 1152) | def __repr__(self):
method decode (line 1160) | def decode(self):
method get_data (line 1217) | def get_data(self):
method get_rawdata (line 1222) | def get_rawdata(self):
method get_decdata (line 1225) | def get_decdata(self):
class PDFSyntaxError (line 1237) | class PDFSyntaxError(PDFException): pass
class PDFNoValidXRef (line 1238) | class PDFNoValidXRef(PDFSyntaxError): pass
class PDFEncryptionError (line 1239) | class PDFEncryptionError(PDFException): pass
class PDFPasswordIncorrect (line 1240) | class PDFPasswordIncorrect(PDFEncryptionError): pass
class PDFXRef (line 1255) | class PDFXRef(object):
method __init__ (line 1257) | def __init__(self):
method __repr__ (line 1261) | def __repr__(self):
method objids (line 1264) | def objids(self):
method load (line 1267) | def load(self, parser):
method load_trailer (line 1302) | def load_trailer(self, parser):
method getpos (line 1315) | def getpos(self, objid):
class PDFXRefStream (line 1325) | class PDFXRefStream(object):
method __init__ (line 1327) | def __init__(self):
method __repr__ (line 1334) | def __repr__(self):
method objids (line 1337) | def objids(self):
method load (line 1342) | def load(self, parser, debug=0):
method getpos (line 1360) | def getpos(self, objid):
class PDFDocument (line 1390) | class PDFDocument(object):
method __init__ (line 1392) | def __init__(self):
method set_parser (line 1405) | def set_parser(self, parser):
method set_root (line 1445) | def set_root(self, root):
method initialize (line 1456) | def initialize(self, password=b''):
method initialize_adobe_ps (line 1472) | def initialize_adobe_ps(self, password, docid, param):
method genkey_adobe_ps (line 1480) | def genkey_adobe_ps(self, param):
method initialize_standard (line 1514) | def initialize_standard(self, password, docid, param):
method initialize_ebx (line 1590) | def initialize_ebx(self, password, docid, param):
method genkey_v2 (line 1639) | def genkey_v2(self, objid, genno):
method genkey_v3 (line 1647) | def genkey_v3(self, objid, genno):
method genkey_v4 (line 1657) | def genkey_v4(self, objid, genno):
method decrypt_aes (line 1665) | def decrypt_aes(self, objid, genno, data):
method decrypt_aes256 (line 1675) | def decrypt_aes256(self, objid, genno, data):
method decrypt_rc4 (line 1685) | def decrypt_rc4(self, objid, genno, data):
method getobj (line 1692) | def getobj(self, objid):
class PDFObjStmRef (line 1774) | class PDFObjStmRef(object):
method __init__ (line 1776) | def __init__(self, objid, stmid, index):
class PDFParser (line 1786) | class PDFParser(PSStackParser):
method __init__ (line 1788) | def __init__(self, doc, fp):
method __repr__ (line 1794) | def __repr__(self):
method do_keyword (line 1802) | def do_keyword(self, pos, token):
method find_xref (line 1865) | def find_xref(self):
method read_xref_from (line 1878) | def read_xref_from(self, start, xrefs):
method read_xref (line 1913) | def read_xref(self):
class PDFObjStrmParser (line 1946) | class PDFObjStrmParser(PDFParser):
method __init__ (line 1948) | def __init__(self, data, doc):
method flush (line 1953) | def flush(self):
method do_keyword (line 1958) | def do_keyword(self, pos, token):
class PDFSerializer (line 1976) | class PDFSerializer(object):
method __init__ (line 1977) | def __init__(self, inf, userkey):
method dump (line 1997) | def dump(self, outf):
method write (line 2092) | def write(self, data):
method tell (line 2096) | def tell(self):
method escape_string (line 2099) | def escape_string(self, string):
method serialize_object (line 2106) | def serialize_object(self, obj):
method serialize_indirect (line 2164) | def serialize_indirect(self, objid, obj):
function decryptBook (line 2174) | def decryptBook(userkey, inpath, outpath):
function cli_main (line 2189) | def cli_main():
function gui_main (line 2205) | def gui_main():
FILE: DeDRM_plugin/ion.py
function _assert (line 96) | def _assert(test, msg="Exception"):
class SystemSymbols (line 101) | class SystemSymbols(object):
class IonCatalogItem (line 113) | class IonCatalogItem(object):
method __init__ (line 118) | def __init__(self, name, version, symnames):
class SymbolToken (line 124) | class SymbolToken(object):
method __init__ (line 128) | def __init__(self, text, sid):
class SymbolTable (line 136) | class SymbolTable(object):
method __init__ (line 139) | def __init__(self):
method findbyid (line 151) | def findbyid(self, sid):
method import_ (line 160) | def import_(self, table, maxid):
method importunknown (line 164) | def importunknown(self, name, maxid):
class ParserState (line 169) | class ParserState:
class BinaryIonParser (line 175) | class BinaryIonParser(object):
method __init__ (line 190) | def __init__(self, stream):
method reset (line 199) | def reset(self):
method addtocatalog (line 208) | def addtocatalog(self, name, version, symbols):
method hasnext (line 211) | def hasnext(self):
method hasnextraw (line 226) | def hasnextraw(self):
method next (line 266) | def next(self):
method push (line 273) | def push(self, typeid, nextposition, nextremaining):
method stepin (line 276) | def stepin(self):
method stepout (line 300) | def stepout(self):
method read (line 322) | def read(self, count=1):
method readfieldid (line 332) | def readfieldid(self):
method readtypeid (line 341) | def readtypeid(self):
method readvarint (line 374) | def readvarint(self):
method readvaruint (line 391) | def readvaruint(self):
method readdecimal (line 405) | def readdecimal(self):
method skip (line 438) | def skip(self, count):
method parsesymboltable (line 446) | def parsesymboltable(self):
method gatherimports (line 469) | def gatherimports(self):
method readimport (line 481) | def readimport(self):
method intvalue (line 520) | def intvalue(self):
method stringvalue (line 526) | def stringvalue(self):
method symbolvalue (line 535) | def symbolvalue(self):
method lobvalue (line 544) | def lobvalue(self):
method decimalvalue (line 554) | def decimalvalue(self):
method preparevalue (line 560) | def preparevalue(self):
method loadscalarvalue (line 564) | def loadscalarvalue(self):
method clearvalue (line 599) | def clearvalue(self):
method loadannotations (line 606) | def loadannotations(self):
method checkversionmarker (line 613) | def checkversionmarker(self):
method findcatalogitem (line 624) | def findcatalogitem(self, name):
method forceimport (line 629) | def forceimport(self, symbols):
method getfieldname (line 633) | def getfieldname(self):
method getfieldnamesymbol (line 638) | def getfieldnamesymbol(self):
method gettypename (line 641) | def gettypename(self):
method printlob (line 648) | def printlob(b):
method ionwalk (line 660) | def ionwalk(self, supert, indent, lst):
method print_ (line 706) | def print_(self, lst):
function addprottable (line 743) | def addprottable(ion):
function pkcs7pad (line 747) | def pkcs7pad(msg, blocklen):
function pkcs7unpad (line 753) | def pkcs7unpad(msg, blocklen):
function obfuscate (line 808) | def obfuscate(secret, version):
class DrmIonVoucher (line 831) | class DrmIonVoucher(object):
method __init__ (line 848) | def __init__(self, voucherenv, dsn, secret):
method decryptvoucher (line 862) | def decryptvoucher(self):
method parse (line 908) | def parse(self):
method parsevoucher (line 949) | def parsevoucher(self):
method printenvelope (line 972) | def printenvelope(self, lst):
method printkey (line 975) | def printkey(self, lst):
method printvoucher (line 983) | def printvoucher(self, lst):
method getlicensetype (line 989) | def getlicensetype(self):
class DrmIon (line 993) | class DrmIon(object):
method __init__ (line 1000) | def __init__(self, ionstream, onvoucherrequired):
method parse (line 1005) | def parse(self, outpages):
method print_ (line 1079) | def print_(self, lst):
method processpage (line 1082) | def processpage(self, ct, civ, outpages, decompress, decrypt):
FILE: DeDRM_plugin/k4mobidedrm.py
class DrmException (line 75) | class DrmException(Exception):
class SafeUnbuffered (line 99) | class SafeUnbuffered:
method __init__ (line 100) | def __init__(self, stream):
method write (line 105) | def write(self, data):
method __getattr__ (line 111) | def __getattr__(self, attr):
function unicode_argv (line 117) | def unicode_argv():
function cleanup_name (line 159) | def cleanup_name(name):
function unescape (line 179) | def unescape(text):
function GetDecryptedBook (line 200) | def GetDecryptedBook(infile, kDatabases, androidFiles, serials, pids, st...
function decryptBook (line 248) | def decryptBook(infile, outdir, kDatabaseFiles, androidFiles, serials, p...
function usage (line 301) | def usage(progname):
function cli_main (line 309) | def cli_main():
FILE: DeDRM_plugin/kfxdedrm.py
class KFXZipBook (line 27) | class KFXZipBook:
method __init__ (line 28) | def __init__(self, infile):
method getPIDMetaInfo (line 33) | def getPIDMetaInfo(self):
method processBook (line 36) | def processBook(self, totalpids):
method decrypt_voucher (line 54) | def decrypt_voucher(self, totalpids):
method getBookTitle (line 100) | def getBookTitle(self):
method getBookExtension (line 103) | def getBookExtension(self):
method getBookType (line 106) | def getBookType(self):
method cleanup (line 109) | def cleanup(self):
method getFile (line 112) | def getFile(self, outpath):
FILE: DeDRM_plugin/kgenpids.py
class DrmException (line 25) | class DrmException(Exception):
function MD5 (line 40) | def MD5(message):
function SHA1 (line 45) | def SHA1(message):
function encode (line 53) | def encode(data, map):
function encodeHash (line 64) | def encodeHash(data,map):
function decode (line 68) | def decode(data,map):
function getTwoBitsFromBitField (line 84) | def getTwoBitsFromBitField(bitField,offset):
function getSixBitsFromBitField (line 90) | def getSixBitsFromBitField(bitField,offset):
function encodePID (line 96) | def encodePID(hash):
function generatePidEncryptionTable (line 104) | def generatePidEncryptionTable() :
function generatePidSeed (line 118) | def generatePidSeed(table,dsn) :
function generateDevicePID (line 126) | def generateDevicePID(table,dsn,nbRoll):
function crc32 (line 140) | def crc32(s):
function checksumPid (line 144) | def checksumPid(s):
function pidFromSerial (line 159) | def pidFromSerial(s, l):
function getKindlePids (line 176) | def getKindlePids(rec209, token, serialnum):
function getK4Pids (line 203) | def getK4Pids(rec209, token, kindleDatabase):
function getPidList (line 289) | def getPidList(md1, md2, serials=[], kDatabases=[]):
FILE: DeDRM_plugin/kindlekey.py
class RegError (line 49) | class RegError(Exception):
class SafeUnbuffered (line 57) | class SafeUnbuffered:
method __init__ (line 58) | def __init__(self, stream):
method write (line 63) | def write(self, data):
method __getattr__ (line 69) | def __getattr__(self, attr):
function unicode_argv (line 78) | def unicode_argv():
class DrmException (line 114) | class DrmException(Exception):
function MD5 (line 120) | def MD5(message):
function SHA1 (line 125) | def SHA1(message):
function SHA256 (line 130) | def SHA256(message):
function primes (line 136) | def primes(n):
function encode (line 160) | def encode(data, map):
function encodeHash (line 171) | def encodeHash(data,map):
function decode (line 175) | def decode(data,map):
class CryptoError (line 218) | class CryptoError(Exception):
method __init__ (line 220) | def __init__(self,errorMessage='Error!'):
method __str__ (line 222) | def __str__(self):
class InitCryptoError (line 225) | class InitCryptoError(CryptoError):
class BadKeySizeError (line 227) | class BadKeySizeError(InitCryptoError):
class EncryptError (line 229) | class EncryptError(CryptoError):
class DecryptError (line 231) | class DecryptError(CryptoError):
class DecryptNotBlockAlignedError (line 233) | class DecryptNotBlockAlignedError(DecryptError):
function xor (line 236) | def xor(a,b):
class BlockCipher (line 250) | class BlockCipher:
method __init__ (line 252) | def __init__(self):
method reset (line 255) | def reset(self):
method resetEncrypt (line 258) | def resetEncrypt(self):
method resetDecrypt (line 261) | def resetDecrypt(self):
method encrypt (line 265) | def encrypt(self, plainText, more = None):
method decrypt (line 289) | def decrypt(self, cipherText, more = None):
class Pad (line 321) | class Pad:
method __init__ (line 322) | def __init__(self):
class padWithPadLen (line 325) | class padWithPadLen(Pad):
method addPad (line 328) | def addPad(self, extraBytes, blockSize):
method removePad (line 335) | def removePad(self, paddedBinaryString, blockSize):
class noPadding (line 341) | class noPadding(Pad):
method addPad (line 344) | def addPad(self, extraBytes, blockSize):
method removePad (line 348) | def removePad(self, paddedBinaryString, blockSize):
class Rijndael (line 359) | class Rijndael(BlockCipher):
method __init__ (line 361) | def __init__(self, key = None, padding = padWithPadLen(), keySize=16, ...
method setKey (line 378) | def setKey(self, key):
method encryptBlock (line 384) | def encryptBlock(self, plainTextBlock):
method decryptBlock (line 399) | def decryptBlock(self, encryptedBlock):
method _toBlock (line 413) | def _toBlock(self, bs):
method _toBString (line 418) | def _toBString(self, block):
function keyExpansion (line 436) | def keyExpansion(algInstance, keyArray):
function AddRoundKey (line 456) | def AddRoundKey(algInstance, keyBlock):
function SubBytes (line 463) | def SubBytes(algInstance):
function InvSubBytes (line 468) | def InvSubBytes(algInstance):
function ShiftRows (line 549) | def ShiftRows(algInstance):
function InvShiftRows (line 556) | def InvShiftRows(algInstance):
function MixColumns (line 564) | def MixColumns(a):
function InvMixColumns (line 574) | def InvMixColumns(a):
function mul (line 587) | def mul(a, b):
class AES (line 638) | class AES(Rijndael):
method __init__ (line 642) | def __init__(self, key = None, padding = padWithPadLen(), keySize=16):
class CBC (line 660) | class CBC(BlockCipher):
method __init__ (line 665) | def __init__(self, blockCipherInstance, padding = padWithPadLen()):
method setKey (line 680) | def setKey(self, key):
method resetEncrypt (line 684) | def resetEncrypt(self):
method resetDecrypt (line 688) | def resetDecrypt(self):
method encrypt (line 692) | def encrypt(self, plainText, iv=None, more=None):
method decrypt (line 703) | def decrypt(self, cipherText, iv=None, more=None):
method encryptBlock (line 714) | def encryptBlock(self, plainTextBlock):
method decryptBlock (line 731) | def decryptBlock(self, encryptedBlock):
class aescbc_AES_CBC (line 755) | class aescbc_AES_CBC(CBC):
method __init__ (line 757) | def __init__(self, key=None, padding=padWithPadLen(), keySize=16):
class AES_CBC (line 761) | class AES_CBC(object):
method __init__ (line 762) | def __init__(self):
method set_decrypt_key (line 767) | def set_decrypt_key(self, userkey, iv):
method decrypt (line 772) | def decrypt(self, data):
class KeyIVGen (line 779) | class KeyIVGen(object):
method pbkdf2 (line 783) | def pbkdf2(self, passwd, salt, iter, keylen):
function UnprotectHeaderData (line 815) | def UnprotectHeaderData(encryptedData):
class DataBlob (line 838) | class DataBlob(Structure):
function GetSystemDirectory (line 844) | def GetSystemDirectory():
function GetVolumeSerialNumber (line 855) | def GetVolumeSerialNumber():
function GetIDString (line 868) | def GetIDString():
function getLastError (line 873) | def getLastError():
function GetUserName (line 882) | def GetUserName():
function CryptUnprotectData (line 909) | def CryptUnprotectData():
method __init__ (line 1423) | def __init__(self, entropy, IDString):
method decrypt (line 1435) | def decrypt(self, encryptedData):
function getEnvironmentVariable (line 930) | def getEnvironmentVariable(name):
function getKindleInfoFiles (line 940) | def getKindleInfoFiles():
function getDBfromFile (line 1019) | def getDBfromFile(kInfoFile):
function _load_crypto_libcrypto (line 1171) | def _load_crypto_libcrypto():
function _load_crypto (line 1265) | def _load_crypto():
function GetVolumesSerialNumbers (line 1289) | def GetVolumesSerialNumbers():
function GetDiskPartitionNames (line 1309) | def GetDiskPartitionNames():
function GetDiskPartitionUUIDs (line 1326) | def GetDiskPartitionUUIDs():
function GetMACAddressesMunged (line 1347) | def GetMACAddressesMunged():
function GetUserName (line 1388) | def GetUserName():
function GetIDStrings (line 1393) | def GetIDStrings():
function UnprotectHeaderData (line 1407) | def UnprotectHeaderData(encryptedData):
class CryptUnprotectData (line 1422) | class CryptUnprotectData(object):
method __init__ (line 1423) | def __init__(self, entropy, IDString):
method decrypt (line 1435) | def decrypt(self, encryptedData):
function getKindleInfoFiles (line 1442) | def getKindleInfoFiles():
function getDBfromFile (line 1501) | def getDBfromFile(kInfoFile):
function getDBfromFile (line 1677) | def getDBfromFile(kInfoFile):
function kindlekeys (line 1681) | def kindlekeys(files = []):
function getkey (line 1698) | def getkey(outpath, files=[]):
function usage (line 1720) | def usage(progname):
function cli_main (line 1728) | def cli_main():
function gui_main (line 1771) | def gui_main():
FILE: DeDRM_plugin/kindlepid.py
class SafeUnbuffered (line 22) | class SafeUnbuffered:
method __init__ (line 23) | def __init__(self, stream):
method write (line 28) | def write(self, data):
method __getattr__ (line 34) | def __getattr__(self, attr):
function unicode_argv (line 40) | def unicode_argv():
function crc32 (line 78) | def crc32(s):
function checksumPid (line 81) | def checksumPid(s):
function pidFromSerial (line 94) | def pidFromSerial(s, l):
function cli_main (line 112) | def cli_main():
FILE: DeDRM_plugin/mobidedrm.py
class SafeUnbuffered (line 90) | class SafeUnbuffered:
method __init__ (line 91) | def __init__(self, stream):
method write (line 96) | def write(self, data):
method __getattr__ (line 102) | def __getattr__(self, attr):
function unicode_argv (line 108) | def unicode_argv():
class DrmException (line 145) | class DrmException(Exception):
function PC1 (line 154) | def PC1(key, src, decryption=True):
function checksumPid (line 195) | def checksumPid(s):
function getSizeOfTrailingDataEntries (line 209) | def getSizeOfTrailingDataEntries(ptr, size, flags):
class MobiBook (line 236) | class MobiBook:
method loadSection (line 237) | def loadSection(self, section):
method cleanup (line 245) | def cleanup(self):
method __init__ (line 249) | def __init__(self, infile):
method getBookTitle (line 329) | def getBookTitle(self):
method getPIDMetaInfo (line 350) | def getPIDMetaInfo(self):
method patch (line 366) | def patch(self, off, new):
method patchSection (line 370) | def patchSection(self, section, new, in_off = 0):
method parseDRM (line 380) | def parseDRM(self, data, count, pidlist):
method getFile (line 413) | def getFile(self, outpath):
method getBookType (line 416) | def getBookType(self):
method getBookExtension (line 425) | def getBookExtension(self):
method processBook (line 433) | def processBook(self, pidlist):
function getUnencryptedBook (line 521) | def getUnencryptedBook(infile,pidlist):
function cli_main (line 529) | def cli_main():
FILE: DeDRM_plugin/openssl_des.py
function load_libcrypto (line 6) | def load_libcrypto():
FILE: DeDRM_plugin/prefs.py
class DeDRM_Prefs (line 16) | class DeDRM_Prefs():
method __init__ (line 17) | def __init__(self):
method __getitem__ (line 51) | def __getitem__(self,kind = None):
method set (line 56) | def set(self, kind, value):
method writeprefs (line 59) | def writeprefs(self,value = True):
method addnamedvaluetoprefs (line 62) | def addnamedvaluetoprefs(self, prefkind, keyname, keyvalue):
method addvaluetoprefs (line 80) | def addvaluetoprefs(self, prefkind, prefsvalue):
function convertprefs (line 91) | def convertprefs(always = False):
FILE: DeDRM_plugin/pycrypto_des.py
function load_pycrypto (line 5) | def load_pycrypto():
FILE: DeDRM_plugin/python_des.py
class Des (line 7) | class Des(object):
method __init__ (line 66) | def __init__(self, key, mode=ECB, IV=None):
method getKey (line 80) | def getKey(self):
method setKey (line 82) | def setKey(self, key):
method getMode (line 85) | def getMode(self):
method setMode (line 87) | def setMode(self, mode):
method getIV (line 89) | def getIV(self):
method setIV (line 91) | def setIV(self, IV):
method getPadding (line 95) | def getPadding(self):
method __String_to_BitList (line 97) | def __String_to_BitList(self, data):
method __BitList_to_String (line 112) | def __BitList_to_String(self, data):
method __permutate (line 123) | def __permutate(self, table, block):
method __create_sub_keys (line 125) | def __create_sub_keys(self):
method __des_crypt (line 140) | def __des_crypt(self, block, crypt_type):
method crypt (line 176) | def crypt(self, data, crypt_type):
method encrypt (line 215) | def encrypt(self, data, pad=''):
method decrypt (line 218) | def decrypt(self, data, pad=''):
FILE: DeDRM_plugin/scriptinterface.py
function decryptepub (line 18) | def decryptepub(infile, outdir, rscpath):
function decryptpdf (line 87) | def decryptpdf(infile, outdir, rscpath):
function decryptpdb (line 117) | def decryptpdb(infile, outdir, rscpath):
function decryptk4mobi (line 145) | def decryptk4mobi(infile, outdir, rscpath):
FILE: DeDRM_plugin/scrolltextwidget.py
class ScrolledText (line 8) | class ScrolledText(tkinter.Text):
method __init__ (line 9) | def __init__(self, master=None, **kw):
method __str__ (line 26) | def __str__(self):
FILE: DeDRM_plugin/simpleprefs.py
class SimplePrefsError (line 9) | class SimplePrefsError(Exception):
class SimplePrefs (line 12) | class SimplePrefs(object):
method __init__ (line 13) | def __init__(self, target, description):
method _loadPreferences (line 40) | def _loadPreferences(self):
method getPreferences (line 53) | def getPreferences(self):
method setPreferences (line 56) | def setPreferences(self, newprefs={}):
FILE: DeDRM_plugin/stylexml2css.py
class DocParser (line 16) | class DocParser(object):
method __init__ (line 17) | def __init__(self, flatxml, fontsize, ph, pw):
method findinDoc (line 52) | def findinDoc(self, tagpath, pos, end) :
method posinDoc (line 78) | def posinDoc(self, tagpath):
method getData (line 90) | def getData(self, tagpath, pos, end, clean=False):
method process (line 105) | def process(self):
function convert2CSS (line 273) | def convert2CSS(flatxml, fontsize, ph, pw):
function getpageIDMap (line 287) | def getpageIDMap(flatxml):
FILE: DeDRM_plugin/subasyncio.py
class Process (line 25) | class Process(object):
method __init__ (line 26) | def __init__(self, *params, **kwparams):
method pid (line 67) | def pid(self):
method kill (line 70) | def kill(self, signal):
method wait (line 74) | def wait(self, flag):
method terminate (line 88) | def terminate(self):
method __reader (line 94) | def __reader(self, collector, source):
method __feeder (line 106) | def __feeder(self, pending, drain):
method read (line 119) | def read(self):
method readerr (line 127) | def readerr(self):
method write (line 135) | def write(self, data):
method closeinput (line 144) | def closeinput(self):
FILE: DeDRM_plugin/topazextract.py
class SafeUnbuffered (line 28) | class SafeUnbuffered:
method __init__ (line 29) | def __init__(self, stream):
method write (line 34) | def write(self, data):
method __getattr__ (line 40) | def __getattr__(self, attr):
function unicode_argv (line 46) | def unicode_argv():
class DrmException (line 93) | class DrmException(Exception):
function zipUpDir (line 98) | def zipUpDir(myzip, tdir, localname):
function bookReadEncodedNumber (line 117) | def bookReadEncodedNumber(fo):
function bookReadString (line 134) | def bookReadString(fo):
function topazCryptoInit (line 143) | def topazCryptoInit(key):
function topazCryptoDecrypt (line 154) | def topazCryptoDecrypt(data, ctx):
function decryptRecord (line 168) | def decryptRecord(data,PID):
function decryptDkeyRecord (line 173) | def decryptDkeyRecord(data,PID):
function decryptDkeyRecords (line 185) | def decryptDkeyRecords(data,PID):
class TopazBook (line 202) | class TopazBook:
method __init__ (line 203) | def __init__(self, filename):
method parseTopazHeaders (line 217) | def parseTopazHeaders(self):
method parseMetadata (line 245) | def parseMetadata(self):
method getPIDMetaInfo (line 262) | def getPIDMetaInfo(self):
method getBookTitle (line 271) | def getBookTitle(self):
method setBookKey (line 277) | def setBookKey(self, key):
method getBookPayloadRecord (line 280) | def getBookPayloadRecord(self, name, index):
method processBook (line 322) | def processBook(self, pidlst):
method createBookDirectory (line 377) | def createBookDirectory(self):
method extractFiles (line 395) | def extractFiles(self):
method getFile (line 422) | def getFile(self, zipname):
method getBookType (line 432) | def getBookType(self):
method getBookExtension (line 435) | def getBookExtension(self):
method getSVGZip (line 438) | def getSVGZip(self, zipname):
method cleanup (line 445) | def cleanup(self):
function usage (line 449) | def usage(progname):
function cli_main (line 455) | def cli_main():
FILE: DeDRM_plugin/utilities.py
function uStrCmp (line 20) | def uStrCmp (s1, s2, caseless=False):
function parseCustString (line 29) | def parseCustString(keystuff):
FILE: DeDRM_plugin/wineutils.py
class NoWinePython3Exception (line 11) | class NoWinePython3Exception(Exception):
class WinePythonCLI (line 15) | class WinePythonCLI:
method __init__ (line 17) | def __init__(self, wineprefix=""):
method check_call (line 54) | def check_call(self, cli_args):
function WineGetKeys (line 68) | def WineGetKeys(scriptpath, extension, wineprefix=""):
FILE: DeDRM_plugin/zipfilerugged.py
class BadZipfile (line 24) | class BadZipfile(Exception):
class LargeZipFile (line 28) | class LargeZipFile(Exception):
function _check_zipfile (line 138) | def _check_zipfile(fp):
function is_zipfile (line 146) | def is_zipfile(filename):
function _EndRecData64 (line 162) | def _EndRecData64(fpin, offset, endrec):
function _EndRecData (line 195) | def _EndRecData(fpin):
class ZipInfo (line 253) | class ZipInfo (object):
method __init__ (line 278) | def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
method FileHeader (line 316) | def FileHeader(self):
method _encodeFilenameFlags (line 350) | def _encodeFilenameFlags(self):
method _decodeExtra (line 359) | def _decodeExtra(self):
class _ZipDecrypter (line 396) | class _ZipDecrypter:
method _GenerateCRCTable (line 409) | def _GenerateCRCTable():
method _crc32 (line 429) | def _crc32(self, ch, crc):
method __init__ (line 433) | def __init__(self, pwd):
method _UpdateKeys (line 440) | def _UpdateKeys(self, c):
method __call__ (line 446) | def __call__(self, c):
class ZipExtFile (line 455) | class ZipExtFile(io.BufferedIOBase):
method __init__ (line 469) | def __init__(self, fileobj, mode, zipinfo, decrypter=None):
method readline (line 495) | def readline(self, limit=-1):
method peek (line 545) | def peek(self, n=1):
method readable (line 554) | def readable(self):
method read (line 557) | def read(self, n=-1):
method read1 (line 572) | def read1(self, n):
class ZipFile (line 623) | class ZipFile:
method __init__ (line 640) | def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=...
method __enter__ (line 699) | def __enter__(self):
method __exit__ (line 702) | def __exit__(self, type, value, traceback):
method _GetContents (line 705) | def _GetContents(self):
method _RealGetContents (line 716) | def _RealGetContents(self):
method namelist (line 779) | def namelist(self):
method infolist (line 786) | def infolist(self):
method printdir (line 791) | def printdir(self):
method testzip (line 798) | def testzip(self):
method getinfo (line 811) | def getinfo(self, name):
method setpassword (line 820) | def setpassword(self, pwd):
method read (line 824) | def read(self, name, pwd=None):
method open (line 828) | def open(self, name, mode="r", pwd=None):
method extract (line 897) | def extract(self, member, path=None, pwd=None):
method extractall (line 911) | def extractall(self, path=None, members=None, pwd=None):
method _extract_member (line 923) | def _extract_member(self, member, targetpath, pwd):
method _writecheck (line 960) | def _writecheck(self, zinfo):
method write (line 983) | def write(self, filename, arcname=None, compress_type=None):
method writestr (line 1064) | def writestr(self, zinfo_or_arcname, bytes, compress_type=None):
method __del__ (line 1107) | def __del__(self):
method close (line 1111) | def close(self):
class PyZipFile (line 1222) | class PyZipFile(ZipFile):
method writepy (line 1225) | def writepy(self, pathname, basename = ""):
method _get_codename (line 1290) | def _get_codename(self, pathname, basename):
function main (line 1321) | def main(args = None):
FILE: DeDRM_plugin/zipfix.py
class ZipInfo (line 41) | class ZipInfo(zipfilerugged.ZipInfo):
method __init__ (line 42) | def __init__(self, *args, **kwargs):
class fixZip (line 48) | class fixZip:
method __init__ (line 49) | def __init__(self, zinput, zoutput):
method getlocalname (line 58) | def getlocalname(self, zi):
method uncompress (line 67) | def uncompress(self, cmpdata):
method getfiledata (line 86) | def getfiledata(self, zi):
method fix (line 114) | def fix(self):
function usage (line 164) | def usage():
function repairBook (line 171) | def repairBook(infile, outfile):
function main (line 184) | def main(argv=sys.argv):
FILE: Obok_plugin/__init__.py
class ObokDeDRMAction (line 29) | class ObokDeDRMAction(InterfaceActionBase):
method is_customizable (line 43) | def is_customizable(self):
method config_widget (line 50) | def config_widget(self):
method save_settings (line 72) | def save_settings(self, config_widget):
FILE: Obok_plugin/action.py
class InterfacePluginAction (line 47) | class InterfacePluginAction(InterfaceAction):
method genesis (line 54) | def genesis(self):
method launchObok (line 62) | def launchObok(self):
method show_help (line 192) | def show_help(self):
method build_book_list (line 207) | def build_book_list(self):
method get_decrypted_kobo_books (line 213) | def get_decrypted_kobo_books(self, book):
method add_new_books (line 233) | def add_new_books(self, books_to_add):
method add_epub_format (line 254) | def add_epub_format(self, book_id, mi, path):
method process_epub_formats (line 271) | def process_epub_formats(self):
method wrap_up_results (line 296) | def wrap_up_results(self):
method ask_about_inserting_epubs (line 311) | def ask_about_inserting_epubs(self):
method find_a_home (line 334) | def find_a_home(self, ids):
method refresh_gui_lib (line 347) | def refresh_gui_lib(self):
method decryptBook (line 364) | def decryptBook(self, book):
method build_report (line 420) | def build_report(self):
FILE: Obok_plugin/common_utils.py
function debug_print (line 31) | def debug_print(*args):
function set_plugin_icon_resources (line 46) | def set_plugin_icon_resources(name, resources):
function get_icon (line 56) | def get_icon(icon_name):
function get_pixmap (line 71) | def get_pixmap(icon_name):
function get_local_images_dir (line 103) | def get_local_images_dir(subfolder=None):
function create_menu_item (line 116) | def create_menu_item(ia, parent_menu, menu_text, image=None, tooltip=None,
function create_menu_action_unique (line 144) | def create_menu_action_unique(ia, parent_menu, menu_text, image=None, to...
function get_library_uuid (line 184) | def get_library_uuid(db):
class ImageLabel (line 192) | class ImageLabel(QLabel):
method __init__ (line 194) | def __init__(self, parent, icon_name, size=16):
class ImageTitleLayout (line 202) | class ImageTitleLayout(QHBoxLayout):
method __init__ (line 206) | def __init__(self, parent, icon_name, title):
method update_title_icon (line 226) | def update_title_icon(self, icon_name):
class SizePersistedDialog (line 237) | class SizePersistedDialog(QDialog):
method __init__ (line 242) | def __init__(self, parent, unique_pref_name):
method resize_dialog (line 249) | def resize_dialog(self):
method dialog_closing (line 255) | def dialog_closing(self, result):
method help_link_activated (line 259) | def help_link_activated(self, url):
class ReadOnlyTableWidgetItem (line 263) | class ReadOnlyTableWidgetItem(QTableWidgetItem):
method __init__ (line 265) | def __init__(self, text):
class RatingTableWidgetItem (line 271) | class RatingTableWidgetItem(QTableWidgetItem):
method __init__ (line 273) | def __init__(self, rating, is_read_only=False):
class DateTableWidgetItem (line 280) | class DateTableWidgetItem(QTableWidgetItem):
method __init__ (line 282) | def __init__(self, date_read, is_read_only=False, default_to_today=Fal...
class DateDelegate (line 295) | class DateDelegate(_DateDelegate):
method __init__ (line 301) | def __init__(self, parent, fmt='dd MMM yyyy', default_to_today=True):
method createEditor (line 312) | def createEditor(self, parent, option, index):
method setEditorData (line 320) | def setEditorData(self, editor, index):
method setModelData (line 329) | def setModelData(self, editor, model, index):
class NoWheelComboBox (line 337) | class NoWheelComboBox(QComboBox):
method wheelEvent (line 339) | def wheelEvent (self, event):
class CheckableTableWidgetItem (line 344) | class CheckableTableWidgetItem(QTableWidgetItem):
method __init__ (line 346) | def __init__(self, checked=False, is_tristate=False):
method get_boolean_value (line 359) | def get_boolean_value(self):
class TextIconWidgetItem (line 370) | class TextIconWidgetItem(QTableWidgetItem):
method __init__ (line 372) | def __init__(self, text, icon):
class ReadOnlyTextIconWidgetItem (line 378) | class ReadOnlyTextIconWidgetItem(ReadOnlyTableWidgetItem):
method __init__ (line 380) | def __init__(self, text, icon):
class ReadOnlyLineEdit (line 386) | class ReadOnlyLineEdit(QLineEdit):
method __init__ (line 388) | def __init__(self, text, parent):
class NumericLineEdit (line 395) | class NumericLineEdit(QLineEdit):
method __init__ (line 399) | def __init__(self, *args):
class KeyValueComboBox (line 404) | class KeyValueComboBox(QComboBox):
method __init__ (line 406) | def __init__(self, parent, values, selected_key):
method populate_combo (line 411) | def populate_combo(self, selected_key):
method selected_key (line 421) | def selected_key(self):
class KeyComboBox (line 427) | class KeyComboBox(QComboBox):
method __init__ (line 429) | def __init__(self, parent, values, selected_key):
method populate_combo (line 434) | def populate_combo(self, selected_key):
method selected_key (line 444) | def selected_key(self):
class CustomColumnComboBox (line 450) | class CustomColumnComboBox(QComboBox):
method __init__ (line 452) | def __init__(self, parent, custom_columns={}, selected_column='', init...
method populate_combo (line 456) | def populate_combo(self, custom_columns, selected_column, initial_item...
method get_selected_column (line 472) | def get_selected_column(self):
class KeyboardConfigDialog (line 476) | class KeyboardConfigDialog(SizePersistedDialog):
method __init__ (line 480) | def __init__(self, gui, group_name):
method initialize (line 500) | def initialize(self):
method commit (line 504) | def commit(self):
class ProgressBar (line 509) | class ProgressBar(QDialog):
method __init__ (line 510) | def __init__(self, parent=None, max_items=100, window_title='Progress ...
method increment (line 530) | def increment(self):
method refresh (line 534) | def refresh(self):
method set_label (line 537) | def set_label(self, value):
method set_maximum (line 541) | def set_maximum(self, value):
method set_value (line 545) | def set_value(self, value):
function convert_kobo_date (line 549) | def convert_kobo_date(kobo_date):
FILE: Obok_plugin/config.py
class ConfigWidget (line 26) | class ConfigWidget(QWidget):
method __init__ (line 27) | def __init__(self, plugin_action):
method edit_serials (line 59) | def edit_serials(self):
method edit_kobo_directory (line 64) | def edit_kobo_directory(self):
method save_settings (line 71) | def save_settings(self):
class ManageKeysDialog (line 80) | class ManageKeysDialog(QDialog):
method __init__ (line 81) | def __init__(self, parent, key_type_name, plugin_keys, create_key, key...
method populate_list (line 134) | def populate_list(self):
method add_key (line 142) | def add_key(self):
method delete_key (line 159) | def delete_key(self):
class AddSerialDialog (line 170) | class AddSerialDialog(QDialog):
method __init__ (line 171) | def __init__(self, parent=None,):
method key_name (line 198) | def key_name(self):
method key_value (line 202) | def key_value(self):
method accept (line 205) | def accept(self):
FILE: Obok_plugin/dialogs.py
class SelectionDialog (line 43) | class SelectionDialog(SizePersistedDialog):
method __init__ (line 47) | def __init__(self, gui, interface_action, books):
method _select_all_clicked (line 104) | def _select_all_clicked(self):
method _select_drm_clicked (line 107) | def _select_drm_clicked(self):
method _select_free_clicked (line 110) | def _select_free_clicked(self):
method _help_link_activated (line 113) | def _help_link_activated(self, url):
method _ok_clicked (line 119) | def _ok_clicked(self):
method getBooks (line 129) | def getBooks(self):
class BookListTableWidget (line 136) | class BookListTableWidget(QTableWidget):
method __init__ (line 138) | def __init__(self, parent):
method populate_table (line 142) | def populate_table(self, books):
method setMinimumColumnWidth (line 167) | def setMinimumColumnWidth(self, col, minimum):
method populate_table_row (line 171) | def populate_table_row(self, row, book):
method get_books (line 187) | def get_books(self):
method select_all (line 201) | def select_all(self):
method select_drm (line 204) | def select_drm(self, has_drm):
class DecryptAddProgressDialog (line 221) | class DecryptAddProgressDialog(QProgressDialog):
method __init__ (line 226) | def __init__(self, gui, indices, callback_fn, db, db_type='calibre', s...
method do_book_action (line 248) | def do_book_action(self):
method do_close (line 283) | def do_close(self):
class AddEpubFormatsProgressDialog (line 287) | class AddEpubFormatsProgressDialog(QProgressDialog):
method __init__ (line 292) | def __init__(self, gui, entries, callback_fn, status_msg_type='formats...
method do_book_action (line 312) | def do_book_action(self):
method do_close (line 339) | def do_close(self):
class ViewLog (line 343) | class ViewLog(QDialog):
method __init__ (line 347) | def __init__(self, title, html, parent=None):
method copy_to_clipboard (line 380) | def copy_to_clipboard(self):
class ResultsSummaryDialog (line 385) | class ResultsSummaryDialog(MessageBox):
method __init__ (line 386) | def __init__(self, parent, title, msg, log='', det_msg=''):
method show_log (line 403) | def show_log(self):
class ReadOnlyTableWidgetItem (line 408) | class ReadOnlyTableWidgetItem(QTableWidgetItem):
method __init__ (line 409) | def __init__(self, text):
class AuthorTableWidgetItem (line 415) | class AuthorTableWidgetItem(ReadOnlyTableWidgetItem):
method __init__ (line 416) | def __init__(self, text, sort_key):
method __lt__ (line 421) | def __lt__(self, other):
class SeriesTableWidgetItem (line 424) | class SeriesTableWidgetItem(ReadOnlyTableWidgetItem):
method __init__ (line 425) | def __init__(self, series, series_index=None):
class IconWidgetItem (line 437) | class IconWidgetItem(ReadOnlyTableWidgetItem):
method __init__ (line 438) | def __init__(self, text, icon, sort_key):
method __lt__ (line 445) | def __lt__(self, other):
class NumericTableWidgetItem (line 448) | class NumericTableWidgetItem(QTableWidgetItem):
method __init__ (line 450) | def __init__(self, number, is_read_only=False):
FILE: Obok_plugin/obok/legacy_obok.py
class legacy_obok (line 9) | class legacy_obok(object):
method __init__ (line 10) | def __init__(self):
method get_legacy_cookie_id (line 14) | def get_legacy_cookie_id(self):
method __bytearraytostring (line 20) | def __bytearraytostring(self, bytearr):
method plist_to_dictionary (line 26) | def plist_to_dictionary(self, filename):
method __oldcookiedeviceid (line 38) | def __oldcookiedeviceid(self):
FILE: Obok_plugin/obok/obok.py
class ENCRYPTIONError (line 190) | class ENCRYPTIONError(Exception):
function _load_crypto_libcrypto (line 193) | def _load_crypto_libcrypto():
function _load_crypto_pycrypto (line 251) | def _load_crypto_pycrypto():
function _load_crypto (line 262) | def _load_crypto():
class SafeUnbuffered (line 278) | class SafeUnbuffered:
method __init__ (line 279) | def __init__(self, stream):
method write (line 284) | def write(self, data):
method __getattr__ (line 289) | def __getattr__(self, attr):
class KoboLibrary (line 293) | class KoboLibrary(object):
method __init__ (line 300) | def __init__ (self, serials = [], device_path = None, desktopkobodir =...
method close (line 426) | def close (self):
method userkeys (line 434) | def userkeys (self):
method books (line 445) | def books (self):
method __bookfile (line 465) | def __bookfile (self, volumeid):
method __getmacaddrs (line 469) | def __getmacaddrs (self):
method __getuserids (line 509) | def __getuserids (self):
method __getuserkeys (line 522) | def __getuserkeys (self, macaddr):
class KoboBook (line 532) | class KoboBook(object):
method __init__ (line 543) | def __init__ (self, volumeid, title, filename, type, cursor, author=No...
method encryptedfiles (line 555) | def encryptedfiles (self):
method has_drm (line 597) | def has_drm (self):
class KoboFile (line 601) | class KoboFile(object):
method __init__ (line 609) | def __init__ (self, filename, mimetype, key):
method decrypt (line 613) | def decrypt (self, userkey, contents):
method check (line 625) | def check (self, contents):
method __removeaespadding (line 693) | def __removeaespadding (self, contents):
function decrypt_book (line 711) | def decrypt_book(book, lib):
function cli_main (line 747) | def cli_main():
FILE: Obok_plugin/utilities.py
function convert_qvariant (line 39) | def convert_qvariant(x):
function debug_print (line 48) | def debug_print(*args):
function format_plural (line 62) | def format_plural(number, possessive=False):
function set_plugin_icon_resources (line 73) | def set_plugin_icon_resources(name, resources):
function get_icon (line 83) | def get_icon(icon_name):
function get_pixmap (line 97) | def get_pixmap(icon_name):
function get_local_images_dir (line 126) | def get_local_images_dir(subfolder=None):
function showErrorDlg (line 138) | def showErrorDlg(errmsg, parent, trcbk=False):
class SizePersistedDialog (line 153) | class SizePersistedDialog(QDialog):
method __init__ (line 158) | def __init__(self, parent, unique_pref_name):
method resize_dialog (line 164) | def resize_dialog(self):
method dialog_closing (line 170) | def dialog_closing(self, result):
method persist_custom_prefs (line 175) | def persist_custom_prefs(self):
method load_custom_pref (line 183) | def load_custom_pref(self, name, default=None):
method save_custom_pref (line 186) | def save_custom_pref(self, name, value):
class ImageTitleLayout (line 189) | class ImageTitleLayout(QHBoxLayout):
method __init__ (line 193) | def __init__(self, parent, icon_name, title):
method update_title_icon (line 211) | def update_title_icon(self, icon_name):
class ReadOnlyTableWidgetItem (line 222) | class ReadOnlyTableWidgetItem(QTableWidgetItem):
method __init__ (line 224) | def __init__(self, text):
FILE: Other_Tools/B_and_N_Download_Helper/BN-Dload.user.js
function doIt (line 9) | function doIt() {
FILE: Other_Tools/Kobo/obok.py
class ENCRYPTIONError (line 184) | class ENCRYPTIONError(Exception):
function _load_crypto_libcrypto (line 187) | def _load_crypto_libcrypto():
function _load_crypto_pycrypto (line 245) | def _load_crypto_pycrypto():
function _load_crypto (line 256) | def _load_crypto():
class SafeUnbuffered (line 272) | class SafeUnbuffered:
method __init__ (line 273) | def __init__(self, stream):
method write (line 278) | def write(self, data):
method __getattr__ (line 283) | def __getattr__(self, attr):
class KoboLibrary (line 287) | class KoboLibrary(object):
method __init__ (line 294) | def __init__ (self, serials = [], device_path = None):
method close (line 393) | def close (self):
method userkeys (line 401) | def userkeys (self):
method books (line 412) | def books (self):
method __bookfile (line 432) | def __bookfile (self, volumeid):
method __getmacaddrs (line 436) | def __getmacaddrs (self):
method __getuserids (line 471) | def __getuserids (self):
method __getuserkeys (line 484) | def __getuserkeys (self, macaddr):
class KoboBook (line 494) | class KoboBook(object):
method __init__ (line 505) | def __init__ (self, volumeid, title, filename, type, cursor, author=No...
method encryptedfiles (line 517) | def encryptedfiles (self):
method has_drm (line 559) | def has_drm (self):
class KoboFile (line 563) | class KoboFile(object):
method __init__ (line 571) | def __init__ (self, filename, mimetype, key):
method decrypt (line 575) | def decrypt (self, userkey, contents):
method check (line 587) | def check (self, contents):
method __removeaespadding (line 655) | def __removeaespadding (self, contents):
function decrypt_book (line 673) | def decrypt_book(book, lib):
function cli_main (line 709) | def cli_main():
FILE: make_release.py
function make_release (line 23) | def make_release(version):
Condensed preview — 109 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,489K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/QUESTION.md",
"chars": 680,
"preview": "---\nname: Question\nabout: Questions for DeDRM Project\ntitle: \"[QUESTION] Title\"\nlabels: Question\n---\n\n## CheckList\n<!-- "
},
{
"path": ".github/workflows/Format.yaml",
"chars": 1333,
"preview": "name: Python code format\non: \n push:\n branches: master\njobs:\n Format:\n if: \"contains(github.event.head_commit.me"
},
{
"path": ".github/workflows/Lint.yaml",
"chars": 743,
"preview": "name: Python code review\non: [push, pull_request]\njobs:\n Test:\n runs-on: ubuntu-20.04\n strategy:\n fail-fast:"
},
{
"path": ".gitignore",
"chars": 10,
"preview": ".DS_Store\n"
},
{
"path": "CALIBRE_CLI_INSTRUCTIONS.md",
"chars": 1556,
"preview": "# Using the DeDRM plugin with the Calibre command line interface\n\nIf you prefer the Calibre CLI instead of the GUI, foll"
},
{
"path": "DeDRM_plugin/DeDRM_Adobe Digital Editions Key_Help.htm",
"chars": 3695,
"preview": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n\t\"http://www.w3.org/TR/html4/strict.dtd\">\n\n<html>\n\n<head>\n<meta http-e"
},
{
"path": "DeDRM_plugin/DeDRM_Barnes and Noble Key_Help.htm",
"chars": 4781,
"preview": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n\t\"http://www.w3.org/TR/html4/strict.dtd\">\n\n<html>\n\n<head>\n<meta http-e"
},
{
"path": "DeDRM_plugin/DeDRM_EInk Kindle Serial Number_Help.htm",
"chars": 2213,
"preview": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n\t\"http://www.w3.org/TR/html4/strict.dtd\">\n\n<html>\n\n<head>\n<meta http-"
},
{
"path": "DeDRM_plugin/DeDRM_Help.htm",
"chars": 5604,
"preview": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n\t\"http://www.w3.org/TR/html4/strict.dtd\">\n\n<html>\n\n<head>\n<meta http-e"
},
{
"path": "DeDRM_plugin/DeDRM_Kindle for Android Key_Help.htm",
"chars": 4060,
"preview": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n\t\"http://www.w3.org/TR/html4/strict.dtd\">\n\n<html>\n\n<head>\n<meta http-"
},
{
"path": "DeDRM_plugin/DeDRM_Kindle for Mac and PC Key_Help.htm",
"chars": 3637,
"preview": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n\t\"http://www.w3.org/TR/html4/strict.dtd\">\n\n<html>\n\n<head>\n<meta http-e"
},
{
"path": "DeDRM_plugin/DeDRM_Mobipocket PID_Help.htm",
"chars": 1743,
"preview": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n\t\"http://www.w3.org/TR/html4/strict.dtd\">\n\n<html>\n\n<head>\n<meta http-e"
},
{
"path": "DeDRM_plugin/DeDRM_eReader Key_Help.htm",
"chars": 3710,
"preview": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n\t\"http://www.w3.org/TR/html4/strict.dtd\">\n\n<html>\n\n<head>\n<meta http-e"
},
{
"path": "DeDRM_plugin/__init__.py",
"chars": 34552,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# __init__.py for DeDRM_plugin\n# Copyright © 2008-2020 Apprentice Harper"
},
{
"path": "DeDRM_plugin/activitybar.py",
"chars": 2968,
"preview": "import sys\nimport tkinter\nimport tkinter.constants\n\nclass ActivityBar(tkinter.Frame):\n\n def __init__(self, master, le"
},
{
"path": "DeDRM_plugin/adobekey.py",
"chars": 21656,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# adobekey.pyw, version 6.0\n# Copyright © 2009-2020 i♥cabbages, Apprenti"
},
{
"path": "DeDRM_plugin/aescbc.py",
"chars": 25394,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"\n Routines for doing AES CBC in one file\n\n Modified by some_upd"
},
{
"path": "DeDRM_plugin/alfcrypto.py",
"chars": 9989,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# crypto library mainly by some_updates\n\n# pbkdf2.py pbkdf2 code taken f"
},
{
"path": "DeDRM_plugin/androidkindlekey.py",
"chars": 15663,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# androidkindlekey.py\n# Copyright © 2010-20 by Thom, Apprentice Harper e"
},
{
"path": "DeDRM_plugin/argv_utils.py",
"chars": 2448,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nimport sys, os\nimport locale\nimport codecs\nimport importlib\n\n# get sys.a"
},
{
"path": "DeDRM_plugin/askfolder_ed.py",
"chars": 6509,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab\n\n# to work around tk_cho"
},
{
"path": "DeDRM_plugin/config.py",
"chars": 47807,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n__license__ = 'GPL v3'\n\n# Python 3, September 2020\n\n# Standard Python mo"
},
{
"path": "DeDRM_plugin/convert2xml.py",
"chars": 31634,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab\n\n# For use with Topaz Sc"
},
{
"path": "DeDRM_plugin/epubtest.py",
"chars": 7548,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n#\n# This is a python script. You need a Python interpreter to run it.\n# F"
},
{
"path": "DeDRM_plugin/erdr2pml.py",
"chars": 24296,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# erdr2pml.py\n# Copyright © 2008-2020 The Dark Reverser, Apprentice Harp"
},
{
"path": "DeDRM_plugin/flatxml2html.py",
"chars": 30165,
"preview": "#! /usr/bin/python\n# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab\n# For use with Topaz Scripts Version 2.6\n\nimport sys"
},
{
"path": "DeDRM_plugin/flatxml2svg.py",
"chars": 10735,
"preview": "#! /usr/bin/python\n# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab\n\nimport sys\nimport csv\nimport os\nimport getopt\nfrom "
},
{
"path": "DeDRM_plugin/genbook.py",
"chars": 26031,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab\n# Python 3 for calibre 5"
},
{
"path": "DeDRM_plugin/ignobleepub.py",
"chars": 17188,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# ignobleepub.py\n# Copyright © 2009-2020 by i♥cabbages, Apprentice Harpe"
},
{
"path": "DeDRM_plugin/ignoblekey.py",
"chars": 11905,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# ignoblekey.py\n# Copyright © 2015-2020 Apprentice Alf, Apprentice Harpe"
},
{
"path": "DeDRM_plugin/ignoblekeyfetch.py",
"chars": 9292,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# ignoblekeyfetch.py\n# Copyright © 2015-2020 Apprentice Harper et al.\n\n#"
},
{
"path": "DeDRM_plugin/ignoblekeygen.py",
"chars": 11721,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# ignoblekeygen.py\n# Copyright © 2009-2020 i♥cabbages, Apprentice Harper"
},
{
"path": "DeDRM_plugin/ignoblepdf.py",
"chars": 71331,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\n# ignoblepdf.py\n# Copyright © 2009-2020 by Apprentice Harper et al.\n\n# "
},
{
"path": "DeDRM_plugin/ineptepub.py",
"chars": 23155,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# ineptepub.py\n# Copyright © 2009-2020 by i♥cabbages, Apprentice Harper "
},
{
"path": "DeDRM_plugin/ineptpdf.py",
"chars": 78227,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# ineptpdf.py\n# Copyright © 2009-2020 by i♥cabbages, Apprentice Harper e"
},
{
"path": "DeDRM_plugin/ion.py",
"chars": 37447,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# ion.py\n# Copyright © 2013-2020 Apprentice Harper et al.\n\n__license__ ="
},
{
"path": "DeDRM_plugin/k4mobidedrm.py",
"chars": 13041,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# k4mobidedrm.py\n# Copyright © 2008-2020 by Apprentice Harper et al.\n\n__"
},
{
"path": "DeDRM_plugin/kfxdedrm.py",
"chars": 3918,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# Engine to remove drm from Kindle KFX ebooks\n\n# 2.0 - Python 3 for c"
},
{
"path": "DeDRM_plugin/kgenpids.py",
"chars": 9442,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# kgenpids.py\n# Copyright © 2008-2020 Apprentice Harper et al.\n\n__licens"
},
{
"path": "DeDRM_plugin/kindlekey.py",
"chars": 79090,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# kindlekey.py\n# Copyright © 2008-2020 Apprentice Harper et al.\n\n__licen"
},
{
"path": "DeDRM_plugin/kindlepid.py",
"chars": 4596,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# Mobipocket PID calculator v0.4 for Amazon Kindle.\n# Copyright (c) 2007"
},
{
"path": "DeDRM_plugin/mobidedrm.py",
"chars": 22743,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# mobidedrm.py\n# Copyright © 2008 The Dark Reverser\n# Portions © 2008–20"
},
{
"path": "DeDRM_plugin/openssl_des.py",
"chars": 3084,
"preview": "#!/usr/bin/env python\n# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab\n\n# implement just enough of des from openssl to m"
},
{
"path": "DeDRM_plugin/plugin-import-name-dedrm.txt",
"chars": 0,
"preview": ""
},
{
"path": "DeDRM_plugin/prefs.py",
"chars": 13437,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai\n\n__license__ = 'GPL v3"
},
{
"path": "DeDRM_plugin/pycrypto_des.py",
"chars": 858,
"preview": "#!/usr/bin/env python\n# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab\n\n\ndef load_pycrypto():\n try :\n from Cry"
},
{
"path": "DeDRM_plugin/python_des.py",
"chars": 9590,
"preview": "#!/usr/bin/env python\n# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab\nimport sys\n\nECB = 0\nCBC = 1\nclass Des(object)"
},
{
"path": "DeDRM_plugin/scriptinterface.py",
"chars": 6810,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab\n\n\nimport sys\nimport os\ni"
},
{
"path": "DeDRM_plugin/scrolltextwidget.py",
"chars": 1156,
"preview": "#!/usr/bin/env python\n# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab\n\nimport tkinter\nimport tkinter.constants\n\n# basic"
},
{
"path": "DeDRM_plugin/simpleprefs.py",
"chars": 2982,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab\n\nimport sys\nimport os, o"
},
{
"path": "DeDRM_plugin/stylexml2css.py",
"chars": 10833,
"preview": "#! /usr/bin/python\n# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab\n# For use with Topaz Scripts Version 2.6\n\n\nimport cs"
},
{
"path": "DeDRM_plugin/subasyncio.py",
"chars": 4968,
"preview": "#!/usr/bin/env python\n# vim:ts=4:sw=4:softtabstop=4:smarttab:expandtab\n\nimport os, sys\nimport signal\nimport threading\nim"
},
{
"path": "DeDRM_plugin/topazextract.py",
"chars": 18666,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# topazextract.py\n# Mostly written by some_updates based on code from ma"
},
{
"path": "DeDRM_plugin/utilities.py",
"chars": 1567,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\nfrom calibre_plugins.dedrm.ignoblekeygen import generate_key\n\n__license_"
},
{
"path": "DeDRM_plugin/wineutils.py",
"chars": 4144,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n__license__ = 'GPL v3'\n\n# Standard Python modules.\nimport os, sys, re, h"
},
{
"path": "DeDRM_plugin/zipfilerugged.py",
"chars": 52229,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n\"\"\"\nRead and write ZIP files.\n\"\"\"\nimport struct, os, time, sys, shutil\ni"
},
{
"path": "DeDRM_plugin/zipfix.py",
"chars": 6424,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# zipfix.py\n# Copyright © 2010-2020 by Apprentice Harper et al.\n\n# Relea"
},
{
"path": "DeDRM_plugin_ReadMe.txt",
"chars": 2198,
"preview": "DeDRM_plugin.zip\n================\n\nThis plugin will remove the DRM from:\n\n - Kindle ebooks (files from Kindle for Mac/PC"
},
{
"path": "FAQs.md",
"chars": 22409,
"preview": "# Overview\n## What's this repository all about?\nProviding free open source tools to remove DRM from your ebooks.\n\n## Wha"
},
{
"path": "Obok_plugin/__init__.py",
"chars": 2947,
"preview": "# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai\nfrom __future__ import (unicode_literals, division, absolute_import,\n"
},
{
"path": "Obok_plugin/action.py",
"chars": 23896,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai\n\n_license__ = 'GPL v"
},
{
"path": "Obok_plugin/common_utils.py",
"chars": 21279,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai\n\n__license__ = 'GPL "
},
{
"path": "Obok_plugin/config.py",
"chars": 9164,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai\n\nfrom PyQt5.Qt import"
},
{
"path": "Obok_plugin/dialogs.py",
"chars": 19168,
"preview": "# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai\nfrom __future__ import (unicode_literals, division, absolute_import,\n"
},
{
"path": "Obok_plugin/obok/__init__.py",
"chars": 115,
"preview": "# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai\n\n__license__ = 'GPL v3'\n__docformat__ = 'restructuredtext en'\n"
},
{
"path": "Obok_plugin/obok/legacy_obok.py",
"chars": 2893,
"preview": "# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai\n\n__license__ = 'GPL v3'\n__docformat__ = 'restructuredtext en'\n\nimpo"
},
{
"path": "Obok_plugin/obok/obok.py",
"chars": 30776,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n# Version 4.1.0 February 2021\n# Add detection for Kobo directory locatio"
},
{
"path": "Obok_plugin/obok_dedrm_Help.htm",
"chars": 1008,
"preview": "<html>\n\n<head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\">\n<title>Obok DeDRM Plugin Configuratio"
},
{
"path": "Obok_plugin/plugin-import-name-obok_dedrm.txt",
"chars": 0,
"preview": ""
},
{
"path": "Obok_plugin/translations/ar.po",
"chars": 11917,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "Obok_plugin/translations/de.po",
"chars": 2521,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "Obok_plugin/translations/default.po",
"chars": 11654,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "Obok_plugin/translations/es.po",
"chars": 16280,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "Obok_plugin/translations/nl.po",
"chars": 2571,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "Obok_plugin/translations/pt.po",
"chars": 14920,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "Obok_plugin/translations/sv.po",
"chars": 15045,
"preview": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same "
},
{
"path": "Obok_plugin/utilities.py",
"chars": 7512,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai\n\n__license__ = 'GPL "
},
{
"path": "Other_Tools/B_and_N_Download_Helper/BN-Dload.user.js",
"chars": 940,
"preview": "// ==UserScript==\n// @name BN-Dload\n// @namespace http://www.mailinator.com/J-man\n// @include https://mynoo"
},
{
"path": "Other_Tools/B_and_N_Download_Helper/BN-Dload.user_ReadMe.txt",
"chars": 1384,
"preview": "INTRODUCTION\n============\n\nTo obtain unencrypted content from the B&N, you have to download it directly from the website"
},
{
"path": "Other_Tools/DRM_Key_Scripts/Adobe_Digital_Editions/adobekey.pyw",
"chars": 22448,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom __future__ import with_statement\n\n# adobekey.pyw, version 6.0\n# Copy"
},
{
"path": "Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekey.pyw",
"chars": 11910,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom __future__ import with_statement\n\n# ignoblekey.py\n# Copyright © 2015"
},
{
"path": "Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeyfetch.pyw",
"chars": 9318,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom __future__ import with_statement\n\n# ignoblekeyfetch.pyw, version 1.1"
},
{
"path": "Other_Tools/DRM_Key_Scripts/Barnes_and_Noble_ePubs/ignoblekeygen.pyw",
"chars": 11704,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom __future__ import with_statement\n\n# ignoblekeygen.pyw, version 2.5\n#"
},
{
"path": "Other_Tools/DRM_Key_Scripts/Kindle_for_Android/androidkindlekey.pyw",
"chars": 15613,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom __future__ import with_statement\n\n# androidkindlekey.py\n# Copyright "
},
{
"path": "Other_Tools/DRM_Key_Scripts/Kindle_for_Mac_and_PC/kindlekey.pyw",
"chars": 74209,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom __future__ import with_statement\n\n# kindlekey.py\n# Copyright © 2008-"
},
{
"path": "Other_Tools/DRM_Key_Scripts/Kindle_for_iOS/kindleiospidgen.pyw",
"chars": 8735,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\nfrom __future__ import with_statement\n\n# kindleforios4key.py\n# Copyright "
},
{
"path": "Other_Tools/Kindle_for_Android_Patches/A_Patching_Experience.txt",
"chars": 6644,
"preview": "Of Historical Interest Only\n===========================\n\nIt is now much simpler and easier to get a backup.ab file from "
},
{
"path": "Other_Tools/Kindle_for_Android_Patches/kindle_version_3.0.1.70/ReadMe_K4Android.txt",
"chars": 3071,
"preview": "Kindle for Android\n------------------\n\nKindle for Android uses a different scheme to generate its books specific PIDs th"
},
{
"path": "Other_Tools/Kindle_for_Android_Patches/kindle_version_3.0.1.70/kindle3.0.1.70.patch",
"chars": 3942,
"preview": "diff -ru kindle3_orig/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali kindle3/smali/com/amazon/k"
},
{
"path": "Other_Tools/Kindle_for_Android_Patches/kindle_version_3.7.0.108/ReadMe_K4Android.txt",
"chars": 4514,
"preview": "Kindle for Android\n------------------\n\nKindle for Android uses a different scheme to generate its books specific PIDs th"
},
{
"path": "Other_Tools/Kindle_for_Android_Patches/kindle_version_3.7.0.108/kindle3.7.0.108.patch",
"chars": 5573,
"preview": "diff -ru kindle3.7.0.108_orig/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali kindle3.7.0.108/sm"
},
{
"path": "Other_Tools/Kindle_for_Android_Patches/kindle_version_4.0.2.1/kindle4.0.2.1.patch",
"chars": 8136,
"preview": "Only in kindle4.0.2.1: build\ndiff -r -u10 kindle4.0.2.1_orig/smali/com/amazon/kcp/application/AndroidDeviceInformationPr"
},
{
"path": "Other_Tools/Kindle_for_Android_Patches/kindle_version_4.8.1.10/Notes on the Patch.txt",
"chars": 578,
"preview": "Notes from UE01 about this patch:\n\n1. Revised the “Other_Tools/Kindle_for_Android_Patches/” for Kindle 4.8.1.10\n2. Built"
},
{
"path": "Other_Tools/Kindle_for_Android_Patches/kindle_version_4.8.1.10/kindle4.8.1.10.patch",
"chars": 5289,
"preview": "diff --git a/smali/com/amazon/kcp/application/AndroidDeviceInformationProvider.smali b/smali/com/amazon/kcp/application/"
},
{
"path": "Other_Tools/Kobo/obok.py",
"chars": 28907,
"preview": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\n# Version 3.2.5 December 2016\n# Improve detection of good text decryption"
},
{
"path": "Other_Tools/Rocket_ebooks/rebhack_ReadMe.txt",
"chars": 228,
"preview": "Rocket eBooks\n=============\n\nRocket ebooks (.rb) are no longer sold.\n\nThis is the only archive of tools for decrypting R"
},
{
"path": "Other_Tools/Scuolabook_DRM/Scuolabook_ReadMe.txt",
"chars": 123,
"preview": "The latest Scuolabook tool can be found at Hex's own blog:\nhttps://thisishex.wordpress.com/scuolabook-drm-remover/\n\nHarp"
},
{
"path": "Other_Tools/Tetrachroma_FileOpen_ineptpdf/ineptpdf_8.4.51.pyw",
"chars": 117695,
"preview": "#! /usr/bin/python\n\n# ineptpdf8.4.51.pyw\n# ineptpdf, version 8.4.51\n\n# To run this program install Python 2.7 from http:"
},
{
"path": "Other_Tools/Tetrachroma_FileOpen_ineptpdf/ineptpdf_8.4.51_ReadMe.txt",
"chars": 265,
"preview": "ineptpdf 8.4.51\n---------------\n\nThis is a version of the ineptpdf script produced by TetraChroma that can remove, on Wi"
},
{
"path": "README.md",
"chars": 2322,
"preview": "# No Longer Maintained\nI have not had the time to devote to this project in recent years that I would have liked. I am d"
},
{
"path": "ReadMe_Overview.txt",
"chars": 4319,
"preview": "Welcome to the tools!\n=====================\n\nThis file is to give users a quick overview of what is available and how t"
},
{
"path": "make_release.py",
"chars": 1306,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n\n'''\nA wrapper script to generate zip files for GitHub releases.\n\nThis sc"
},
{
"path": "obok_plugin_ReadMe.txt",
"chars": 2218,
"preview": "obok_plugin.zip\n================\n\nThis plugin will remove the DRM from Kobo ebooks download on Mac or Windows using the"
}
]
// ... and 6 more files (download for full content)
About this extraction
This page contains the full source code of the apprenticeharper/DeDRM_tools GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 109 files (1.4 MB), approximately 372.9k tokens, and a symbol index with 1339 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.