├── .gitattributes ├── .gitignore ├── MANIFEST.in ├── README.rst ├── doc ├── Makefile ├── make.bat └── source │ ├── conf.py │ └── index.rst ├── license.txt ├── pycipher ├── __init__.py ├── adfgvx.py ├── adfgx.py ├── affine.py ├── atbash.py ├── autokey.py ├── base.py ├── beaufort.py ├── bifid.py ├── caesar.py ├── columnartransposition.py ├── delastelle.py ├── enigma.py ├── foursquare.py ├── fracmorse.py ├── gronsfeld.py ├── m209.py ├── playfair.py ├── polybius.py ├── porta.py ├── railfence.py ├── rot13.py ├── simplesubstitution.py ├── util.py └── vigenere.py ├── setup.py └── tests ├── __init__.py ├── test_adfgvx.py ├── test_adfgx.py ├── test_affine.py ├── test_atbash.py ├── test_autokey.py ├── test_beaufort.py ├── test_bifid.py ├── test_caesar.py ├── test_coltrans.py ├── test_enigma.py ├── test_foursquare.py ├── test_gronsfeld.py ├── test_m209.py ├── test_playfair.py ├── test_polybius.py ├── test_porta.py ├── test_railfence.py ├── test_rot13.py ├── test_simple.py └── test_vigenere.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .DS_Store 3 | *.sublime-* 4 | 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | bin/ 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # Installer logs 30 | pip-log.txt 31 | pip-delete-this-directory.txt 32 | 33 | # Unit test / coverage reports 34 | .tox/ 35 | .coverage 36 | .cache 37 | nosetests.xml 38 | coverage.xml 39 | 40 | # Translations 41 | *.mo 42 | 43 | # Mr Developer 44 | .mr.developer.cfg 45 | .project 46 | .pydevproject 47 | 48 | # Rope 49 | .ropeproject 50 | 51 | # Django stuff: 52 | *.log 53 | *.pot 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | .idea/ 59 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.rst 2 | recursive-include tests * 3 | recursive-exclude tests *.pyc 4 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | pycipher 2 | ======== 3 | 4 | Common classical ciphers implemented in Python. 5 | Documentation is available at http://pycipher.readthedocs.io/en/master/ . 6 | 7 | Install 8 | ------- 9 | 10 | :: 11 | 12 | pip install pycipher 13 | 14 | 15 | Example usage 16 | ------------- 17 | 18 | :: 19 | 20 | >>> from pycipher import ADFGVX 21 | >>> adfgvx = ADFGVX(key='PH0QG64MEA1YL2NOFDXKR3CVS5ZW7BJ9UTI8', keyword='GERMAN') 22 | >>> adfgvx.encipher("Hello world!") 23 | 'FVFDAGXAFFFFGFAGADFG' 24 | >>> adfgvx.decipher(_) 25 | 'HELLOWORLD' 26 | 27 | 28 | Feedback 29 | -------- 30 | 31 | The code is hosted on GitHub: https://github.com/jameslyons/pycipher 32 | 33 | If you find any bugs make an issue or create a pull request. 34 | 35 | To run the test suite:: 36 | 37 | python setup.py test 38 | 39 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # Internal variables. 11 | PAPEROPT_a4 = -D latex_paper_size=a4 12 | PAPEROPT_letter = -D latex_paper_size=letter 13 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 14 | # the i18n builder cannot share the environment and doctrees with the others 15 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 16 | 17 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 18 | 19 | help: 20 | @echo "Please use \`make ' where is one of" 21 | @echo " html to make standalone HTML files" 22 | @echo " dirhtml to make HTML files named index.html in directories" 23 | @echo " singlehtml to make a single large HTML file" 24 | @echo " pickle to make pickle files" 25 | @echo " json to make JSON files" 26 | @echo " htmlhelp to make HTML files and a HTML help project" 27 | @echo " qthelp to make HTML files and a qthelp project" 28 | @echo " devhelp to make HTML files and a Devhelp project" 29 | @echo " epub to make an epub" 30 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 31 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 32 | @echo " text to make text files" 33 | @echo " man to make manual pages" 34 | @echo " texinfo to make Texinfo files" 35 | @echo " info to make Texinfo files and run them through makeinfo" 36 | @echo " gettext to make PO message catalogs" 37 | @echo " changes to make an overview of all changed/added/deprecated items" 38 | @echo " linkcheck to check all external links for integrity" 39 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 40 | 41 | clean: 42 | -rm -rf $(BUILDDIR)/* 43 | 44 | html: 45 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 46 | @echo 47 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 48 | 49 | dirhtml: 50 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 51 | @echo 52 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 53 | 54 | singlehtml: 55 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 56 | @echo 57 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 58 | 59 | pickle: 60 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 61 | @echo 62 | @echo "Build finished; now you can process the pickle files." 63 | 64 | json: 65 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 66 | @echo 67 | @echo "Build finished; now you can process the JSON files." 68 | 69 | htmlhelp: 70 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 71 | @echo 72 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 73 | ".hhp project file in $(BUILDDIR)/htmlhelp." 74 | 75 | qthelp: 76 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 77 | @echo 78 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 79 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 80 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pycipher.qhcp" 81 | @echo "To view the help file:" 82 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pycipher.qhc" 83 | 84 | devhelp: 85 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 86 | @echo 87 | @echo "Build finished." 88 | @echo "To view the help file:" 89 | @echo "# mkdir -p $$HOME/.local/share/devhelp/pycipher" 90 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pycipher" 91 | @echo "# devhelp" 92 | 93 | epub: 94 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 95 | @echo 96 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 97 | 98 | latex: 99 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 100 | @echo 101 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 102 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 103 | "(use \`make latexpdf' here to do that automatically)." 104 | 105 | latexpdf: 106 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 107 | @echo "Running LaTeX files through pdflatex..." 108 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 109 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 110 | 111 | text: 112 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 113 | @echo 114 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 115 | 116 | man: 117 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 118 | @echo 119 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 120 | 121 | texinfo: 122 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 123 | @echo 124 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 125 | @echo "Run \`make' in that directory to run these through makeinfo" \ 126 | "(use \`make info' here to do that automatically)." 127 | 128 | info: 129 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 130 | @echo "Running Texinfo files through makeinfo..." 131 | make -C $(BUILDDIR)/texinfo info 132 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 133 | 134 | gettext: 135 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 136 | @echo 137 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 138 | 139 | changes: 140 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 141 | @echo 142 | @echo "The overview file is in $(BUILDDIR)/changes." 143 | 144 | linkcheck: 145 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 146 | @echo 147 | @echo "Link check complete; look for any errors in the above output " \ 148 | "or in $(BUILDDIR)/linkcheck/output.txt." 149 | 150 | doctest: 151 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 152 | @echo "Testing of doctests in the sources finished, look at the " \ 153 | "results in $(BUILDDIR)/doctest/output.txt." 154 | -------------------------------------------------------------------------------- /doc/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source 10 | set I18NSPHINXOPTS=%SPHINXOPTS% source 11 | if NOT "%PAPER%" == "" ( 12 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 13 | set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% 14 | ) 15 | 16 | if "%1" == "" goto help 17 | 18 | if "%1" == "help" ( 19 | :help 20 | echo.Please use `make ^` where ^ is one of 21 | echo. html to make standalone HTML files 22 | echo. dirhtml to make HTML files named index.html in directories 23 | echo. singlehtml to make a single large HTML file 24 | echo. pickle to make pickle files 25 | echo. json to make JSON files 26 | echo. htmlhelp to make HTML files and a HTML help project 27 | echo. qthelp to make HTML files and a qthelp project 28 | echo. devhelp to make HTML files and a Devhelp project 29 | echo. epub to make an epub 30 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 31 | echo. text to make text files 32 | echo. man to make manual pages 33 | echo. texinfo to make Texinfo files 34 | echo. gettext to make PO message catalogs 35 | echo. changes to make an overview over all changed/added/deprecated items 36 | echo. linkcheck to check all external links for integrity 37 | echo. doctest to run all doctests embedded in the documentation if enabled 38 | goto end 39 | ) 40 | 41 | if "%1" == "clean" ( 42 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 43 | del /q /s %BUILDDIR%\* 44 | goto end 45 | ) 46 | 47 | if "%1" == "html" ( 48 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 49 | if errorlevel 1 exit /b 1 50 | echo. 51 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 52 | goto end 53 | ) 54 | 55 | if "%1" == "dirhtml" ( 56 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 57 | if errorlevel 1 exit /b 1 58 | echo. 59 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 60 | goto end 61 | ) 62 | 63 | if "%1" == "singlehtml" ( 64 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 65 | if errorlevel 1 exit /b 1 66 | echo. 67 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 68 | goto end 69 | ) 70 | 71 | if "%1" == "pickle" ( 72 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 73 | if errorlevel 1 exit /b 1 74 | echo. 75 | echo.Build finished; now you can process the pickle files. 76 | goto end 77 | ) 78 | 79 | if "%1" == "json" ( 80 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 81 | if errorlevel 1 exit /b 1 82 | echo. 83 | echo.Build finished; now you can process the JSON files. 84 | goto end 85 | ) 86 | 87 | if "%1" == "htmlhelp" ( 88 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 89 | if errorlevel 1 exit /b 1 90 | echo. 91 | echo.Build finished; now you can run HTML Help Workshop with the ^ 92 | .hhp project file in %BUILDDIR%/htmlhelp. 93 | goto end 94 | ) 95 | 96 | if "%1" == "qthelp" ( 97 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 98 | if errorlevel 1 exit /b 1 99 | echo. 100 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 101 | .qhcp project file in %BUILDDIR%/qthelp, like this: 102 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pycipher.qhcp 103 | echo.To view the help file: 104 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pycipher.ghc 105 | goto end 106 | ) 107 | 108 | if "%1" == "devhelp" ( 109 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 110 | if errorlevel 1 exit /b 1 111 | echo. 112 | echo.Build finished. 113 | goto end 114 | ) 115 | 116 | if "%1" == "epub" ( 117 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 118 | if errorlevel 1 exit /b 1 119 | echo. 120 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 121 | goto end 122 | ) 123 | 124 | if "%1" == "latex" ( 125 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 126 | if errorlevel 1 exit /b 1 127 | echo. 128 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 129 | goto end 130 | ) 131 | 132 | if "%1" == "text" ( 133 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 134 | if errorlevel 1 exit /b 1 135 | echo. 136 | echo.Build finished. The text files are in %BUILDDIR%/text. 137 | goto end 138 | ) 139 | 140 | if "%1" == "man" ( 141 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 142 | if errorlevel 1 exit /b 1 143 | echo. 144 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 145 | goto end 146 | ) 147 | 148 | if "%1" == "texinfo" ( 149 | %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo 150 | if errorlevel 1 exit /b 1 151 | echo. 152 | echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. 153 | goto end 154 | ) 155 | 156 | if "%1" == "gettext" ( 157 | %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale 158 | if errorlevel 1 exit /b 1 159 | echo. 160 | echo.Build finished. The message catalogs are in %BUILDDIR%/locale. 161 | goto end 162 | ) 163 | 164 | if "%1" == "changes" ( 165 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 166 | if errorlevel 1 exit /b 1 167 | echo. 168 | echo.The overview file is in %BUILDDIR%/changes. 169 | goto end 170 | ) 171 | 172 | if "%1" == "linkcheck" ( 173 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 174 | if errorlevel 1 exit /b 1 175 | echo. 176 | echo.Link check complete; look for any errors in the above output ^ 177 | or in %BUILDDIR%/linkcheck/output.txt. 178 | goto end 179 | ) 180 | 181 | if "%1" == "doctest" ( 182 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 183 | if errorlevel 1 exit /b 1 184 | echo. 185 | echo.Testing of doctests in the sources finished, look at the ^ 186 | results in %BUILDDIR%/doctest/output.txt. 187 | goto end 188 | ) 189 | 190 | :end 191 | -------------------------------------------------------------------------------- /doc/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # pycipher documentation build configuration file, created by 4 | # sphinx-quickstart on Sat Feb 08 18:44:38 2014. 5 | # 6 | # This file is execfile()d with the current directory set to its containing dir. 7 | # 8 | # Note that not all possible configuration values are present in this 9 | # autogenerated file. 10 | # 11 | # All configuration values have a default; values that are commented out 12 | # serve to show the default. 13 | 14 | import sys, os 15 | sys.path.append('../../pycipher/') 16 | 17 | # If extensions (or modules to document with autodoc) are in another directory, 18 | # add these directories to sys.path here. If the directory is relative to the 19 | # documentation root, use os.path.abspath to make it absolute, like shown here. 20 | #sys.path.insert(0, os.path.abspath('.')) 21 | 22 | # -- General configuration ----------------------------------------------------- 23 | 24 | # If your documentation needs a minimal Sphinx version, state it here. 25 | #needs_sphinx = '1.0' 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be extensions 28 | # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 29 | extensions = ['sphinx.ext.autodoc'] 30 | 31 | # Add any paths that contain templates here, relative to this directory. 32 | templates_path = ['_templates'] 33 | 34 | # The suffix of source filenames. 35 | source_suffix = '.rst' 36 | 37 | # The encoding of source files. 38 | #source_encoding = 'utf-8-sig' 39 | 40 | # The master toctree document. 41 | master_doc = 'index' 42 | 43 | # General information about the project. 44 | project = u'pycipher' 45 | copyright = u'2014, James Lyons' 46 | 47 | # The version info for the project you're documenting, acts as replacement for 48 | # |version| and |release|, also used in various other places throughout the 49 | # built documents. 50 | # 51 | # The short X.Y version. 52 | version = '0.5' 53 | # The full version, including alpha/beta/rc tags. 54 | release = '1' 55 | 56 | # The language for content autogenerated by Sphinx. Refer to documentation 57 | # for a list of supported languages. 58 | #language = None 59 | 60 | # There are two options for replacing |today|: either, you set today to some 61 | # non-false value, then it is used: 62 | #today = '' 63 | # Else, today_fmt is used as the format for a strftime call. 64 | #today_fmt = '%B %d, %Y' 65 | 66 | # List of patterns, relative to source directory, that match files and 67 | # directories to ignore when looking for source files. 68 | exclude_patterns = [] 69 | 70 | # The reST default role (used for this markup: `text`) to use for all documents. 71 | #default_role = None 72 | 73 | # If true, '()' will be appended to :func: etc. cross-reference text. 74 | #add_function_parentheses = True 75 | 76 | # If true, the current module name will be prepended to all description 77 | # unit titles (such as .. function::). 78 | #add_module_names = True 79 | 80 | # If true, sectionauthor and moduleauthor directives will be shown in the 81 | # output. They are ignored by default. 82 | #show_authors = False 83 | 84 | # The name of the Pygments (syntax highlighting) style to use. 85 | pygments_style = 'sphinx' 86 | 87 | # A list of ignored prefixes for module index sorting. 88 | #modindex_common_prefix = [] 89 | 90 | 91 | # -- Options for HTML output --------------------------------------------------- 92 | 93 | # The theme to use for HTML and HTML Help pages. See the documentation for 94 | # a list of builtin themes. 95 | html_theme = 'default' 96 | 97 | # Theme options are theme-specific and customize the look and feel of a theme 98 | # further. For a list of options available for each theme, see the 99 | # documentation. 100 | #html_theme_options = {} 101 | 102 | # Add any paths that contain custom themes here, relative to this directory. 103 | #html_theme_path = [] 104 | 105 | # The name for this set of Sphinx documents. If None, it defaults to 106 | # " v documentation". 107 | #html_title = None 108 | 109 | # A shorter title for the navigation bar. Default is the same as html_title. 110 | #html_short_title = None 111 | 112 | # The name of an image file (relative to this directory) to place at the top 113 | # of the sidebar. 114 | #html_logo = None 115 | 116 | # The name of an image file (within the static path) to use as favicon of the 117 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 118 | # pixels large. 119 | #html_favicon = None 120 | 121 | # Add any paths that contain custom static files (such as style sheets) here, 122 | # relative to this directory. They are copied after the builtin static files, 123 | # so a file named "default.css" will overwrite the builtin "default.css". 124 | html_static_path = ['_static'] 125 | 126 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 127 | # using the given strftime format. 128 | #html_last_updated_fmt = '%b %d, %Y' 129 | 130 | # If true, SmartyPants will be used to convert quotes and dashes to 131 | # typographically correct entities. 132 | #html_use_smartypants = True 133 | 134 | # Custom sidebar templates, maps document names to template names. 135 | #html_sidebars = {} 136 | 137 | # Additional templates that should be rendered to pages, maps page names to 138 | # template names. 139 | #html_additional_pages = {} 140 | 141 | # If false, no module index is generated. 142 | #html_domain_indices = True 143 | 144 | # If false, no index is generated. 145 | #html_use_index = True 146 | 147 | # If true, the index is split into individual pages for each letter. 148 | #html_split_index = False 149 | 150 | # If true, links to the reST sources are added to the pages. 151 | #html_show_sourcelink = True 152 | 153 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 154 | #html_show_sphinx = True 155 | 156 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 157 | #html_show_copyright = True 158 | 159 | # If true, an OpenSearch description file will be output, and all pages will 160 | # contain a tag referring to it. The value of this option must be the 161 | # base URL from which the finished HTML is served. 162 | #html_use_opensearch = '' 163 | 164 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 165 | #html_file_suffix = None 166 | 167 | # Output file base name for HTML help builder. 168 | htmlhelp_basename = 'pycipherdoc' 169 | 170 | 171 | # -- Options for LaTeX output -------------------------------------------------- 172 | 173 | latex_elements = { 174 | # The paper size ('letterpaper' or 'a4paper'). 175 | #'papersize': 'letterpaper', 176 | 177 | # The font size ('10pt', '11pt' or '12pt'). 178 | #'pointsize': '10pt', 179 | 180 | # Additional stuff for the LaTeX preamble. 181 | #'preamble': '', 182 | } 183 | 184 | # Grouping the document tree into LaTeX files. List of tuples 185 | # (source start file, target name, title, author, documentclass [howto/manual]). 186 | latex_documents = [ 187 | ('index', 'pycipher.tex', u'pycipher Documentation', 188 | u'James Lyons', 'manual'), 189 | ] 190 | 191 | # The name of an image file (relative to this directory) to place at the top of 192 | # the title page. 193 | #latex_logo = None 194 | 195 | # For "manual" documents, if this is true, then toplevel headings are parts, 196 | # not chapters. 197 | #latex_use_parts = False 198 | 199 | # If true, show page references after internal links. 200 | #latex_show_pagerefs = False 201 | 202 | # If true, show URL addresses after external links. 203 | #latex_show_urls = False 204 | 205 | # Documents to append as an appendix to all manuals. 206 | #latex_appendices = [] 207 | 208 | # If false, no module index is generated. 209 | #latex_domain_indices = True 210 | 211 | 212 | # -- Options for manual page output -------------------------------------------- 213 | 214 | # One entry per manual page. List of tuples 215 | # (source start file, name, description, authors, manual section). 216 | man_pages = [ 217 | ('index', 'pycipher', u'pycipher Documentation', 218 | [u'James Lyons'], 1) 219 | ] 220 | 221 | # If true, show URL addresses after external links. 222 | #man_show_urls = False 223 | 224 | 225 | # -- Options for Texinfo output ------------------------------------------------ 226 | 227 | # Grouping the document tree into Texinfo files. List of tuples 228 | # (source start file, target name, title, author, 229 | # dir menu entry, description, category) 230 | texinfo_documents = [ 231 | ('index', 'pycipher', u'pycipher Documentation', 232 | u'James Lyons', 'pycipher', 'One line description of project.', 233 | 'Miscellaneous'), 234 | ] 235 | 236 | # Documents to append as an appendix to all manuals. 237 | #texinfo_appendices = [] 238 | 239 | # If false, no module index is generated. 240 | #texinfo_domain_indices = True 241 | 242 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 243 | #texinfo_show_urls = 'footnote' 244 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | .. pycipher documentation master file, created by 2 | sphinx-quickstart on Sat Feb 08 18:44:38 2014. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to pycipher's documentation! 7 | ==================================== 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | 12 | Common classical ciphers implemented in Python. 13 | 14 | The code is hosted on GitHub: https://github.com/jameslyons/pycipher 15 | 16 | If you find any bugs make an issue or create a pull request. 17 | 18 | Example usage 19 | ------------- 20 | 21 | :: 22 | 23 | >>> from pycipher import ADFGVX 24 | >>> adfgvx = ADFGVX(key='PH0QG64MEA1YL2NOFDXKR3CVS5ZW7BJ9UTI8', keyword='GERMAN') 25 | >>> adfgvx.encipher("Hello world!") 26 | 'FVFDAGXAFFFFGFAGADFG' 27 | >>> adfgvx.decipher(_) 28 | 'HELLOWORLD' 29 | 30 | .. contents:: Table of Contents 31 | :depth: 2 32 | 33 | A Short Aside on Keysquares 34 | ---------------------------- 35 | Many of the ciphers mentioned here e.g. :py:meth:`pycipher.Playfair`, :py:meth:`pycipher.Foursquare` use keysquares as part of their key information. A keysquare looks like this:: 36 | 37 | Z G P T F 38 | O I H M U 39 | W D R C N 40 | Y K E Q A 41 | X V S B L 42 | 43 | The keysquare above is a 5 by 5 keysquare consisting of 25 characters. To use this keysquare as part of a key, read each row starting with the top row, then the second row etc. The keysquare above 44 | becomes "ZGPTFOIHMUWDRCNYKEQAXVSBL". Note that with only 25 characters, 1 character can't be included. This is usually the letter 'J', wherever this letter appears it is 45 | replaced by 'I'. 46 | 47 | Keysquares can also be 6 by 6. In this case the 26 letters A-Z plus the 10 numbers 0-9 are used. An example of this sort of keysquare is:: 48 | 49 | p h 0 q g 6 50 | 4 m e a 1 y 51 | l 2 n o f d 52 | x k r 3 c v 53 | s 5 z w 7 b 54 | j 9 u t i 8 55 | 56 | The keysquare above becomes "ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8". 57 | 58 | The Ciphers 59 | =========== 60 | 61 | ADFGX Cipher 62 | ------------ 63 | This cipher uses a keysquare as part of its key, see `A Short Aside on Keysquares`_ for information. 64 | 65 | .. autoclass:: pycipher.ADFGX 66 | :members: 67 | 68 | ADFGVX Cipher 69 | ------------- 70 | This cipher uses a keysquare as part of its key, see `A Short Aside on Keysquares`_ for information. 71 | 72 | .. autoclass:: pycipher.ADFGVX 73 | :members: 74 | 75 | Affine Cipher 76 | ------------- 77 | .. autoclass:: pycipher.Affine 78 | :members: 79 | 80 | Autokey Cipher 81 | -------------- 82 | .. autoclass:: pycipher.Autokey 83 | :members: 84 | 85 | Atbash Cipher 86 | -------------- 87 | .. autoclass:: pycipher.Atbash 88 | :members: 89 | 90 | Beaufort Cipher 91 | --------------- 92 | .. autoclass:: pycipher.Beaufort 93 | :members: 94 | 95 | Bifid Cipher 96 | -------------- 97 | This cipher uses a keysquare as part of its key, see `A Short Aside on Keysquares`_ for information. 98 | 99 | .. autoclass:: pycipher.Bifid 100 | :members: 101 | 102 | Caesar Cipher 103 | -------------- 104 | .. autoclass:: pycipher.Caesar 105 | :members: 106 | 107 | Columnar Transposition Cipher 108 | ----------------------------- 109 | .. autoclass:: pycipher.ColTrans 110 | :members: 111 | 112 | Enigma M3 Cipher 113 | ---------------- 114 | The Enigma M3 was used by the German army during the Second World War. The keying information for mechanical ciphers can be a little more complicated 115 | than for the simpler ciphers, it may be beneficial to read `this guide `_ to get an 116 | understanding of how the Enigma works before using it. 117 | 118 | .. autoclass:: pycipher.Enigma 119 | :members: 120 | 121 | Foursquare Cipher 122 | ----------------- 123 | This cipher uses a keysquare as part of its key, see `A Short Aside on Keysquares`_ for information. 124 | 125 | .. autoclass:: pycipher.Foursquare 126 | :members: 127 | 128 | Gronsfeld Cipher 129 | ----------------- 130 | .. autoclass:: pycipher.Gronsfeld 131 | :members: 132 | 133 | M-209 Cipher 134 | ---------------- 135 | The M-209 was used by the American army during the Second World War. The keying information for mechanical ciphers can be a little more complicated 136 | than for the simpler ciphers, it may be beneficial to read `this page `_ , in particular the parts about "Example Configuration" to get an 137 | understanding of how the M-209 works before using it. 138 | 139 | .. autoclass:: pycipher.M209 140 | :members: 141 | 142 | Playfair Cipher 143 | ----------------- 144 | This cipher uses a keysquare as part of its key, see `A Short Aside on Keysquares`_ for information. 145 | 146 | .. autoclass:: pycipher.Playfair 147 | :members: 148 | 149 | Polybius Square Cipher 150 | ---------------------- 151 | This cipher uses a keysquare as part of its key, see `A Short Aside on Keysquares`_ for information. 152 | 153 | .. autoclass:: pycipher.PolybiusSquare 154 | :members: 155 | 156 | Porta Cipher 157 | -------------- 158 | .. autoclass:: pycipher.Porta 159 | :members: 160 | 161 | Railfence Cipher 162 | -------------- 163 | .. autoclass:: pycipher.Railfence 164 | :members: 165 | 166 | Rot13 Cipher 167 | -------------- 168 | .. autoclass:: pycipher.Rot13 169 | :members: 170 | 171 | Simple Substitution Cipher 172 | -------------------------- 173 | .. autoclass:: pycipher.SimpleSubstitution 174 | :members: 175 | 176 | Vigenere Cipher 177 | ----------------- 178 | .. autoclass:: pycipher.Vigenere 179 | :members: 180 | 181 | Indices and tables 182 | ================== 183 | 184 | * :ref:`genindex` 185 | * :ref:`modindex` 186 | * :ref:`search` 187 | 188 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 James Lyons 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 4 | software and associated documentation files (the "Software"), to deal in the Software 5 | without restriction, including without limitation the rights to use, copy, modify, 6 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 7 | permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in 10 | all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 13 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 14 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 15 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | -------------------------------------------------------------------------------- /pycipher/__init__.py: -------------------------------------------------------------------------------- 1 | from pycipher.adfgx import ADFGX 2 | from pycipher.adfgvx import ADFGVX 3 | from pycipher.simplesubstitution import SimpleSubstitution 4 | from pycipher.caesar import Caesar 5 | from pycipher.affine import Affine 6 | from pycipher.enigma import Enigma 7 | from pycipher.autokey import Autokey 8 | from pycipher.beaufort import Beaufort 9 | from pycipher.bifid import Bifid as Bifid 10 | from pycipher.columnartransposition import ColTrans 11 | from pycipher.gronsfeld import Gronsfeld 12 | from pycipher.foursquare import Foursquare 13 | from pycipher.m209 import M209 as M209 14 | from pycipher.polybius import PolybiusSquare 15 | from pycipher.playfair import Playfair 16 | from pycipher.vigenere import Vigenere 17 | from pycipher.rot13 import Rot13 18 | from pycipher.atbash import Atbash 19 | from pycipher.railfence import Railfence 20 | from pycipher.porta import Porta 21 | from pycipher.fracmorse import FracMorse 22 | import pycipher.util 23 | #from lorentz import Lorentz as Lorentz 24 | __all__=["Atbash","ADFGX","ADFGVX","SimpleSubstitution","Caesar","Affine","Enigma","Autokey","Beaufort", 25 | "Bifid","ColTrans","Gronsfeld","Foursquare","M209","PolybiusSquare","Playfair","Vigenere","Rot13","util", 26 | "Railfence","Porta","FracMorse"] 27 | 28 | __version__ = "0.5.1" 29 | -------------------------------------------------------------------------------- /pycipher/adfgvx.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements ADFGVX cipher 3 | Author: James Lyons 4 | Created: 2012-04-28 5 | ''' 6 | from pycipher.base import Cipher 7 | from pycipher.columnartransposition import ColTrans 8 | from pycipher.polybius import PolybiusSquare 9 | 10 | #################################################################################### 11 | class ADFGVX(Cipher): 12 | """The ADFGVX Cipher has a key consisting of a 6x6 key square and a word e.g. 'GERMAN'. 13 | The algorithm is described here: http://www.practicalcryptography.com/ciphers/classical-era/adfgvx/ 14 | The key square consists of the letters A-Z and the numbers 0-9 (36 characters total). 15 | 16 | :param key: The keysquare, as a 36 character string. 17 | :param keyword: The keyword, any word or phrase will do. 18 | """ 19 | def __init__(self,key='ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8',keyword='GERMAN'): 20 | self.key = [k.upper() for k in key] 21 | self.keyword = keyword 22 | assert len(key)==36, 'invalid key in init: must have length 36, has length '+str(len(key)) 23 | assert len(keyword)>0, 'invalid keyword in init: should have length >= 1' 24 | 25 | def encipher(self,string): 26 | """Encipher string using ADFGVX cipher according to initialised key information. Punctuation and whitespace 27 | are removed from the input. 28 | 29 | Example:: 30 | 31 | ciphertext = ADFGVX('ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8','HELLO').encipher(plaintext) 32 | 33 | :param string: The string to encipher. 34 | :returns: The enciphered string. 35 | """ 36 | step1 = PolybiusSquare(self.key,size=6,chars='ADFGVX').encipher(string) 37 | step2 = ColTrans(self.keyword).encipher(step1) 38 | return step2 39 | 40 | def decipher(self,string): 41 | """Decipher string using ADFGVX cipher according to initialised key information. Punctuation and whitespace 42 | are removed from the input. 43 | 44 | Example:: 45 | 46 | plaintext = ADFGVX('ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8','HELLO').decipher(ciphertext) 47 | 48 | :param string: The string to decipher. 49 | :returns: The enciphered string. 50 | """ 51 | step2 = ColTrans(self.keyword).decipher(string) 52 | step1 = PolybiusSquare(self.key,size=6,chars='ADFGVX').decipher(step2) 53 | return step1 54 | 55 | if __name__ == '__main__': 56 | print('use "import pycipher" to access functions') 57 | -------------------------------------------------------------------------------- /pycipher/adfgx.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements ADFGX cipher 3 | Author: James Lyons 4 | Created: 2012-04-28 5 | ''' 6 | from pycipher.base import Cipher 7 | from pycipher.columnartransposition import ColTrans 8 | from pycipher.polybius import PolybiusSquare 9 | 10 | #################################################################################### 11 | class ADFGX(Cipher): 12 | """The ADFGX Cipher has a key consisting of a 5x5 key square and a word e.g. 'GERMAN'. 13 | The algorithm is described here: http://www.practicalcryptography.com/ciphers/classical-era/adfgvx/ 14 | The key square consists of the letters A-Z with J omitted (25 characters total). 15 | 16 | :param key: The keysquare, as a 25 character string. 17 | :param keyword: The keyword, any word or phrase will do. 18 | """ 19 | def __init__(self,key='phqgmeaylnofdxkrcvszwbuti',keyword='GERMAN'): 20 | self.key = [k.upper() for k in key] 21 | self.keyword = keyword 22 | assert len(key)==25, 'invalid key in init: must have length 25, has length '+str(len(key)) 23 | assert len(keyword)>0, 'invalid keyword in init: should have length >= 1' 24 | 25 | def encipher(self,string): 26 | """Encipher string using ADFGX cipher according to initialised key information. Punctuation and whitespace 27 | are removed from the input. 28 | 29 | Example:: 30 | 31 | ciphertext = ADFGX('phqgmeaylnofdxkrcvszwbuti','HELLO').encipher(plaintext) 32 | 33 | :param string: The string to encipher. 34 | :returns: The enciphered string. 35 | """ 36 | step1 = PolybiusSquare(self.key,size=5,chars='ADFGX').encipher(string) 37 | step2 = ColTrans(self.keyword).encipher(step1) 38 | return step2 39 | 40 | def decipher(self,string): 41 | """Decipher string using ADFGX cipher according to initialised key information. Punctuation and whitespace 42 | are removed from the input. 43 | 44 | Example:: 45 | 46 | plaintext = ADFGX('phqgmeaylnofdxkrcvszwbuti','HELLO').decipher(ciphertext) 47 | 48 | :param string: The string to decipher. 49 | :returns: The enciphered string. 50 | """ 51 | step2 = ColTrans(self.keyword).decipher(string) 52 | step1 = PolybiusSquare(self.key,size=5,chars='ADFGX').decipher(step2) 53 | return step1 54 | 55 | if __name__ == '__main__': 56 | print('use "import pycipher" to access functions') 57 | -------------------------------------------------------------------------------- /pycipher/affine.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements affine substitution cipher 3 | Author: James Lyons 4 | Created: 2012-04-28 5 | ''' 6 | from pycipher.base import Cipher 7 | 8 | #################################################################################### 9 | class Affine(Cipher): 10 | """The Affine Cipher has two components to the key, numbers *a* and *b*. 11 | This cipher encrypts a letter according to the following equation:: 12 | 13 | c = (a*p + b)%26 14 | 15 | where c is the ciphertext letter, p the plaintext letter. 16 | *b* is an integer 0-25, *a* is an integer that has an inverse (mod 26). 17 | Allowable values for *a* are: 1,3,5,7,9,11,15,17,19,21,23,25 18 | For more info on the Affine cipher see http://www.practicalcryptography.com/ciphers/affine-cipher/ 19 | 20 | :param a: The multiplicative part of the key. Allowable values are: 1,3,5,7,9,11,15,17,19,21,23,25 21 | :param b: The additive part of the key. Allowable values are integers 0-25 22 | """ 23 | 24 | def __init__(self,a=5,b=9): 25 | self.a = a 26 | self.b = b 27 | self.inva = -1 28 | for i in range(1,26,2): 29 | if (self.a*i) % 26 == 1: self.inva = i 30 | assert 0 <= self.inva <= 25, 'invalid key: a='+str(a)+', no inverse exists (mod 26)' 31 | 32 | def encipher(self,string,keep_punct=False): 33 | """Encipher string using affine cipher according to initialised key. 34 | 35 | Example:: 36 | 37 | ciphertext = Affine(a,b).encipher(plaintext) 38 | 39 | :param string: The string to encipher. 40 | :param keep_punct: if true, punctuation and spacing are retained. If false, it is all removed. Default is False. 41 | :returns: The enciphered string. 42 | """ 43 | if not keep_punct: string = self.remove_punctuation(string) 44 | ret = '' 45 | for c in string: 46 | if c.isalpha(): ret += self.i2a(self.a*self.a2i(c) + self.b) 47 | else: ret += c 48 | return ret 49 | 50 | def decipher(self,string,keep_punct=False): 51 | """Decipher string using affine cipher according to initialised key. 52 | 53 | Example:: 54 | 55 | plaintext = Affine(a,b).decipher(ciphertext) 56 | 57 | :param string: The string to decipher. 58 | :param keep_punct: if true, punctuation and spacing are retained. If false, it is all removed. Default is False. 59 | :returns: The deciphered string. 60 | """ 61 | if not keep_punct: string = self.remove_punctuation(string) 62 | ret = '' 63 | for c in string: 64 | if c.isalpha(): ret += self.i2a(self.inva*(self.a2i(c) - self.b)) 65 | else: ret += c 66 | return ret 67 | 68 | if __name__ == '__main__': 69 | print('use "import pycipher" to access functions') 70 | -------------------------------------------------------------------------------- /pycipher/atbash.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements atbash cipher 3 | Author: James Lyons 4 | Created: 2014-02-09 5 | ''' 6 | 7 | from pycipher.base import Cipher 8 | 9 | class Atbash(Cipher): 10 | """The Atbash Cipher has no key. 11 | For more information see http://www.practicalcryptography.com/ciphers/atbash-cipher-cipher/. 12 | """ 13 | def __init__(self): 14 | self.key = 'ZYXWVUTSRQPONMLKJIHGFEDCBA' 15 | 16 | def encipher(self,string,keep_punct=False): 17 | """Encipher string using Atbash cipher. 18 | 19 | Example:: 20 | 21 | ciphertext = Atbash().encipher(plaintext) 22 | 23 | :param string: The string to encipher. 24 | :param keep_punct: if true, punctuation and spacing are retained. If false, it is all removed. Default is False. 25 | :returns: The enciphered string. 26 | """ 27 | if not keep_punct: string = self.remove_punctuation(string) 28 | ret = '' 29 | for c in string.upper(): 30 | if c.isalpha(): ret += self.key[self.a2i(c)] 31 | else: ret += c 32 | return ret 33 | 34 | def decipher(self,string,keep_punct=False): 35 | """Decipher string using the Atbash cipher. 36 | 37 | Example:: 38 | 39 | plaintext = Atbash().decipher(ciphertext) 40 | 41 | :param string: The string to decipher. 42 | :param keep_punct: if true, punctuation and spacing are retained. If false, it is all removed. Default is False. 43 | :returns: The deciphered string. 44 | """ 45 | return self.encipher(string,keep_punct) 46 | 47 | if __name__ == '__main__': 48 | print('use "import pycipher" to access functions') 49 | -------------------------------------------------------------------------------- /pycipher/autokey.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements autokey cipher 3 | Author: James Lyons 4 | Created: 2012-04-28 5 | ''' 6 | from pycipher.base import Cipher 7 | 8 | #################################################################################### 9 | class Autokey(Cipher): 10 | """The Autokey Cipher has a key consisting of a word e.g. 'FORTIFICATION'. 11 | This cipher encrypts a letter according to the Vigenere tableau, the algorithm can be 12 | seen e.g. http://www.practicalcryptography.com/ciphers/classical-era/autokey/ 13 | 14 | :param key: The keyword, any word or phrase will do. Must consist of alphabetical characters only, no punctuation of numbers. 15 | """ 16 | def __init__(self,key='FORTIFICATION'): 17 | self.key = [k.upper() for k in key] 18 | 19 | def encipher(self,string): 20 | """Encipher string using Autokey cipher according to initialised key. Punctuation and whitespace 21 | are removed from the input. 22 | 23 | Example:: 24 | 25 | ciphertext = Autokey('HELLO').encipher(plaintext) 26 | 27 | :param string: The string to encipher. 28 | :returns: The enciphered string. 29 | """ 30 | string = self.remove_punctuation(string) 31 | ret = '' 32 | for (i,c) in enumerate(string): 33 | if iint->letter conversions 4 | Author: James Lyons 5 | Created: 2012-04-28 6 | ''' 7 | import re 8 | 9 | class Cipher(object): 10 | def encipher(self,string): 11 | return string 12 | 13 | def decipher(self,string): 14 | return string 15 | 16 | def a2i(self,ch): 17 | ch = ch.upper() 18 | arr = {'A':0,'B':1,'C':2,'D':3,'E':4,'F':5,'G':6,'H':7,'I':8,'J':9,'K':10, 19 | 'L':11,'M':12,'N':13,'O':14,'P':15,'Q':16,'R':17,'S':18,'T':19,'U':20, 20 | 'V':21,'W':22,'X':23,'Y':24,'Z':25} 21 | return arr[ch] 22 | 23 | def i2a(self,i): 24 | i = i%26 25 | arr = ('A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z') 26 | return arr[i] 27 | 28 | def remove_punctuation(self,text,filter='[^A-Z]'): 29 | return re.sub(filter,'',text.upper()) 30 | -------------------------------------------------------------------------------- /pycipher/beaufort.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements beaufort cipher 3 | Author: James Lyons 4 | Created: 2012-04-28 5 | ''' 6 | from pycipher.base import Cipher 7 | 8 | #################################################################################### 9 | class Beaufort(Cipher): 10 | """The Beaufort Cipher is similar to the Vigenere Cipher, and has a key consisting of a word e.g. 'FORTIFICATION'. 11 | This cipher encrypts a letter according to the Vigenere tableau, the but uses a different algorithm to find the 12 | ciphertext letter. The algorithm can be 13 | seen e.g. http://www.practicalcryptography.com/ciphers/beaufort-cipher/ 14 | 15 | :param key: The keyword, any word or phrase will do. Must consist of alphabetical characters only, no punctuation of numbers. 16 | """ 17 | def __init__(self,key='FORTIFICATION'): 18 | self.key = [k.upper() for k in key] 19 | 20 | def encipher(self,string): 21 | """Encipher string using Beaufort cipher according to initialised key. Punctuation and whitespace 22 | are removed from the input. 23 | 24 | Example:: 25 | 26 | ciphertext = Beaufort('HELLO').encipher(plaintext) 27 | 28 | :param string: The string to encipher. 29 | :returns: The enciphered string. 30 | """ 31 | string = self.remove_punctuation(string) 32 | ret = '' 33 | for (i,c) in enumerate(string): 34 | i = i%len(self.key) 35 | ret += self.i2a(self.a2i(self.key[i])-self.a2i(c)) 36 | return ret 37 | 38 | def decipher(self,string): 39 | """Decipher string using Beaufort cipher according to initialised key. Punctuation and whitespace 40 | are removed from the input. For the Beaufort cipher, enciphering and deciphering are the same operation. 41 | 42 | Example:: 43 | 44 | plaintext = Beaufort('HELLO').decipher(ciphertext) 45 | 46 | :param string: The string to decipher. 47 | :returns: The deciphered string. 48 | """ 49 | return self.encipher(string) 50 | 51 | if __name__ == '__main__': 52 | print('use "import pycipher" to access functions') 53 | -------------------------------------------------------------------------------- /pycipher/bifid.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements bifid cipher 3 | Author: James Lyons 4 | Created: 2012-04-28 5 | ''' 6 | from pycipher.base import Cipher 7 | from pycipher.polybius import PolybiusSquare 8 | #################################################################################### 9 | class Bifid(Cipher): 10 | """The Bifid Cipher is a fractionating cipher, and has a key consisting of a 25 letter keysquare (with a letter removed e.g. 'J'), along with 11 | a 'period', which is an integer. 12 | For more information, the algorithm can be 13 | seen e.g. http://www.practicalcryptography.com/ciphers/bifid-cipher/ 14 | 15 | :param key: The keysquare, as a 25 character string. 16 | :param period: an integer. 17 | """ 18 | def __init__(self,key='phqgmeaylnofdxkrcvszwbuti',period=5): 19 | self.key = [k.upper() for k in key] 20 | self.pb = PolybiusSquare(self.key,size=5) 21 | self.period = period 22 | assert len(key)==25, 'invalid key in init: must have length 25, has length '+str(len(key)) 23 | assert period>=1, 'invalid period in init: period should be >= 1' 24 | 25 | def encipher(self,string): 26 | """Encipher string using Bifid cipher according to initialised key. Punctuation and whitespace 27 | are removed from the input. 28 | 29 | Example:: 30 | 31 | ciphertext = Bifid('phqgmeaylnofdxkrcvszwbuti',5).encipher(plaintext) 32 | 33 | :param string: The string to encipher. 34 | :returns: The enciphered string. 35 | """ 36 | string = self.remove_punctuation(string) 37 | step1 = self.pb.encipher(string) 38 | evens = step1[::2] 39 | odds = step1[1::2] 40 | step2 = [] 41 | for i in range(0,len(string),self.period): 42 | step2 += evens[i:int(i+self.period)] 43 | step2 += odds[i:int(i+self.period)] 44 | return self.pb.decipher(''.join(step2)) 45 | 46 | def decipher(self,string): 47 | """Decipher string using Bifid cipher according to initialised key. Punctuation and whitespace 48 | are removed from the input. 49 | 50 | Example:: 51 | 52 | plaintext = Bifid('phqgmeaylnofdxkrcvszwbuti',5).decipher(ciphertext) 53 | 54 | :param string: The string to decipher. 55 | :returns: The deciphered string. 56 | """ 57 | ret = '' 58 | string = string.upper() 59 | rowseq,colseq = [],[] 60 | # take blocks of length period, reform rowseq,colseq from them 61 | for i in range(0,len(string),self.period): 62 | tempseq = [] 63 | for j in range(0,self.period): 64 | if i+j >= len(string): continue 65 | tempseq.append(int(self.key.index(string[i + j]) / 5)) 66 | tempseq.append(int(self.key.index(string[i + j]) % 5)) 67 | rowseq.extend(tempseq[0:int(len(tempseq)/2)]) 68 | colseq.extend(tempseq[int(len(tempseq)/2):]) 69 | for i in range(len(rowseq)): 70 | ret += self.key[rowseq[i]*5 + colseq[i]] 71 | return ret 72 | 73 | if __name__ == '__main__': 74 | print('use "import pycipher" to access functions') -------------------------------------------------------------------------------- /pycipher/caesar.py: -------------------------------------------------------------------------------- 1 | #implements Caesar substitution cipher 2 | #Author: James Lyons 3 | #Created: 2012-04-28 4 | 5 | from pycipher.base import Cipher 6 | 7 | class Caesar(Cipher): 8 | """The Caesar Cipher has a key consisting of an integer 1-25. 9 | This cipher encrypts a letter according to the following equation:: 10 | 11 | c = (p + key)%26 12 | 13 | where c is the ciphertext letter, p the plaintext letter. 14 | For more details on the Caesar cipher, see http://www.practicalcryptography.com/ciphers/caesar-cipher/ 15 | 16 | :param key: The additive key. Allowable values are integers 0-25. 17 | """ 18 | 19 | def __init__(self,key=13): 20 | self.key = key % 26 21 | 22 | def encipher(self,string,keep_punct=False): 23 | r"""Encipher string using Caesar cipher according to initialised key. 24 | 25 | Example:: 26 | 27 | ciphertext = Caesar(3).encipher(plaintext) 28 | 29 | :param string: The string to encipher. 30 | :param keep_punct: if true, punctuation and spacing are retained. If false, it is all removed. Default is False. 31 | :returns: The enciphered string. 32 | """ 33 | if not keep_punct: string = self.remove_punctuation(string) 34 | ret = '' 35 | for c in string: 36 | if c.isalpha(): ret += self.i2a( self.a2i(c) + self.key ) 37 | else: ret += c 38 | return ret 39 | 40 | def decipher(self,string,keep_punct=False): 41 | r"""Decipher string using Caesar cipher according to initialised key. 42 | 43 | Example:: 44 | 45 | plaintext = Caesar(3).decipher(ciphertext) 46 | 47 | :param string: The string to decipher. 48 | :param keep_punct: if true, punctuation and spacing are retained. If false, it is all removed. Default is False. 49 | :returns: The deciphered string. 50 | """ 51 | if not keep_punct: string = self.remove_punctuation(string) 52 | ret = '' 53 | for c in string: 54 | if c.isalpha(): ret += self.i2a( self.a2i(c) - self.key ) 55 | else: ret += c 56 | return ret 57 | 58 | if __name__ == '__main__': 59 | print('use "import pycipher" to access functions') 60 | -------------------------------------------------------------------------------- /pycipher/columnartransposition.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements Columnar transposition cipher 3 | Author: James Lyons 4 | Created: 2012-04-28 5 | ''' 6 | from pycipher.base import Cipher 7 | 8 | #################################################################################### 9 | class ColTrans(Cipher): 10 | """The Columnar Transposition Cipher is a fractionating cipher, and has a key consisting of a word e.g. 'GERMAN' 11 | For more information, the algorithm can be 12 | seen e.g. http://www.practicalcryptography.com/ciphers/columnar-transposition-cipher/ . 13 | 14 | :param key: The keyword, any word or phrase will do. Must consist of alphabetical characters only, no punctuation of numbers. 15 | """ 16 | 17 | def __init__(self,keyword='GERMAN'): 18 | self.keyword = keyword.upper() 19 | assert len(keyword)>0, 'invalid keyword in init: should be >= 1' 20 | 21 | # return the sorted indices of a word e.g. 'german' = [2,1,5,3,0,4] ''' 22 | def sortind(self,word): 23 | t1 = [(word[i],i) for i in range(len(word))] 24 | t2 = [(k[1],i) for i,k in enumerate(sorted(t1))] 25 | return [q[1] for q in sorted(t2)] 26 | 27 | # return the unsorted indices of a word ''' 28 | def unsortind(self,word): 29 | t1 = [(word[i],i) for i in range(len(word))] 30 | return [q[1] for q in sorted(t1)] 31 | 32 | def encipher(self,string): 33 | """Encipher string using Columnar Transposition cipher according to initialised key. Punctuation and whitespace 34 | are removed from the input. 35 | 36 | Example:: 37 | 38 | ciphertext = ColTrans('GERMAN').encipher(plaintext) 39 | 40 | :param string: The string to encipher. 41 | :returns: The enciphered string. 42 | """ 43 | string = self.remove_punctuation(string) 44 | ret = '' 45 | ind = self.sortind(self.keyword) 46 | for i in range(len(self.keyword)): 47 | ret += string[ind.index(i)::len(self.keyword)] 48 | return ret 49 | 50 | # deciphering is messy because the columns may be ragged ''' 51 | def decipher(self,string): 52 | '''Decipher string using Columnar Transposition cipher according to initialised key. Punctuation and whitespace 53 | are removed from the input. 54 | 55 | Example:: 56 | 57 | plaintext = ColTrans('GERMAN').decipher(ciphertext) 58 | 59 | :param string: The string to decipher. 60 | :returns: The deciphered string. 61 | ''' 62 | string = self.remove_punctuation(string) 63 | ret = ['_']*len(string) 64 | L,M = len(string),len(self.keyword) 65 | ind = self.unsortind(self.keyword) 66 | upto = 0 67 | for i in range(len(self.keyword)): 68 | thiscollen = (int)(L/M) 69 | if ind[i]< L%M: thiscollen += 1 70 | ret[ind[i]::M] = string[upto:upto+thiscollen] 71 | upto += thiscollen 72 | return ''.join(ret) 73 | 74 | if __name__ == '__main__': 75 | print('use "import pycipher" to access functions') -------------------------------------------------------------------------------- /pycipher/delastelle.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements delastelle cipher 3 | Author: James Lyons 4 | Created: 2014-02-12 5 | ''' 6 | from itertools import product 7 | from pycipher.base import Cipher 8 | import re 9 | 10 | #################################################################################### 11 | class Delastelle(Cipher): 12 | """The Delastelle cipher is a simple substitution cipher that outputs 3 characters of ciphertext for each character of plaintext. It has a key consisting 13 | of a 'key cube' 27 characters in length. This cipher is used as part of the Trifid cipher. 14 | For a more detailed look at how it works see http://www.practicalcryptography.com/ciphers/trifid-cipher/. 15 | 16 | :param key: The keycube. The key must by 27 characters in length. 17 | :param chars: the set of characters to use. By default 123 are used. 18 | """ 19 | def __init__(self,key='phqgiumeaylnofdxkrcvst.zwb',chars="123"): 20 | self.key = ''.join([k.upper() for k in key]) 21 | self.chars = chars 22 | assert len(self.key)==27, 'invalid key in init: must have length 27, has length '+str(len(key)) 23 | self.IND2L = dict(zip(list(product(chars,repeat=3)),key)) 24 | self.L2IND = dict(zip(key,list(product(chars,repeat=3)))) 25 | 26 | def encipher(self,string): 27 | """Encipher string using Delastelle cipher according to initialised key. 28 | 29 | Example:: 30 | 31 | ciphertext = Delastelle('APCZ WRLFBDKOTYUQGENHXMIVS').encipher(plaintext) 32 | 33 | :param string: The string to encipher. 34 | :returns: The enciphered string. The ciphertext will be 3 times the length of the plaintext. 35 | """ 36 | string = self.remove_punctuation(string,filter='[^'+self.key+']') 37 | ctext = "" 38 | for c in string: 39 | ctext += ''.join([str(i) for i in L2IND[c]]) 40 | return ctext 41 | 42 | def decipher(self,string): 43 | """Decipher string using Delastelle cipher according to initialised key. 44 | 45 | Example:: 46 | 47 | plaintext = Delastelle('APCZ WRLFBDKOTYUQGENHXMIVS').decipher(ciphertext) 48 | 49 | :param string: The string to decipher. 50 | :returns: The deciphered string. The plaintext will be 1/3 the length of the ciphertext. 51 | """ 52 | string = self.remove_punctuation(string,filter='[^'+self.chars+']') 53 | ret = '' 54 | for i in range(0,len(string),3): 55 | ind = tuple([int(string[i+k]) for k in [0,1,2]]) 56 | ret += IND2L[ind] 57 | return ret 58 | 59 | if __name__ == '__main__': 60 | print('use "import pycipher" to access functions') -------------------------------------------------------------------------------- /pycipher/enigma.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements enigma cipher 3 | Author: James Lyons 4 | Created: 2012-04-28 5 | ''' 6 | from pycipher.base import Cipher 7 | 8 | class Enigma(Cipher): 9 | ''' The Enigma M3 cipher. The key consists of several parameters. 10 | 11 | :param settings: The rotor start positions, consists of 3 characters e.g. ('V','B','Q') 12 | :param rotors: The rotors and their order e.g. (2,3,1). There are 8 possible rotors. 13 | :param reflector: The reflector in use, a single character 'A','B' or 'C' 14 | :param ringstellung: The ring settings, consists of 3 characters e.g. ('V','B','Q') 15 | :param steckers: The plugboard settings, indicating which characters are replaced by which others. Consists of a 10-tuple of 2-tuples e.g. [('P','O'), ('M','L'), ('I','U'), ('K','J'), ('N','H'), ('Y','T'), ('G','B'), ('V','F'), ('R','E'), ('D','C')]. If fewer plugs are required leave them out e.g. a 5-tuple of 2-tuples would be used if 5 plugs were applied. 16 | ''' 17 | def __init__(self,settings=('A','A','A'),rotors=(1,2,3),reflector='B', 18 | ringstellung=('F','V','N'),steckers=[('P','O'),('M','L'), 19 | ('I','U'),('K','J'),('N','H'),('Y','T'),('G','B'),('V','F'), 20 | ('R','E'),('D','C')]): 21 | # Key setting include the following information: 22 | # settings=('A','A','A') - The rotor start positions, 23 | # gets updated with each character enciphered 24 | # rotors=(1,2,3) - The physical rotors used by the enigma, can be numbers between 25 | # 1 and 8 (there were 8 total rotors). The order is important. 26 | # reflector='B' - The physical reflector used by the enigma, can be a letter A,B,C 27 | # ringstellung=('F','V','N') - preset rotor wiring positions 28 | # steckers=(('P','O'),('M','L'),('I','U'),('K','J'),('N','H'), 29 | # ('Y','T'),('G','B'),('V','F'),('R','E'),('D','C'))) 30 | # - performs substitutions according to the pairs. Whenever one letter 31 | # is found it is replaced by the other letter in the pair. Can have up 32 | # to 13 pairs. 33 | self.initsettings = settings # remember the starting settings 34 | self.settings = list(settings) # these settings will be updated each character 35 | self.rotors = tuple([q-1 for q in rotors]) 36 | self.reflector = self.a2i(reflector) 37 | self.ringstellung = ringstellung 38 | self.steckers = steckers 39 | self.rotorkey =("EKMFLGDQVZNTOWYHXUSPAIBRCJ", # rotor keys 40 | "AJDKSIRUXBLHWTMCQGZNPYFVOE", 41 | "BDFHJLCPRTXVZNYEIWGAKMUSQO", 42 | "ESOVPZJAYQUIRHXLNFTGKDCMWB", 43 | "VZBRGITYUPSDNHLXAWMJQOFECK", 44 | "JPGVOUMFYQBENHZRDKASXLICTW", 45 | "NZJHGRCXMYSWBOUFAIVLPEKQDT", 46 | "FKQHTLXOCBJSPDZRAMEWNIUYGV") 47 | self.invrotor =("UWYGADFPVZBECKMTHXSLRINQOJ", # inverse rotor keys 48 | "AJPCZWRLFBDKOTYUQGENHXMIVS", 49 | "TAGBPCSDQEUFVNZHYIXJWLRKOM", 50 | "HZWVARTNLGUPXQCEJMBSKDYOIF", 51 | "QCYLXWENFTZOSMVJUDKGIARPHB", 52 | "SKXQLHCNWARVGMEBJPTYFDZUIO", 53 | "QMGYVPEDRCWTIANUXFKZOSLHJB", 54 | "QJINSAYDVKBFRUHMCPLEWZTGXO") 55 | self.reflectorkey = ("EJMZALYXVBWFCRQUONTSPIKHGD", 56 | "YRUHQSLDPXNGOKMIEBFZCWVJAT", 57 | "FVPJIAOYEDRZXWGCTKUQSBNMHL") 58 | # notches indicate where the rotors increment. rotors 6-8 have two notches 59 | self.notch = (('Q',),('E',),('V',),('J',),('Z',),('Z','M'),('Z','M'),('Z','M')) 60 | 61 | def reset_settings(self): 62 | # Use to restart rotors at initial position. Useful when deciphering. ''' 63 | self.settings = list(self.initsettings) 64 | 65 | def apply_rotor(self,ch,offset,key): 66 | # ch - the character to encode 67 | # offset - how many times has the rotor been incremented 68 | # key - which of the 8 possible rotors is this one ''' 69 | ch = self.subst(ch,key,offset) 70 | return self.subst(ch,offset=-offset) 71 | 72 | def reflect(self,ch): 73 | # performs the reflector substitution. The reflector choice is part of 74 | #the key material. It is represented by an integer 0-2 ''' 75 | return self.subst(ch,self.reflectorkey[self.reflector]) 76 | 77 | def subst(self,ch,key='ABCDEFGHIJKLMNOPQRSTUVWXYZ',offset=0): 78 | # substitute a single character according to the key ''' 79 | index = (self.a2i(ch)+offset)%26 80 | return key[index] 81 | 82 | def apply_steckers(self,ch): 83 | # steckers is a 13-tuple of 2-tuples representing character substitutions ''' 84 | for i in self.steckers: 85 | if ch == i[0]: return i[1] 86 | if ch == i[1]: return i[0] 87 | return ch # no substitution need be performed 88 | 89 | def advance_rotor(self): 90 | # rotors advance the next rotor along depending on their position ''' 91 | if self.settings[1] in self.notch[self.rotors[1]]: 92 | self.settings[0] = self.subst(self.settings[0],offset=1) # increment the position 1 letter 93 | self.settings[1] = self.subst(self.settings[1],offset=1) 94 | if self.settings[2] in self.notch[self.rotors[2]]: 95 | self.settings[1] = self.subst(self.settings[1],offset=1) 96 | self.settings[2] = self.subst(self.settings[2],offset=1) 97 | 98 | def encipher_char(self,ch): 99 | self.advance_rotor() # the rotor gets advanced before the character is enciphered 100 | ch = self.apply_steckers(ch) 101 | for i in [2,1,0]: # go through each of the 3 rotors 102 | offset = ord(self.settings[i])-ord(self.ringstellung[i]) 103 | ch = self.apply_rotor(ch,offset,self.rotorkey[self.rotors[i]]) 104 | ch = self.reflect(ch) 105 | for i in [0,1,2]: # go back through the rotors the other way 106 | offset = ord(self.settings[i])-ord(self.ringstellung[i]) 107 | ch = self.apply_rotor(ch,offset,self.invrotor[self.rotors[i]]) 108 | ch = self.apply_steckers(ch) 109 | return ch 110 | 111 | def decipher(self,string): 112 | """Decipher string using Enigma M3 cipher according to initialised key. Punctuation and whitespace 113 | are removed from the input. The encipher and decipher operations of the Enigma are identical. 114 | 115 | Example:: 116 | 117 | plaintext = Enigma(settings=('A','A','A'),rotors=(1,2,3),reflector='B', 118 | ringstellung=('F','V','N'),steckers=[('P','O'),('M','L'), 119 | ('I','U'),('K','J'),('N','H'),('Y','T'),('G','B'),('V','F'), 120 | ('R','E'),('D','C')])).decipher(ciphertext) 121 | 122 | :param string: The string to decipher. 123 | :returns: The deciphered string. 124 | """ 125 | # enciphering and deciphering are the same operation. ''' 126 | return self.encipher(string) 127 | 128 | def encipher(self,string): 129 | """Encipher string using Enigma M3 cipher according to initialised key. Punctuation and whitespace 130 | are removed from the input. 131 | 132 | Example:: 133 | 134 | ciphertext = Enigma(settings=('A','A','A'),rotors=(1,2,3),reflector='B', 135 | ringstellung=('F','V','N'),steckers=[('P','O'),('M','L'), 136 | ('I','U'),('K','J'),('N','H'),('Y','T'),('G','B'),('V','F'), 137 | ('R','E'),('D','C')])).encipher(plaintext) 138 | 139 | :param string: The string to encipher. 140 | :returns: The enciphered string. 141 | """ 142 | string = self.remove_punctuation(string) 143 | ret = '' 144 | for c in string.upper(): 145 | if c.isalpha(): ret += self.encipher_char(c) 146 | else: ret += c 147 | return ret 148 | 149 | if __name__ == '__main__': 150 | print('use "import pycipher" to access functions') 151 | -------------------------------------------------------------------------------- /pycipher/foursquare.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements Foursquare cipher 3 | Author: James Lyons 4 | Created: 2012-04-28 5 | ''' 6 | from pycipher.base import Cipher 7 | 8 | #################################################################################### 9 | class Foursquare(Cipher): 10 | """The Foursquare Cipher enciphers pairs of characters, the key consists of 2 keysquares, each 25 characters in length. 11 | More information about the algorithm can be 12 | found at http://www.practicalcryptography.com/ciphers/four-square-cipher/. 13 | 14 | :param key1: The first keysquare, as a 25 character string. 15 | :param key2: The second keysquare, as a 25 character string. 16 | """ 17 | def __init__(self,key1='zgptfoihmuwdrcnykeqaxvsbl',key2='mfnbdcrhsaxyogvituewlqzkp'): 18 | self.key1 = [k.upper() for k in key1] 19 | self.key2 = [k.upper() for k in key2] 20 | self.alph = 'ABCDEFGHIKLMNOPQRSTUVWXYZ' # no letter j 21 | assert len(self.key1)==25, 'key1 is not length 25' 22 | assert len(self.key2)==25, 'key2 is not length 25' 23 | 24 | def encipher_pair(self,a,b): 25 | arow,acol = self.alph.index(a)/5, self.alph.index(a)%5 26 | brow,bcol = self.alph.index(b)/5, self.alph.index(b)%5 27 | return (self.key1[arow*5+bcol], self.key2[brow*5+acol]) 28 | 29 | def decipher_pair(self,a,b): 30 | arow,acol = self.key1.index(a)/5, self.key1.index(a)%5 31 | brow,bcol = self.key2.index(b)/5, self.key2.index(b)%5 32 | return (self.alph[arow*5+bcol], self.alph[brow*5+acol]) 33 | 34 | def encipher(self,string): 35 | """Encipher string using Foursquare cipher according to initialised key. Punctuation and whitespace 36 | are removed from the input. If the input plaintext is not an even number of characters, an 'X' will be appended. 37 | 38 | Example:: 39 | 40 | ciphertext = Foursquare(key1='zgptfoihmuwdrcnykeqaxvsbl',key2='mfnbdcrhsaxyogvituewlqzkp').encipher(plaintext) 41 | 42 | :param string: The string to encipher. 43 | :returns: The enciphered string. 44 | """ 45 | string = self.remove_punctuation(string) 46 | if len(string)%2 == 1: string = string + 'X' 47 | ret = '' 48 | for c in range(0,len(string.upper()),2): 49 | a,b = self.encipher_pair(string[c],string[c+1]) 50 | ret += a + b 51 | return ret 52 | 53 | def decipher(self,string): 54 | """Decipher string using Foursquare cipher according to initialised key. Punctuation and whitespace 55 | are removed from the input. The ciphertext should be an even number of characters. If the input ciphertext is not an even number of characters, an 'X' will be appended. 56 | 57 | Example:: 58 | 59 | plaintext = Foursquare(key1='zgptfoihmuwdrcnykeqaxvsbl',key2='mfnbdcrhsaxyogvituewlqzkp').decipher(ciphertext) 60 | 61 | :param string: The string to decipher. 62 | :returns: The deciphered string. 63 | """ 64 | string = self.remove_punctuation(string) 65 | if len(string)%2 == 1: string = string + 'X' 66 | ret = '' 67 | for c in range(0,len(string.upper()),2): 68 | a,b = self.decipher_pair(string[c],string[c+1]) 69 | ret += a + b 70 | return ret 71 | 72 | if __name__ == '__main__': 73 | print('use "import pycipher" to access functions') -------------------------------------------------------------------------------- /pycipher/fracmorse.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements fractionated morse cipher 3 | Author: James Lyons 4 | Created: 2014-01-26 5 | ''' 6 | from pycipher.base import Cipher 7 | import re 8 | import sys 9 | #################################################################################### 10 | class FracMorse(Cipher): 11 | """The Fractionated Morse Cipher has a 26 letter key, similar to that of a substitution cipher. 12 | This cipher first converts the plaintext to morse code, then converts fixed-length chunks 13 | of morse code back to ciphertext letters 14 | """ 15 | def __init__(self,key='ROUNDTABLECFGHIJKMPQSVWXYZ'): 16 | self.key = [k.upper() for k in key] 17 | assert len(key)==26 18 | 19 | table = ['...', '..-', '..x', '.-.', '.--', '.-x', '.x.', '.x-', '.xx', '-..', '-.-', '-.x', '--.', '---', '--x', '-x.', '-x-','-xx', 'x..', 'x.-', 'x.x', 'x-.', 'x--', 'x-x', 'xx.', 'xx-'] 20 | 21 | def encipher(self,string): 22 | """Encipher string using FracMorse cipher according to initialised key. 23 | 24 | Example:: 25 | 26 | ciphertext = FracMorse('ROUNDTABLECFGHIJKMPQSVWXYZ').encipher(plaintext) 27 | 28 | :param string: The string to encipher. 29 | :returns: The enciphered string. 30 | """ 31 | string = string.upper() 32 | #print string 33 | morsestr = self.enmorse(string) 34 | # make sure the morse string is a multiple of 3 in length 35 | if len(morsestr) % 3 == 1: 36 | morsestr = morsestr[0:-1] 37 | elif len(morsestr) % 3 == 2: 38 | morsestr = morsestr + 'x' 39 | #print morsestr 40 | mapping = dict(zip(self.table,self.key)) 41 | ctext = "" 42 | for i in range(0,len(morsestr),3): 43 | ctext += mapping[morsestr[i:i+3]] 44 | return ctext 45 | 46 | def decipher(self,string): 47 | """Decipher string using FracMorse cipher according to initialised key. 48 | 49 | Example:: 50 | 51 | plaintext = FracMorse('ROUNDTABLECFGHIJKMPQSVWXYZ').decipher(ciphertext) 52 | 53 | :param string: The string to decipher. 54 | :returns: The enciphered string. 55 | """ 56 | string = string.upper() 57 | mapping = dict(zip(self.key,self.table)) 58 | ptext = "" 59 | for i in string: 60 | ptext += mapping[i] 61 | return self.demorse(ptext) 62 | 63 | morsetab= {' ': '', '(': '-.--.-', ',': '--..--', '.': '.-.-.-', '0': '-----', '2': '..---', '4': '....-', '6': '-....', '8': '---..', ':': '---...', 'B': '-...', 'D': '-..', 'F': '..-.', 'H': '....', 'J': '.---', 'L': '.-..', 'N': '-.', 'P': '.--.', 'R': '.-.', 'T': '-', 'V': '...-', 'X': '-..-', 'Z': '--..', "'": '.----.', ')': '-.--.-', '-': '-....-', '/': '-..-.', '1': '.----', '3': '...--', '5': '.....', '7': '--...', '9': '----.', ';': '-.-.-.', '?': '..--..', 'A': '.-', 'C': '-.-.', 'E': '.', 'G': '--.', 'I': '..', 'K': '-.-', 'M': '--', 'O': '---', 'Q': '--.-', 'S': '...', 'U': '..-', 'W': '.--', 'Y': '-.--', '_': '..--.-'} 64 | 65 | invmorse={'': ' ', '..--.-': '_', '...--': '3', '--..--': ',', '....-': '4', '.....': '5', '-...': 'B', '-..-': 'X', '.-.': 'R','--.-': 'Q', '--..': 'Z', '.': 'E', '.----.': "'", '..---': '2', '.--': 'W', '.-': 'A', '..': 'I', '-.-.': 'C', '---...': ':', '---': 'O', '-.--': 'Y', '-': 'T', '-..-.': '/', '.-..': 'L', '--.': 'G', '...': 'S', '-.--.-': ')', '..--..': '?', '.----': '1', '-----': '0', '-.-': 'K', '-..': 'D', '----.': '9', '-....': '6', '.---': 'J', '.--.': 'P', '.-.-.-': '.', '--': 'M', '-.': 'N', '....': 'H', '---..': '8', '...-': 'V', '--...': '7', '-.-.-.': ';', '..-': 'U', '..-.': 'F','-....-': '-'} 66 | 67 | # converts plaintext to morse code with 'x's between chars and 'xx' between words 68 | def enmorse(self,string): 69 | string = re.sub("[^ (,.02468:BDFHJLNPRTVXZ')\-/13579;?ACEGIKMOQSUWY_]",'',string) 70 | string = re.sub(" *",' ',string) # replace any double spaces with a single space 71 | string = re.sub(" *$",'',string) # make sure no spaces on the end 72 | ret = "" 73 | for c in string: 74 | ret += self.morsetab[c] + 'x' 75 | return ret 76 | 77 | # converts the morse output of enmorse back to text 78 | def demorse(self,string): 79 | c = 0 80 | ret = "" 81 | if string[-1] != 'x': string = string + 'x' 82 | while c < len(string): 83 | letter = '' 84 | while string[c] != 'x': 85 | letter += string[c] 86 | c+=1 87 | if letter not in self.invmorse: ret+='Q' 88 | else: ret+= self.invmorse[letter] 89 | c+=1 90 | return ret 91 | 92 | 93 | if __name__ == '__main__': 94 | print('use "import pycipher" to access functions') 95 | -------------------------------------------------------------------------------- /pycipher/gronsfeld.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements gronsfeld cipher 3 | Author: James Lyons 4 | Created: 2012-04-28 5 | ''' 6 | from pycipher.base import Cipher 7 | 8 | #################################################################################### 9 | class Gronsfeld(Cipher): 10 | """The Gronsfeld Cipher is similar to the Vigenere Cipher, and has a key consisting of a sequence of numbers 0-9 e.g. [4,9,2,0,2]. 11 | This cipher encrypts a letter according to the Vigenere tableau. More information about the algorithm can be 12 | found at http://www.practicalcryptography.com/ciphers/vigenere-gronsfeld-and-autokey-cipher/ 13 | 14 | :param key: The keyword, any word or phrase will do. Must consist of alphabetical characters only, no punctuation of numbers. 15 | """ 16 | def __init__(self,key=[5, 4, 7, 9, 8, 5, 8, 2, 0, 9, 8, 4, 3]): 17 | self.key = key 18 | 19 | def encipher(self,string): 20 | """Encipher string using Gronsfeld cipher according to initialised key. Punctuation and whitespace 21 | are removed from the input. 22 | 23 | Example:: 24 | 25 | ciphertext = Gronsfeld([5, 4, 7, 9, 8, 5, 8, 2, 0, 9, 8, 4, 3]).encipher(plaintext) 26 | 27 | :param string: The string to encipher. 28 | :returns: The enciphered string. 29 | """ 30 | string = self.remove_punctuation(string) 31 | ret = '' 32 | for (i,c) in enumerate(string): 33 | i = i%len(self.key) 34 | ret += self.i2a(self.a2i(c) + self.key[i]) 35 | return ret 36 | 37 | def decipher(self,string): 38 | """Decipher string using Gronsfeld cipher according to initialised key. Punctuation and whitespace 39 | are removed from the input. 40 | 41 | Example:: 42 | 43 | plaintext = Gronsfeld([5, 4, 7, 9, 8, 5, 8, 2, 0, 9, 8, 4, 3]).decipher(ciphertext) 44 | 45 | :param string: The string to decipher. 46 | :returns: The deciphered string. 47 | """ 48 | string = self.remove_punctuation(string) 49 | ret = '' 50 | for (i,c) in enumerate(string): 51 | i = i%len(self.key) 52 | ret += self.i2a(self.a2i(c) - self.key[i]) 53 | return ret 54 | 55 | 56 | if __name__ == '__main__': 57 | print('use "import pycipher" to access functions') -------------------------------------------------------------------------------- /pycipher/m209.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements american m209 cipher 3 | Author: James Lyons 4 | Created: 2012-04-28 5 | ''' 6 | 7 | from pycipher.base import Cipher 8 | 9 | class M209(Cipher): 10 | ''' The M209 cipher. The key consists of several parameters. 11 | 12 | :param wheel_starts: The rotor start positions, consists of 6 characters e.g. "AAAAAA". Note that not all character combinations are possible, e.g. wheel 6 has only 17 characters. 13 | :param w1s: wheel 1 settings. An array of 26 binary values. 14 | :param w2s: wheel 2 settings. An array of 25 binary values. 15 | :param w3s: wheel 3 settings. An array of 23 binary values. 16 | :param w4s: wheel 4 settings. An array of 21 binary values. 17 | :param w5s: wheel 5 settings. An array of 19 binary values. 18 | :param w6s: wheel 6 settings. An array of 17 binary values. 19 | :param lugpos: The lugs, a 27-tuple of 2-tuples. See below for an example. 20 | 21 | Example:: 22 | 23 | wheel_1_settings = [1,1,0,1,0,0,0,1,1,0,1,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0] 24 | wheel_2_settings = [1,0,0,1,1,0,1,0,0,1,1,1,0,0,1,0,0,1,1,0,1,0,1,0,0] 25 | wheel_3_settings = [1,1,0,0,0,0,1,1,0,1,0,1,1,1,0,0,0,1,1,1,1,0,1] 26 | wheel_4_settings = [0,0,1,0,1,1,0,1,1,0,0,0,1,1,0,1,0,0,1,1,1] 27 | wheel_5_settings = [0,1,0,1,1,1,0,1,1,0,0,0,1,1,0,1,0,0,1] 28 | wheel_6_settings = [1,1,0,1,0,0,0,1,0,0,1,0,0,1,1,0,1] 29 | wheel_starts = "AAAAAA" 30 | lug_positions = ((0,6),(3,6),(1,6),(1,5),(4,5),(0,4),(0,4),(0,4),(0,4), 31 | (2,0),(2,0),(2,0),(2,0),(2,0),(2,0),(2,0),(2,0),(2,0), 32 | (2,0),(2,5),(2,5),(0,5),(0,5),(0,5),(0,5),(0,5),(0,5)) 33 | m = M209(wheel_1_settings, wheel_2_settings, wheel_3_settings 34 | wheel_4_settings, wheel_5_settings, wheel_6_settings 35 | wheel_starts, lug_positions) 36 | 37 | ''' 38 | # these key settings correspond to the wheel settings on wikipedia: http://en.wikipedia.org/wiki/M-209 ''' 39 | def __init__(self,wheel_starts='AAAAAA',w1s=None,w2s=None,w3s=None,w4s=None,w5s=None,w6s=None,lugpos=None): 40 | # 1 corresponds to effective, 0 to ineffective. Position in array determines character. 41 | self.wheel_1_settings = w1s or [1,1,0,1,0,0,0,1,1,0,1,0,1,1,0,0,0,0,1,1,0,1,1,0,0,0] 42 | self.wheel_2_settings = w2s or [1,0,0,1,1,0,1,0,0,1,1,1,0,0,1,0,0,1,1,0,1,0,1,0,0] 43 | self.wheel_3_settings = w3s or [1,1,0,0,0,0,1,1,0,1,0,1,1,1,0,0,0,1,1,1,1,0,1] 44 | self.wheel_4_settings = w4s or [0,0,1,0,1,1,0,1,1,0,0,0,1,1,0,1,0,0,1,1,1] 45 | self.wheel_5_settings = w5s or [0,1,0,1,1,1,0,1,1,0,0,0,1,1,0,1,0,0,1] 46 | self.wheel_6_settings = w6s or [1,1,0,1,0,0,0,1,0,0,1,0,0,1,1,0,1] 47 | self.wheel_starts = [self.a2i(i) for i in wheel_starts] 48 | self.wheel_lengths = (26,25,23,21,19,17) # lengths of each wheel 49 | self.lug_positions = lugpos or ((0,6),(3,6),(1,6),(1,5),(4,5),(0,4),(0,4),(0,4),(0,4), 50 | (2,0),(2,0),(2,0),(2,0),(2,0),(2,0),(2,0),(2,0),(2,0), 51 | (2,0),(2,5),(2,5),(0,5),(0,5),(0,5),(0,5),(0,5),(0,5)) 52 | self.actual_key = [0,0,0,0,0,0] # this is the key the machine sees 53 | self.reset_settings() 54 | 55 | def reset_settings(self): 56 | # the actual key chars are on the other side of the rotors to the wheel_starts 57 | for i in range(0,6): self.actual_key[i] = (self.wheel_starts[i] + 15 - i)%self.wheel_lengths[i] 58 | 59 | def encipher(self,message): 60 | """Encipher string using M209 cipher according to initialised key. Punctuation and whitespace 61 | are removed from the input. 62 | 63 | Example (continuing from the example above):: 64 | 65 | ciphertext = m.encipher(plaintext) 66 | 67 | :param string: The string to encipher. 68 | :returns: The enciphered string. 69 | """ 70 | message = self.remove_punctuation(message) 71 | effective_ch = [0,0,0,0,0,0,0] # these are the wheels which are effective currently, 1 for yes, 0 no 72 | # -the zero at the beginning is extra, indicates lug was in pos 0 73 | ret = '' 74 | # from now we no longer need the wheel starts, we can just increment the actual key 75 | for j in range(len(message)): 76 | shift = 0 77 | effective_ch[0] = 0; 78 | effective_ch[1] = self.wheel_1_settings[self.actual_key[0]] 79 | effective_ch[2] = self.wheel_2_settings[self.actual_key[1]] 80 | effective_ch[3] = self.wheel_3_settings[self.actual_key[2]] 81 | effective_ch[4] = self.wheel_4_settings[self.actual_key[3]] 82 | effective_ch[5] = self.wheel_5_settings[self.actual_key[4]] 83 | effective_ch[6] = self.wheel_6_settings[self.actual_key[5]] 84 | 85 | for i in range(0,27): # implements the cylindrical drum with lugs on it 86 | if effective_ch[self.lug_positions[i][0]] or effective_ch[self.lug_positions[i][1]]: shift+=1 87 | # shift has been found, now actually encrypt letter 88 | ret += self.subst(message[j],key='ZYXWVUTSRQPONMLKJIHGFEDCBA',offset=-shift); # encrypt letter 89 | self.advance_key(); # advance the key wheels 90 | return ret 91 | 92 | def decipher(self,message): 93 | """Decipher string using M209 cipher according to initialised key. Punctuation and whitespace 94 | are removed from the input. The encipher and decipher operations of the M209 are identical. 95 | 96 | Example (continuing from the example above):: 97 | 98 | plaintext = m.decipher(ciphertext) 99 | 100 | :param string: The string to decipher. 101 | :returns: The deciphered string. 102 | """ 103 | return self.encipher(message) 104 | 105 | def subst(self,ch,key,offset=0): 106 | index = (self.a2i(ch) + offset)%26 107 | return key[index] 108 | 109 | def advance_key(self): 110 | # advance each key wheel, noting that each is a different size and wraps at a different length ''' 111 | for i in range(0,6): self.actual_key[i] = (self.actual_key[i] + 1) % self.wheel_lengths[i] 112 | 113 | 114 | if __name__ == '__main__': 115 | print('use "import pycipher" to access functions') -------------------------------------------------------------------------------- /pycipher/playfair.py: -------------------------------------------------------------------------------- 1 | """ 2 | implements Playfair cipher 3 | Author: James Lyons 4 | Created: 2012-04-28 5 | """ 6 | from pycipher.base import Cipher 7 | import re 8 | 9 | 10 | #################################################################################### 11 | class Playfair(Cipher): 12 | """The Playfair Cipher enciphers pairs of characters, the key consists of a keysquare 25 characters in length. 13 | More information about the algorithm can be 14 | found at http://www.practicalcryptography.com/ciphers/playfair-cipher/. 15 | 16 | :param key: The keysquare, as a 25 character string. 17 | """ 18 | def __init__(self, key='ABCDEFGHIKLMNOPQRSTUVWXYZ'): 19 | self.key = [k.upper() for k in key] 20 | 21 | def encipher_pair(self, a, b): 22 | if a == b: 23 | b = 'X' 24 | arow, acol = int(self.key.index(a) / 5), self.key.index(a) % 5 25 | brow, bcol = int(self.key.index(b) / 5), self.key.index(b) % 5 26 | if arow == brow: 27 | return self.key[arow * 5 + (acol + 1) % 5] + self.key[brow * 5 + (bcol + 1) % 5] 28 | elif acol == bcol: 29 | return self.key[((arow + 1) % 5) * 5 + acol] + self.key[((brow + 1) % 5) * 5 + bcol] 30 | else: 31 | return self.key[arow * 5 + bcol] + self.key[brow * 5 + acol] 32 | 33 | def decipher_pair(self, a, b): 34 | assert a != b, 'two of the same letters occurred together, illegal in playfair' 35 | arow, acol = int(self.key.index(a) / 5), self.key.index(a) % 5 36 | brow, bcol = int(self.key.index(b) / 5), self.key.index(b) % 5 37 | if arow == brow: 38 | return self.key[arow * 5 + (acol - 1) % 5] + self.key[brow * 5 + (bcol - 1) % 5] 39 | elif acol == bcol: 40 | return self.key[((arow - 1) % 5) * 5 + acol] + self.key[((brow - 1) % 5) * 5 + bcol] 41 | else: 42 | return self.key[arow * 5 + bcol] + self.key[brow * 5 + acol] 43 | 44 | def encipher(self, string): 45 | """Encipher string using Playfair cipher according to initialised key. Punctuation and whitespace 46 | are removed from the input. If the input plaintext is not an even number of characters, an 'X' will be appended. 47 | 48 | Example:: 49 | 50 | ciphertext = Playfair(key='zgptfoihmuwdrcnykeqaxvsbl').encipher(plaintext) 51 | 52 | :param string: The string to encipher. 53 | :returns: The enciphered string. 54 | """ 55 | string = self.remove_punctuation(string) 56 | string = re.sub(r'[J]', 'I', string) 57 | if len(string) % 2 == 1: 58 | string += 'X' 59 | ret = '' 60 | for c in range(0, len(string), 2): 61 | ret += self.encipher_pair(string[c], string[c + 1]) 62 | return ret 63 | 64 | def decipher(self, string): 65 | """Decipher string using Playfair cipher according to initialised key. Punctuation and whitespace 66 | are removed from the input. The ciphertext should be an even number of characters. If the input ciphertext is not an even number of characters, an 'X' will be appended. 67 | 68 | Example:: 69 | 70 | plaintext = Playfair(key='zgptfoihmuwdrcnykeqaxvsbl').decipher(ciphertext) 71 | 72 | :param string: The string to decipher. 73 | :returns: The deciphered string. 74 | """ 75 | string = self.remove_punctuation(string) 76 | if len(string) % 2 == 1: 77 | string += 'X' 78 | ret = '' 79 | for c in range(0, len(string), 2): 80 | ret += self.decipher_pair(string[c], string[c + 1]) 81 | return ret 82 | 83 | if __name__ == '__main__': 84 | print('use "import pycipher" to access functions') 85 | -------------------------------------------------------------------------------- /pycipher/polybius.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements pycipher.polybius square cipher 3 | Author: James Lyons 4 | Created: 2012-04-28 5 | ''' 6 | from pycipher.base import Cipher 7 | import re 8 | 9 | #################################################################################### 10 | class PolybiusSquare(Cipher): 11 | """The Polybius square is a simple substitution cipher that outputs 2 characters of ciphertext for each character of plaintext. It has a key consisting 12 | which depends on 'size'. By default 'size' is 5, and the key is 25 letters (5^2). For a size of 6 a 36 letter key required etc. 13 | For a more detailed look at how it works see http://www.practicalcryptography.com/ciphers pycipher.polybius-square-cipher/. 14 | 15 | :param key: The keysquare, each row one after the other. The key must by size^2 characters in length. 16 | :param size: The size of the keysquare, if size=5, the keysquare uses 5^2 or 25 characters. 17 | :param chars: the set of characters to use. By default ABCDE are used, this parameter should have the same length as size. 18 | """ 19 | def __init__(self,key='phqgiumeaylnofdxkrcvstzwb',size=5,chars=None): 20 | self.key = ''.join([k.upper() for k in key]) 21 | self.chars = chars or 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[:size] 22 | self.size = size 23 | assert len(self.key)==size*size, 'invalid key in init: must have length size*size, has length '+str(len(key)) 24 | assert len(self.chars)==size, 'invalid chars in init: must have length=size, has length '+str(len(chars)) 25 | 26 | def encipher_char(self,ch): 27 | row = (int)(self.key.index(ch) / self.size) 28 | col = (self.key.index(ch) % self.size) 29 | return self.chars[row] + self.chars[col] 30 | 31 | def decipher_pair(self,pair): 32 | row = self.chars.index(pair[0]) 33 | col = self.chars.index(pair[1]) 34 | return self.key[row*self.size + col] 35 | 36 | def encipher(self,string): 37 | """Encipher string using Polybius square cipher according to initialised key. 38 | 39 | Example:: 40 | 41 | ciphertext = Polybius('APCZWRLFBDKOTYUQGENHXMIVS',5,'MKSBU').encipher(plaintext) 42 | 43 | :param string: The string to encipher. 44 | :returns: The enciphered string. The ciphertext will be twice the length of the plaintext. 45 | """ 46 | string = self.remove_punctuation(string)#,filter='[^'+self.key+']') 47 | ret = '' 48 | for c in range(0,len(string)): 49 | ret += self.encipher_char(string[c]) 50 | return ret 51 | 52 | def decipher(self,string): 53 | """Decipher string using Polybius square cipher according to initialised key. 54 | 55 | Example:: 56 | 57 | plaintext = Polybius('APCZWRLFBDKOTYUQGENHXMIVS',5,'MKSBU').decipher(ciphertext) 58 | 59 | :param string: The string to decipher. 60 | :returns: The deciphered string. The plaintext will be half the length of the ciphertext. 61 | """ 62 | string = self.remove_punctuation(string)#,filter='[^'+self.chars+']') 63 | ret = '' 64 | for i in range(0,len(string),2): 65 | ret += self.decipher_pair(string[i:i+2]) 66 | return ret 67 | 68 | if __name__ == '__main__': 69 | print('use "import pycipher" to access functions') -------------------------------------------------------------------------------- /pycipher/porta.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements porta cipher 3 | Author: James Lyons 4 | Created: 2014-02-12 5 | ''' 6 | from pycipher.base import Cipher 7 | 8 | #################################################################################### 9 | class Porta(Cipher): 10 | """The Porta Cipher is a polyalphabetic substitution cipher, and has a key consisting of a word e.g. 'FORTIFICATION'. 11 | 12 | :param key: The keyword, any word or phrase will do. Must consist of alphabetical characters only, no punctuation of numbers. 13 | """ 14 | def __init__(self,key='FORTIFICATION'): 15 | self.key = [k.upper() for k in key] 16 | 17 | def encipher(self,string): 18 | """Encipher string using Porta cipher according to initialised key. Punctuation and whitespace 19 | are removed from the input. 20 | 21 | Example:: 22 | 23 | ciphertext = Porta('HELLO').encipher(plaintext) 24 | 25 | :param string: The string to encipher. 26 | :returns: The enciphered string. 27 | """ 28 | string = self.remove_punctuation(string) 29 | ret = '' 30 | for (i,c) in enumerate(string): 31 | i = i%len(self.key) 32 | if self.key[i] in 'AB': ret += 'NOPQRSTUVWXYZABCDEFGHIJKLM'[self.a2i(c)] 33 | elif self.key[i] in 'YZ': ret += 'ZNOPQRSTUVWXYBCDEFGHIJKLMA'[self.a2i(c)] 34 | elif self.key[i] in 'WX': ret += 'YZNOPQRSTUVWXCDEFGHIJKLMAB'[self.a2i(c)] 35 | elif self.key[i] in 'UV': ret += 'XYZNOPQRSTUVWDEFGHIJKLMABC'[self.a2i(c)] 36 | elif self.key[i] in 'ST': ret += 'WXYZNOPQRSTUVEFGHIJKLMABCD'[self.a2i(c)] 37 | elif self.key[i] in 'QR': ret += 'VWXYZNOPQRSTUFGHIJKLMABCDE'[self.a2i(c)] 38 | elif self.key[i] in 'OP': ret += 'UVWXYZNOPQRSTGHIJKLMABCDEF'[self.a2i(c)] 39 | elif self.key[i] in 'MN': ret += 'TUVWXYZNOPQRSHIJKLMABCDEFG'[self.a2i(c)] 40 | elif self.key[i] in 'KL': ret += 'STUVWXYZNOPQRIJKLMABCDEFGH'[self.a2i(c)] 41 | elif self.key[i] in 'IJ': ret += 'RSTUVWXYZNOPQJKLMABCDEFGHI'[self.a2i(c)] 42 | elif self.key[i] in 'GH': ret += 'QRSTUVWXYZNOPKLMABCDEFGHIJ'[self.a2i(c)] 43 | elif self.key[i] in 'EF': ret += 'PQRSTUVWXYZNOLMABCDEFGHIJK'[self.a2i(c)] 44 | elif self.key[i] in 'CD': ret += 'OPQRSTUVWXYZNMABCDEFGHIJKL'[self.a2i(c)] 45 | return ret 46 | 47 | def decipher(self,string): 48 | """Decipher string using Porta cipher according to initialised key. Punctuation and whitespace 49 | are removed from the input. For the Porta cipher, enciphering and deciphering are the same operation. 50 | 51 | Example:: 52 | 53 | plaintext = Porta('HELLO').decipher(ciphertext) 54 | 55 | :param string: The string to decipher. 56 | :returns: The deciphered string. 57 | """ 58 | return self.encipher(string) 59 | 60 | if __name__ == '__main__': 61 | print('use "import pycipher" to access functions') 62 | -------------------------------------------------------------------------------- /pycipher/railfence.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements Railfence cipher 3 | Author: James Lyons 4 | Created: 2014-02-10 5 | ''' 6 | from pycipher.base import Cipher 7 | 8 | #################################################################################### 9 | class Railfence(Cipher): 10 | """The Railfence Cipher has a single number that forms the key. 11 | For more info on the Railfence cipher see http://www.practicalcryptography.com/ciphers/rail-fence-cipher/ . 12 | 13 | :param key: an integer, must be greater than zero. 14 | """ 15 | 16 | def __init__(self,key=5): 17 | self.key = key 18 | assert 0 < self.key, 'invalid key: key='+str(key)+', must be greater than zero' 19 | 20 | def encipher(self,string,keep_punct=False): 21 | """Encipher string using Railfence cipher according to initialised key. 22 | 23 | Example:: 24 | 25 | ciphertext = Railfence(3).encipher(plaintext) 26 | 27 | :param string: The string to encipher. 28 | :param keep_punct: if true, punctuation and spacing are retained. If false, it is all removed. Default is False. 29 | :returns: The enciphered string. 30 | """ 31 | if not keep_punct: string = self.remove_punctuation(string) 32 | return ''.join(self.buildfence(string, self.key)) 33 | 34 | def decipher(self,string,keep_punct=False): 35 | """Decipher string using Railfence cipher according to initialised key. 36 | 37 | Example:: 38 | 39 | plaintext = Railfence(3).decipher(ciphertext) 40 | 41 | :param string: The string to decipher. 42 | :param keep_punct: if true, punctuation and spacing are retained. If false, it is all removed. Default is False. 43 | :returns: The deciphered string. 44 | """ 45 | if not keep_punct: string = self.remove_punctuation(string) 46 | ind = range(len(string)) 47 | pos = self.buildfence(ind, self.key) 48 | return ''.join(string[pos.index(i)] for i in ind) 49 | 50 | def buildfence(self,chars, numrails): 51 | fence = [[None] * len(chars) for n in range(numrails)] 52 | rails = list(range(numrails - 1)) + list(range(numrails - 1, 0, -1)) 53 | for n, x in enumerate(chars): 54 | fence[rails[n % len(rails)]][n] = x 55 | return [c for rail in fence for c in rail if c is not None] 56 | 57 | if __name__ == '__main__': 58 | print('use "import pycipher" to access functions') 59 | -------------------------------------------------------------------------------- /pycipher/rot13.py: -------------------------------------------------------------------------------- 1 | #implements rot13 cipher 2 | #Author: James Lyons 3 | #Created: 2014-02-09 4 | 5 | from pycipher.base import Cipher 6 | 7 | class Rot13(Cipher): 8 | """The Rot13 Cipher has no key, it is commonly used just to hide text. 9 | This cipher encrypts a letter according to the following equation:: 10 | 11 | c = (p + 13)%26 12 | 13 | where c is the ciphertext letter, p the plaintext letter. This is equivalent to the Caesar cipher with a key of 13. 14 | For more details on the rot13 cipher, see http://www.practicalcryptography.com/ciphers/rot13-cipher/ . 15 | """ 16 | 17 | def __init__(self): 18 | pass 19 | 20 | def encipher(self,string,keep_punct=False): 21 | r"""Encipher string using rot13 cipher. 22 | 23 | Example:: 24 | 25 | ciphertext = Rot13().encipher(plaintext) 26 | 27 | :param string: The string to encipher. 28 | :param keep_punct: if true, punctuation and spacing are retained. If false, it is all removed. Default is False. 29 | :returns: The enciphered string. 30 | """ 31 | if not keep_punct: string = self.remove_punctuation(string) 32 | ret = '' 33 | for c in string: 34 | if c.isalpha(): ret += self.i2a( self.a2i(c) + 13 ) 35 | else: ret += c 36 | return ret 37 | 38 | def decipher(self,string,keep_punct=False): 39 | r"""Decipher string using rot13 cipher. The Deciphering and enciphering operations are identical. 40 | 41 | Example:: 42 | 43 | plaintext = Rot13().decipher(ciphertext) 44 | 45 | :param string: The string to decipher. 46 | :param keep_punct: if true, punctuation and spacing are retained. If false, it is all removed. Default is False. 47 | :returns: The deciphered string. 48 | """ 49 | return self.encipher(string,keep_punct) 50 | 51 | if __name__ == '__main__': 52 | print('use "import pycipher" to access functions') 53 | -------------------------------------------------------------------------------- /pycipher/simplesubstitution.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements simple substitution cipher 3 | Author: James Lyons 4 | Created: 2012-04-28 5 | ''' 6 | 7 | from pycipher.base import Cipher 8 | 9 | class SimpleSubstitution(Cipher): 10 | """The Simple Substitution Cipher has a key consisting of the letters A-Z jumbled up. 11 | e.g. 'AJPCZWRLFBDKOTYUQGENHXMIVS' 12 | This cipher encrypts a letter according to the following equation:: 13 | 14 | plaintext = ABCDEFGHIJKLMNOPQRSTUVWXYZ 15 | ciphertext = AJPCZWRLFBDKOTYUQGENHXMIVS 16 | 17 | To convert a plaintext letter into ciphertext, read along the plaintext row until the desired 18 | letter is found, then substitute it with the letter below it. For more information see http://www.practicalcryptography.com/ciphers/simple-substitution-cipher/. 19 | 20 | :param key: The key, a permutation of the 26 characters of the alphabet. 21 | """ 22 | def __init__(self,key='AJPCZWRLFBDKOTYUQGENHXMIVS'): 23 | assert len(key) == 26 24 | self.key = [k.upper() for k in key] 25 | self.invkey = '' 26 | 27 | def encipher(self,string,keep_punct=False): 28 | """Encipher string using Simple Substitution cipher according to initialised key. 29 | 30 | Example:: 31 | 32 | ciphertext = SimpleSubstitution('AJPCZWRLFBDKOTYUQGENHXMIVS').encipher(plaintext) 33 | 34 | :param string: The string to encipher. 35 | :param keep_punct: if true, punctuation and spacing are retained. If false, it is all removed. Default is False. 36 | :returns: The enciphered string. 37 | """ 38 | if not keep_punct: string = self.remove_punctuation(string) 39 | ret = '' 40 | for c in string.upper(): 41 | if c.isalpha(): ret += self.key[self.a2i(c)] 42 | else: ret += c 43 | return ret 44 | 45 | def decipher(self,string,keep_punct=False): 46 | """Decipher string using Simple Substitution cipher according to initialised key. 47 | 48 | Example:: 49 | 50 | plaintext = SimpleSubstitution('AJPCZWRLFBDKOTYUQGENHXMIVS').decipher(ciphertext) 51 | 52 | :param string: The string to decipher. 53 | :param keep_punct: if true, punctuation and spacing are retained. If false, it is all removed. Default is False. 54 | :returns: The deciphered string. 55 | """ 56 | # if we have not yet calculated the inverse key, calculate it now 57 | if self.invkey == '': 58 | for i in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ': 59 | self.invkey += self.i2a(self.key.index(i)) 60 | if not keep_punct: string = self.remove_punctuation(string) 61 | ret = '' 62 | for c in string.upper(): 63 | if c.isalpha(): ret += self.invkey[self.a2i(c)] 64 | else: ret += c 65 | return ret 66 | 67 | if __name__ == '__main__': 68 | print('use "import pycipher" to access functions') 69 | -------------------------------------------------------------------------------- /pycipher/util.py: -------------------------------------------------------------------------------- 1 | ''' 2 | some statistics routines for cryptanalysis 3 | ''' 4 | import math 5 | import re 6 | 7 | def ic(ctext): 8 | ''' takes ciphertext, calculates index of coincidence.''' 9 | counts = ngram_count(ctext,N=1) 10 | icval = 0 11 | for k in counts.keys(): 12 | icval += counts[k]*(counts[k]-1) 13 | icval /= (len(ctext)*(len(ctext)-1)) 14 | return icval 15 | 16 | def ngram_count(text,N=1,keep_punct=False): 17 | ''' if N=1, return a dict containing each letter along with how many times the letter occurred. 18 | if N=2, returns a dict containing counts of each bigram (pair of letters) 19 | etc. 20 | There is an option to remove all spaces and punctuation prior to processing ''' 21 | if not keep_punct: text = re.sub('[^A-Z]','',text.upper()) 22 | count = {} 23 | for i in range(len(text)-N+1): 24 | c = text[i:i+N] 25 | if c in count: count[c] += 1 26 | else: count[c] = 1.0 27 | return count 28 | 29 | def ngram_freq(text,N=1,log=False,floor=0.01): 30 | ''' returns the n-gram frequencies of all n-grams encountered in text. 31 | Option to return log probabilities or standard probabilities. 32 | Note that only n-grams occurring in 'text' will have probabilities. 33 | For the probability of not-occurring n-grams, use freq['floor']. 34 | This is set to floor/len(text) ''' 35 | freq = ngram_count(text,N) 36 | L = 1.0*(len(text)-N+1) 37 | for c in freq.keys(): 38 | if log: freq[c] = math.log10(freq[c]/L) 39 | else: freq[c] = freq[c]/L 40 | if log: freq['floor'] = math.log10(floor/L) 41 | else: freq['floor'] = floor/L 42 | return freq 43 | 44 | def restore_punctuation(original,modified): 45 | ''' If punctuation was accidently removed, use this function to restore it. 46 | requires the orignial string with punctuation. ''' 47 | ret = '' 48 | count = 0 49 | try: 50 | for c in original: 51 | if c.isalpha(): 52 | ret+=modified[count] 53 | count+=1 54 | else: ret+=c 55 | except IndexError: 56 | print('restore_punctuation: strings must have same number of alphabetic chars') 57 | raise 58 | return ret 59 | 60 | 61 | def keyword_to_key(word,alphabet='ABCDEFGHIJKLMNOPQRSTUVWXYZ'): 62 | ''' convert a key word to a key by appending on the other letters of the alphabet. 63 | e.g. MONARCHY -> MONARCHYBDEFGIJKLPQSTUVWXZ 64 | ''' 65 | ret = '' 66 | word = (word + alphabet).upper() 67 | for i in word: 68 | if i in ret: continue 69 | ret += i 70 | return ret 71 | -------------------------------------------------------------------------------- /pycipher/vigenere.py: -------------------------------------------------------------------------------- 1 | ''' 2 | implements vigenere cipher 3 | Author: James Lyons 4 | Created: 2012-04-28 5 | ''' 6 | from pycipher.base import Cipher 7 | 8 | #################################################################################### 9 | class Vigenere(Cipher): 10 | """The Vigenere Cipher has a key consisting of a word e.g. 'FORTIFICATION'. 11 | This cipher encrypts a letter according to the Vigenere tableau, the algorithm can be 12 | seen e.g. http://practicalcryptography.com/ciphers/vigenere-gronsfeld-and-autokey-cipher/ 13 | 14 | :param key: The keyword, any word or phrase will do. Must consist of alphabetical characters only, no punctuation of numbers. 15 | """ 16 | def __init__(self,key='fortification'): 17 | self.key = [k.upper() for k in key] 18 | 19 | def encipher(self,string): 20 | """Encipher string using Vigenere cipher according to initialised key. Punctuation and whitespace 21 | are removed from the input. 22 | 23 | Example:: 24 | 25 | ciphertext = Vigenere('HELLO').encipher(plaintext) 26 | 27 | :param string: The string to encipher. 28 | :returns: The enciphered string. 29 | """ 30 | string = self.remove_punctuation(string) 31 | ret = '' 32 | for (i,c) in enumerate(string): 33 | i = i%len(self.key) 34 | ret += self.i2a(self.a2i(c) + self.a2i(self.key[i])) 35 | return ret 36 | 37 | def decipher(self,string): 38 | """Decipher string using Vigenere cipher according to initialised key. Punctuation and whitespace 39 | are removed from the input. 40 | 41 | Example:: 42 | 43 | plaintext = Vigenere('HELLO').decipher(ciphertext) 44 | 45 | :param string: The string to decipher. 46 | :returns: The enciphered string. 47 | """ 48 | string = self.remove_punctuation(string) 49 | ret = '' 50 | for (i,c) in enumerate(string): 51 | i = i%len(self.key) 52 | ret += self.i2a(self.a2i(c) - self.a2i(self.key[i])) 53 | return ret 54 | 55 | if __name__ == '__main__': 56 | print('use "import pycipher" to access functions') 57 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup, find_packages 3 | 4 | 5 | setup( 6 | name='pycipher', 7 | version='0.5.1', 8 | description='Several simple cipher algorithms', 9 | author='James Lyons', 10 | author_email='james.lyons0@gmail.com', 11 | 12 | packages=find_packages(exclude=['tests','tests.*']), 13 | include_package_data=True, # declarations in MANIFEST.in 14 | url='https://github.com/jameslyons/pycipher', 15 | test_suite='tests', 16 | 17 | classifiers=[ 18 | 'Development Status :: 3 - Alpha', 19 | 'License :: OSI Approved :: MIT License', 20 | 'Intended Audience :: Developers', 21 | 'Operating System :: OS Independent', 22 | 'Programming Language :: Python', 23 | ], 24 | long_description=open('README.rst', 'rt').read() 25 | ) 26 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | #import test_adfgvx 2 | #from unittest import TestSuite 3 | 4 | #def suite(): 5 | # return TestSuite((TestADFGX,TestADFGVX,TestAutokey,TestEnigma,TestAffine,TestSimple,TestCaesar,TestM209)) 6 | -------------------------------------------------------------------------------- /tests/test_adfgvx.py: -------------------------------------------------------------------------------- 1 | import pycipher 2 | import unittest 3 | 4 | class TestADFGVX(unittest.TestCase): 5 | def test_encipher(self): 6 | keys = (('ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8','GERMAN'), 7 | ('dxkr3cvs5zw7bj9uti8ph0qg64mea1yl2nof','german'), 8 | ('ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8','ciphers')) 9 | plaintext = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 10 | ciphertext = ('GFXFFGXGDFAXDAVGDGXVADAAXXXFDDFGGGFDFAXDAVGDVDAGFAXVVXFDDFGGGFVVVAGFFAVVVAGFFAGXVADAAXXVDAGFAXVVGFXFFGXG', 11 | 'AXFXXAFAVAGFVGDDXVAXDFDDAAAGFFGVVVGVAGFVGDDXFVGAXGFDDAGFFGVVVGXXXDVGGDXXXDVGGDVAXDFDDAAFVGAXGFDDAXFXXAFA', 12 | 'DXADFFGXGVXDAGGGVXDAGGGDDFGVXVXFVDAXGDXADFFGXGDDFGVXVVAAFGXAVFXAAAVFFVGFGFDVAAFGXAVFXAAAVFFVGFGFDXFVDAXG') 13 | for i,key in enumerate(keys): 14 | enc = pycipher.ADFGVX(*key).encipher(plaintext) 15 | self.assertEqual(enc.upper(), ciphertext[i].upper()) 16 | 17 | def test_decipher(self): 18 | keys = (('ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8','german'), 19 | ('dxkr3cvs5zw7bj9uti8ph0qg64mea1yl2nof','GERMAN'))#, 20 | #('ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8','ciphers')) 21 | plaintext = ('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 22 | 'Z1QI5T3XOYPBS9UDRH6N2GE87MZ1QI5T3XOYPBS9UDRH6N2GE87M') 23 | ciphertext =('GFXFFGXGDFAXDAVGDGXVADAAXXXFDDFGGGFDFAXDAVGDVDAGFAXVVXFDDFGGGFVVVAGFFAVVVAGFFAGXVADAAXXVDAGFAXVVGFXFFGXG') 24 | 25 | for i,key in enumerate(keys): 26 | dec = pycipher.ADFGVX(*key).decipher(ciphertext) 27 | self.assertEqual(dec.upper(), plaintext[i].upper()) 28 | 29 | if __name__ == '__main__': 30 | unittest.main() 31 | -------------------------------------------------------------------------------- /tests/test_adfgx.py: -------------------------------------------------------------------------------- 1 | from pycipher import ADFGX 2 | import unittest 3 | 4 | class TestADFGX(unittest.TestCase): 5 | 6 | def test_encipher(self): 7 | keys = (('ypgknveqzxmdhwcblfisrauto','GERMAN'), 8 | ('haoqkzdmpieslycbwvgufntrx','CIPHERS')) 9 | plaintext = ('abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz', 10 | 'abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz') 11 | ciphertext = ('FGGGXXXDXFAGFAGDADDFGADXAAADFGXFGGGXFAGFAGDAGDFAADXFDADFGXFGGGXFGDXAFXXFGDXAFXDDFGADXAAGDFAADXFDFGGGXXXD', 12 | 'ADAXAGGGFADFAFXFADFAFXDFAFFFFDAXXDGXDADAXAGGGDFAFFFFDXGXXGGXGADFDDGADGADXXFXGXXGGXGADFDDGADGADXXFAXXDGXD') 13 | for i,key in enumerate(keys): 14 | enc = ADFGX(*key).encipher(plaintext[i]) 15 | self.assertEqual(enc.upper(), ciphertext[i].upper()) 16 | 17 | def test_decipher(self): 18 | keys = (('ypgknveqzxmdhwcblfisrauto','GERMAN'), 19 | ('haoqkzdmpieslycbwvgufntrx','CIPHERS')) 20 | plaintext = ('abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz', 21 | 'abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz') 22 | ciphertext = ('FGGGXXXDXFAGFAGDADDFGADXAAADFGXFGGGXFAGFAGDAGDFAADXFDADFGXFGGGXFGDXAFXXFGDXAFXDDFGADXAAGDFAADXFDFGGGXXXD', 23 | 'ADAXAGGGFADFAFXFADFAFXDFAFFFFDAXXDGXDADAXAGGGDFAFFFFDXGXXGGXGADFDDGADGADXXFXGXXGGXGADFDDGADGADXXFAXXDGXD') 24 | for i,key in enumerate(keys): 25 | dec = ADFGX(*key).decipher(ciphertext[i]) 26 | self.assertEqual(dec.upper(), plaintext[i].upper()) 27 | 28 | if __name__ == '__main__': 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /tests/test_affine.py: -------------------------------------------------------------------------------- 1 | from pycipher.affine import Affine 2 | import unittest 3 | 4 | class TestAffine(unittest.TestCase): 5 | 6 | def test_decipher(self): 7 | ''' AffineSubstitution (test_decipher): test known ciphertext->plaintext pairs ''' 8 | text = 'pmjgdaxurolifczwtqnkhebyvspmjgdaxurolifczwtqnkhebyvs' 9 | declist = ['yfmtahovcjqxelszgnubipwdkryfmtahovcjqxelszgnubipwdkr', 10 | 'onmlkjihgfedcbazyxwvutsrqponmlkjihgfedcbazyxwvutsrqp', 11 | 'jarizqhypgxofwnevmdulctkbsjarizqhypgxofwnevmdulctkbs', 12 | 'pmjgdaxurolifczwtqnkhebyvspmjgdaxurolifczwtqnkhebyvs', 13 | 'tmfyrkdwpibungzslexqjcvohatmfyrkdwpibungzslexqjcvoha', 14 | 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz'] 15 | for i,key in enumerate(((7,3),(3,25),(9,12),(1,0),(19,18),(23,15))): 16 | a,b = key 17 | dec = Affine(a,b).decipher(text) 18 | self.assertEqual(dec.upper(), declist[i].upper()) 19 | 20 | def test_encipher(self): 21 | ''' AffineSubstitution (test_encipher): test known plaintext->ciphertext pairs ''' 22 | text = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 23 | enclist = ['hijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg', 24 | 'dgjmpsvybehknqtwzcfiloruxadgjmpsvybehknqtwzcfiloruxa', 25 | 'afkpuzejotydinsxchmrwbglqvafkpuzejotydinsxchmrwbglqv', 26 | 'ovcjqxelszgnubipwdkryfmtahovcjqxelszgnubipwdkryfmtah', 27 | 'sbktcludmvenwfoxgpyhqzirajsbktcludmvenwfoxgpyhqziraj', 28 | 'pmjgdaxurolifczwtqnkhebyvspmjgdaxurolifczwtqnkhebyvs'] 29 | for i,key in enumerate(((1,7),(3,3),(5,0),(7,14),(9,18),(23,15))): 30 | a,b = key 31 | enc = Affine(a,b).encipher(text) 32 | self.assertEqual(enc.upper(), enclist[i].upper()) 33 | 34 | def test_punctuation(self): 35 | ''' AffineSubstitution (test_punctuation): punctuation should be unmodified ''' 36 | e = Affine(a=7,b=8) 37 | original = '!@$%%^&*()_-+={}[]|":;<>,./?' 38 | enciphered = e.encipher(original,keep_punct=True) 39 | self.assertEqual(original.upper(), enciphered.upper()) 40 | e = Affine(a=7,b=8) 41 | original = '!@$%%^&*()_-+={}[]|":;<>,./?' 42 | enciphered = e.encipher(original,keep_punct=False) 43 | self.assertEqual('', enciphered.upper()) 44 | -------------------------------------------------------------------------------- /tests/test_atbash.py: -------------------------------------------------------------------------------- 1 | from pycipher import Atbash 2 | import unittest 3 | 4 | class TestAtbash(unittest.TestCase): 5 | 6 | def test_decipher(self): 7 | text = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 8 | declist = ['zyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcba'] 9 | dec = Atbash().decipher(text) 10 | self.assertEqual(dec.upper(), declist[0].upper()) 11 | 12 | def test_encipher(self): 13 | text = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 14 | enclist = ['zyxwvutsrqponmlkjihgfedcbazyxwvutsrqponmlkjihgfedcba'] 15 | enc = Atbash().encipher(text) 16 | self.assertEqual(enc.upper(), enclist[0].upper()) 17 | -------------------------------------------------------------------------------- /tests/test_autokey.py: -------------------------------------------------------------------------------- 1 | from pycipher import Autokey 2 | import unittest 3 | 4 | class TestAutokey(unittest.TestCase): 5 | 6 | def test_encipher(self): 7 | keys = ('GERMAN', 8 | 'CIPHERS') 9 | plaintext = ('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 10 | 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz') 11 | ciphertext = ('gftpesgikmoqsuwyacegikmoqsuwyacegikmoqsuwyacegikmoqs', 12 | 'cjrkiwyhjlnprtvxzbdfhjlnprtvxzbdfhjlnprtvxzbdfhjlnpr') 13 | for i,key in enumerate(keys): 14 | enc = Autokey(key).encipher(plaintext[i]) 15 | self.assertEqual(enc.upper(), ciphertext[i].upper()) 16 | 17 | def test_decipher(self): 18 | keys = ('GERMAN', 19 | 'CIPHERS') 20 | plaintext= ('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 21 | 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz') 22 | ciphertext = ('gftpesgikmoqsuwyacegikmoqsuwyacegikmoqsuwyacegikmoqs', 23 | 'cjrkiwyhjlnprtvxzbdfhjlnprtvxzbdfhjlnprtvxzbdfhjlnpr') 24 | for i,key in enumerate(keys): 25 | dec = Autokey(key).decipher(ciphertext[i]) 26 | self.assertEqual(dec.upper(), plaintext[i].upper()) 27 | 28 | if __name__ == '__main__': 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /tests/test_beaufort.py: -------------------------------------------------------------------------------- 1 | from pycipher import Beaufort 2 | import unittest 3 | 4 | class TestBeaufort(unittest.TestCase): 5 | 6 | def test_encipher(self): 7 | keys = ('GERMAN', 8 | 'CIPHERS') 9 | plaintext = ('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 10 | 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz') 11 | ciphertext = ('gdpjwiaxjdqcurdxkwolxreqifrlykczlfsewtfzmyqnztgskhtn', 12 | 'chneammvagxtffotzqmyyhmsjfrraflcykktyevrddmrxokwwfkq') 13 | for i,key in enumerate(keys): 14 | enc = Beaufort(key).encipher(plaintext[i]) 15 | self.assertEqual(enc.upper(), ciphertext[i].upper()) 16 | 17 | def test_decipher(self): 18 | keys = ('GERMAN', 19 | 'CIPHERS') 20 | plaintext= ('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 21 | 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz') 22 | ciphertext = ('gdpjwiaxjdqcurdxkwolxreqifrlykczlfsewtfzmyqnztgskhtn', 23 | 'chneammvagxtffotzqmyyhmsjfrraflcykktyevrddmrxokwwfkq') 24 | for i,key in enumerate(keys): 25 | dec = Beaufort(key).decipher(ciphertext[i]) 26 | self.assertEqual(dec.upper(), plaintext[i].upper()) 27 | 28 | if __name__ == '__main__': 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /tests/test_bifid.py: -------------------------------------------------------------------------------- 1 | from pycipher import Bifid 2 | import unittest 3 | 4 | class TestBifid(unittest.TestCase): 5 | 6 | def test_encipher(self): 7 | keys = (('tgcmpfyxuiewdhbzrvalknqso',5), 8 | ('ezrxdkuatgvncmiwhsqpyfblo',6)) 9 | plaintext = ('abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz', 10 | 'abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz') 11 | ciphertext = ('vchqefwfuospksiplpwzuwuwwaeeldwcfglizoprksoqugvfvxuf', 12 | 'gvdciztcgfoxclwhoshawmkxygvzcidtczfogclxhowhasmkwyxz') 13 | for i,key in enumerate(keys): 14 | enc = Bifid(*key).encipher(plaintext[i]) 15 | self.assertEqual(enc.upper(), ciphertext[i].upper()) 16 | 17 | def test_decipher(self): 18 | keys = (('tgcmpfyxuiewdhbzrvalknqso',5), 19 | ('ezrxdkuatgvncmiwhsqpyfblo',6)) 20 | plaintext= ('abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz', 21 | 'abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz') 22 | ciphertext = ('vchqefwfuospksiplpwzuwuwwaeeldwcfglizoprksoqugvfvxuf', 23 | 'gvdciztcgfoxclwhoshawmkxygvzcidtczfogclxhowhasmkwyxz') 24 | for i,key in enumerate(keys): 25 | dec = Bifid(*key).decipher(ciphertext[i]) 26 | self.assertEqual(dec.upper(), plaintext[i].upper()) 27 | 28 | if __name__ == '__main__': 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /tests/test_caesar.py: -------------------------------------------------------------------------------- 1 | from pycipher.caesar import Caesar 2 | import unittest 3 | 4 | class TestCaesar(unittest.TestCase): 5 | 6 | def test_decipher(self): 7 | ''' Caesar (test_decipher): test known ciphertext->plaintext pairs ''' 8 | text = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 9 | declist = ['xyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvw', 10 | 'vwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu', 11 | 'stuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqr', 12 | 'pqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmno', 13 | 'lmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk', 14 | 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 15 | 'bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyza'] 16 | for i,key in enumerate((3,5,8,11,15,0,25)): 17 | dec = Caesar(key).decipher(text) 18 | self.assertEqual(dec.upper(), declist[i].upper()) 19 | 20 | def test_encipher(self): 21 | ''' Caesar (test_encipher): test known plaintext->ciphertext pairs ''' 22 | text = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 23 | enclist = ['bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyza', 24 | 'cdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzab', 25 | 'efghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd', 26 | 'hijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefg', 27 | 'jklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi', 28 | 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 29 | 'zabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxy'] 30 | for i,key in enumerate((1,2,4,7,9,0,25)): 31 | enc = Caesar(key).encipher(text) 32 | self.assertEqual(enc.upper(), enclist[i].upper()) 33 | 34 | def test_punctuation(self): 35 | ''' Caesar (test_punctuation): punctuation should remain unmodified ''' 36 | e = Caesar(key=14) 37 | original = '!@$%%^&*()_-+={}[]|":;<>,./?' 38 | enciphered = e.encipher(original,keep_punct=True) 39 | self.assertEqual(original.upper(), enciphered.upper()) 40 | e = Caesar(key=14) 41 | original = '!@$%%^&*()_-+={}[]|":;<>,./?' 42 | enciphered = e.encipher(original,keep_punct=False) 43 | self.assertEqual('', enciphered.upper()) -------------------------------------------------------------------------------- /tests/test_coltrans.py: -------------------------------------------------------------------------------- 1 | from pycipher import ColTrans 2 | import unittest 3 | 4 | class TestColtrans(unittest.TestCase): 5 | 6 | def test_encipher(self): 7 | keys = ('GERMAN', 8 | 'CIPHERS') 9 | plaintext = ('abcdefghijklmnopqrstuvwxyz', 10 | 'abcdefghijklmnopqrstuvwxyz') 11 | ciphertext = ('EKQWBHNTZAGMSYDJPVFLRXCIOU', 12 | 'AHOVELSZDKRYBIPWCJQXFMTGNU') 13 | for i,key in enumerate(keys): 14 | enc = ColTrans(key).encipher(plaintext[i]) 15 | self.assertEqual(enc.upper(), ciphertext[i].upper()) 16 | 17 | def test_decipher(self): 18 | keys = ('GERMAN', 19 | 'CIPHERS') 20 | plaintext= ('abcdefghijklmnopqrstuvwxyz', 21 | 'abcdefghijklmnopqrstuvwxyz') 22 | ciphertext = ('EKQWBHNTZAGMSYDJPVFLRXCIOU', 23 | 'AHOVELSZDKRYBIPWCJQXFMTGNU') 24 | for i,key in enumerate(keys): 25 | dec = ColTrans(key).decipher(ciphertext[i]) 26 | self.assertEqual(dec.upper(), plaintext[i].upper()) 27 | 28 | if __name__ == '__main__': 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /tests/test_enigma.py: -------------------------------------------------------------------------------- 1 | from pycipher.enigma import Enigma 2 | import unittest 3 | 4 | class TestEnigma(unittest.TestCase): 5 | 6 | def test_decipher(self): 7 | 'Enigma (test_decipher): try deciphering a few known pairs' 8 | text = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 9 | declist = ['ZVEKPJXYYORZCJPOWSFVDROSCJIUOESGAIPCPOHLJSSWVKWOLEDH', 10 | 'SRBHDZXZESFHYOBCXLBUCOAYUQZPZBWLAYBOEEVJYOEEINLCIHIV', 11 | 'UNLLTTNBWKQEJFSUTGFQVTSGNQLSFEZVLUYDNUDZZKOIFGBYXGUJ', 12 | 'YYQULKSDHEGZECAIUKAZVSIENOQVUCSDZGNCAWUJAYJOTJCPTJOU'] 13 | keys = [(('Q','W','R'),(3,2,4),'C',('R','T','W'),[('E','T'),('N','P')]), 14 | (('T','B','L'),(1,8,2),'B',('A','B','C'),[('U','P'),('Z','X'),('L','K')]), 15 | (('A','B','C'),(6,5,4),'C',('C','B','A'),[('B','V')]), 16 | (('E','W','Q'),(8,7,6),'B',('L','L','L'),[])] 17 | for i,key in enumerate(keys): 18 | dec = Enigma(key[0],key[1],key[2],key[3],key[4]).decipher(text) 19 | self.assertEqual(dec.upper(), declist[i].upper()) 20 | 21 | def test_encipher(self): 22 | 'Enigma (test_encipher): encipher operation is identical to decipher operation' 23 | text = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 24 | declist = 'UNLLTTNBWKQEJFSUTGFQVTSGNQLSFEZVLUYDNUDZZKOIFGBYXGUJ' 25 | key = (('A','B','C'),(6,5,4),'C',('C','B','A'),[('B','V')]) 26 | dec = Enigma(key[0],key[1],key[2],key[3],key[4]).encipher(text) 27 | self.assertEqual(dec.upper(), declist.upper()) 28 | 29 | 30 | #if __name__ == '__main__': 31 | # unittest.main() 32 | -------------------------------------------------------------------------------- /tests/test_foursquare.py: -------------------------------------------------------------------------------- 1 | from pycipher import Foursquare 2 | import unittest 3 | 4 | class TestFoursquare(unittest.TestCase): 5 | 6 | def test_encipher(self): 7 | keys = (('zgptfoihmuwdrcnykeqaxvsbl','mfnbdcrhsaxyogvituewlqzkp'), 8 | ('iebvsurpxanmqoywdlztfkcgh','fobgqehdpwrviknazytmsculx')) 9 | plaintext = ('abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz', 10 | 'abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz') 11 | ciphertext = ('gmtnzahrmsovryngkiquypsqlkgmtnzahrmsovryngkiquypsqlk', 12 | 'efvbiwphxpunqvykdazywxcchlefvbiwphxpunqvykdazywxcchl') 13 | for i,key in enumerate(keys): 14 | enc = Foursquare(*key).encipher(plaintext[i]) 15 | self.assertEqual(enc.upper(), ciphertext[i].upper()) 16 | 17 | def test_decipher(self): 18 | keys = (('zgptfoihmuwdrcnykeqaxvsbl','mfnbdcrhsaxyogvituewlqzkp'), 19 | ('iebvsurpxanmqoywdlztfkcgh','fobgqehdpwrviknazytmsculx')) 20 | plaintext= ('abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz', 21 | 'abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz') 22 | ciphertext = ('gmtnzahrmsovryngkiquypsqlkgmtnzahrmsovryngkiquypsqlk', 23 | 'efvbiwphxpunqvykdazywxcchlefvbiwphxpunqvykdazywxcchl') 24 | for i,key in enumerate(keys): 25 | dec = Foursquare(*key).decipher(ciphertext[i]) 26 | self.assertEqual(dec.upper(), plaintext[i].upper()) 27 | 28 | if __name__ == '__main__': 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /tests/test_gronsfeld.py: -------------------------------------------------------------------------------- 1 | from pycipher import Gronsfeld 2 | import unittest 3 | 4 | class TestGronsfeld(unittest.TestCase): 5 | 6 | def test_encipher(self): 7 | keys = ([2,7,8,1,3,9,3], 8 | [2,7,8,1,0,3,9,3]) 9 | plaintext = ('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 10 | 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz') 11 | ciphertext = ('cikehojjprlovqqwysvcxxdfzcjeekmgjqllrtnqxssyauxezzfh', 12 | 'cikeeipkkqsmmqxssyauuyfaagiccgniioqkkovqqwysswdyyega') 13 | for i,key in enumerate(keys): 14 | enc = Gronsfeld(key).encipher(plaintext[i]) 15 | self.assertEqual(enc.upper(), ciphertext[i].upper()) 16 | 17 | def test_decipher(self): 18 | keys = ([2,7,8,1,3,9,3], 19 | [2,7,8,1,0,3,9,3]) 20 | plaintext= ('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 21 | 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz') 22 | ciphertext = ('cikehojjprlovqqwysvcxxdfzcjeekmgjqllrtnqxssyauxezzfh', 23 | 'cikeeipkkqsmmqxssyauuyfaagiccgniioqkkovqqwysswdyyega') 24 | for i,key in enumerate(keys): 25 | dec = Gronsfeld(key).decipher(ciphertext[i]) 26 | self.assertEqual(dec.upper(), plaintext[i].upper()) 27 | 28 | if __name__ == '__main__': 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /tests/test_m209.py: -------------------------------------------------------------------------------- 1 | from pycipher.m209 import M209 2 | import unittest 3 | 4 | class TestM209(unittest.TestCase): 5 | 6 | def test_encipher(self): 7 | text = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 8 | enclist = ['TMHRSVOJLBSOYAGEYCJJCFVPKPJGWQHRJHIEBXWHINKNLGSZLRBN', 9 | 'CIRLWCGMGBMBCNLEMVGPWTSTPPEVWUZFOBACQBDCFSTVYJEFOTSN'] 10 | keys = ['AAAAAA','ZSMJDE'] 11 | for i,key in enumerate(keys): 12 | enc = M209(key).encipher(text) 13 | self.assertEqual(enc.upper(), enclist[i].upper()) 14 | 15 | if __name__ == '__main__': 16 | unittest.main() 17 | -------------------------------------------------------------------------------- /tests/test_playfair.py: -------------------------------------------------------------------------------- 1 | from pycipher.playfair import Playfair 2 | import unittest 3 | 4 | class TestPlayfair(unittest.TestCase): 5 | 6 | def test_decipher(self): 7 | ciphertext = 'abcdefghiklmnopqrstuvwxyzABCDEFGHIKLMNOPQRSTUVWXYZ' 8 | keys = ('lsfdpxwirnyveamoqgkchbtzu', 9 | 'dytqplngirabskxcvoemzufwh', 10 | 'ecsyinfmkpdrtuvgwqzbolaxh', 11 | 'xquwfstzgeipboalykdnvrmch', 12 | 'bxuthifmzrweasdovlpgcqnyk') 13 | plaintext = ['vzkpitotrgpyxcscwdbzwslxkruqfatetxodnpclkwfbbmxnah', 14 | 'xaazowrfqircgvqtgxyfeubpclavqcotwraivrmtpigfvbhkdu', 15 | 'hqercnboypafegmbtcrtrbzxqxwinonwbhfxfphnwtamtuzlxu', 16 | 'opdofhecblkvdatrvtzqcxqlebomngwevaynhkbiryesxmufkt', 17 | 'wukwfxdkrcauclvyzduxoetqmscoswrvbrngulglkfzyxlebpt'] 18 | for i,key in enumerate(keys): 19 | dec = Playfair(key).decipher(ciphertext) 20 | self.assertEqual(dec.upper(), plaintext[i].upper()) 21 | 22 | def test_encipher(self): 23 | # test odd length string 24 | keys = ('czbniwemuorkpdslqfxagthyv', 25 | 'ohnuwkdbtiepyzxcsqamlgrvf', 26 | 'pslqofaiymcnwtuhdxezbkgrv', 27 | 'xqvlpnakrsewfcyhutdbgziom', 28 | 'xbhpuvmiyesdnwlakcrgfzotq') 29 | plaintext = 'abcdefghiklmnopqrstuvwxyzABCDEFGHIKLMNOPQRSTUVWXY' 30 | ciphertext = ['finrmqtyzsfwiukfkryegoyniqnzkulhvbrqubmslkkvoyulny', 31 | 'qtskxlhdkdfcuhysgqztfuezavkqkplrwdeoqwhernadtuimze', 32 | 'fknhhybxagoiussokqucgueidmphxzibxfgsaupsyqqnzoxgie', 33 | 'sudowcxgvfporgxvsndtqfpeqwdyhceitgrvgsmllakbtqeqep', 34 | 'kxknvqcumcdechutawqpyspvfkhklmqaingdidthtgwfxespvp'] 35 | for i,key in enumerate(keys): 36 | enc = Playfair(key).encipher(plaintext) 37 | self.assertEqual(enc.upper(), ciphertext[i].upper()) 38 | # test even length string 39 | plaintext ='aaaaaaaaajjjjjkkkkkllllmmmmnnnnffffooooossssoffffffe' 40 | key = 'tpaydcmhfoizwsuvxrbkelqgn' 41 | ciphertext = 'prprprprtwzvzvvrvrxnplpzzlollkgombocmkmkzbzbcombmbcg' 42 | enc = Playfair(key).encipher(plaintext) 43 | self.assertEqual(enc.upper(), ciphertext.upper()) 44 | -------------------------------------------------------------------------------- /tests/test_polybius.py: -------------------------------------------------------------------------------- 1 | from pycipher import PolybiusSquare 2 | import unittest 3 | 4 | class TestPolybius(unittest.TestCase): 5 | 6 | def test_encipher(self): 7 | keys = (('phqgiumeaylnofdxkrcvstzwb',5,'ABCDE'), 8 | ('uqfigkydlvmznxephrswaotcb',5,'BCDEF')) 9 | plaintext = ('abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz', 10 | 'abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz') 11 | ciphertext = ('BDEEDDCEBCCDADABAEAEDBCABBCBCCAAACDCEAEBBADEEDDABEECBDEEDDCEBCCDADABAEAEDBCABBCBCCAAACDCEAEBBADEEDDABEEC', 12 | 'FBFFFECDDFBDBFECBEBECBCEDBDDFCEBBCEDEEFDBBCFEFDECCDCFBFFFECDDFBDBFECBEBECBCEDBDDFCEBBCEDEEFDBBCFEFDECCDC') 13 | for i,key in enumerate(keys): 14 | enc = PolybiusSquare(*key).encipher(plaintext[i]) 15 | self.assertEqual(enc.upper(), ciphertext[i].upper()) 16 | 17 | def test_decipher(self): 18 | keys = (('phqgiumeaylnofdxkrcvstzwb',5,'ABCDE'), 19 | ('uqfigkydlvmznxephrswaotcb',5,'BCDEF')) 20 | plaintext= ('abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz', 21 | 'abcdefghiiklmnopqrstuvwxyzabcdefghiiklmnopqrstuvwxyz') 22 | ciphertext = ('BDEEDDCEBCCDADABAEAEDBCABBCBCCAAACDCEAEBBADEEDDABEECBDEEDDCEBCCDADABAEAEDBCABBCBCCAAACDCEAEBBADEEDDABEEC', 23 | 'FBFFFECDDFBDBFECBEBECBCEDBDDFCEBBCEDEEFDBBCFEFDECCDCFBFFFECDDFBDBFECBEBECBCEDBDDFCEBBCEDEEFDBBCFEFDECCDC') 24 | for i,key in enumerate(keys): 25 | dec = PolybiusSquare(*key).decipher(ciphertext[i]) 26 | self.assertEqual(dec.upper(), plaintext[i].upper()) 27 | 28 | if __name__ == '__main__': 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /tests/test_porta.py: -------------------------------------------------------------------------------- 1 | from pycipher.porta import Porta 2 | import unittest 3 | 4 | class TestPorta(unittest.TestCase): 5 | 6 | def test_encipher(self): 7 | keys = ('HELLO','FORTIFICATION') 8 | plaintext = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 9 | ciphertext = ('qquvyvvznqnnrihmbmamegefejptuxuuyzpzzqrglalmldfdedik', 10 | 'pvxzvuxvvsosslhhhadcgiagegpvxzvuxvvsosslhhhadcgiageg') 11 | for i,key in enumerate(keys): 12 | enc = Porta(key).encipher(plaintext) 13 | self.assertEqual(enc.upper(), ciphertext[i].upper()) 14 | 15 | def test_decipher(self): 16 | keys = ('HELLO','FORTIFICATION') 17 | ciphertext = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 18 | plaintext = ('qquvyvvznqnnrihmbmamegefejptuxuuyzpzzqrglalmldfdedik', 19 | 'pvxzvuxvvsosslhhhadcgiagegpvxzvuxvvsosslhhhadcgiageg') 20 | for i,key in enumerate(keys): 21 | dec = Porta(key).decipher(ciphertext) 22 | self.assertEqual(dec.upper(), plaintext[i].upper()) 23 | 24 | if __name__ == '__main__': 25 | unittest.main() 26 | -------------------------------------------------------------------------------- /tests/test_railfence.py: -------------------------------------------------------------------------------- 1 | from pycipher.railfence import Railfence 2 | import unittest 3 | 4 | class TestRailfence(unittest.TestCase): 5 | 6 | def test_encipher(self): 7 | keys = (3,6,7,8) 8 | plaintext = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 9 | ciphertext = ('aeimquycgkoswbdfhjlnprtvxzbdfhjlnprtvxzcgkoswaeimquy', 10 | 'akueoybjltvdfnpxzcimswcgmqwdhnrxbhlrvegoqyaiksufpzjt', 11 | 'amykwblnxzjlvxckowaimuydjpvbhntzeiqucgosfhrtdfprgseq', 12 | 'aocqbnpbdprcmqaeosdlrzfnteksygmufjtxhlvzgiuwikwyhvjx') 13 | for i,key in enumerate(keys): 14 | enc = Railfence(key).encipher(plaintext) 15 | self.assertEqual(enc.upper(), ciphertext[i].upper()) 16 | 17 | def test_decipher(self): 18 | keys = (3,6,7,8) 19 | ciphertext = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 20 | plaintext = ('annobpoqcrpsdtquevrwfxsygztahbucidvejfwgkhxiljykmlzm', 21 | 'agrblvmcshbitdnwoeujckvfpxqgwldmxhrysiyneozjtzukapfq', 22 | 'afoxgowphypgbhqziqxrjaricjsbksytlctkdludmuzvnevmenwf', 23 | 'aelszgowphatmfbgnubiqxrjcvohcipwdksytlexqjdkryfmuzvn') 24 | for i,key in enumerate(keys): 25 | dec = Railfence(key).decipher(ciphertext) 26 | self.assertEqual(dec.upper(), plaintext[i].upper()) 27 | 28 | if __name__ == '__main__': 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /tests/test_rot13.py: -------------------------------------------------------------------------------- 1 | from pycipher import Rot13 2 | import unittest 3 | 4 | class TestRot13(unittest.TestCase): 5 | 6 | def test_decipher(self): 7 | text = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 8 | declist = ['nopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'] 9 | dec = Rot13().decipher(text) 10 | self.assertEqual(dec.upper(), declist[0].upper()) 11 | 12 | def test_encipher(self): 13 | text = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 14 | enclist = ['nopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklm'] 15 | enc = Rot13().encipher(text) 16 | self.assertEqual(enc.upper(), enclist[0].upper()) 17 | -------------------------------------------------------------------------------- /tests/test_simple.py: -------------------------------------------------------------------------------- 1 | from pycipher.simplesubstitution import SimpleSubstitution 2 | import unittest 3 | 4 | class TestSimple(unittest.TestCase): 5 | 6 | def test_encipher(self): 7 | keys = ('abronjdfuetchiszlgwqvxkymp', 8 | 'mufykewgqtnrlopcbadsvxzijh', 9 | 'rtuzesbxjaniypqclghmvwodkf', 10 | 'ymdsvtxizewurqfnbgjlckoahp', 11 | 'lcvmwezojbgdtsrniufyqphxak') 12 | plaintext = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 13 | ciphertext = ('abronjdfuetchiszlgwqvxkympabronjdfuetchiszlgwqvxkymp', 14 | 'mufykewgqtnrlopcbadsvxzijhmufykewgqtnrlopcbadsvxzijh', 15 | 'rtuzesbxjaniypqclghmvwodkfrtuzesbxjaniypqclghmvwodkf', 16 | 'ymdsvtxizewurqfnbgjlckoahpymdsvtxizewurqfnbgjlckoahp', 17 | 'lcvmwezojbgdtsrniufyqphxaklcvmwezojbgdtsrniufyqphxak') 18 | for i,key in enumerate(keys): 19 | enc = SimpleSubstitution(key).encipher(plaintext) 20 | self.assertEqual(enc.upper(), ciphertext[i].upper()) 21 | 22 | def test_decipher(self): 23 | keys = ('zidvgmlefwpsktrnaoqjyubhxc', 24 | 'wyqtnaopsxigdbzlhumvckrejf', 25 | 'bqcjpkfeuzlnxmgdastwhriyvo', 26 | 'enwdirhmykbfzsaojulpcqtvxg', 27 | 'rtzeawivnubkyjfchsldomqxpg') 28 | ciphertext = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 29 | plaintext = ('qwzchiexbtmgfprksolnvdjyuaqwzchiexbtmgfprksolnvdjyua', 30 | 'fnumxzlqkyvpseghcwidrtajbofnumxzlqkyvpseghcwidrtajbo', 31 | 'qacphgouwdfknlzebvrsiytmxjqacphgouwdfknlzebvrsiytmxj', 32 | 'okudalzgeqjshbptvfnwrxcyimokudalzgeqjshbptvfnwrxcyim', 33 | 'ekptdozqgnlsviuywarbjhfxmcekptdozqgnlsviuywarbjhfxmc') 34 | for i,key in enumerate(keys): 35 | dec = SimpleSubstitution(key).decipher(ciphertext) 36 | self.assertEqual(dec.upper(), plaintext[i].upper()) 37 | 38 | if __name__ == '__main__': 39 | unittest.main() 40 | -------------------------------------------------------------------------------- /tests/test_vigenere.py: -------------------------------------------------------------------------------- 1 | from pycipher import Vigenere 2 | import unittest 3 | 4 | class TestVigenere(unittest.TestCase): 5 | 6 | def test_encipher(self): 7 | keys = ('GERMAN', 8 | 'CIPHERS') 9 | plaintext = ('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 10 | 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz') 11 | ciphertext = ('gftpesmlzvkysrfbqeyxlhwkedrncqkjxtiwqpdzocwvjfuicbpl', 12 | 'cjrkiwyjqyrpdfqxfywkmxemfdrteltmkyalsatrfhszhaymozgo') 13 | for i,key in enumerate(keys): 14 | enc = Vigenere(key).encipher(plaintext[i]) 15 | self.assertEqual(enc.upper(), ciphertext[i].upper()) 16 | 17 | def test_decipher(self): 18 | keys = ('GERMAN', 19 | 'CIPHERS') 20 | plaintext= ('abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz', 21 | 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz') 22 | ciphertext = ('gftpesmlzvkysrfbqeyxlhwkedrncqkjxtiwqpdzocwvjfuicbpl', 23 | 'cjrkiwyjqyrpdfqxfywkmxemfdrteltmkyalsatrfhszhaymozgo') 24 | for i,key in enumerate(keys): 25 | dec = Vigenere(key).decipher(ciphertext[i]) 26 | self.assertEqual(dec.upper(), plaintext[i].upper()) 27 | 28 | if __name__ == '__main__': 29 | unittest.main() 30 | --------------------------------------------------------------------------------