├── .gitignore ├── LICENSE ├── README.markdown ├── manager.py ├── notebook.sublime-project └── sublime_notebook ├── __init__.py ├── cryptlib.py ├── docs └── README.markdown ├── message.py ├── pyaes ├── __init__.py ├── aes.py ├── blockfeeder.py └── util.py ├── settings.py └── sublime_notebook.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.txt 2 | *.md 3 | *.pyc 4 | __pycache__ 5 | *.sublime-workspace 6 | .DS_Store 7 | FLAG_FILE 8 | settings.json 9 | .release/ 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Avi Aryan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 |

2 | Sublime Notebook :memo: 3 |

4 | 5 | **v2.1** 6 | 7 | Sublime Notebook is an attempt to use Sublime Text as a complete note taking application. 8 | 9 | > Blog post for v0.3.0 (no longer recommended) https://medium.com/aviaryan/b8d846c47905#.hy8alq2ip 10 | 11 | 12 | ## Why did you build this? :thinking: 13 | 14 | I have been note-taking for as long as I started using computers. I use notes mostly for technical stuff, but these days I am using notes to record all kinds of information like journals, ideas, snippets etc. In my career, I have tried a number of note-taking tools like OneNote, Evernote, CintaNotes, SimpleNote, Cherrytree, Google Keep, etc. But I have never been satisfied with them mainly because - 15 | 16 | 1. I don't have any control over how or where my notes are stored. - What if the company closes or the developer stops building the product? 17 | 2. Most of these services are paid or work on only certain Operating Systems. And even if they are truly free and cross-platform, they lack critical features like fast full notebook search or hierarchical organization. 18 | 19 | Because of these reasons, I had to lose my notes a number of times and was forced to start from scratch. This was frustrating, and finally, I decided to do something about it. 20 | 21 | The result is this project, a wrapper/idea that converts my favorite text editor, Sublime Text, to a feature-rich note-taking tool. Sure it might not be as polished as all those premium note-taking tools, but it works and that too in the way I want it to. And if for some reason I get tired of using Sublime Text, I can just port this to any other text editor like VSCode. It will be easy because the dependency on the text editor is very minimal here, not to mention the notes are nothing but plain text files. 😉 22 | 23 | 24 | ## Features :sunglasses: 25 | 26 | * Faaaast Search across all notes (thanks to Sublime Text) 27 | * Hierarchical organization and display of notes 28 | * Password based encryption for notes (thanks to [pyAES](https://github.com/ricmoo/pyaes)) 29 | * Cloud sync (Dropbox, Google Drive, Box, etc) 30 | * Periodic git backup (to Github, Gitlab, your own private git server, etc) 31 | * Markdown based markup and code syntax highlighting 32 | 33 | 34 | ## Documentation :yum: 35 | 36 | Find the docs in the [sublime_notebook/docs](sublime_notebook/docs/README.markdown) folder. 37 | 38 | 39 | ## Ports :left_right_arrow: 40 | 41 | [VSCode Notebook](https://github.com/aviaryan/VSCodeNotebook) - A spin-off of this project that works with [Visual Studio Code](https://code.visualstudio.com/). 42 | 43 | 44 | ## Support the project :money_with_wings: 45 | 46 | Are you using this project regularly and find it adding value to your life? 47 | 48 | If yes, please consider supporting the author by donating **$5**. 49 | 50 | [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.me/aviaryan/5) 51 | -------------------------------------------------------------------------------- /manager.py: -------------------------------------------------------------------------------- 1 | #!/Users/aviaryan/miniconda3/bin/python 2 | 3 | from sublime_notebook import sublime_notebook 4 | 5 | 6 | if __name__ == '__main__': 7 | sublime_notebook.main() 8 | -------------------------------------------------------------------------------- /notebook.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "build_systems": 3 | [ 4 | { 5 | "linux": 6 | { 7 | "shell_cmd": "gnome-terminal -e './manager.py; exec bash\"'" 8 | }, 9 | "name": "manager.py", 10 | "osx": 11 | { 12 | "shell_cmd": "open -a Terminal.app ${project_path}" 13 | }, 14 | "windows": 15 | { 16 | "cmd": 17 | [ 18 | "start", 19 | "cmd", 20 | "/k", 21 | "python manager.py" 22 | ], 23 | "shell": true 24 | }, 25 | "working_dir": "${project_path}" 26 | } 27 | ], 28 | "folders": 29 | [ 30 | { 31 | "file_exclude_patterns": 32 | [ 33 | "*.pyc", 34 | "////sublime_notebook/*.py" 35 | ], 36 | "folder_exclude_patterns": 37 | [ 38 | "__pycache__", 39 | ".release", 40 | "////sublime_notebook/pyaes" 41 | ], 42 | "path": "." 43 | } 44 | ], 45 | "settings": 46 | { 47 | "margin": 20, 48 | "word_wrap": true 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /sublime_notebook/__init__.py: -------------------------------------------------------------------------------- 1 | SETTINGS_PATH = 'sublime_notebook/settings.json' 2 | VERSION = 2.1 3 | -------------------------------------------------------------------------------- /sublime_notebook/cryptlib.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import os 3 | from traceback import print_exc 4 | import re 5 | from getpass import getpass 6 | from .settings import Settings 7 | from .message import print_err 8 | from .pyaes import AESModeOfOperationCTR 9 | 10 | 11 | EXTRA_STR = 'ENCo0D#DT{xTCh$cKe>' 12 | ENCODED_IDF = '=*=EnC0d3dH3aDer==*' 13 | EXTRA_STR_2 = '3NCo0D#DT{xTCh$cKe>' 14 | ENCODED_IDF_2 = '=*=3nC0d3dH3aDer==*' 15 | 16 | # Vigenere's Cipher: http://stackoverflow.com/a/38223403 17 | 18 | def encode_1(key, clear): 19 | if clear.startswith(ENCODED_IDF): # already encoded, no need to encode 20 | return clear 21 | clear += EXTRA_STR # used to check if decrypt is correct 22 | # encode string 23 | enc = [] 24 | for i in range(len(clear)): 25 | key_c = key[i % len(key)] 26 | enc_c = chr((ord(clear[i]) + ord(key_c)) % 256) 27 | enc.append(enc_c) 28 | return ENCODED_IDF + base64.urlsafe_b64encode("".join(enc).encode()).decode() 29 | 30 | 31 | def decode_1(key, enc): 32 | st = '' 33 | if not enc.startswith(ENCODED_IDF): # not encoded, so not decode 34 | return enc 35 | enc = enc[len(ENCODED_IDF):] # trim out idf 36 | # decode string 37 | dec = [] 38 | enc = base64.urlsafe_b64decode(enc).decode() 39 | for i in range(len(enc)): 40 | key_c = key[i % len(key)] 41 | dec_c = chr((256 + ord(enc[i]) - ord(key_c)) % 256) 42 | dec.append(dec_c) 43 | st = "".join(dec) 44 | # check if correctly decoded 45 | if not st.endswith(EXTRA_STR): 46 | return None 47 | else: 48 | return st[:-1 * len(EXTRA_STR)] 49 | 50 | 51 | def encode(key, clear): 52 | if clear.startswith(ENCODED_IDF) or clear.startswith(ENCODED_IDF_2): # already encoded, no need to encode 53 | return clear 54 | clear += EXTRA_STR_2 # used to check if decrypt is correct 55 | # encrypt string 56 | aes = AESModeOfOperationCTR(key_32(key)) 57 | clear = bytes(clear, 'utf8') # everyone writes in utf8 58 | ciphertext = aes.encrypt(clear) 59 | return ENCODED_IDF_2 + base64.urlsafe_b64encode(ciphertext).decode('iso-8859-1') 60 | 61 | 62 | def decode(key, enc): 63 | st = '' 64 | if not (enc.startswith(ENCODED_IDF_2) or enc.startswith(ENCODED_IDF)): # not encoded, so not decode 65 | return enc 66 | if enc.startswith(ENCODED_IDF): # old version 67 | return decode_1(key, enc) 68 | # new version 69 | enc = enc[len(ENCODED_IDF_2):] # trim out idf 70 | # https://wiki.python.org/moin/Python3UnicodeDecodeError 71 | # seems like the bytes created by pyaes are best decrypted using it 72 | enc = base64.urlsafe_b64decode(enc).decode('iso-8859-1') 73 | # decode string 74 | aes = AESModeOfOperationCTR(key_32(key)) 75 | st = aes.decrypt(enc) 76 | st = st.decode('utf8') # because utf8 is what everyone uses 77 | # ^^ decode error might be returned even when password is wrong 78 | # check if correctly decoded 79 | if not st.endswith(EXTRA_STR_2): 80 | return None 81 | else: 82 | return st[:-1 * len(EXTRA_STR_2)] 83 | 84 | 85 | def get_file_list(): 86 | listFiles = [] 87 | sts = Settings() 88 | exts = tuple(sts.json['note_extensions']) 89 | # loop through directory 90 | for dirpath, dnames, fnames in os.walk('./'): 91 | dirname = dirpath.replace('./', '', 1) 92 | dirname = re.sub(r'/.*$', '', dirname) 93 | # print(dirname) 94 | if dirname.startswith('.'): # hidden like .git 95 | continue 96 | if not sts.check_folder_private(dirname): 97 | continue 98 | for f in fnames: 99 | if not f.endswith(exts): 100 | continue 101 | listFiles.append(os.path.join(dirpath, f)) 102 | # print(listFiles) 103 | return listFiles 104 | 105 | 106 | def update_file(funcptr, flist, key): 107 | failed = False 108 | for file in flist: 109 | fptr = open(file, 'r') 110 | data = fptr.read() 111 | fptr.close() 112 | fptr = open(file, 'w') 113 | try: 114 | newData = funcptr(key, data) 115 | except Exception: 116 | print_exc() 117 | newData = None # handled sufficiently well now 118 | if newData is None: 119 | newData = data 120 | failed = True 121 | print_err('Failed processing %s' % file) 122 | fptr.write(newData) 123 | fptr.close() 124 | # check if failed 125 | if failed: 126 | break 127 | return failed 128 | 129 | 130 | def get_key(): 131 | key = '' 132 | while key == '': 133 | key = getpass('Enter key > ') 134 | return key 135 | 136 | 137 | def key_32(key): 138 | if len(key) > 32: 139 | key = key[:32] 140 | else: 141 | key = key + ('0' * (32 - len(key))) 142 | return bytes(key, 'utf8') 143 | -------------------------------------------------------------------------------- /sublime_notebook/docs/README.markdown: -------------------------------------------------------------------------------- 1 | 2 | # Table of Contents 3 | 4 | :one: [Requirements](#rq) 5 | :two: [Getting Started](#gs) 6 | :three: [Accessing your notes](#ac) 7 | :four: [Encrypting your notes](#en) 8 | :five: [Note taking features](#nt) 9 | :six: [Changing SublimeNotebook password](#cp) 10 | :seven: [Customizing which folders and files are encrypted](#custen) 11 | :eight: [Automatic git backups](#git) 12 | :nine: [Setting up better Markdown highlighting in Sublime Text](#mdext) 13 | :keycap_ten: [FAQ](#faq) 14 | 15 | 16 | 17 | ## :one: Requirements 18 | :point_up_2: [[back to top](#docs)] 19 | 20 | The requirements for using this tool are as follows. Make sure to have them installed before proceeding to the next section. 21 | 22 | * Sublime Text 23 | * Python 3 24 | * [Optional] A cloud sync application setup (Dropbox, Google Drive, OneDrive etc) 25 | 26 | 27 | 28 | ## :two: Getting started 29 | :point_up_2: [[back to top](#docs)] 30 | 31 | The first step is downloading the release (`SublimeNotebook_vX.Y.zip`) from https://github.com/aviaryan/SublimeNotebook/releases/latest. 32 | 33 | Then you extract the zip file and put the contents in a cloud synced or local folder of your choice. 34 | 35 | Done! You can now create any number of notes in that folder. For hierarchy, you can use folders and sub-folders. 36 | 37 | Notes [by default](#custen), can be `txt` or `md` files and they will be encrypted with your password. 38 | 39 | By default, only `diary` folder (if it exists) is encrypted. You can learn more about changing this setting [here](#custen). 40 | 41 | ![Sublime Notebook: how it looks like](https://user-images.githubusercontent.com/4047597/35737506-c78a8d0a-0851-11e8-8d90-cd71117b08c7.png) 42 | 43 | 44 | 45 | ## :three: Accessing your notes 46 | :point_up_2: [[back to top](#docs)] 47 | 48 | To access your notes, we will use the Projects feature of Sublime Text. 49 | 50 | Open Sublime Text and click on "Open Project" in the Project menu. 51 | 52 | Browse for the `notebook.sublime-project` file in the folder you downloaded and open it. Now open the Sidebar (View -> Side Bar). You will see all your notes presented there with the hierarchy. 53 | 54 | Whenever you want to open your Sublime Notebook, you can use the switch project shortcut (Cmd-Ctrl-P or Ctrl-Alt-P) and select `notebook.sublime-project` to switch to the Notebook project. 55 | 56 | ![Project Selector](https://user-images.githubusercontent.com/4047597/35473121-4556dd7a-03a1-11e8-8c3a-6e85592d5d5f.png) 57 | 58 | PS - To open SublimeNotebook from commandline, see [this section](#subl-cli). 59 | 60 | 61 | 62 | ## :four: Encrypting your notes 63 | :point_up_2: [[back to top](#docs)] 64 | 65 | To encrypt or decrypt notes, you use the `manager.py` file located in the notebook root. It runs in Python 3 and requires no additional dependencies. 66 | I recommend changing the first line of the file to point to your interpreter. 67 | 68 | ```python 69 | #!/Users/aviaryan/miniconda3/bin/python 70 | ``` 71 | 72 | To run `manager.py`, you can use the shortcut Ctrl-B (Cmd-B on OSX) to launch a terminal window in the `manager.py`'s directory. 73 | 74 | Then use `python manager.py` or `./manager.py` to run the script. 75 | 76 | When it runs for the first time, it will find the notes and ask you a password for encryption. 77 | After getting the password, it will encrypt all [non-public notes](#custen) using that password. 78 | 79 | ![first time encryption](https://user-images.githubusercontent.com/4047597/35779481-b09abd92-09f3-11e8-8dee-accbf5a64581.png) 80 | 81 | In the subsequent runs, `manager.py` will work as an un-locker where it will ask password to decrypt the notes and then pause its execution. 82 | Now you can view and edit your notes and then later on encrypt them again by entering 'e' in the prompt. 83 | 84 | ![second run](https://user-images.githubusercontent.com/4047597/35779488-dc1046cc-09f3-11e8-8773-ae66da8325c4.png) 85 | 86 | 87 | 88 | ## :five: Note taking features 89 | :point_up_2: [[back to top](#docs)] 90 | 91 | * To search through all your notes, use the Sublime Text’s search in project feature (Ctrl-Shift-F or Cmd-Shift-F). 92 | 93 | * You can use the Sublime Text sidebar to view your notes in a hierarchical fashion. 94 | 95 | * Store the folder in Dropbox, Google Drive or Box to have it on all your computers (as well as secure a backup). 96 | 97 | * The Python 3 script uses no extra dependencies so you can run the script out-of-the-box on any system that has Python installed (popular Linux distros and Macs for example have it by default). 98 | 99 | 100 | 101 | ## :six: Changing SublimeNotebook password 102 | :point_up_2: [[back to top](#docs)] 103 | 104 | To change password of your Sublime Notebook, decrypt your existing notes using old `manager.py`, then exit the script in decrypted state (using "d"). 105 | 106 | Then start `manager.py` again to re-encrypt your notes. This time you will be asked for a new password to encrypt your notes. 107 | 108 | ![changing password](https://user-images.githubusercontent.com/4047597/35779512-310e70a4-09f4-11e8-9298-4243ae3fe04d.png) 109 | 110 | 111 | 112 | ## :seven: Customizing which folders and files are encrypted 113 | :point_up_2: [[back to top](#docs)] 114 | 115 | To customize which folders are encrypted, use the `settings.json` file in `sublime_notebook/` directory. 116 | 117 | 1. "private_folders" are the one that are encrypted. 118 | 2. "public_folders" are not encrypted. 119 | 120 | A folder by default is public if it is not included in either of them. 121 | 122 | You can also use the "*" symbol to select all folders. For example, in the following `settings.json` file, all folders except "web_links" are private(encrypted). 123 | 124 | ```json 125 | { 126 | "private_folders": [ 127 | "*" 128 | ], 129 | "public_folders": [ 130 | "web_links" 131 | ] 132 | } 133 | ``` 134 | 135 | ---- 136 | 137 | You can also change which files are to be considered as notes, and thus encrypted. For that, change the `note_extensions` setting. 138 | 139 | ```json 140 | { 141 | "note_extensions": [ 142 | "txt", 143 | "md", 144 | "rst" 145 | ] 146 | } 147 | ``` 148 | 149 | **NOTE** - You should edit `settings.json` file only when the notebook is in a decrypted state. Changing it when notebook is encrypted can cause 150 | unintentional side-effects. `"is_encrypted": false` will be present in `settings.json` when notebook is decrypted. 151 | 152 | 153 | 154 | ## :eight: Automatic git backups 155 | :point_up_2: [[back to top](#docs)] 156 | 157 | > This feature comes in handy for those who don't trust cloud data storage providers. You can even use this as a second backup for your data. I personally have auto git backups set up so that my notes are stored on both Dropbox and GitHub. 158 | 159 | To enable git backups, enable the feature from `sublime_notebook/settings.json`. 160 | 161 | ```json 162 | { 163 | "do_git_backup": true, 164 | } 165 | ``` 166 | 167 | Once this setting is enabled, you will have to make your notebook a git repository and set `notebookbackup` remote to the git remote you want to backup to. 168 | Start with an empty remote repository to avoid any conflicts. 169 | 170 | ```sh 171 | # pwd is the directory with manager.py and sublime_notebook/ folder. 172 | $ git init 173 | $ git remote add notebookbackup 174 | # ^ ssh git url preferred 175 | ``` 176 | 177 | The git backup will run when you re-encrypt after decrypting the notebook. 178 | 179 | ![git backup](https://user-images.githubusercontent.com/4047597/35779595-e2e04022-09f5-11e8-8fb6-2e808b29cdb6.png) 180 | 181 | To change how frequently git backup happens, change the `git_push_interval_minutes` value in `settings.json`. 182 | 183 | ```js 184 | { 185 | "git_push_interval_minutes": 1440, 186 | } 187 | ``` 188 | 189 | 1440 minutes means 24 hours i.e. 1 day. Set it to `0` to enable instant backups. 190 | 191 | 192 | 193 | ## :nine: Setting up better Markdown highlighting in Sublime Text 194 | :point_up_2: [[back to top](#docs)] 195 | 196 | * Install the packages from here. 197 | 198 | * [Sublime Markdown Extended](https://github.com/jonschlinkert/sublime-markdown-extended) 199 | * [Sublime Monokai Extended](https://github.com/jonschlinkert/sublime-monokai-extended) - companion to the first package. 200 | 201 | * Make Sublime Markdown Extended as default language for markdown. 202 | 203 | > Navigate through the following menus in Sublime Text: View -> Syntax -> Open all with current extension as... -> Markdown Extended 204 | 205 | * Make Sublime Monokai Extended default theme for Markdown extended. Open `Settings - Syntax Specific` from preferences and update the file as follows. 206 | 207 | ```js 208 | { 209 | "color_scheme": "Packages/Monokai Extended/Monokai Extended.tmTheme", 210 | "extensions": 211 | [ 212 | "md" 213 | ] 214 | } 215 | ``` 216 | 217 | ![sublime monkai](https://camo.githubusercontent.com/e5112e65510eada23f8cdc306ba46bfe1043f201/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f3338333939342f3732363833332f30666465306431362d653133382d313165322d386533642d3864626663393132323465372e706e67) 218 | 219 | 220 | ## :keycap_ten: FAQ 221 | :point_up_2: [[back to top](#docs)] 222 | 223 | * Only *.txt and *.md files are detected as notes. 224 | 225 | * You don't need to be in decrypted state to create a new note. Even when in encrypted state, you can create a note. When `manager.py` starts decrypting the notes, this new file will be ignored and will be encrypted when it's time to encrypt. 226 | 227 | 228 | * **Open SublimeNotebook from commandline:** You can open a Sublime Text project from the command line with `subl --project path/to/your/project`, provided that you set up the `subl` command on your system ([see the official Sublime Text documentation](https://www.sublimetext.com/docs/3/osx_command_line.html)). You might want to set up an alias to open your notebook project. Also check out other command line options listed in the [unofficial documentation for Sublime Text](http://docs.sublimetext.info/en/latest/command_line/command_line.html). 229 | -------------------------------------------------------------------------------- /sublime_notebook/message.py: -------------------------------------------------------------------------------- 1 | """ 2 | Print statements 3 | """ 4 | 5 | def print_info(msg): 6 | if msg.find('\n') > -1: 7 | print('\n' + msg) 8 | else: 9 | print('\n[[ ' + msg + ' ]]') 10 | 11 | def print_err(msg): 12 | if msg.find('\n') > -1: 13 | print('\n' + msg) 14 | else: 15 | print('\n<< ' + msg + ' >>') 16 | -------------------------------------------------------------------------------- /sublime_notebook/pyaes/__init__.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2014 Richard Moore 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | # This is a pure-Python implementation of the AES algorithm and AES common 24 | # modes of operation. 25 | 26 | # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard 27 | # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation 28 | 29 | 30 | # Supported key sizes: 31 | # 128-bit 32 | # 192-bit 33 | # 256-bit 34 | 35 | 36 | # Supported modes of operation: 37 | # ECB - Electronic Codebook 38 | # CBC - Cipher-Block Chaining 39 | # CFB - Cipher Feedback 40 | # OFB - Output Feedback 41 | # CTR - Counter 42 | 43 | # See the README.md for API details and general information. 44 | 45 | # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: 46 | # https://www.dlitz.net/software/pycrypto/ 47 | 48 | 49 | VERSION = [1, 3, 0] 50 | 51 | from .aes import AES, AESModeOfOperationCTR, AESModeOfOperationCBC, AESModeOfOperationCFB, AESModeOfOperationECB, AESModeOfOperationOFB, AESModesOfOperation, Counter 52 | from .blockfeeder import decrypt_stream, Decrypter, encrypt_stream, Encrypter 53 | from .blockfeeder import PADDING_NONE, PADDING_DEFAULT 54 | -------------------------------------------------------------------------------- /sublime_notebook/pyaes/aes.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2014 Richard Moore 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | # This is a pure-Python implementation of the AES algorithm and AES common 24 | # modes of operation. 25 | 26 | # See: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard 27 | 28 | # Honestly, the best description of the modes of operations are the wonderful 29 | # diagrams on Wikipedia. They explain in moments what my words could never 30 | # achieve. Hence the inline documentation here is sparer than I'd prefer. 31 | # See: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation 32 | 33 | # Also useful, PyCrypto, a crypto library implemented in C with Python bindings: 34 | # https://www.dlitz.net/software/pycrypto/ 35 | 36 | 37 | # Supported key sizes: 38 | # 128-bit 39 | # 192-bit 40 | # 256-bit 41 | 42 | 43 | # Supported modes of operation: 44 | # ECB - Electronic Codebook 45 | # CBC - Cipher-Block Chaining 46 | # CFB - Cipher Feedback 47 | # OFB - Output Feedback 48 | # CTR - Counter 49 | 50 | 51 | # See the README.md for API details and general information. 52 | 53 | 54 | import copy 55 | import struct 56 | 57 | __all__ = ["AES", "AESModeOfOperationCTR", "AESModeOfOperationCBC", "AESModeOfOperationCFB", 58 | "AESModeOfOperationECB", "AESModeOfOperationOFB", "AESModesOfOperation", "Counter"] 59 | 60 | 61 | def _compact_word(word): 62 | return (word[0] << 24) | (word[1] << 16) | (word[2] << 8) | word[3] 63 | 64 | def _string_to_bytes(text): 65 | return list(ord(c) for c in text) 66 | 67 | def _bytes_to_string(binary): 68 | return "".join(chr(b) for b in binary) 69 | 70 | def _concat_list(a, b): 71 | return a + b 72 | 73 | 74 | # Python 3 compatibility 75 | try: 76 | xrange 77 | except Exception: 78 | xrange = range 79 | 80 | # Python 3 supports bytes, which is already an array of integers 81 | def _string_to_bytes(text): 82 | if isinstance(text, bytes): 83 | return text 84 | return [ord(c) for c in text] 85 | 86 | # In Python 3, we return bytes 87 | def _bytes_to_string(binary): 88 | return bytes(binary) 89 | 90 | # Python 3 cannot concatenate a list onto a bytes, so we bytes-ify it first 91 | def _concat_list(a, b): 92 | return a + bytes(b) 93 | 94 | 95 | # Based *largely* on the Rijndael implementation 96 | # See: http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf 97 | class AES(object): 98 | '''Encapsulates the AES block cipher. 99 | 100 | You generally should not need this. Use the AESModeOfOperation classes 101 | below instead.''' 102 | 103 | # Number of rounds by keysize 104 | number_of_rounds = {16: 10, 24: 12, 32: 14} 105 | 106 | # Round constant words 107 | rcon = [ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ] 108 | 109 | # S-box and Inverse S-box (S is for Substitution) 110 | S = [ 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 ] 111 | Si =[ 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 ] 112 | 113 | # Transformations for encryption 114 | T1 = [ 0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a ] 115 | T2 = [ 0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616 ] 116 | T3 = [ 0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16 ] 117 | T4 = [ 0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c ] 118 | 119 | # Transformations for decryption 120 | T5 = [ 0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742 ] 121 | T6 = [ 0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857 ] 122 | T7 = [ 0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8 ] 123 | T8 = [ 0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0 ] 124 | 125 | # Transformations for decryption key expansion 126 | U1 = [ 0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3 ] 127 | U2 = [ 0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697 ] 128 | U3 = [ 0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46 ] 129 | U4 = [ 0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d ] 130 | 131 | def __init__(self, key): 132 | 133 | if len(key) not in (16, 24, 32): 134 | raise ValueError('Invalid key size') 135 | 136 | rounds = self.number_of_rounds[len(key)] 137 | 138 | # Encryption round keys 139 | self._Ke = [[0] * 4 for i in xrange(rounds + 1)] 140 | 141 | # Decryption round keys 142 | self._Kd = [[0] * 4 for i in xrange(rounds + 1)] 143 | 144 | round_key_count = (rounds + 1) * 4 145 | KC = len(key) // 4 146 | 147 | # Convert the key into ints 148 | tk = [ struct.unpack('>i', key[i:i + 4])[0] for i in xrange(0, len(key), 4) ] 149 | 150 | # Copy values into round key arrays 151 | for i in xrange(0, KC): 152 | self._Ke[i // 4][i % 4] = tk[i] 153 | self._Kd[rounds - (i // 4)][i % 4] = tk[i] 154 | 155 | # Key expansion (fips-197 section 5.2) 156 | rconpointer = 0 157 | t = KC 158 | while t < round_key_count: 159 | 160 | tt = tk[KC - 1] 161 | tk[0] ^= ((self.S[(tt >> 16) & 0xFF] << 24) ^ 162 | (self.S[(tt >> 8) & 0xFF] << 16) ^ 163 | (self.S[ tt & 0xFF] << 8) ^ 164 | self.S[(tt >> 24) & 0xFF] ^ 165 | (self.rcon[rconpointer] << 24)) 166 | rconpointer += 1 167 | 168 | if KC != 8: 169 | for i in xrange(1, KC): 170 | tk[i] ^= tk[i - 1] 171 | 172 | # Key expansion for 256-bit keys is "slightly different" (fips-197) 173 | else: 174 | for i in xrange(1, KC // 2): 175 | tk[i] ^= tk[i - 1] 176 | tt = tk[KC // 2 - 1] 177 | 178 | tk[KC // 2] ^= (self.S[ tt & 0xFF] ^ 179 | (self.S[(tt >> 8) & 0xFF] << 8) ^ 180 | (self.S[(tt >> 16) & 0xFF] << 16) ^ 181 | (self.S[(tt >> 24) & 0xFF] << 24)) 182 | 183 | for i in xrange(KC // 2 + 1, KC): 184 | tk[i] ^= tk[i - 1] 185 | 186 | # Copy values into round key arrays 187 | j = 0 188 | while j < KC and t < round_key_count: 189 | self._Ke[t // 4][t % 4] = tk[j] 190 | self._Kd[rounds - (t // 4)][t % 4] = tk[j] 191 | j += 1 192 | t += 1 193 | 194 | # Inverse-Cipher-ify the decryption round key (fips-197 section 5.3) 195 | for r in xrange(1, rounds): 196 | for j in xrange(0, 4): 197 | tt = self._Kd[r][j] 198 | self._Kd[r][j] = (self.U1[(tt >> 24) & 0xFF] ^ 199 | self.U2[(tt >> 16) & 0xFF] ^ 200 | self.U3[(tt >> 8) & 0xFF] ^ 201 | self.U4[ tt & 0xFF]) 202 | 203 | def encrypt(self, plaintext): 204 | 'Encrypt a block of plain text using the AES block cipher.' 205 | 206 | if len(plaintext) != 16: 207 | raise ValueError('wrong block length') 208 | 209 | rounds = len(self._Ke) - 1 210 | (s1, s2, s3) = [1, 2, 3] 211 | a = [0, 0, 0, 0] 212 | 213 | # Convert plaintext to (ints ^ key) 214 | t = [(_compact_word(plaintext[4 * i:4 * i + 4]) ^ self._Ke[0][i]) for i in xrange(0, 4)] 215 | 216 | # Apply round transforms 217 | for r in xrange(1, rounds): 218 | for i in xrange(0, 4): 219 | a[i] = (self.T1[(t[ i ] >> 24) & 0xFF] ^ 220 | self.T2[(t[(i + s1) % 4] >> 16) & 0xFF] ^ 221 | self.T3[(t[(i + s2) % 4] >> 8) & 0xFF] ^ 222 | self.T4[ t[(i + s3) % 4] & 0xFF] ^ 223 | self._Ke[r][i]) 224 | t = copy.copy(a) 225 | 226 | # The last round is special 227 | result = [ ] 228 | for i in xrange(0, 4): 229 | tt = self._Ke[rounds][i] 230 | result.append((self.S[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) 231 | result.append((self.S[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) 232 | result.append((self.S[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) 233 | result.append((self.S[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) 234 | 235 | return result 236 | 237 | def decrypt(self, ciphertext): 238 | 'Decrypt a block of cipher text using the AES block cipher.' 239 | 240 | if len(ciphertext) != 16: 241 | raise ValueError('wrong block length') 242 | 243 | rounds = len(self._Kd) - 1 244 | (s1, s2, s3) = [3, 2, 1] 245 | a = [0, 0, 0, 0] 246 | 247 | # Convert ciphertext to (ints ^ key) 248 | t = [(_compact_word(ciphertext[4 * i:4 * i + 4]) ^ self._Kd[0][i]) for i in xrange(0, 4)] 249 | 250 | # Apply round transforms 251 | for r in xrange(1, rounds): 252 | for i in xrange(0, 4): 253 | a[i] = (self.T5[(t[ i ] >> 24) & 0xFF] ^ 254 | self.T6[(t[(i + s1) % 4] >> 16) & 0xFF] ^ 255 | self.T7[(t[(i + s2) % 4] >> 8) & 0xFF] ^ 256 | self.T8[ t[(i + s3) % 4] & 0xFF] ^ 257 | self._Kd[r][i]) 258 | t = copy.copy(a) 259 | 260 | # The last round is special 261 | result = [ ] 262 | for i in xrange(0, 4): 263 | tt = self._Kd[rounds][i] 264 | result.append((self.Si[(t[ i ] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) 265 | result.append((self.Si[(t[(i + s1) % 4] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) 266 | result.append((self.Si[(t[(i + s2) % 4] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) 267 | result.append((self.Si[ t[(i + s3) % 4] & 0xFF] ^ tt ) & 0xFF) 268 | 269 | return result 270 | 271 | 272 | class Counter(object): 273 | '''A counter object for the Counter (CTR) mode of operation. 274 | 275 | To create a custom counter, you can usually just override the 276 | increment method.''' 277 | 278 | def __init__(self, initial_value = 1): 279 | 280 | # Convert the value into an array of bytes long 281 | self._counter = [ ((initial_value >> i) % 256) for i in xrange(128 - 8, -1, -8) ] 282 | 283 | value = property(lambda s: s._counter) 284 | 285 | def increment(self): 286 | '''Increment the counter (overflow rolls back to 0).''' 287 | 288 | for i in xrange(len(self._counter) - 1, -1, -1): 289 | self._counter[i] += 1 290 | 291 | if self._counter[i] < 256: break 292 | 293 | # Carry the one 294 | self._counter[i] = 0 295 | 296 | # Overflow 297 | else: 298 | self._counter = [ 0 ] * len(self._counter) 299 | 300 | 301 | class AESBlockModeOfOperation(object): 302 | '''Super-class for AES modes of operation that require blocks.''' 303 | def __init__(self, key): 304 | self._aes = AES(key) 305 | 306 | def decrypt(self, ciphertext): 307 | raise Exception('not implemented') 308 | 309 | def encrypt(self, plaintext): 310 | raise Exception('not implemented') 311 | 312 | 313 | class AESStreamModeOfOperation(AESBlockModeOfOperation): 314 | '''Super-class for AES modes of operation that are stream-ciphers.''' 315 | 316 | class AESSegmentModeOfOperation(AESStreamModeOfOperation): 317 | '''Super-class for AES modes of operation that segment data.''' 318 | 319 | segment_bytes = 16 320 | 321 | 322 | 323 | class AESModeOfOperationECB(AESBlockModeOfOperation): 324 | '''AES Electronic Codebook Mode of Operation. 325 | 326 | o Block-cipher, so data must be padded to 16 byte boundaries 327 | 328 | Security Notes: 329 | o This mode is not recommended 330 | o Any two identical blocks produce identical encrypted values, 331 | exposing data patterns. (See the image of Tux on wikipedia) 332 | 333 | Also see: 334 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_.28ECB.29 335 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.1''' 336 | 337 | 338 | name = "Electronic Codebook (ECB)" 339 | 340 | def encrypt(self, plaintext): 341 | if len(plaintext) != 16: 342 | raise ValueError('plaintext block must be 16 bytes') 343 | 344 | plaintext = _string_to_bytes(plaintext) 345 | return _bytes_to_string(self._aes.encrypt(plaintext)) 346 | 347 | def decrypt(self, ciphertext): 348 | if len(ciphertext) != 16: 349 | raise ValueError('ciphertext block must be 16 bytes') 350 | 351 | ciphertext = _string_to_bytes(ciphertext) 352 | return _bytes_to_string(self._aes.decrypt(ciphertext)) 353 | 354 | 355 | 356 | class AESModeOfOperationCBC(AESBlockModeOfOperation): 357 | '''AES Cipher-Block Chaining Mode of Operation. 358 | 359 | o The Initialization Vector (IV) 360 | o Block-cipher, so data must be padded to 16 byte boundaries 361 | o An incorrect initialization vector will only cause the first 362 | block to be corrupt; all other blocks will be intact 363 | o A corrupt bit in the cipher text will cause a block to be 364 | corrupted, and the next block to be inverted, but all other 365 | blocks will be intact. 366 | 367 | Security Notes: 368 | o This method (and CTR) ARE recommended. 369 | 370 | Also see: 371 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 372 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.2''' 373 | 374 | 375 | name = "Cipher-Block Chaining (CBC)" 376 | 377 | def __init__(self, key, iv = None): 378 | if iv is None: 379 | self._last_cipherblock = [ 0 ] * 16 380 | elif len(iv) != 16: 381 | raise ValueError('initialization vector must be 16 bytes') 382 | else: 383 | self._last_cipherblock = _string_to_bytes(iv) 384 | 385 | AESBlockModeOfOperation.__init__(self, key) 386 | 387 | def encrypt(self, plaintext): 388 | if len(plaintext) != 16: 389 | raise ValueError('plaintext block must be 16 bytes') 390 | 391 | plaintext = _string_to_bytes(plaintext) 392 | precipherblock = [ (p ^ l) for (p, l) in zip(plaintext, self._last_cipherblock) ] 393 | self._last_cipherblock = self._aes.encrypt(precipherblock) 394 | 395 | return _bytes_to_string(self._last_cipherblock) 396 | 397 | def decrypt(self, ciphertext): 398 | if len(ciphertext) != 16: 399 | raise ValueError('ciphertext block must be 16 bytes') 400 | 401 | cipherblock = _string_to_bytes(ciphertext) 402 | plaintext = [ (p ^ l) for (p, l) in zip(self._aes.decrypt(cipherblock), self._last_cipherblock) ] 403 | self._last_cipherblock = cipherblock 404 | 405 | return _bytes_to_string(plaintext) 406 | 407 | 408 | 409 | class AESModeOfOperationCFB(AESSegmentModeOfOperation): 410 | '''AES Cipher Feedback Mode of Operation. 411 | 412 | o A stream-cipher, so input does not need to be padded to blocks, 413 | but does need to be padded to segment_size 414 | 415 | Also see: 416 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher_feedback_.28CFB.29 417 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.3''' 418 | 419 | 420 | name = "Cipher Feedback (CFB)" 421 | 422 | def __init__(self, key, iv, segment_size = 1): 423 | if segment_size == 0: segment_size = 1 424 | 425 | if iv is None: 426 | self._shift_register = [ 0 ] * 16 427 | elif len(iv) != 16: 428 | raise ValueError('initialization vector must be 16 bytes') 429 | else: 430 | self._shift_register = _string_to_bytes(iv) 431 | 432 | self._segment_bytes = segment_size 433 | 434 | AESBlockModeOfOperation.__init__(self, key) 435 | 436 | segment_bytes = property(lambda s: s._segment_bytes) 437 | 438 | def encrypt(self, plaintext): 439 | if len(plaintext) % self._segment_bytes != 0: 440 | raise ValueError('plaintext block must be a multiple of segment_size') 441 | 442 | plaintext = _string_to_bytes(plaintext) 443 | 444 | # Break block into segments 445 | encrypted = [ ] 446 | for i in xrange(0, len(plaintext), self._segment_bytes): 447 | plaintext_segment = plaintext[i: i + self._segment_bytes] 448 | xor_segment = self._aes.encrypt(self._shift_register)[:len(plaintext_segment)] 449 | cipher_segment = [ (p ^ x) for (p, x) in zip(plaintext_segment, xor_segment) ] 450 | 451 | # Shift the top bits out and the ciphertext in 452 | self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) 453 | 454 | encrypted.extend(cipher_segment) 455 | 456 | return _bytes_to_string(encrypted) 457 | 458 | def decrypt(self, ciphertext): 459 | if len(ciphertext) % self._segment_bytes != 0: 460 | raise ValueError('ciphertext block must be a multiple of segment_size') 461 | 462 | ciphertext = _string_to_bytes(ciphertext) 463 | 464 | # Break block into segments 465 | decrypted = [ ] 466 | for i in xrange(0, len(ciphertext), self._segment_bytes): 467 | cipher_segment = ciphertext[i: i + self._segment_bytes] 468 | xor_segment = self._aes.encrypt(self._shift_register)[:len(cipher_segment)] 469 | plaintext_segment = [ (p ^ x) for (p, x) in zip(cipher_segment, xor_segment) ] 470 | 471 | # Shift the top bits out and the ciphertext in 472 | self._shift_register = _concat_list(self._shift_register[len(cipher_segment):], cipher_segment) 473 | 474 | decrypted.extend(plaintext_segment) 475 | 476 | return _bytes_to_string(decrypted) 477 | 478 | 479 | 480 | class AESModeOfOperationOFB(AESStreamModeOfOperation): 481 | '''AES Output Feedback Mode of Operation. 482 | 483 | o A stream-cipher, so input does not need to be padded to blocks, 484 | allowing arbitrary length data. 485 | o A bit twiddled in the cipher text, twiddles the same bit in the 486 | same bit in the plain text, which can be useful for error 487 | correction techniques. 488 | 489 | Also see: 490 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Output_feedback_.28OFB.29 491 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.4''' 492 | 493 | 494 | name = "Output Feedback (OFB)" 495 | 496 | def __init__(self, key, iv = None): 497 | if iv is None: 498 | self._last_precipherblock = [ 0 ] * 16 499 | elif len(iv) != 16: 500 | raise ValueError('initialization vector must be 16 bytes') 501 | else: 502 | self._last_precipherblock = _string_to_bytes(iv) 503 | 504 | self._remaining_block = [ ] 505 | 506 | AESBlockModeOfOperation.__init__(self, key) 507 | 508 | def encrypt(self, plaintext): 509 | encrypted = [ ] 510 | for p in _string_to_bytes(plaintext): 511 | if len(self._remaining_block) == 0: 512 | self._remaining_block = self._aes.encrypt(self._last_precipherblock) 513 | self._last_precipherblock = [ ] 514 | precipherbyte = self._remaining_block.pop(0) 515 | self._last_precipherblock.append(precipherbyte) 516 | cipherbyte = p ^ precipherbyte 517 | encrypted.append(cipherbyte) 518 | 519 | return _bytes_to_string(encrypted) 520 | 521 | def decrypt(self, ciphertext): 522 | # AES-OFB is symetric 523 | return self.encrypt(ciphertext) 524 | 525 | 526 | 527 | class AESModeOfOperationCTR(AESStreamModeOfOperation): 528 | '''AES Counter Mode of Operation. 529 | 530 | o A stream-cipher, so input does not need to be padded to blocks, 531 | allowing arbitrary length data. 532 | o The counter must be the same size as the key size (ie. len(key)) 533 | o Each block independant of the other, so a corrupt byte will not 534 | damage future blocks. 535 | o Each block has a uniue counter value associated with it, which 536 | contributes to the encrypted value, so no data patterns are 537 | leaked. 538 | o Also known as: Counter Mode (CM), Integer Counter Mode (ICM) and 539 | Segmented Integer Counter (SIC 540 | 541 | Security Notes: 542 | o This method (and CBC) ARE recommended. 543 | o Each message block is associated with a counter value which must be 544 | unique for ALL messages with the same key. Otherwise security may be 545 | compromised. 546 | 547 | Also see: 548 | 549 | o https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29 550 | o See NIST SP800-38A (http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf); section 6.5 551 | and Appendix B for managing the initial counter''' 552 | 553 | 554 | name = "Counter (CTR)" 555 | 556 | def __init__(self, key, counter = None): 557 | AESBlockModeOfOperation.__init__(self, key) 558 | 559 | if counter is None: 560 | counter = Counter() 561 | 562 | self._counter = counter 563 | self._remaining_counter = [ ] 564 | 565 | def encrypt(self, plaintext): 566 | while len(self._remaining_counter) < len(plaintext): 567 | self._remaining_counter += self._aes.encrypt(self._counter.value) 568 | self._counter.increment() 569 | 570 | plaintext = _string_to_bytes(plaintext) 571 | 572 | encrypted = [ (p ^ c) for (p, c) in zip(plaintext, self._remaining_counter) ] 573 | self._remaining_counter = self._remaining_counter[len(encrypted):] 574 | 575 | return _bytes_to_string(encrypted) 576 | 577 | def decrypt(self, crypttext): 578 | # AES-CTR is symetric 579 | return self.encrypt(crypttext) 580 | 581 | 582 | # Simple lookup table for each mode 583 | AESModesOfOperation = dict( 584 | ctr = AESModeOfOperationCTR, 585 | cbc = AESModeOfOperationCBC, 586 | cfb = AESModeOfOperationCFB, 587 | ecb = AESModeOfOperationECB, 588 | ofb = AESModeOfOperationOFB, 589 | ) 590 | -------------------------------------------------------------------------------- /sublime_notebook/pyaes/blockfeeder.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2014 Richard Moore 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | 24 | from .aes import AESBlockModeOfOperation, AESSegmentModeOfOperation, AESStreamModeOfOperation 25 | from .util import append_PKCS7_padding, strip_PKCS7_padding, to_bufferable 26 | 27 | 28 | # First we inject three functions to each of the modes of operations 29 | # 30 | # _can_consume(size) 31 | # - Given a size, determine how many bytes could be consumed in 32 | # a single call to either the decrypt or encrypt method 33 | # 34 | # _final_encrypt(data, padding = PADDING_DEFAULT) 35 | # - call and return encrypt on this (last) chunk of data, 36 | # padding as necessary; this will always be at least 16 37 | # bytes unless the total incoming input was less than 16 38 | # bytes 39 | # 40 | # _final_decrypt(data, padding = PADDING_DEFAULT) 41 | # - same as _final_encrypt except for decrypt, for 42 | # stripping off padding 43 | # 44 | 45 | PADDING_NONE = 'none' 46 | PADDING_DEFAULT = 'default' 47 | 48 | # @TODO: Ciphertext stealing and explicit PKCS#7 49 | # PADDING_CIPHERTEXT_STEALING 50 | # PADDING_PKCS7 51 | 52 | # ECB and CBC are block-only ciphers 53 | 54 | def _block_can_consume(self, size): 55 | if size >= 16: return 16 56 | return 0 57 | 58 | # After padding, we may have more than one block 59 | def _block_final_encrypt(self, data, padding = PADDING_DEFAULT): 60 | if padding == PADDING_DEFAULT: 61 | data = append_PKCS7_padding(data) 62 | 63 | elif padding == PADDING_NONE: 64 | if len(data) != 16: 65 | raise Exception('invalid data length for final block') 66 | else: 67 | raise Exception('invalid padding option') 68 | 69 | if len(data) == 32: 70 | return self.encrypt(data[:16]) + self.encrypt(data[16:]) 71 | 72 | return self.encrypt(data) 73 | 74 | 75 | def _block_final_decrypt(self, data, padding = PADDING_DEFAULT): 76 | if padding == PADDING_DEFAULT: 77 | return strip_PKCS7_padding(self.decrypt(data)) 78 | 79 | if padding == PADDING_NONE: 80 | if len(data) != 16: 81 | raise Exception('invalid data length for final block') 82 | return self.decrypt(data) 83 | 84 | raise Exception('invalid padding option') 85 | 86 | AESBlockModeOfOperation._can_consume = _block_can_consume 87 | AESBlockModeOfOperation._final_encrypt = _block_final_encrypt 88 | AESBlockModeOfOperation._final_decrypt = _block_final_decrypt 89 | 90 | 91 | 92 | # CFB is a segment cipher 93 | 94 | def _segment_can_consume(self, size): 95 | return self.segment_bytes * int(size // self.segment_bytes) 96 | 97 | # CFB can handle a non-segment-sized block at the end using the remaining cipherblock 98 | def _segment_final_encrypt(self, data, padding = PADDING_DEFAULT): 99 | if padding != PADDING_DEFAULT: 100 | raise Exception('invalid padding option') 101 | 102 | faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) 103 | padded = data + to_bufferable(faux_padding) 104 | return self.encrypt(padded)[:len(data)] 105 | 106 | # CFB can handle a non-segment-sized block at the end using the remaining cipherblock 107 | def _segment_final_decrypt(self, data, padding = PADDING_DEFAULT): 108 | if padding != PADDING_DEFAULT: 109 | raise Exception('invalid padding option') 110 | 111 | faux_padding = (chr(0) * (self.segment_bytes - (len(data) % self.segment_bytes))) 112 | padded = data + to_bufferable(faux_padding) 113 | return self.decrypt(padded)[:len(data)] 114 | 115 | AESSegmentModeOfOperation._can_consume = _segment_can_consume 116 | AESSegmentModeOfOperation._final_encrypt = _segment_final_encrypt 117 | AESSegmentModeOfOperation._final_decrypt = _segment_final_decrypt 118 | 119 | 120 | 121 | # OFB and CTR are stream ciphers 122 | 123 | def _stream_can_consume(self, size): 124 | return size 125 | 126 | def _stream_final_encrypt(self, data, padding = PADDING_DEFAULT): 127 | if padding not in [PADDING_NONE, PADDING_DEFAULT]: 128 | raise Exception('invalid padding option') 129 | 130 | return self.encrypt(data) 131 | 132 | def _stream_final_decrypt(self, data, padding = PADDING_DEFAULT): 133 | if padding not in [PADDING_NONE, PADDING_DEFAULT]: 134 | raise Exception('invalid padding option') 135 | 136 | return self.decrypt(data) 137 | 138 | AESStreamModeOfOperation._can_consume = _stream_can_consume 139 | AESStreamModeOfOperation._final_encrypt = _stream_final_encrypt 140 | AESStreamModeOfOperation._final_decrypt = _stream_final_decrypt 141 | 142 | 143 | 144 | class BlockFeeder(object): 145 | '''The super-class for objects to handle chunking a stream of bytes 146 | into the appropriate block size for the underlying mode of operation 147 | and applying (or stripping) padding, as necessary.''' 148 | 149 | def __init__(self, mode, feed, final, padding = PADDING_DEFAULT): 150 | self._mode = mode 151 | self._feed = feed 152 | self._final = final 153 | self._buffer = to_bufferable("") 154 | self._padding = padding 155 | 156 | def feed(self, data = None): 157 | '''Provide bytes to encrypt (or decrypt), returning any bytes 158 | possible from this or any previous calls to feed. 159 | 160 | Call with None or an empty string to flush the mode of 161 | operation and return any final bytes; no further calls to 162 | feed may be made.''' 163 | 164 | if self._buffer is None: 165 | raise ValueError('already finished feeder') 166 | 167 | # Finalize; process the spare bytes we were keeping 168 | if data is None: 169 | result = self._final(self._buffer, self._padding) 170 | self._buffer = None 171 | return result 172 | 173 | self._buffer += to_bufferable(data) 174 | 175 | # We keep 16 bytes around so we can determine padding 176 | result = to_bufferable('') 177 | while len(self._buffer) > 16: 178 | can_consume = self._mode._can_consume(len(self._buffer) - 16) 179 | if can_consume == 0: break 180 | result += self._feed(self._buffer[:can_consume]) 181 | self._buffer = self._buffer[can_consume:] 182 | 183 | return result 184 | 185 | 186 | class Encrypter(BlockFeeder): 187 | 'Accepts bytes of plaintext and returns encrypted ciphertext.' 188 | 189 | def __init__(self, mode, padding = PADDING_DEFAULT): 190 | BlockFeeder.__init__(self, mode, mode.encrypt, mode._final_encrypt, padding) 191 | 192 | 193 | class Decrypter(BlockFeeder): 194 | 'Accepts bytes of ciphertext and returns decrypted plaintext.' 195 | 196 | def __init__(self, mode, padding = PADDING_DEFAULT): 197 | BlockFeeder.__init__(self, mode, mode.decrypt, mode._final_decrypt, padding) 198 | 199 | 200 | # 8kb blocks 201 | BLOCK_SIZE = (1 << 13) 202 | 203 | def _feed_stream(feeder, in_stream, out_stream, block_size = BLOCK_SIZE): 204 | 'Uses feeder to read and convert from in_stream and write to out_stream.' 205 | 206 | while True: 207 | chunk = in_stream.read(block_size) 208 | if not chunk: 209 | break 210 | converted = feeder.feed(chunk) 211 | out_stream.write(converted) 212 | converted = feeder.feed() 213 | out_stream.write(converted) 214 | 215 | 216 | def encrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): 217 | 'Encrypts a stream of bytes from in_stream to out_stream using mode.' 218 | 219 | encrypter = Encrypter(mode, padding = padding) 220 | _feed_stream(encrypter, in_stream, out_stream, block_size) 221 | 222 | 223 | def decrypt_stream(mode, in_stream, out_stream, block_size = BLOCK_SIZE, padding = PADDING_DEFAULT): 224 | 'Decrypts a stream of bytes from in_stream to out_stream using mode.' 225 | 226 | decrypter = Decrypter(mode, padding = padding) 227 | _feed_stream(decrypter, in_stream, out_stream, block_size) 228 | -------------------------------------------------------------------------------- /sublime_notebook/pyaes/util.py: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | # 3 | # Copyright (c) 2014 Richard Moore 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to deal 7 | # in the Software without restriction, including without limitation the rights 8 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | # copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | # THE SOFTWARE. 22 | 23 | # Why to_bufferable? 24 | # Python 3 is very different from Python 2.x when it comes to strings of text 25 | # and strings of bytes; in Python 3, strings of bytes do not exist, instead to 26 | # represent arbitrary binary data, we must use the "bytes" object. This method 27 | # ensures the object behaves as we need it to. 28 | 29 | def to_bufferable(binary): 30 | return binary 31 | 32 | def _get_byte(c): 33 | return ord(c) 34 | 35 | try: 36 | xrange 37 | except: 38 | 39 | def to_bufferable(binary): 40 | if isinstance(binary, bytes): 41 | return binary 42 | return bytes(ord(b) for b in binary) 43 | 44 | def _get_byte(c): 45 | return c 46 | 47 | def append_PKCS7_padding(data): 48 | pad = 16 - (len(data) % 16) 49 | return data + to_bufferable(chr(pad) * pad) 50 | 51 | def strip_PKCS7_padding(data): 52 | if len(data) % 16 != 0: 53 | raise ValueError("invalid length") 54 | 55 | pad = _get_byte(data[-1]) 56 | 57 | if pad > 16: 58 | raise ValueError("invalid padding byte") 59 | 60 | return data[:-pad] 61 | -------------------------------------------------------------------------------- /sublime_notebook/settings.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import time 4 | from traceback import print_exc 5 | from subprocess import check_output, STDOUT 6 | from sublime_notebook import SETTINGS_PATH, VERSION 7 | from .message import print_err, print_info 8 | 9 | 10 | class Settings: 11 | """ 12 | Settings module 13 | """ 14 | default_json = { 15 | 'public_folders': ['*'], 16 | 'private_folders': ['diary'], 17 | 'is_encrypted': False, 18 | 'version': VERSION, 19 | 'do_git_backup': False, 20 | 'git_push_interval_minutes': 1440, 21 | 'last_git_push': 0, 22 | 'note_extensions': ['txt', 'md'] 23 | } 24 | json = default_json.copy() 25 | where_star = 'public' 26 | file = SETTINGS_PATH 27 | 28 | def __init__(self, file=None): 29 | if file: 30 | self.file = file 31 | self.load_file() 32 | 33 | def load_file(self): 34 | """ 35 | Loads file as JSON 36 | """ 37 | try: 38 | fp = open(self.file, 'r') 39 | data = fp.read() 40 | self.json = json.loads(data) 41 | fp.close() 42 | self.find_star() 43 | except Exception as e: 44 | # load default settings 45 | print_err('JSON Exception occurred: ' + str(e)) 46 | 47 | def find_star(self): 48 | if Settings._find_in_array('*', self.json['private_folders']): 49 | self.where_star = 'private' 50 | else: 51 | # default behavior public 52 | self.where_star = 'public' 53 | 54 | def check_folder_private(self, dirname): 55 | st = Settings._find_in_array(dirname, self.json['private_folders']) 56 | if st: 57 | return True 58 | st = Settings._find_in_array(dirname, self.json['public_folders']) 59 | if st: 60 | return False 61 | # star situation 62 | return True if self.where_star == 'private' else False 63 | 64 | def change_encrypted_status(self, status): 65 | self.load_file() 66 | self.json['is_encrypted'] = status 67 | self.save_settings() 68 | 69 | def get_encrypted_status(self): 70 | return self.json['is_encrypted'] 71 | 72 | def save_settings(self): 73 | Settings._write_settings(self.json, self.file) 74 | 75 | def upgrade_settings(self): 76 | if VERSION > self.json['version']: 77 | new = self.default_json.copy() 78 | new.update(self.json) # only adds new keys 79 | self.json = new.copy() 80 | self.json['version'] = VERSION # upgrade version again 81 | self.save_settings() 82 | return True 83 | return False 84 | 85 | def is_git_setup(self): 86 | curpath = os.path.dirname(os.path.realpath(__file__)) 87 | git_path = curpath.rstrip('/\\') + '/../.git' 88 | # print(git_path) 89 | return os.path.isdir(git_path) 90 | 91 | def do_git_push(self): 92 | if not self.json['do_git_backup']: 93 | return False 94 | mins = int(round(time.time()) / 60.0) 95 | if mins < (self.json['last_git_push'] + self.json['git_push_interval_minutes']): 96 | return False 97 | # start backup 98 | print_info('Starting git backup') 99 | # check remote 100 | out = check_output("git remote", shell=True).decode() 101 | if not out: 102 | print_err('Error with git remote: ' + str(out)) 103 | return False 104 | if out and out.find('notebookbackup') == -1: 105 | print_err('notebookbackup remote not found') 106 | return False 107 | # push to remote 108 | commit_msg = "auto backup " + str(mins) 109 | # push only if changes 110 | out = check_output("git status -s", stderr=STDOUT, shell=True).decode() 111 | if not out: 112 | print_info('No changes detected, hence skipping git backup') 113 | return 114 | # save last push min in advance 115 | old_mins = self.json['last_git_push'] 116 | self.json['last_git_push'] = mins 117 | self.save_settings() 118 | # actual push 119 | try: 120 | print_info('Pushing to remote') 121 | out = check_output("git add -A && git commit -m \"{}\" && git push notebookbackup master".format(commit_msg), 122 | stderr=STDOUT, shell=True).decode() 123 | print_info('GIT LOG:\n\n' + out) 124 | except Exception: 125 | # revert back 126 | print_exc() 127 | print_err('git push did not happen') 128 | self.json['last_git_push'] = old_mins 129 | self.save_settings() 130 | 131 | @staticmethod 132 | def _find_in_array(item, arr): 133 | status = False 134 | for i in arr: 135 | if item == i: 136 | status = True 137 | break 138 | return status 139 | 140 | @staticmethod 141 | def _create_default_file(): 142 | Settings._write_settings(Settings.json, Settings.file) 143 | 144 | @staticmethod 145 | def _write_settings(setting, file): 146 | data = json.dumps(setting, indent=4, sort_keys=True) 147 | fp = open(file, 'w') 148 | fp.write(data) 149 | fp.close() 150 | -------------------------------------------------------------------------------- /sublime_notebook/sublime_notebook.py: -------------------------------------------------------------------------------- 1 | import os 2 | from sys import exit 3 | from .cryptlib import get_file_list, encode, update_file, get_key, decode 4 | from .message import print_info, print_err 5 | from .settings import Settings 6 | from sublime_notebook import SETTINGS_PATH 7 | 8 | 9 | def get_first_time_key(): 10 | key = get_key() 11 | print('Re-enter key') 12 | key2 = get_key() 13 | if key != key2: 14 | print_info('Keys don\'t match, exiting') 15 | exit(1) 16 | return key2 17 | 18 | 19 | def main(): 20 | """ 21 | Executes Notebook 22 | """ 23 | if not os.path.exists(SETTINGS_PATH): 24 | # new case 25 | # or decrypted state in power fail 26 | print_info('Not encrypted, encrypting ....') 27 | # create settings 28 | print_info( 29 | 'Created settings.json in sublime_notebook/ directory.\n' + 30 | 'By default, only the "diary" directory is privated (encrypted), you can change this setting by editing settings.json.\n' + 31 | 'See the docs for more info.' 32 | ) 33 | Settings._create_default_file() 34 | # get password 35 | print_info('Starting encryption process') 36 | key = get_first_time_key() 37 | update_file(encode, get_file_list(), key) 38 | # update encryption status 39 | sts = Settings() 40 | sts.change_encrypted_status(True) 41 | print_info('Sublime Notebook setup complete') 42 | else: 43 | # get settings 44 | sts = Settings() 45 | # check Notebook settings version 46 | check = sts.upgrade_settings() 47 | if check: 48 | print_info('settings.json upgraded to current version') 49 | # decrypt 50 | key = '' 51 | if sts.get_encrypted_status(): 52 | # already encrypted 53 | print_info('Encrypted. Enter key to unlock') 54 | key = get_key() 55 | failStatus = update_file(decode, get_file_list(), key) 56 | if failStatus: 57 | print_err('You entered wrong key. Please try again.') 58 | exit(2) 59 | # remove encryption status 60 | sts.change_encrypted_status(False) 61 | # decoded, wait to close 62 | print_info('Notes have been decrypted') 63 | else: 64 | print_info('Notes are already decrypted') 65 | # now decrypted 66 | ans = '' 67 | while (True): 68 | ans = input('Press "e" to encrypt\nPress "d" to leave decrypted\n> ') 69 | if ans == 'd' or ans == 'e': 70 | if ans == 'e' and key == '': # already decrypt case 71 | key = get_first_time_key() 72 | break 73 | if ans == 'e': 74 | # encrypt 75 | st = update_file(encode, get_file_list(), key) 76 | if st: 77 | print_err('Something went wrong while encrypting') 78 | exit(3) 79 | sts.change_encrypted_status(True) 80 | print_info('Notes encrypted') 81 | # do git push 82 | if sts.is_git_setup(): 83 | sts.do_git_push() 84 | else: 85 | # disable notebook 86 | # exit as-is 87 | print_info('Notes have been left decrypted') 88 | pass 89 | --------------------------------------------------------------------------------