commit 18ef4a051529a631abe8acf229ed3cb7dc41272f Author: jika Date: Mon Oct 10 13:00:38 2022 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/python/.gitignore b/python/.gitignore new file mode 100644 index 0000000..7e86cf0 --- /dev/null +++ b/python/.gitignore @@ -0,0 +1,140 @@ +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + diff --git a/python/DES-20221006/.gitignore b/python/DES-20221006/.gitignore new file mode 100644 index 0000000..6e92aa9 --- /dev/null +++ b/python/DES-20221006/.gitignore @@ -0,0 +1,3 @@ +pyDes.py.dec +pyDes.py.enc +*.pyc diff --git a/python/DES-20221006/Changelog.txt b/python/DES-20221006/Changelog.txt new file mode 100644 index 0000000..3d90d46 --- /dev/null +++ b/python/DES-20221006/Changelog.txt @@ -0,0 +1,45 @@ +** 12th December 2011 ** + Put on Github, use MIT license + +** 28th April 2010 ** + Release: 2.0.1 + Fix triple des error when creating a CBC instance with no IV. + +** 16th March 2009 ** + Release: 2.0.0 + Updated to work with Python3. + +** 14th November 2008 ** + Release: 1.3.1 + Code: Patch by Shaya: fix PAD_PKCS5 padding modes when using triple des CBC. + Tests: Moved to separate test file. + +** 28th October 2008 ** + Release: 1.3 + Code: Added PAD_NORMAL and PAD_PKCS5 padding modes. + Code: Implemented PKCS5 padding suport. + Code: Allow pad character and padmode to be set on a class instance. + +** 23rd March 2007 ** + License: Specified license as Public Domain everywhere. + +** 12th September 2005 ** + Release: 1.2 + Code: Fixed errors with pyDes.triple_des() CBC mode. + Code: Added triple DES CBC checks to the testing procedures in __fulltest__() + +** 7 May 2003 ** + Code: pyDes.des and pyDes.triple_des now have an extra optional argument for + the encrypt and decrypt methods. This is for the padding character and + immediately follows the data field. + Code: Added error checking to the testing procedures in __fulltest__() + +** 5 May 2003 ** + Doc: Modified comments in Readme.txt and in pyDes.py header + + Code: Changed Triple DES method to DES-EDE3 or DES-EDE2, depending on key size + Code: Optimized code by replacing inner for loops with map (now twice as fast) + Code: Changed all permutation tables to contain indexed locations instead of + byte offset locations + Code: Removed debugging line "Len of data: 1.0000" + Code: Included __filetest__() testing procedure diff --git a/python/DES-20221006/Image1.bmp b/python/DES-20221006/Image1.bmp new file mode 100644 index 0000000..6398876 Binary files /dev/null and b/python/DES-20221006/Image1.bmp differ diff --git a/python/DES-20221006/Image2.bmp b/python/DES-20221006/Image2.bmp new file mode 100644 index 0000000..021487a Binary files /dev/null and b/python/DES-20221006/Image2.bmp differ diff --git a/python/DES-20221006/Image3.bmp b/python/DES-20221006/Image3.bmp new file mode 100644 index 0000000..dcb6716 Binary files /dev/null and b/python/DES-20221006/Image3.bmp differ diff --git a/python/DES-20221006/LICENSE.txt b/python/DES-20221006/LICENSE.txt new file mode 100644 index 0000000..8d3a1c9 --- /dev/null +++ b/python/DES-20221006/LICENSE.txt @@ -0,0 +1,23 @@ +Copyright (c) 2005-2012 Todd Whiteman. All rights reserved. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + diff --git a/python/DES-20221006/MANIFEST.in b/python/DES-20221006/MANIFEST.in new file mode 100644 index 0000000..f1bf41d --- /dev/null +++ b/python/DES-20221006/MANIFEST.in @@ -0,0 +1,3 @@ +include LICENSE.txt +include README.md +include pyDes.py diff --git a/python/DES-20221006/README.md b/python/DES-20221006/README.md new file mode 100644 index 0000000..1c1a545 --- /dev/null +++ b/python/DES-20221006/README.md @@ -0,0 +1,130 @@ +# About + + Author: Todd Whiteman + Version: 2.0.1 + Release: 28th April, 2010 + License: MIT + +This is a pure python implementation of the DES encryption algorithm. +It's pure python to avoid portability issues, since most DES +implementations are programmed in C (for performance reasons). + +## Installation + +Using the Python package manager: +```bash +$ pip install pydes +``` + +### Or, for manual installation + +Extract the files from the downloaded archive and run: +``` +$ python setup.py install +``` + +If you'd like to run the tests, run the command: +``` +$ python test_pydes.py +``` + +## Usage + +```bash +python -c "import pyDes; des = pyDes.des('This Key'); \ + print des.encrypt('SomeData').encode('hex')" +``` + +### Docs and Examples + +``` +Class initialization +-------------------- +pyDes.des(key, [mode], [IV], [pad], [padmode]) +pyDes.triple_des(key, [mode], [IV], [pad], [padmode]) + +key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes + for Triple DES +mode -> Optional argument for encryption type, can be either + pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining) +IV -> Optional Initial Value bytes, must be supplied if using CBC mode. + Length must be 8 bytes. +pad -> Optional argument, set the pad character (PAD_NORMAL) to use during + all encrypt/decrpt operations done with this instance. +padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5) + to use during all encrypt/decrypt operations done with this instance. + +I recommend to use PAD_PKCS5 padding, as then you never need to worry about any +padding issues, as the padding can be removed unambiguously upon decrypting +data that was encrypted using PAD_PKCS5 padmode. + +Common methods +-------------- +encrypt(data, [pad], [padmode]) +decrypt(data, [pad], [padmode]) + +data -> Bytes to be encrypted/decrypted +pad -> Optional argument. Only when using padmode of PAD_NORMAL. For + encryption, adds this characters to the end of the data block when + data is not a multiple of 8 bytes. For decryption, will remove the + trailing characters that match this pad character from the last 8 + bytes of the unencrypted data block. +padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL + or PAD_PKCS5). Defaults to PAD_NORMAL. + + +Example +------- +import pyDes + +# For Python3, you'll need to use bytes, i.e.: +# data = b"Please encrypt my data" +# k = pyDes.des(b"DESCRYPT", pyDes.CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=pyDes.PAD_PKCS5) + +data = "Please encrypt my data" +k = pyDes.des("DESCRYPT", pyDes.CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=pyDes.PAD_PKCS5) +d = k.encrypt(data) +print "Encrypted: %r" % d +print "Decrypted: %r" % k.decrypt(d) +assert k.decrypt(d) == data + + +See the pyDes test file (test_pydes.py) for more examples of use. + +Note: This code was not written for high-end systems needing a fast + implementation, but rather a handy portable solution with small usage. +``` + +## Performance + +The code is not written for speed or performance, so not for those +needing a fast DES implementation, but rather a handy portable solution ideal +for small usages. The speed at which pyDes encrypts/decrypts is around 10Kb/s +(using the DES method) - that's very SLOW!! + + +## About triple DES + +Triple DES is just running the DES algorithm 3 times over the data with the +specified key. The supplied key is split up into 3 parts, each part being 8 +bytes long (the mandatory key size for DES). + +The triple DES algorithm uses the DES-EDE3 method when a 24 byte key is +supplied. This means there are three DES operations in the sequence +encrypt-decrypt-encrypt with the three different keys. The first key will be +bytes 1 to 8, the second key bytes 9 to 16 and the third key bytes 17 to 24. + +If a 16 byte key is supplied instead, the triple DES method used will be +DES-EDE2. This means there are three DES operations in the sequence +encrypt-decrypt-encrypt, but the first and third operations use the same key. +The first/third key will be bytes 1 to 8 and the second key bytes 9 to 16. + + +## Credits + +Thanks go to: + - David Broadwell: Ideas, comments and suggestions. + - Mario Wolff: Finding and debugging triple des CBC errors. + - Santiago Palladino: Providing the PKCS5 padding technique. + - Shaya: Fixing triple DES CBC errors with PAD_PKCS5. + - Yoav Aner: For spotting a triple DES CBC IV error. diff --git a/python/DES-20221006/pyDes.py b/python/DES-20221006/pyDes.py new file mode 100644 index 0000000..e7cb7ea --- /dev/null +++ b/python/DES-20221006/pyDes.py @@ -0,0 +1,852 @@ +############################################################################# +# Documentation # +############################################################################# + +# Author: Todd Whiteman +# Date: 28th April, 2010 +# Version: 2.0.1 +# License: MIT +# Homepage: http://twhiteman.netfirms.com/des.html +# +# This is a pure python implementation of the DES encryption algorithm. +# It's pure python to avoid portability issues, since most DES +# implementations are programmed in C (for performance reasons). +# +# Triple DES class is also implemented, utilizing the DES base. Triple DES +# is either DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key. +# +# See the README.txt that should come with this python module for the +# implementation methods used. +# +# Thanks to: +# * David Broadwell for ideas, comments and suggestions. +# * Mario Wolff for pointing out and debugging some triple des CBC errors. +# * Santiago Palladino for providing the PKCS5 padding technique. +# * Shaya for correcting the PAD_PKCS5 triple des CBC errors. +# +"""A pure python implementation of the DES and TRIPLE DES encryption algorithms. + +Class initialization +-------------------- +pyDes.des(key, [mode], [IV], [pad], [padmode]) +pyDes.triple_des(key, [mode], [IV], [pad], [padmode]) + +key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes + for Triple DES +mode -> Optional argument for encryption type, can be either + pyDes.ECB (Electronic Code Book) or pyDes.CBC (Cypher Block Chaining) +IV -> Optional Initial Value bytes, must be supplied if using CBC mode. + Length must be 8 bytes. +pad -> Optional argument, set the pad character (PAD_NORMAL) to use during + all encrypt/decrypt operations done with this instance. +padmode -> Optional argument, set the padding mode (PAD_NORMAL or PAD_PKCS5) + to use during all encrypt/decrypt operations done with this instance. + +I recommend to use PAD_PKCS5 padding, as then you never need to worry about any +padding issues, as the padding can be removed unambiguously upon decrypting +data that was encrypted using PAD_PKCS5 padmode. + +Common methods +-------------- +encrypt(data, [pad], [padmode]) +decrypt(data, [pad], [padmode]) + +data -> Bytes to be encrypted/decrypted +pad -> Optional argument. Only when using padmode of PAD_NORMAL. For + encryption, adds this characters to the end of the data block when + data is not a multiple of 8 bytes. For decryption, will remove the + trailing characters that match this pad character from the last 8 + bytes of the unencrypted data block. +padmode -> Optional argument, set the padding mode, must be one of PAD_NORMAL + or PAD_PKCS5). Defaults to PAD_NORMAL. + + +Example +------- +from pyDes import * + +data = "Please encrypt my data" +k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) +# For Python3, you'll need to use bytes, i.e.: +# data = b"Please encrypt my data" +# k = des(b"DESCRYPT", CBC, b"\0\0\0\0\0\0\0\0", pad=None, padmode=PAD_PKCS5) +d = k.encrypt(data) +print "Encrypted: %r" % d +print "Decrypted: %r" % k.decrypt(d) +assert k.decrypt(d, padmode=PAD_PKCS5) == data + + +See the module source (pyDes.py) for more examples of use. +You can also run the pyDes.py file without and arguments to see a simple test. + +Note: This code was not written for high-end systems needing a fast + implementation, but rather a handy portable solution with small usage. + +""" + +import sys + +# _pythonMajorVersion is used to handle Python2 and Python3 differences. +_pythonMajorVersion = sys.version_info[0] + +# Modes of crypting / cyphering +ECB = 0 +CBC = 1 + +# Modes of padding +PAD_NORMAL = 1 +PAD_PKCS5 = 2 + +# PAD_PKCS5: is a method that will unambiguously remove all padding +# characters after decryption, when originally encrypted with +# this padding mode. +# For a good description of the PKCS5 padding technique, see: +# http://www.faqs.org/rfcs/rfc1423.html + +# The base class shared by des and triple des. +class _baseDes(object): + def __init__(self, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): + if IV: + IV = self._guardAgainstUnicode(IV) + if pad: + pad = self._guardAgainstUnicode(pad) + self.block_size = 8 + # Sanity checking of arguments. + if pad and padmode == PAD_PKCS5: + raise ValueError("Cannot use a pad character with PAD_PKCS5") + if IV and len(IV) != self.block_size: + raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") + + # Set the passed in variables + self._mode = mode + self._iv = IV + self._padding = pad + self._padmode = padmode + + def getKey(self): + """getKey() -> bytes""" + return self.__key + + def setKey(self, key): + """Will set the crypting key for this object.""" + key = self._guardAgainstUnicode(key) + self.__key = key + + def getMode(self): + """getMode() -> pyDes.ECB or pyDes.CBC""" + return self._mode + + def setMode(self, mode): + """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" + self._mode = mode + + def getPadding(self): + """getPadding() -> bytes of length 1. Padding character.""" + return self._padding + + def setPadding(self, pad): + """setPadding() -> bytes of length 1. Padding character.""" + if pad is not None: + pad = self._guardAgainstUnicode(pad) + self._padding = pad + + def getPadMode(self): + """getPadMode() -> pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" + return self._padmode + + def setPadMode(self, mode): + """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" + self._padmode = mode + + def getIV(self): + """getIV() -> bytes""" + return self._iv + + def setIV(self, IV): + """Will set the Initial Value, used in conjunction with CBC mode""" + if not IV or len(IV) != self.block_size: + raise ValueError("Invalid Initial Value (IV), must be a multiple of " + str(self.block_size) + " bytes") + IV = self._guardAgainstUnicode(IV) + self._iv = IV + + def _padData(self, data, pad, padmode): + # Pad data depending on the mode + if padmode is None: + # Get the default padding mode. + padmode = self.getPadMode() + if pad and padmode == PAD_PKCS5: + raise ValueError("Cannot use a pad character with PAD_PKCS5") + + if padmode == PAD_NORMAL: + if len(data) % self.block_size == 0: + # No padding required. + return data + + if not pad: + # Get the default padding. + pad = self.getPadding() + if not pad: + raise ValueError("Data must be a multiple of " + str(self.block_size) + " bytes in length. Use padmode=PAD_PKCS5 or set the pad character.") + data += (self.block_size - (len(data) % self.block_size)) * pad + + elif padmode == PAD_PKCS5: + pad_len = 8 - (len(data) % self.block_size) + if _pythonMajorVersion < 3: + data += pad_len * chr(pad_len) + else: + data += bytes([pad_len] * pad_len) + + return data + + def _unpadData(self, data, pad, padmode): + # Unpad data depending on the mode. + if not data: + return data + if pad and padmode == PAD_PKCS5: + raise ValueError("Cannot use a pad character with PAD_PKCS5") + if padmode is None: + # Get the default padding mode. + padmode = self.getPadMode() + + if padmode == PAD_NORMAL: + if not pad: + # Get the default padding. + pad = self.getPadding() + if pad: + data = data[:-self.block_size] + \ + data[-self.block_size:].rstrip(pad) + + elif padmode == PAD_PKCS5: + if _pythonMajorVersion < 3: + pad_len = ord(data[-1]) + else: + pad_len = data[-1] + data = data[:-pad_len] + + return data + + def _guardAgainstUnicode(self, data): + # Only accept byte strings or ascii unicode values, otherwise + # there is no way to correctly decode the data into bytes. + if _pythonMajorVersion < 3: + if isinstance(data, unicode): + raise ValueError("pyDes can only work with bytes, not Unicode strings.") + else: + if isinstance(data, str): + # Only accept ascii unicode values. + try: + return data.encode('ascii') + except UnicodeEncodeError: + pass + raise ValueError("pyDes can only work with encoded strings, not Unicode.") + return data + +############################################################################# +# DES # +############################################################################# +class des(_baseDes): + """DES encryption/decrytpion class + + Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. + + pyDes.des(key,[mode], [IV]) + + key -> Bytes containing the encryption key, must be exactly 8 bytes + mode -> Optional argument for encryption type, can be either pyDes.ECB + (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) + IV -> Optional Initial Value bytes, must be supplied if using CBC mode. + Must be 8 bytes in length. + pad -> Optional argument, set the pad character (PAD_NORMAL) to use + during all encrypt/decrypt operations done with this instance. + padmode -> Optional argument, set the padding mode (PAD_NORMAL or + PAD_PKCS5) to use during all encrypt/decrypt operations done + with this instance. + """ + + + # Permutation and translation tables for DES + __pc1 = [56, 48, 40, 32, 24, 16, 8, + 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, + 18, 10, 2, 59, 51, 43, 35, + 62, 54, 46, 38, 30, 22, 14, + 6, 61, 53, 45, 37, 29, 21, + 13, 5, 60, 52, 44, 36, 28, + 20, 12, 4, 27, 19, 11, 3 + ] + + # number left rotations of pc1 + __left_rotations = [ + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 + ] + + # permuted choice key (table 2) + __pc2 = [ + 13, 16, 10, 23, 0, 4, + 2, 27, 14, 5, 20, 9, + 22, 18, 11, 3, 25, 7, + 15, 6, 26, 19, 12, 1, + 40, 51, 30, 36, 46, 54, + 29, 39, 50, 44, 32, 47, + 43, 48, 38, 55, 33, 52, + 45, 41, 49, 35, 28, 31 + ] + + # initial permutation IP + __ip = [57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7, + 56, 48, 40, 32, 24, 16, 8, 0, + 58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6 + ] + + # Expansion table for turning 32 bit blocks into 48 bits + __expansion_table = [ + 31, 0, 1, 2, 3, 4, + 3, 4, 5, 6, 7, 8, + 7, 8, 9, 10, 11, 12, + 11, 12, 13, 14, 15, 16, + 15, 16, 17, 18, 19, 20, + 19, 20, 21, 22, 23, 24, + 23, 24, 25, 26, 27, 28, + 27, 28, 29, 30, 31, 0 + ] + + # The (in)famous S-boxes + __sbox = [ + # S1 + [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13], + + # S2 + [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9], + + # S3 + [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12], + + # S4 + [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14], + + # S5 + [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3], + + # S6 + [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13], + + # S7 + [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12], + + # S8 + [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11], + ] + + + # 32-bit permutation function P used on the output of the S-boxes + __p = [ + 15, 6, 19, 20, 28, 11, + 27, 16, 0, 14, 22, 25, + 4, 17, 30, 9, 1, 7, + 23,13, 31, 26, 2, 8, + 18, 12, 29, 5, 21, 10, + 3, 24 + ] + + # final permutation IP^-1 + __fp = [ + 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, + 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, + 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, + 33, 1, 41, 9, 49, 17, 57, 25, + 32, 0, 40, 8, 48, 16, 56, 24 + ] + + # Type of crypting being done + ENCRYPT = 0x00 + DECRYPT = 0x01 + + # Initialisation + def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): + # Sanity checking of arguments. + if len(key) != 8: + raise ValueError("Invalid DES key size. Key must be exactly 8 bytes long.") + _baseDes.__init__(self, mode, IV, pad, padmode) + self.key_size = 8 + + self.L = [] + self.R = [] + self.Kn = [ [0] * 48 ] * 16 # 16 48-bit keys (K1 - K16) + self.final = [] + + self.setKey(key) + + def setKey(self, key): + """Will set the crypting key for this object. Must be 8 bytes.""" + _baseDes.setKey(self, key) + self.__create_sub_keys() + + def __String_to_BitList(self, data): + """Turn the string data, into a list of bits (1, 0)'s""" + if _pythonMajorVersion < 3: + # Turn the strings into integers. Python 3 uses a bytes + # class, which already has this behaviour. + data = [ord(c) for c in data] + l = len(data) * 8 + result = [0] * l + pos = 0 + for ch in data: + i = 7 + while i >= 0: + if ch & (1 << i) != 0: + result[pos] = 1 + else: + result[pos] = 0 + pos += 1 + i -= 1 + + return result + + def __BitList_to_String(self, data): + """Turn the list of bits -> data, into a string""" + result = [] + pos = 0 + c = 0 + while pos < len(data): + c += data[pos] << (7 - (pos % 8)) + if (pos % 8) == 7: + result.append(c) + c = 0 + pos += 1 + + if _pythonMajorVersion < 3: + return ''.join([ chr(c) for c in result ]) + else: + return bytes(result) + + def __permutate(self, table, block): + """Permutate this block with the specified table""" + return list(map(lambda x: block[x], table)) + + # Transform the secret key, so that it is ready for data processing + # Create the 16 subkeys, K[1] - K[16] + def __create_sub_keys(self): + """Create the 16 subkeys K[1] to K[16] from the given key""" + key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey())) + i = 0 + # Split into Left and Right sections + self.L = key[:28] + self.R = key[28:] + while i < 16: + j = 0 + # Perform circular left shifts + while j < des.__left_rotations[i]: + self.L.append(self.L[0]) + del self.L[0] + + self.R.append(self.R[0]) + del self.R[0] + + j += 1 + + # Create one of the 16 subkeys through pc2 permutation + self.Kn[i] = self.__permutate(des.__pc2, self.L + self.R) + + i += 1 + + # Main part of the encryption algorithm, the number cruncher :) + def __des_crypt(self, block, crypt_type): + """Crypt the block of data through DES bit-manipulation""" + block = self.__permutate(des.__ip, block) + self.L = block[:32] + self.R = block[32:] + + # Encryption starts from Kn[1] through to Kn[16] + if crypt_type == des.ENCRYPT: + iteration = 0 + iteration_adjustment = 1 + # Decryption starts from Kn[16] down to Kn[1] + else: + iteration = 15 + iteration_adjustment = -1 + + i = 0 + while i < 16: + # Make a copy of R[i-1], this will later become L[i] + tempR = self.R[:] + + # Permutate R[i - 1] to start creating R[i] + self.R = self.__permutate(des.__expansion_table, self.R) + + # Exclusive or R[i - 1] with K[i], create B[1] to B[8] whilst here + self.R = list(map(lambda x, y: x ^ y, self.R, self.Kn[iteration])) + B = [self.R[:6], self.R[6:12], self.R[12:18], self.R[18:24], self.R[24:30], self.R[30:36], self.R[36:42], self.R[42:]] + # Optimization: Replaced below commented code with above + #j = 0 + #B = [] + #while j < len(self.R): + # self.R[j] = self.R[j] ^ self.Kn[iteration][j] + # j += 1 + # if j % 6 == 0: + # B.append(self.R[j-6:j]) + + # Permutate B[1] to B[8] using the S-Boxes + j = 0 + Bn = [0] * 32 + pos = 0 + while j < 8: + # Work out the offsets + m = (B[j][0] << 1) + B[j][5] + n = (B[j][1] << 3) + (B[j][2] << 2) + (B[j][3] << 1) + B[j][4] + + # Find the permutation value + v = des.__sbox[j][(m << 4) + n] + + # Turn value into bits, add it to result: Bn + Bn[pos] = (v & 8) >> 3 + Bn[pos + 1] = (v & 4) >> 2 + Bn[pos + 2] = (v & 2) >> 1 + Bn[pos + 3] = v & 1 + + pos += 4 + j += 1 + + # Permutate the concatination of B[1] to B[8] (Bn) + self.R = self.__permutate(des.__p, Bn) + + # Xor with L[i - 1] + self.R = list(map(lambda x, y: x ^ y, self.R, self.L)) + # Optimization: This now replaces the below commented code + #j = 0 + #while j < len(self.R): + # self.R[j] = self.R[j] ^ self.L[j] + # j += 1 + + # L[i] becomes R[i - 1] + self.L = tempR + + i += 1 + iteration += iteration_adjustment + + # Final permutation of R[16]L[16] + self.final = self.__permutate(des.__fp, self.R + self.L) + return self.final + + + # Data to be encrypted/decrypted + def crypt(self, data, crypt_type): + """Crypt the data in blocks, running it through des_crypt()""" + + # Error check the data + if not data: + return '' + if len(data) % self.block_size != 0: + if crypt_type == des.DECRYPT: # Decryption must work on 8 byte blocks + raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n.") + if not self.getPadding(): + raise ValueError("Invalid data length, data must be a multiple of " + str(self.block_size) + " bytes\n. Try setting the optional padding character") + else: + data += (self.block_size - (len(data) % self.block_size)) * self.getPadding() + # print "Len of data: %f" % (len(data) / self.block_size) + + if self.getMode() == CBC: + if self.getIV(): + iv = self.__String_to_BitList(self.getIV()) + else: + raise ValueError("For CBC mode, you must supply the Initial Value (IV) for ciphering") + + # Split the data into blocks, crypting each one seperately + i = 0 + dict = {} + result = [] + #cached = 0 + #lines = 0 + while i < len(data): + # Test code for caching encryption results + #lines += 1 + #if dict.has_key(data[i:i+8]): + #print "Cached result for: %s" % data[i:i+8] + # cached += 1 + # result.append(dict[data[i:i+8]]) + # i += 8 + # continue + + block = self.__String_to_BitList(data[i:i+8]) + + # Xor with IV if using CBC mode + if self.getMode() == CBC: + if crypt_type == des.ENCRYPT: + block = list(map(lambda x, y: x ^ y, block, iv)) + #j = 0 + #while j < len(block): + # block[j] = block[j] ^ iv[j] + # j += 1 + + processed_block = self.__des_crypt(block, crypt_type) + + if crypt_type == des.DECRYPT: + processed_block = list(map(lambda x, y: x ^ y, processed_block, iv)) + #j = 0 + #while j < len(processed_block): + # processed_block[j] = processed_block[j] ^ iv[j] + # j += 1 + iv = block + else: + iv = processed_block + else: + processed_block = self.__des_crypt(block, crypt_type) + + + # Add the resulting crypted block to our list + #d = self.__BitList_to_String(processed_block) + #result.append(d) + result.append(self.__BitList_to_String(processed_block)) + #dict[data[i:i+8]] = d + i += 8 + + # print "Lines: %d, cached: %d" % (lines, cached) + + # Return the full crypted string + if _pythonMajorVersion < 3: + return ''.join(result) + else: + return bytes.fromhex('').join(result) + + def encrypt(self, data, pad=None, padmode=None): + """encrypt(data, [pad], [padmode]) -> bytes + + data : Bytes to be encrypted + pad : Optional argument for encryption padding. Must only be one byte + padmode : Optional argument for overriding the padding mode. + + The data must be a multiple of 8 bytes and will be encrypted + with the already specified key. Data does not have to be a + multiple of 8 bytes if the padding character is supplied, or + the padmode is set to PAD_PKCS5, as bytes will then added to + ensure the be padded data is a multiple of 8 bytes. + """ + data = self._guardAgainstUnicode(data) + if pad is not None: + pad = self._guardAgainstUnicode(pad) + data = self._padData(data, pad, padmode) + return self.crypt(data, des.ENCRYPT) + + def decrypt(self, data, pad=None, padmode=None): + """decrypt(data, [pad], [padmode]) -> bytes + + data : Bytes to be decrypted + pad : Optional argument for decryption padding. Must only be one byte + padmode : Optional argument for overriding the padding mode. + + The data must be a multiple of 8 bytes and will be decrypted + with the already specified key. In PAD_NORMAL mode, if the + optional padding character is supplied, then the un-encrypted + data will have the padding characters removed from the end of + the bytes. This pad removal only occurs on the last 8 bytes of + the data (last data block). In PAD_PKCS5 mode, the special + padding end markers will be removed from the data after decrypting. + """ + data = self._guardAgainstUnicode(data) + if pad is not None: + pad = self._guardAgainstUnicode(pad) + data = self.crypt(data, des.DECRYPT) + return self._unpadData(data, pad, padmode) + + + +############################################################################# +# Triple DES # +############################################################################# +class triple_des(_baseDes): + """Triple DES encryption/decrytpion class + + This algorithm uses the DES-EDE3 (when a 24 byte key is supplied) or + the DES-EDE2 (when a 16 byte key is supplied) encryption methods. + Supports ECB (Electronic Code Book) and CBC (Cypher Block Chaining) modes. + + pyDes.des(key, [mode], [IV]) + + key -> Bytes containing the encryption key, must be either 16 or + 24 bytes long + mode -> Optional argument for encryption type, can be either pyDes.ECB + (Electronic Code Book), pyDes.CBC (Cypher Block Chaining) + IV -> Optional Initial Value bytes, must be supplied if using CBC mode. + Must be 8 bytes in length. + pad -> Optional argument, set the pad character (PAD_NORMAL) to use + during all encrypt/decrypt operations done with this instance. + padmode -> Optional argument, set the padding mode (PAD_NORMAL or + PAD_PKCS5) to use during all encrypt/decrypt operations done + with this instance. + """ + def __init__(self, key, mode=ECB, IV=None, pad=None, padmode=PAD_NORMAL): + _baseDes.__init__(self, mode, IV, pad, padmode) + self.setKey(key) + + def setKey(self, key): + """Will set the crypting key for this object. Either 16 or 24 bytes long.""" + self.key_size = 24 # Use DES-EDE3 mode + if len(key) != self.key_size: + if len(key) == 16: # Use DES-EDE2 mode + self.key_size = 16 + else: + raise ValueError("Invalid triple DES key size. Key must be either 16 or 24 bytes long") + if self.getMode() == CBC: + if not self.getIV(): + # Use the first 8 bytes of the key + self._iv = key[:self.block_size] + if len(self.getIV()) != self.block_size: + raise ValueError("Invalid IV, must be 8 bytes in length") + self.__key1 = des(key[:8], self._mode, self._iv, + self._padding, self._padmode) + self.__key2 = des(key[8:16], self._mode, self._iv, + self._padding, self._padmode) + if self.key_size == 16: + self.__key3 = self.__key1 + else: + self.__key3 = des(key[16:], self._mode, self._iv, + self._padding, self._padmode) + _baseDes.setKey(self, key) + + # Override setter methods to work on all 3 keys. + + def setMode(self, mode): + """Sets the type of crypting mode, pyDes.ECB or pyDes.CBC""" + _baseDes.setMode(self, mode) + for key in (self.__key1, self.__key2, self.__key3): + key.setMode(mode) + + def setPadding(self, pad): + """setPadding() -> bytes of length 1. Padding character.""" + _baseDes.setPadding(self, pad) + for key in (self.__key1, self.__key2, self.__key3): + key.setPadding(pad) + + def setPadMode(self, mode): + """Sets the type of padding mode, pyDes.PAD_NORMAL or pyDes.PAD_PKCS5""" + _baseDes.setPadMode(self, mode) + for key in (self.__key1, self.__key2, self.__key3): + key.setPadMode(mode) + + def setIV(self, IV): + """Will set the Initial Value, used in conjunction with CBC mode""" + _baseDes.setIV(self, IV) + for key in (self.__key1, self.__key2, self.__key3): + key.setIV(IV) + + def encrypt(self, data, pad=None, padmode=None): + """encrypt(data, [pad], [padmode]) -> bytes + + data : bytes to be encrypted + pad : Optional argument for encryption padding. Must only be one byte + padmode : Optional argument for overriding the padding mode. + + The data must be a multiple of 8 bytes and will be encrypted + with the already specified key. Data does not have to be a + multiple of 8 bytes if the padding character is supplied, or + the padmode is set to PAD_PKCS5, as bytes will then added to + ensure the be padded data is a multiple of 8 bytes. + """ + ENCRYPT = des.ENCRYPT + DECRYPT = des.DECRYPT + data = self._guardAgainstUnicode(data) + if pad is not None: + pad = self._guardAgainstUnicode(pad) + # Pad the data accordingly. + data = self._padData(data, pad, padmode) + if self.getMode() == CBC: + self.__key1.setIV(self.getIV()) + self.__key2.setIV(self.getIV()) + self.__key3.setIV(self.getIV()) + i = 0 + result = [] + while i < len(data): + block = self.__key1.crypt(data[i:i+8], ENCRYPT) + block = self.__key2.crypt(block, DECRYPT) + block = self.__key3.crypt(block, ENCRYPT) + self.__key1.setIV(block) + self.__key2.setIV(block) + self.__key3.setIV(block) + result.append(block) + i += 8 + if _pythonMajorVersion < 3: + return ''.join(result) + else: + return bytes.fromhex('').join(result) + else: + data = self.__key1.crypt(data, ENCRYPT) + data = self.__key2.crypt(data, DECRYPT) + return self.__key3.crypt(data, ENCRYPT) + + def decrypt(self, data, pad=None, padmode=None): + """decrypt(data, [pad], [padmode]) -> bytes + + data : bytes to be encrypted + pad : Optional argument for decryption padding. Must only be one byte + padmode : Optional argument for overriding the padding mode. + + The data must be a multiple of 8 bytes and will be decrypted + with the already specified key. In PAD_NORMAL mode, if the + optional padding character is supplied, then the un-encrypted + data will have the padding characters removed from the end of + the bytes. This pad removal only occurs on the last 8 bytes of + the data (last data block). In PAD_PKCS5 mode, the special + padding end markers will be removed from the data after + decrypting, no pad character is required for PAD_PKCS5. + """ + ENCRYPT = des.ENCRYPT + DECRYPT = des.DECRYPT + data = self._guardAgainstUnicode(data) + if pad is not None: + pad = self._guardAgainstUnicode(pad) + if self.getMode() == CBC: + self.__key1.setIV(self.getIV()) + self.__key2.setIV(self.getIV()) + self.__key3.setIV(self.getIV()) + i = 0 + result = [] + while i < len(data): + iv = data[i:i+8] + block = self.__key3.crypt(iv, DECRYPT) + block = self.__key2.crypt(block, ENCRYPT) + block = self.__key1.crypt(block, DECRYPT) + self.__key1.setIV(iv) + self.__key2.setIV(iv) + self.__key3.setIV(iv) + result.append(block) + i += 8 + if _pythonMajorVersion < 3: + data = ''.join(result) + else: + data = bytes.fromhex('').join(result) + else: + data = self.__key3.crypt(data, DECRYPT) + data = self.__key2.crypt(data, ENCRYPT) + data = self.__key1.crypt(data, DECRYPT) + return self._unpadData(data, pad, padmode) diff --git a/python/DES-20221006/setup.cfg b/python/DES-20221006/setup.cfg new file mode 100644 index 0000000..79bc678 --- /dev/null +++ b/python/DES-20221006/setup.cfg @@ -0,0 +1,5 @@ +[bdist_wheel] +# This flag says that the code is written to work on both Python 2 and Python +# 3. If at all possible, it is good practice to do this. If you cannot, you +# will need to generate wheels for each Python version that you support. +universal=1 diff --git a/python/DES-20221006/setup.py b/python/DES-20221006/setup.py new file mode 100644 index 0000000..be6d946 --- /dev/null +++ b/python/DES-20221006/setup.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +from distutils.core import setup + +setup(name="pyDes", + version="2.0.1", + description="Pure python implementation of DES and TRIPLE DES encryption algorithm", + author="Todd Whiteman", + author_email="twhitema@gmail.com", + license='MIT', + url="http://twhiteman.netfirms.com/des.html", + classifiers=[ + 'Development Status :: 6 - Mature' + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 3', + 'Topic :: Security :: Cryptography', + ], + platforms=["All"], + keywords=["DES", "TRIPLE-DES", "ENCRYPTION", "ALGORITHM", "SECURITY"], + py_modules=["pyDes"] +) diff --git a/python/DES-20221006/test.py b/python/DES-20221006/test.py new file mode 100644 index 0000000..ed97b6f --- /dev/null +++ b/python/DES-20221006/test.py @@ -0,0 +1,24 @@ +from pyDes import * +from binascii import unhexlify as unhex + +if __name__ == '__main__': + f = open("Image1.bmp", "rb") + head = f.read(62) + image = f.read() + f.close() + + k1 = des(unhex("133457799BBCDFF1"), ECB, padmode=PAD_PKCS5) + k2 = des(unhex("133457799BBCDFF1"), CBC, IV=b'01110010', padmode=PAD_PKCS5) + + e1 = k1.encrypt(image) + e2 = k2.encrypt(image) + + with open("Image2.bmp", "wb") as f: + f.write(head) + f.write(e1) + + with open("Image3.bmp", "wb") as f: + f.write(head) + f.write(e2) + + # d1 = k1.decrypt(e1) diff --git a/python/DES-20221006/test_pydes.py b/python/DES-20221006/test_pydes.py new file mode 100644 index 0000000..5734c5e --- /dev/null +++ b/python/DES-20221006/test_pydes.py @@ -0,0 +1,259 @@ +from pyDes import * + +############################################################################# +# Examples # +############################################################################# +def _example_triple_des_(): + from time import time + + # Utility module + from binascii import unhexlify as unhex + + # example shows triple-des encryption using the des class + print ("Example of triple DES encryption in default ECB mode (DES-EDE3)\n") + + print ("Triple des using the des class (3 times)") + t = time() + k1 = des(unhex("133457799BBCDFF1")) + k2 = des(unhex("1122334455667788")) + k3 = des(unhex("77661100DD223311")) + d = "Triple DES test string, to be encrypted and decrypted..." + print ("Key1: %r" % k1.getKey()) + print ("Key2: %r" % k2.getKey()) + print ("Key3: %r" % k3.getKey()) + print ("Data: %r" % d) + + e1 = k1.encrypt(d) + e2 = k2.decrypt(e1) + e3 = k3.encrypt(e2) + print ("Encrypted: %r" % e3) + + d3 = k3.decrypt(e3) + d2 = k2.encrypt(d3) + d1 = k1.decrypt(d2) + print ("Decrypted: %r" % d1) + print ("DES time taken: %f (%d crypt operations)" % (time() - t, 6 * (len(d) / 8))) + print ("") + + # Example below uses the triple-des class to achieve the same as above + print ("Now using triple des class") + t = time() + t1 = triple_des(unhex("133457799BBCDFF1112233445566778877661100DD223311")) + print ("Key: %r" % t1.getKey()) + print ("Data: %r" % d) + + td1 = t1.encrypt(d) + print ("Encrypted: %r" % td1) + + td2 = t1.decrypt(td1) + print ("Decrypted: %r" % td2) + + print ("Triple DES time taken: %f (%d crypt operations)" % (time() - t, 6 * (len(d) / 8))) + +def _example_des_(): + from time import time + + # example of DES encrypting in CBC mode with the IV of "\0\0\0\0\0\0\0\0" + print ("Example of DES encryption using CBC mode\n") + t = time() + k = des("DESCRYPT", CBC, "\0\0\0\0\0\0\0\0") + data = "DES encryption algorithm" + print ("Key : %r" % k.getKey()) + print ("Data : %r" % data) + + d = k.encrypt(data) + print ("Encrypted: %r" % d) + + d = k.decrypt(d) + print ("Decrypted: %r" % d) + print ("DES time taken: %f (6 crypt operations)" % (time() - t)) + print ("") + +def _filetest_(): + from time import time + + f = open("pyDes.py", "rb+") + d = f.read() + f.close() + + t = time() + k = des("MyDESKey") + + d = k.encrypt(d, " ") + f = open("pyDes.py.enc", "wb+") + f.write(d) + f.close() + + d = k.decrypt(d, " ") + f = open("pyDes.py.dec", "wb+") + f.write(d) + f.close() + print ("DES file test time: %f" % (time() - t)) + +def _profile_(): + try: + import cProfile as profile + except: + import profile + profile.run('_fulltest_()') + #profile.run('_filetest_()') + +def _fulltest_(): + # This should not produce any unexpected errors or exceptions + from time import time + from binascii import unhexlify as unhex + from binascii import hexlify as dohex + + t = time() + + data = "DES encryption algorithm".encode('ascii') + k = des("\0\0\0\0\0\0\0\0", CBC, "\0\0\0\0\0\0\0\0") + d = k.encrypt(data) + if k.decrypt(d) != data: + print ("Test 1: Error: decrypt does not match. %r != %r" % (data, k.decrypt(d))) + else: + print ("Test 1: Successful") + + data = "Default string of text".encode('ascii') + k = des("\0\0\0\0\0\0\0\0", CBC, "\0\0\0\0\0\0\0\0") + d = k.encrypt(data, "*") + if k.decrypt(d, "*") != data: + print ("Test 2: Error: decrypt does not match. %r != %r" % (data, k.decrypt(d))) + else: + print ("Test 2: Successful") + + data = "String to Pad".encode('ascii') + k = des("\r\n\tABC\r\n") + d = k.encrypt(data, "*") + if k.decrypt(d, "*") != data: + print ("Test 3: Error: decrypt does not match. %r != %r" % (data, k.decrypt(d))) + else: + print ("Test 3: Successful") + + k = des("\r\n\tABC\r\n") + d = k.encrypt(unhex("000102030405060708FF8FDCB04080"), unhex("44")) + if k.decrypt(d, unhex("44")) != unhex("000102030405060708FF8FDCB04080"): + print ("Test 4a: Error: Unencypted data block does not match start data") + elif k.decrypt(d) != unhex("000102030405060708FF8FDCB0408044"): + print ("Test 4b: Error: Unencypted data block does not match start data") + else: + print ("Test 4: Successful") + + data = "String to Pad".encode('ascii') + k = des("\r\n\tkey\r\n") + d = k.encrypt(data, padmode=PAD_PKCS5) + if k.decrypt(d, padmode=PAD_PKCS5) != data: + print ("Test 5a: Error: decrypt does not match. %r != %r" % (data, k.decrypt(d))) + # Try same with padmode set on the class instance. + k = des("\r\n\tkey\r\n", padmode=PAD_PKCS5) + d = k.encrypt(data) + if k.decrypt(d) != data: + print ("Test 5b: Error: decrypt does not match. %r != %r" % (data, k.decrypt(d))) + else: + print ("Test 5: Successful") + + k = triple_des("MyDesKey\r\n\tABC\r\n0987*543") + d = k.encrypt(unhex("000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080")) + if k.decrypt(d) != unhex("000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080"): + print ("Test 6: Error: Unencypted data block does not match start data") + else: + print ("Test 6: Successful") + + k = triple_des("\r\n\tABC\r\n0987*543") + d = k.encrypt(unhex("000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080")) + if k.decrypt(d) != unhex("000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080"): + print ("Test 7: Error: Unencypted data block does not match start data") + else: + print ("Test 7: Successful") + + k = triple_des("MyDesKey\r\n\tABC\r\n0987*54B", CBC, "12341234") + d = k.encrypt(unhex("000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080")) + if k.decrypt(d) != unhex("000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080"): + print ("Test 8: Error: Triple DES CBC failed.") + else: + print ("Test 8: Successful") + + k = triple_des("MyDesKey\r\n\tABC\r\n0987*54B", CBC, "12341234") + d = k.encrypt(unhex("000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDC"), '.') + if k.decrypt(d, '.') != unhex("000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDCB04080000102030405060708FF8FDC"): + print ("Test 9: Error: Triple DES CBC with padding failed.") + else: + print ("Test 9: Successful") + + k = triple_des("\r\n\tkey\rIsGoodKey") + data = "String to Pad".encode('ascii') + d = k.encrypt(data, padmode=PAD_PKCS5) + if k.decrypt(d, padmode=PAD_PKCS5) != data: + print ("Test 10: Error: decrypt does not match. %r != %r" % (data, k.decrypt(d))) + else: + print ("Test 10: Successful") + + k = triple_des("\r\n\tkey\rIsGoodKey") + data = "String not need Padding.".encode('ascii') + d = k.encrypt(data, padmode=PAD_PKCS5) + if k.decrypt(d, padmode=PAD_PKCS5) != data: + print ("Test 11: Error: decrypt does not match. %r != %r" % (data, k.decrypt(d))) + else: + print ("Test 11: Successful") + + # Test PAD_PKCS5 with CBC encryption mode. + + k = des("IGoodKey", mode=CBC, IV="\0\1\2\3\4\5\6\7") + data = "String to Pad".encode('ascii') + d = k.encrypt(data, padmode=PAD_PKCS5) + if k.decrypt(d, padmode=PAD_PKCS5) != data: + print ("Test 12: Error: decrypt does not match. %r != %r" % (data, k.decrypt(d))) + else: + print ("Test 12: Successful") + + k = des("IGoodKey", mode=CBC, IV="\0\1\2\3\4\5\6\7") + data = "String not need Padding.".encode('ascii') + d = k.encrypt(data, padmode=PAD_PKCS5) + if k.decrypt(d, padmode=PAD_PKCS5) != data: + print ("Test 13: Error: decrypt does not match. %r != %r" % (data, k.decrypt(d))) + else: + print ("Test 13: Successful") + + k = triple_des("\r\n\tkey\rIsGoodKey", mode=CBC, IV="\0\1\2\3\4\5\6\7") + data = "String to Pad".encode('ascii') + d = k.encrypt(data, padmode=PAD_PKCS5) + if k.decrypt(d, padmode=PAD_PKCS5) != data: + print ("Test 14: Error: decrypt does not match. %r != %r" % (data, k.decrypt(d))) + else: + print ("Test 14: Successful") + + k = triple_des("\r\n\tkey\rIsGoodKey", mode=CBC, IV="\0\1\2\3\4\5\6\7") + data = "String not need Padding.".encode('ascii') + d = k.encrypt(data, padmode=PAD_PKCS5) + if k.decrypt(d, padmode=PAD_PKCS5) != data: + print ("Test 15: Error: decrypt does not match. %r != %r" % (data, k.decrypt(d))) + else: + print ("Test 15: Successful") + + k = triple_des("\r\n\tkey\rIsGoodKey", mode=CBC, IV="\0\1\2\3\4\5\6\7", padmode=PAD_PKCS5) + data = "String to Pad".encode('ascii') + d = k.encrypt(data) + if k.decrypt(d) != data: + print ("Test 16: Error: decrypt does not match. %r != %r" % (data, k.decrypt(d))) + else: + print ("Test 16: Successful") + + # Ensure no error occurs when creating an instance with no IV yet set, + # test supplied by "Yoav Aner". + k = triple_des("\0" * 24, mode=CBC, pad=None, padmode=PAD_PKCS5) + data = "String to Pad".encode('ascii') + d = k.encrypt(data) + if k.decrypt(d) != data: + print ("Test 17: Error: decrypt does not match. %r != %r" % (data, k.decrypt(d))) + else: + print ("Test 17: Successful") + + print ("") + print ("Total time taken: %f" % (time() - t)) + +if __name__ == '__main__': + #_example_des_() + #_example_triple_des_() + _fulltest_() + #_filetest_() + #_profile_() diff --git a/python/des/DES-AES_test..txt b/python/des/DES-AES_test..txt new file mode 100644 index 0000000..ba0a4e1 --- /dev/null +++ b/python/des/DES-AES_test..txt @@ -0,0 +1,37 @@ +DES +Plaintext: 02468aceeca86420 +Key: 0f1571c947d9e859 +Ciphertext: da02ce3a89ecac3b + + +Change the 4th bit in the plaintext +Plaintext: 12468aceeca86420 +Key: 0f1571c947d9e859 +Ciphertext: 057cde97d7683f2a => 38e8c912b2bf81ef + + +Change the 4th bit in the key +Plaintext: 02468aceeca86420 +Key: 1f1571c947d9e859 +Ciphertext: ee92b50606b62b0b => eae5cedbb5fd55f8 + +Weak keys + + + +AES +Plaintext: 0123456789abcdeffedcba9876543210 +Key: 0f1571c947d9e8590cb7add6af7f6798 +Ciphertext: + + +Change of the 8th bit in the plaintext +Plaintext: 0023456789abcdeffedcba9876543210 +Key: 0f1571c947d9e8590cb7add6af7f6798 +Ciphertext: + + +Change of the 8th bit in the key +Plaintext: 0123456789abcdeffedcba9876543210 +Key: 0e1571c947d9e8590cb7add6af7f6798 +Ciphertext: \ No newline at end of file diff --git a/python/des/README.md b/python/des/README.md new file mode 100644 index 0000000..8875c91 --- /dev/null +++ b/python/des/README.md @@ -0,0 +1,47 @@ +pydes +===== + +Basic but pure DES implementation in Python +I have written it for fun because nothing else. + + +How it works ? +-------------- + +Everything is made within a class called "des". This class can be instanciated once and used to cipher and decipher multiple datas. +It also support padding using the PKCS5 specification. (So the data is padding even if it is multiple of 8 to be sure that the last byte il be padding data). +The generation of all the keys used is made in the method generatekeys and substitute apply the SBOX permutation. +The main method is run which is called by both encrypt and decrypt but in a different mode. This method do basically all the stuff, it loop +throught all the blocks and for each do the 16th rounds. + +Be careful: This module implement DES in ECB mode, so you can't make it weaker. I didn't made it to be strong but for fun. + +How to use it ? +--------------- + +I have not done any interface to take argument in command line so this module can't be used as a script. (feel free to modify it). +To use it from python shell or in another module do: + + from pydes import des + + key = "secret_k" + text= "Hello wo" + d = des() + ciphered = d.encrypt(key,text) + plain = d.decrypt(key,ciphered) + print "Ciphered: %r" % ciphered + print "Deciphered: ", plain + +Note: In this exemple no padding is specified so you have to provide a text which is multiple of 8 bytes. The key is cut to 8 bytes if longer. + +To use padding: + + from pydes import des + + key = "secret_k" + text= "Hello world !" + d = des() + ciphered = d.encrypt(key,text,padding=True) #Or just True in third arg + plain = d.decrypt(key,ciphered,padding=True) + print "Ciphered: %r" % ciphered + print "Deciphered: ", plain diff --git a/python/des/pydes.py b/python/des/pydes.py new file mode 100644 index 0000000..964eabc --- /dev/null +++ b/python/des/pydes.py @@ -0,0 +1,270 @@ +# -*- coding: utf8 -*- +import binascii + +# Initial permut matrix for the datas +PI = [58, 50, 42, 34, 26, 18, 10, 2, + 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, + 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, + 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, + 63, 55, 47, 39, 31, 23, 15, 7] + +# Initial permut made on the key +CP_1 = [57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4] + +# Permut applied on shifted key to get Ki+1 +CP_2 = [14, 17, 11, 24, 1, 5, 3, 28, + 15, 6, 21, 10, 23, 19, 12, 4, + 26, 8, 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, 30, 40, + 51, 45, 33, 48, 44, 49, 39, 56, + 34, 53, 46, 42, 50, 36, 29, 32] + +# Expand matrix to get a 48bits matrix of datas to apply the xor with Ki +E = [32, 1, 2, 3, 4, 5, + 4, 5, 6, 7, 8, 9, + 8, 9, 10, 11, 12, 13, + 12, 13, 14, 15, 16, 17, + 16, 17, 18, 19, 20, 21, + 20, 21, 22, 23, 24, 25, + 24, 25, 26, 27, 28, 29, + 28, 29, 30, 31, 32, 1] + +# SBOX +S_BOX = [ + + [[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7], + [0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8], + [4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0], + [15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13], + ], + + [[15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10], + [3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5], + [0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15], + [13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9], + ], + + [[10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8], + [13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1], + [13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7], + [1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12], + ], + + [[7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15], + [13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9], + [10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4], + [3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14], + ], + + [[2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9], + [14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6], + [4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14], + [11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3], + ], + + [[12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11], + [10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8], + [9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6], + [4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13], + ], + + [[4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1], + [13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6], + [1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2], + [6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12], + ], + + [[13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7], + [1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2], + [7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8], + [2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11], + ] +] + +# Permut made after each SBox substitution for each round +P = [16, 7, 20, 21, 29, 12, 28, 17, + 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, + 19, 13, 30, 6, 22, 11, 4, 25] + +# Final permut for datas after the 16 rounds +PI_1 = [40, 8, 48, 16, 56, 24, 64, 32, + 39, 7, 47, 15, 55, 23, 63, 31, + 38, 6, 46, 14, 54, 22, 62, 30, + 37, 5, 45, 13, 53, 21, 61, 29, + 36, 4, 44, 12, 52, 20, 60, 28, + 35, 3, 43, 11, 51, 19, 59, 27, + 34, 2, 42, 10, 50, 18, 58, 26, + 33, 1, 41, 9, 49, 17, 57, 25] + +# Matrix that determine the shift for each round of keys +SHIFT = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1] + + +def string_to_bit_array(text): # Convert a string into a list of bits + array = list() + for char in text: + binval = binvalue(char, 8) # Get the char value on one byte + array.extend([int(x) for x in list(binval)]) # Add the bits to the final list + return array + + +def bit_array_to_string(array): # Recreate the string from the bit array + res = ''.join([chr(int(y, 2)) for y in [''.join([str(x) for x in bytes]) for bytes in nsplit(array, 8)]]) + return res + + +def binvalue(val, bitsize): # Return the binary value as a string of the given size + binval = bin(val)[2:] if isinstance(val, int) else bin(ord(val))[2:] + if len(binval) > bitsize: + raise "binary value larger than the expected size" + while len(binval) < bitsize: + binval = "0" + binval # Add as many 0 as needed to get the wanted size + return binval + + +def nsplit(s, n): # Split a list into sublists of size "n" + return [s[k:k + n] for k in range(0, len(s), n)] + + +ENCRYPT = 1 +DECRYPT = 0 + + +class des(): + def __init__(self): + self.password = None + self.text = None + self.keys = list() + + def run(self, key, text, action=ENCRYPT, padding=False): + if len(key) < 8: + raise "Key Should be 8 bytes long" + elif len(key) > 8: + key = key[:8] # If key size is above 8bytes, cut to be 8bytes long + + self.password = key + self.text = text + + if padding and action == ENCRYPT: + self.addPadding() + elif len(self.text) % 8 != 0: # If not padding specified data size must be multiple of 8 bytes + raise "Data size should be multiple of 8" + + self.generatekeys() # Generate all the keys + text_blocks = nsplit(self.text, 8) # Split the text in blocks of 8 bytes so 64 bits + result = list() + for block in text_blocks: # Loop over all the blocks of data + block = string_to_bit_array(block) # Convert the block in bit array + block = self.permut(block, PI) # Apply the initial permutation + g, d = nsplit(block, 32) # g(LEFT), d(RIGHT) + tmp = None + for i in range(16): # Do the 16 rounds + d_e = self.expand(d, E) # Expand d to match Ki size (48bits) + if action == ENCRYPT: + tmp = self.xor(self.keys[i], d_e) # If encrypt use Ki + else: + tmp = self.xor(self.keys[15 - i], d_e) # If decrypt start by the last key + tmp = self.substitute(tmp) # Method that will apply the SBOXes + tmp = self.permut(tmp, P) + tmp = self.xor(g, tmp) + g = d + d = tmp + print(g, end="") + print(d) + print(hex(int("".join(str(x) for x in g+d), 2))) + result += self.permut(d + g, PI_1) # Do the last permut and append the result to result + print(" -------- ") + print("Result : ",result) + print(hex(int("".join(str(x) for x in result), 2))) + print() + final_res = bit_array_to_string(result) + if padding and action == DECRYPT: + return self.removePadding(final_res) # Remove the padding if decrypt and padding is true + else: + return final_res # Return the final string of data ciphered/deciphered + + def substitute(self, d_e): # Substitute bytes using SBOX + subblocks = nsplit(d_e, 6) # Split bit array into sublist of 6 bits + result = list() + for i in range(len(subblocks)): # For all the sublists + block = subblocks[i] + row = int(str(block[0]) + str(block[5]), 2) # Get the row with the first and last bit + column = int(''.join([str(x) for x in block[1:][:-1]]), 2) # Column is the 2,3,4,5th bits + val = S_BOX[i][row][column] # Take the value in the SBOX appropriated for the round (i) + bin = binvalue(val, 4) # Convert the value to binary + result += [int(x) for x in bin] # And append it to the resulting list + return result + + def permut(self, block, table): # Permut the given block using the given table (so generic method) + return [block[x - 1] for x in table] + + def expand(self, block, table): # Do the exact same thing than permut but for more clarity has been renamed + return [block[x - 1] for x in table] + + def xor(self, t1, t2): # Apply a xor and return the resulting list + return [x ^ y for x, y in zip(t1, t2)] + + def generatekeys(self): # Algorithm that generates all the keys + self.keys = [] + key = string_to_bit_array(self.password) + key = self.permut(key, CP_1) # Apply the initial permut on the key + g, d = nsplit(key, 28) # Split it in to (g->LEFT),(d->RIGHT) + for i in range(16): # Apply the 16 rounds + g, d = self.shift(g, d, SHIFT[i]) # Apply the shift associated with the round (not always 1) + tmp = g + d # Merge them + self.keys.append(self.permut(tmp, CP_2)) # Apply the permut to get the Ki + + def shift(self, g, d, n): # Shift a list of the given value + return g[n:] + g[:n], d[n:] + d[:n] + + def addPadding(self): # Add padding to the datas using PKCS5 spec. + pad_len = 8 - (len(self.text) % 8) + self.text += pad_len * chr(pad_len) + + def removePadding(self, data): # Remove the padding of the plain text (it assume there is padding) + pad_len = ord(data[-1]) + return data[:-pad_len] + + def encrypt(self, key, text, padding=False): + return self.run(key, text, ENCRYPT, padding) + + def decrypt(self, key, text, padding=False): + return self.run(key, text, DECRYPT, padding) + + +def flip(txt, pos): + if len(text) > pos: + if txt[pos] == 1: + txt[pos] = 0 + else: + txt[pos] = 1 + + +if __name__ == '__main__': + + key_hex = b'02468aceeca86420' + key = binascii.unhexlify(key_hex) + + text_hex = b'0f1571c947d9e859' + text = binascii.unhexlify(text_hex) + print(text) + + d = des() + r = d.encrypt(key, text) + r2 = d.decrypt(key, r) + + print("Ciphered: %r" % r) + print("Ciphered: %r" % binascii.hexlify(r.encode('latin1'))) + print("Deciphered: %r" % r2) + print("Deciphered: %r" % binascii.hexlify(r2.encode('latin1'))) diff --git a/python/des/string_conversion_tools.py b/python/des/string_conversion_tools.py new file mode 100644 index 0000000..ccc33ad --- /dev/null +++ b/python/des/string_conversion_tools.py @@ -0,0 +1,268 @@ +# -*- coding: utf-8 -*- + +""" +Created on Sun Nov 3 15:30:59 2019 +This module converts string to hex, hex to string, ... + +@author: Patrice MEGRET +""" + +import binascii + + +def hex_to_string(h, show='y', coding='Latin-1'): + """Conversion from hex to string. + + Args: + h: hexa string + show: print conversion (default 'y') + coding: string coding (default 'Latin-1') + Returns: + h_s: string coded with coding + Internal: + h_by: h in byte + """ + h_by = binascii.unhexlify(h) + h_s = h_by.decode(coding) + if show == "y": + print("Hex = ", h, "Len = ", len(h)) + print("Bytes = ", h_by, "Len = ", len(h_by)) + print('{0} = {1}, Len = {2!r}'.format(coding, h_s, len(h_s))) + print('ASCII = {0!a}, Len = {1}'.format(h_s, len(h_s))) + print("\n") + return h_s + + +def hex_to_byte(h, show='y', coding='Latin-1'): + """Conversion from hex to byte. + + Args: + h: hexa string + show: print conversion (default 'y') + coding: string coding (default 'Latin-1') + Returns: + h_by: h in byte + """ + h_by = binascii.unhexlify(h) + if show == "y": + print("Hex = ", h, "Len = ", len(h)) + print("Bytes = ", h_by, "Len = ", len(h_by)) + print("\n") + return h_by + + +def string_to_hex(s, show="y", coding="Latin-1"): + """Conversion from string to hex. + + Args: + s: string + show: print conversion (default 'y') + coding: string coding (default 'Latin-1') + Returns: + s_h: string in hexa + Internal: + s_by: s in byte with coding + """ + s_by = s.encode(coding) + s_h = binascii.hexlify(s_by) + if show == "y": + print('ASCII = {0!a}, Len = {1}'.format(s, len(s))) + print('{0} = {1}, Len = {2!r}'.format(coding, s, len(s))) + print("Bytes = ", s_by, "Len = ", len(s_by)) + print("Hex = ", s_h, "Len = ", len(s_h)) + print("\n") + return s_h + + +def byte_to_hex(b, show="y", coding="Latin-1"): + """Conversion from byte to hex. + + Args: + b: bytes + show: print conversion (default 'y') + coding: string coding (default 'Latin-1') + Returns: + b_h: string in hexa + """ + b_h = binascii.hexlify(b) + if show == "y": + print("Bytes = ", b, "Len = ", len(b)) + print("Hex = ", b_h, "Len = ", len(b_h)) + print("\n") + return b_h + + +def hex_to_bin(h, show="y"): + """Conversion from hex to bin. + + Args: + h: hexa string + show: print conversion (default 'y') + Returns: + b: binary equivalent of h + """ + b = bin(int(h, 16)) # b is a string beginning with 0b + if show == "y": + print("Bin = ", b, "Len = ", len(b) - 2) + print('\n') + return b + + +def hex_to_bin_des(h, show="y"): + """Conversion from hex to bin and display 64 bits. + + Args: + h: hexa string + show: print conversion (default 'y') + Returns: + b: binary equivalent of h + """ + b = bin(int(h, 16)) # b is a string beginning with 0b + if show == "y": + print('Bin = {0:064b}'.format(int(h, 16))) + print('\n') + return b + + +def bin_to_hex(b, show="y"): + """Conversion bin to hex. + + Args: + b: binary string + show: print conversion (default 'y') + Returns: + h: hexa equivalent of h + """ + h = hex(int(b, 2)) # h is a string beginning with 0x + if show == "y": + print("Hex = ", h, "Len = ", len(h) - 2) + print('\n') + return h + + +def bin_to_hex_des(b, show="y"): + """Conversion bin to hex and display 16 hexa. + + Args: + b: binary string + show: print conversion (default 'y') + Returns: + h: hexa equivalent of h + """ + h = hex(int(b, 2)) # h is a string beginning with 0x + if show == "y": + print('Hex = {:016x}'.format(int(b, 2))) + print('\n') + return h + + +def xor_hex(h1_str, h2_str): + """Xor between two hexa strings. + + Args: + h1_str: hexa string 1 + h2_str: hexa string 2 + Returns: + print xor between the two hexa strings + """ + i1 = int(h1_str, 16) + i2 = int(h2_str, 16) + x = i1 ^ i2 + xb_str = bin(x) + n1 = xb_str.count('1') + print(xb_str, n1) + print('\n') + + +def binvalue(val, bitsize): + """Return the binary value as a string of a given size. + + Args: + val: integer or one character + bitsize: number of bits for the conversion + Returns: + binval: string of bitsize-length + """ + # bin returns a string beginning with 0b ==> extract [2:] to cut these two characters + + binval = bin(val)[2:] if isinstance(val, int) else bin(ord(val))[2:] + if len(binval) > bitsize: + raise "binary value larger than the expected size" + while len(binval) < bitsize: + binval = "0" + binval # Add as many 0 as needed to get the wanted size + return binval + + +def hexvalue(val, hexsize): + """Return the hex value as a string of the given size. + + Args: + val: integer or one character + hexsize: number of hex for the conversion + Returns: + hexval: string of hexsize-length + """ + # hex returns a string beginning with 0x ==> extract [2:] to cut these two characters + + hexval = hex(val)[2:] if isinstance(val, int) else hex(ord(val))[2:] + if len(hexval) > hexsize: + raise "hex value larger than the expected size" + while len(hexval) < hexsize: + hexval = "0" + hexval # Add as many 0 as needed to get the wanted size + return hexval + + +def string_to_hex_array(text): + """Convert a string into a list of hex. + + Args: + text: string + Returns: + array: array of hexvalues + """ + array = list() + for char in text: + hexval = hexvalue(char, 2) # Get the char value on two hex + array.extend([x for x in list(hexval)]) # Add the hex to the final list + return array + + +def hex_array_to_string(array): + """Recreate the string from the hex array. + + Args: + array: array of hex + Returns: + res: string + """ + res = ''.join([chr(int(y, 16)) for y in [''.join([str(x) for x in hexa]) for hexa in nsplit(array, 2)]]) + return res + + +def bit_array_to_hex(array, bitgroup, hexsize): + """Return the hex value from an array of bits. + + Args: + array: array of bits + bitgroup: number of bits to be grpoupo for the conversion + hexsize: number of hex for the conversion of bitgroup bits + Returns: + res: string of hex + """ + res = ''.join( + [hexvalue(int(y, 2), hexsize) for y in [''.join([str(x) for x in hexa]) for hexa in nsplit(array, bitgroup)]]) + return res + + +def nsplit(s, n): # Split a list into sublists of size "n" + return [s[k:k + n] for k in range(0, len(s), n)] + + +if __name__ == '__main__': + hs = b'370FFF' + hex_to_string(hs) + ss = 'test é' + string_to_hex(ss) + hex_to_bin('FF') + bin_to_hex('1111') + xor_hex('FF', '11') diff --git a/python/freq/main.py b/python/freq/main.py new file mode 100644 index 0000000..fe893d6 --- /dev/null +++ b/python/freq/main.py @@ -0,0 +1,81 @@ +txt = "NTCGPDOPANFLHJINTOOFITOVJHJCTMMHIHEMTCPFDWTSOFSHTOGFWTETTJJTBTOOFSZOVEOCHCVCHPJHOCGTOHNQMTOCNTCGPDCGFCSTQMFBTOFBGFSFBCTSHJCGTQMFHJCTYCXHCGFAHYTDDHAATSTJCBGFSFBCTSHJCGTBHQGTSCTYCCGHONTCGPDQSTOTSWTOCGTMTCCTSASTRVTJBZHJCGTQMFHJCTYCFJDOPPJTBFJOTFSBGAPSCGTQMFHJCTYCASPNFIHWTJBHQGTSCTYCEZBPNQFSHJICGTASTRVTJBZPATFBGMTCCTSFIFHJOCCGTLJPXJBPNNPJASTRVTJBZHJCGTVJDTSMZHJIMFJIVFIT" + +freq_tab = ["E", "T", "A", "I", "N", "O", "S", "R", "L", "D", "H", "C", "U", "M", "F", "P", "Y", "G", "W", "V", "B", + "K", "X", "J", "Q", "Z"] + + +def freq_letter(txt): + dic = {} + for letter in txt: + if letter in dic: + dic[letter] += 1 + else: + dic[letter] = 1 + return dict(sorted(dic.items(), key=lambda item: item[1])) + + +def freq_m_letter(txt, nbr=2): + dic = {} + i = 0 + while i < len(txt) - 1: + letter = txt[i:i + nbr] + if letter in dic: + dic[letter] += 1 + else: + dic[letter] = 1 + i += 1 + return dict(sorted(filter(lambda x: x[1] > 2, dic.items()), key=lambda item: item[1])) + + +def trad(txt_, replace_): + sol = "" + for letter in txt_: + if letter in replace_: + sol += replace_[letter] + else: + sol += letter + return sol + + +if __name__ == '__main__': + replace = {"C": "t", + "G": "h", + "T": "e", + "H": "i", + "J": "n", + "S": "r", + "F": "a", + "Y": "x", + "B": "c", + "I": "g", + "M": "l", + "Q": "p", + "V": "u", + "A": "f", + "R": "q", + "Z": "y", + "D": "d", + "P": "o", + "O": "s", + "N": "m", + "L": "k", + "E": "b", + "W": "v", + "X": "w" + } + + freq = freq_letter(txt) + print(freq) + for i in range(2, 10): + print("===") + freq_2 = freq_m_letter(txt, i) + print(freq_2) + print('{', end=" ") + for a in freq_2: + print(trad(a, replace), end=', ') + print('}') + + sol = trad(txt, replace) + + print('====') + print(sol) diff --git a/python/prime/prime.py b/python/prime/prime.py new file mode 100644 index 0000000..a23e900 --- /dev/null +++ b/python/prime/prime.py @@ -0,0 +1,36 @@ +def factor_decomposition(n): + p = 2 + dec = {} + # print(n,"= ",end="") + while n >= p * p: + if n % p == 0: + if p in dec: + dec[p] += 1 + else: + dec[p] = 1 + # print(p, "* ", end="") + n /= p + else: + p += 1 + # print(n) + if n in dec: + dec[n] += 1 + else: + dec[n] = 1 + return dec + + +def phi(n): + sol = 1 + dec = factor_decomposition(n) + for (p, a) in dec.items(): + sol *= (p**(a-1))*(p-1) + return sol + + +if __name__ == '__main__': + print(factor_decomposition(85)) + print(factor_decomposition(1200)) + print(factor_decomposition(11011)) + print(phi(9)) + print(phi(85)) diff --git a/python/python.iml b/python/python.iml new file mode 100644 index 0000000..456e24f --- /dev/null +++ b/python/python.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/rust/.gitignore b/rust/.gitignore new file mode 100644 index 0000000..951b62d --- /dev/null +++ b/rust/.gitignore @@ -0,0 +1,14 @@ +/target +### Rust template +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..9c8fa07 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "TPNSM" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +num = "0.4" +rayon = "1.5.3" \ No newline at end of file diff --git a/rust/algo.md b/rust/algo.md new file mode 100644 index 0000000..b79d918 --- /dev/null +++ b/rust/algo.md @@ -0,0 +1,2 @@ +Elliptic_curve_primality +![image](./pictures/ecp.png) \ No newline at end of file diff --git a/rust/pictures/ecp.png b/rust/pictures/ecp.png new file mode 100644 index 0000000..6eefdf8 Binary files /dev/null and b/rust/pictures/ecp.png differ diff --git a/rust/rust.iml b/rust/rust.iml new file mode 100644 index 0000000..2fecef3 --- /dev/null +++ b/rust/rust.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/rust/src/lib.rs b/rust/src/lib.rs new file mode 100644 index 0000000..94a8f2f --- /dev/null +++ b/rust/src/lib.rs @@ -0,0 +1,2 @@ +pub mod prime; +pub mod utils; \ No newline at end of file diff --git a/rust/src/main.rs b/rust/src/main.rs new file mode 100644 index 0000000..8a2b537 --- /dev/null +++ b/rust/src/main.rs @@ -0,0 +1,15 @@ +use TPNSM::prime::{prime_classical, prime_dumb_rayon}; + +fn main() { + println!("Hello, world!"); + + let max = 1000000; + find_prime(prime_classical(max)); + //find_prime(prime_dumb_rayon(max)); +} + +fn find_prime(primes: Vec) { + //println!("{:?}", primes); + println!("{:?}", primes.last()); + println!("{}", primes.len()); +} \ No newline at end of file diff --git a/rust/src/prime.rs b/rust/src/prime.rs new file mode 100644 index 0000000..a3afcc2 --- /dev/null +++ b/rust/src/prime.rs @@ -0,0 +1,50 @@ +use num::integer::sqrt; +use rayon::prelude::*; + +fn prime_gen(max: u32, method: F) -> Vec + where F: Fn(u32) -> bool { + let mut primes = vec![2_u32]; + let mut i = 1; + while i < max { + i += 2; + let prime = method(i); + + if prime { + primes.push(i); + } + } + primes +} + +pub fn prime_classical(max: u32) -> Vec { + let mut primes = vec![2_u32]; + let mut i = 1; + 'l: while i < max { + i += 2; + for p in &primes { + if i % p == 0 { + continue 'l; + } else if *p > sqrt(i) { + break; + } + } + primes.push(i); + } + primes +} + +pub fn prime_dumb_rayon(max: u32) -> Vec { + let mut primes = vec![2_u32]; + let mut i = 1; + while i < max { + i += 2; + let found = primes.par_iter() + .filter(|x| { **x <= sqrt(i) }) + .any(|x| { i % *x == 0 }); + + if !found { + primes.push(i); + } + } + primes +} \ No newline at end of file diff --git a/rust/src/utils.rs b/rust/src/utils.rs new file mode 100644 index 0000000..be52dae --- /dev/null +++ b/rust/src/utils.rs @@ -0,0 +1,127 @@ +pub fn prime_factor(mut n: u32) -> Vec<(u32, u32)> { + let mut p = 2; + let mut dec = vec![]; + + let mut last = 2; + let mut count = 0; + + while n >= p * p { + if n % p == 0 { + n /= p; + + if last == p { + count += 1 + } else { + last = p; + count = 1; + } + + if (n % p != 0 || n < p * p) && last != n { + dec.push((last, count)); + p += 1; + } + } else { + p += 1; + } + } + + if last == n { + count += 1 + } else { + last = n; + count = 1; + } + dec.push((last, count)); + dec +} + +pub fn phi(n: u32) -> u32 { + let mut res = 1; + let dec = prime_factor(n); + for (p, a) in dec { + res *= (p.pow(a - 1)) * (p - 1) + } + res +} + +pub fn congruent(a: i32, b: i32, n: i32) -> bool { + (a - b) % n == 0 +} + + +pub fn gcd_euclid(mut a: u32, mut b: u32) -> u32 { + let mut r = a % b; + while r != 0 { + a = b; + b = r; + r = a % b; + } + b +} + +pub fn modular_inverse(a: u32, n: u32) -> Option { + let (mut t, mut new_t) = (0_i32, 1_i32); + let (mut r, mut new_r) = (n as i32, a as i32); + + while new_r != 0 { + let q = r / new_r; + (t, new_t) = (new_t, t - q * new_t); + (r, new_r) = (new_r, r - q * new_r); + } + + if r > 1 { + return None; + } + if t < 0 { + t += n as i32; + } + + Some(t as u32) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_prime_factor() { + assert_eq!(prime_factor(27), vec![(3, 3)]); + assert_eq!(prime_factor(85), vec![(5, 1), (17, 1)]); + assert_eq!(prime_factor(1200), vec![(2, 4), (3, 1), (5, 2)]); + assert_eq!(prime_factor(11011), vec![(7, 1), (11, 2), (13, 1)]); + assert_eq!(prime_factor(99991), vec![(99991, 1)]); + } + + #[test] + fn test_phi() { + assert_eq!(phi(9), 6); + assert_eq!(phi(51), 32); + assert_eq!(phi(85), 64); + assert_eq!(phi(100), 40); + } + + #[test] + fn test_congruent() { + assert!(congruent(37, 57, 10)); + assert!(congruent(29, 8, 7)); + assert!(congruent(60, 0, 15)); + assert!(congruent(340, 700, 6)); + } + + #[test] + fn test_gcd_euclid() { + assert_eq!(gcd_euclid(2, 3), 1); + assert_eq!(gcd_euclid(8, 40), 8); + assert_eq!(gcd_euclid(34, 51), 17); + } + + #[test] + fn test_modular_inverse() { + assert_eq!(modular_inverse(2, 12), None); + assert_eq!(modular_inverse(1, 2), Some(1)); + assert_eq!(modular_inverse(5, 4), Some(1)); + assert_eq!(modular_inverse(8, 9), Some(8)); + assert_eq!(modular_inverse(10, 19), Some(2)); + assert_eq!(modular_inverse(4, 6), None); + } +} \ No newline at end of file