├── .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 | [](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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------