diff options
Diffstat (limited to 'doc')
-rw-r--r-- | doc/Makefile | 139 | ||||
-rw-r--r-- | doc/_build/.keep | 0 | ||||
-rw-r--r-- | doc/_static/.keep | 0 | ||||
-rw-r--r-- | doc/_templates/.keep | 0 | ||||
-rw-r--r-- | doc/cli.rst | 43 | ||||
-rw-r--r-- | doc/compatibility.rst | 62 | ||||
-rw-r--r-- | doc/conf.py | 222 | ||||
-rw-r--r-- | doc/index.rst | 54 | ||||
-rw-r--r-- | doc/installation.rst | 55 | ||||
-rw-r--r-- | doc/intro.rst | 38 | ||||
-rw-r--r-- | doc/licence.rst | 18 | ||||
-rw-r--r-- | doc/make.bat | 170 | ||||
-rw-r--r-- | doc/reference.rst | 112 | ||||
-rw-r--r-- | doc/upgrading.rst | 73 | ||||
-rw-r--r-- | doc/usage.rst | 353 |
15 files changed, 1339 insertions, 0 deletions
diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..9495ae3 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,139 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest + +default: html + +help: + @echo "Please use \`make <target>' where <target> is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Python-RSA.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Python-RSA.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/Python-RSA" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Python-RSA" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + make -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +upload: html + @echo + @echo "UPLOADING to webserver" + @echo + rsync _build/html/* stuvel@stuvel.eu:site-stuvel.eu/htdocs/python-rsa-doc/ -va --delete + diff --git a/doc/_build/.keep b/doc/_build/.keep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/doc/_build/.keep diff --git a/doc/_static/.keep b/doc/_static/.keep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/doc/_static/.keep diff --git a/doc/_templates/.keep b/doc/_templates/.keep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/doc/_templates/.keep diff --git a/doc/cli.rst b/doc/cli.rst new file mode 100644 index 0000000..af2b5f1 --- /dev/null +++ b/doc/cli.rst @@ -0,0 +1,43 @@ +Commandline interface +================================================== + +A lot of the Python-RSA functionality is also available as commandline +scripts. On Linux and other unix-like systems they are executable +Python scripts, on Windows they are .exe files. + +All scripts accept a ``--help`` parameter that give you instructions +on how to use them. Here is a short overview: + +.. index:: CLI interface +.. index:: pyrsa-keygen, pyrsa-encrypt, pyrsa-decrypt, pyrsa-sign +.. index:: pyrsa-verify, pyrsa-priv2pub, pyrsa-encrypt-bigfile +.. index:: pyrsa-decrypt-bigfile, pyrsa-decrypt-bigfile + ++-----------------------+--------------------------------------------------+-----------------------------------------+ +| Command | Usage | Core function | ++=======================+==================================================+=========================================+ +| pyrsa-keygen | Generates a new RSA keypair in PEM or DER format | :py:func:`rsa.newkeys` | ++-----------------------+--------------------------------------------------+-----------------------------------------+ +| pyrsa-encrypt | Encrypts a file. The file must be shorter than | :py:func:`rsa.encrypt` | +| | the key length in order to be encrypted. | | ++-----------------------+--------------------------------------------------+-----------------------------------------+ +| pyrsa-decrypt | Decrypts a file. | :py:func:`rsa.decrypt` | ++-----------------------+--------------------------------------------------+-----------------------------------------+ +| pyrsa-sign | Signs a file, outputs the signature. | :py:func:`rsa.sign` | ++-----------------------+--------------------------------------------------+-----------------------------------------+ +| pyrsa-verify | Verifies a signature. The result is written to | :py:func:`rsa.verify` | +| | the console as well as returned in the exit | | +| | status code. | | ++-----------------------+--------------------------------------------------+-----------------------------------------+ +| pyrsa-priv2pub | Reads a private key and outputs the | \- | +| | corresponding public key. | | ++-----------------------+--------------------------------------------------+-----------------------------------------+ +| pyrsa-encrypt-bigfile | Encrypts a file to an encrypted VARBLOCK file. | :py:func:`rsa.bigfile.encrypt_bigfile` | +| | The file can be larger than the key length, but | | +| | the output file is only compatible with | | +| | Python-RSA. | | ++-----------------------+--------------------------------------------------+-----------------------------------------+ +| pyrsa-decrypt-bigfile | Decrypts an encrypted VARBLOCK file. | :py:func:`rsa.bigfile.encrypt_bigfile` | ++-----------------------+--------------------------------------------------+-----------------------------------------+ + + diff --git a/doc/compatibility.rst b/doc/compatibility.rst new file mode 100644 index 0000000..aedfcb6 --- /dev/null +++ b/doc/compatibility.rst @@ -0,0 +1,62 @@ +Compatibility with standards +============================ + +.. index:: OpenSSL +.. index:: compatibility + +Python-RSA implements encryption and signatures according to PKCS#1 +version 1.5. This makes it compatible with the OpenSSL RSA module. + +Keys are stored in PEM or DER format according to PKCS#1 v1.5. Private +keys are compatible with OpenSSL. However, OpenSSL uses X.509 for its +public keys, which are not supported. + +Encryption: + PKCS#1 v1.5 with at least 8 bytes of random padding + +Signatures: + PKCS#1 v1.5 using the following hash methods: + MD5, SHA-1, SHA-256, SHA-384, SHA-512 + +Private keys: + PKCS#1 v1.5 in PEM and DER format, ASN.1 type RSAPrivateKey + +Public keys: + PKCS#1 v1.5 in PEM and DER format, ASN.1 type RSAPublicKey + +:ref:`VARBLOCK <bigfiles>` encryption: + Python-RSA only, not compatible with any other known application. + +.. _openssl: + +Interoperability with OpenSSL +----------------------------- + +You can create a 512-bit RSA key in OpenSSL as follows:: + + openssl genrsa -out myprivatekey.pem 512 + +To get a Python-RSA-compatible public key from OpenSSL, you need the +private key first, then run it through the ``pyrsa-priv2pub`` +command:: + + pyrsa-priv2pub -i myprivatekey.pem -o mypublickey.pem + +Encryption and decryption is also compatible:: + + $ echo hello there > testfile.txt + $ pyrsa-encrypt -i testfile.txt -o testfile.rsa publickey.pem + $ openssl rsautl -in testfile.rsa -inkey privatekey.pem -decrypt + hello there + +Interoperability with PKCS#8 +---------------------------- + +The standard PKCS#8 is widely used, and more complex than the PKCS#1 +v1.5 supported by Python-RSA. In order to extract a key from the +PKCS#8 format you need an external tool such as OpenSSL:: + + openssl rsa -in privatekey-pkcs8.pem -out privatekey.pem + +You can then extract the corresponding public key as described above. + diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..95317b2 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,222 @@ +# -*- coding: utf-8 -*- +# +# Python-RSA documentation build configuration file, created by +# sphinx-quickstart on Sat Jul 30 23:11:07 2011. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# import sys +# import os +import rsa + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', + 'sphinx.ext.coverage', 'sphinx.ext.pngmath'] + +# I would like to add 'sphinx.ext.viewcode', but it causes a UnicodeDecodeError + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +source_encoding = 'utf-8' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Python-RSA' +copyright = u'2011-2016, Sybren A. Stüvel' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = rsa.__version__ +# The full version, including alpha/beta/rc tags. +release = rsa.__version__ + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +language = 'en' + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +# today = '' +# Else, today_fmt is used as the format for a strftime call. +# today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +# default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +# add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +# add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +# show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +# modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'sphinxdoc' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +# html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# "<project> v<release> documentation". +# html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +# html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +# html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +# html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +# html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +# html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +# html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +# html_additional_pages = {} + +# If false, no module index is generated. +# html_domain_indices = True + +# If false, no index is generated. +# html_use_index = True + +# If true, the index is split into individual pages for each letter. +# html_split_index = False + +# If true, links to the reST sources are added to the pages. +# html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +# html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +# html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a <link> tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +# html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +# html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Python-RSAdoc' + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +# latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +# latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'Python-RSA.tex', u'Python-RSA Documentation', + u'Sybren A. Stüvel', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +# latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +# latex_use_parts = False + +# If true, show page references after internal links. +# latex_show_pagerefs = False + +# If true, show URL addresses after external links. +# latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +# latex_preamble = '' + +# Documents to append as an appendix to all manuals. +# latex_appendices = [] + +# If false, no module index is generated. +# latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'python-rsa', u'Python-RSA Documentation', + [u'Sybren A. Stüvel'], 1) +] + +todo_include_todos = True diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..a0a1573 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,54 @@ +.. Python-RSA documentation master file, created by + sphinx-quickstart on Sat Jul 30 23:11:07 2011. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Python-RSA's documentation! +====================================== + +Python-RSA is a pure-Python RSA implementation. It supports +encryption and decryption, signing and verifying signatures, and key +generation according to PKCS#1 version 1.5. + +If you have the time and skill to improve the implementation, by all +means be my guest. The best way is to clone the `Git +repository`_ and send me a merge request when you've got something +worth merging. + +.. _`Git repository`: https://github.com/sybrenstuvel/python-rsa + + +Security notice +--------------- + +This RSA implementation has seen the eyes of a security expert, and it +uses an industry standard random padding method. However, there are +still possible vectors of attack. Just to name one example, it doesn't +compress the input stream to remove repetitions, and if you display +the stack trace of a :py:class:`rsa.pkcs1.CryptoError` exception +you'll leak information about the reason why decryption or +verification failed. + +I'm sure that those aren't the only insecurities. Use your own +judgement to decide whether this module is secure enough for your +application. + +Contents +-------- + +.. toctree:: + :maxdepth: 2 + :numbered: + + intro + installation + upgrading + licence + usage + cli + compatibility + reference + + +* :ref:`genindex` +* :ref:`search` diff --git a/doc/installation.rst b/doc/installation.rst new file mode 100644 index 0000000..578dc86 --- /dev/null +++ b/doc/installation.rst @@ -0,0 +1,55 @@ +Installation +============ + +Installation can be done in various ways. The simplest form uses pip +or easy_install. Either one will work:: + + pip install rsa + easy_install rsa + +Depending on your system you may need to use ``sudo pip`` or ``sudo +easy_install``. + +Installation from source is also quite easy. Download the source and +then type:: + + python setup.py install + +or if that doesn't work:: + + sudo python setup.py install + + +The sources are tracked in our `Git repository`_ at +Github. It also hosts the `issue tracker`_. + +.. _`Git repository`: https://github.com/sybrenstuvel/python-rsa.git +.. _`issue tracker`: https://github.com/sybrenstuvel/python-rsa/issues + + +Dependencies +------------ + +Python-RSA has very few dependencies. As a matter of fact, to use it +you only need Python itself. Loading and saving keys does require an +extra module, though: pyasn1. If you used pip or easy_install like +described above, you should be ready to go. + + +Development dependencies +------------------------ + +In order to start developing on Python-RSA you need a bit more. Use +pip to install the development requirements in a virtual environment:: + + virtualenv -p /path/to/your-python-version python-rsa-venv + . python-rsa-venv/bin/activate + pip install -r python-rsa/requirements.txt + + +Once these are installed, use Git_ to get a copy of the source:: + + hg clone https://github.com/sybrenstuvel/python-rsa.git + python setup.py develop + +.. _Git: https://git-scm.com/ diff --git a/doc/intro.rst b/doc/intro.rst new file mode 100644 index 0000000..e689bde --- /dev/null +++ b/doc/intro.rst @@ -0,0 +1,38 @@ +Introduction & history +====================== + +Python-RSA's history starts in 2006. As a student assignment for the +University of Amsterdam we wrote a RSA implementation. We chose Python +for various reasons; one of the most important reasons was the +`unlimited precision integer`_ support. + +.. _`unlimited precision integer`: + https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex + +It started out as just a module for calculating large primes, and RSA +encryption, decryption, signing and verification using those large +numbers. It also included generating public and private keys. There +was no functionality for working with byte sequences (such as files) +yet. + +Version 1.0 did include support for byte sequences, but quite clunky, +mostly because it didn't support 0-bytes and thus was unsuitable for +binary messages. + +Version 2.0 introduced a lot of improvements by Barry Mead, but still +wasn't compatible with other RSA implementations and used no random +padding. + +Version 3.0 introduced PKCS#1 v1.5 functionality, which resulted in +compatibility with OpenSSL and many others implementing the same +standard. Random padding was introduced that considerably increased +security, which also resulted in the ability to encrypt and decrypt +binary messages. + +Key generation was also improved in version 3.0, ensuring that you +really get the number of bits you asked for. At the same time key +generation speed was greatly improved. The ability to save and load +public and private keys in PEM and DER format as also added. + + + diff --git a/doc/licence.rst b/doc/licence.rst new file mode 100644 index 0000000..bc07dbd --- /dev/null +++ b/doc/licence.rst @@ -0,0 +1,18 @@ +Licence +======= + +The source code and documentation are protected under copyright by +Sybren A. Stüvel <sybren@stuvel.eu> + +The software is licensed under the Apache License, Version 2.0 (the +"License"); you may not use the software except in compliance with the +License. You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied. See the License for the specific language governing +permissions and limitations under the License. + diff --git a/doc/make.bat b/doc/make.bat new file mode 100644 index 0000000..9fb9761 --- /dev/null +++ b/doc/make.bat @@ -0,0 +1,170 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=_build +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^<target^>` where ^<target^> is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. changes to make an overview over all changed/added/deprecated items + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\Python-RSA.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\Python-RSA.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +:end diff --git a/doc/reference.rst b/doc/reference.rst new file mode 100644 index 0000000..d1b0b6d --- /dev/null +++ b/doc/reference.rst @@ -0,0 +1,112 @@ +Reference +========= + +This is the class and function reference. For more usage information +see the :ref:`usage` page. + +Functions +--------- + +.. autofunction:: rsa.encrypt + +.. autofunction:: rsa.decrypt + +.. autofunction:: rsa.sign + +.. autofunction:: rsa.verify + +.. autofunction:: rsa.newkeys(keysize) + + +Classes +------- + +.. note:: + + Storing public and private keys via the `pickle` module is possible. + However, it is insecure to load a key from an untrusted source. + The pickle module is not secure against erroneous or maliciously + constructed data. Never unpickle data received from an untrusted + or unauthenticated source. + +.. autoclass:: rsa.PublicKey + :members: + :inherited-members: + +.. autoclass:: rsa.PrivateKey + :members: + :inherited-members: + +Exceptions +---------- + +.. autoclass:: rsa.pkcs1.CryptoError(Exception) + +.. autoclass:: rsa.pkcs1.DecryptionError(CryptoError) + +.. autoclass:: rsa.pkcs1.VerificationError(CryptoError) + + +.. index:: VARBLOCK (file format) + +Module: rsa.bigfile +------------------- + +.. warning:: + + The :py:mod:`rsa.bigfile` module is NOT recommended for general use, has been + deprecated since Python-RSA 3.4, and will be removed in a future release. It's + vulnerable to a number of attacks. See :ref:`bigfiles` for more information. + +The :py:mod:`rsa.bigfile` module contains functions for encrypting and +decrypting files that are larger than the RSA key. See +:ref:`bigfiles` for more information. + +.. autofunction:: rsa.bigfile.encrypt_bigfile + +.. autofunction:: rsa.bigfile.decrypt_bigfile + +.. _VARBLOCK: + +The VARBLOCK file format +++++++++++++++++++++++++ + +.. warning:: + + The VARBLOCK format is NOT recommended for general use, has been deprecated since + Python-RSA 3.4, and will be removed in a future release. It's vulnerable to a + number of attacks. See :ref:`bigfiles` for more information. + +The VARBLOCK file format allows us to encrypt files that are larger +than the RSA key. The format is as follows; || denotes byte string +concatenation:: + + VARBLOCK := VERSION || BLOCK || BLOCK || ... + + VERSION := 1 + + BLOCK := LENGTH || DATA + + LENGTH := varint-encoded length of the following data, in bytes + + DATA := the data to store in the block + +The varint-format was taken from Google's Protobuf_, and allows us to +efficiently encode an arbitrarily long integer. + +.. _Protobuf: + https://code.google.com/apis/protocolbuffers/docs/encoding.html#varints + + +Module: rsa.core +---------------- + +At the core of the RSA encryption method lie these functions. They +both operate on (arbitrarily long) integers only. They probably aren't +of much use to you, but I wanted to document them anyway as they are +the core of the entire library. + +.. autofunction:: rsa.core.encrypt_int + +.. autofunction:: rsa.core.decrypt_int + diff --git a/doc/upgrading.rst b/doc/upgrading.rst new file mode 100644 index 0000000..0ec18eb --- /dev/null +++ b/doc/upgrading.rst @@ -0,0 +1,73 @@ +Upgrading from older versions +============================= + +Previous versions of Python-RSA were less secure than the current +version. In order to be able to gradually upgrade your software, those +old versions will be available until Python-RSA 4.0. + +To use version 1.3.3, use this:: + + import rsa._version133 as rsa + +And to use version 2.0, use this:: + + import rsa._version200 as rsa + +You can import all three versions at the same time. This allows you to +use an old version to decrypt your messages, and a new version to +re-encrypt them:: + + import rsa._version200 as rsa200 + import rsa # this imports version 3.0 + + decrypted = rsa200.decrypt(old_crypto, version_200_private_key) + new_crypto = rsa.encrypt(decrypted, version_3_public_key) + +Those import statements *will create warnings* as they import much +less secure code into your project. + +.. warning:: + + These modules are included to allow upgrading to the latest version + of Python-RSA, and not as a way to keep using those old versions. + They will be removed in version 4.0. + +The random padding introduced in version 3.0 made things much more +secure, but also requires a larger key to encrypt the same message. +You can either generate a new key with :py:func:`rsa.newkeys`, or use +:py:func:`rsa.bigfile.encrypt_bigfile` to encrypt your files. + +Converting keys +--------------- + +Version 3.0 introduced industrial standard RSA keys according to +PKCS#1. The old keys were just dictionaries. To convert a key from an +older version of Python-RSA, use the following:: + + import rsa + + # Load the old key somehow. + old_pub_key = { + 'e': 65537, + 'n': 31698122414741849421263704398157795847591L + } + + old_priv_key = { + 'd': 7506520894712811128876594754922157377793L, + 'p': 4169414332984308880603L, + 'q': 7602535963858869797L + } + + # Create new key objects like this: + pub_key = rsa.PublicKey(n=old_pub_key['n'], e=old_pub_key['e']) + + priv_key = rsa.PrivateKey(n=old_pub_key['n'], e=old_pub_key['e'], + d=old_priv_key['d'], p=old_priv_key['p'], q=old_priv_key['q']) + + + # Or use this shorter notation: + pub_key = rsa.PublicKey(**old_pub_key) + + old_priv_key.update(old_pub_key) + priv_key = rsa.PrivateKey(**old_priv_key) + diff --git a/doc/usage.rst b/doc/usage.rst new file mode 100644 index 0000000..a3d128d --- /dev/null +++ b/doc/usage.rst @@ -0,0 +1,353 @@ +.. _usage: + +Usage +===== + +This section describes the usage of the Python-RSA module. + +Before you can use RSA you need keys. You will receive a private key +and a public key. + +.. important:: + + The private key is called *private* for a reason. Never share this + key with anyone. + +The public key is used for encypting a message such that it can only +be read by the owner of the private key. As such it's also referred to +as the *encryption key*. Decrypting a message can only be done using +the private key, hence it's also called the *decryption key*. + +The private key is used for signing a message. With this signature and +the public key, the receiver can verifying that a message was signed +by the owner of the private key, and that the message was not modified +after signing. + + +Generating keys +--------------- + +You can use the :py:func:`rsa.newkeys` function to create a keypair: + + >>> import rsa + >>> (pubkey, privkey) = rsa.newkeys(512) + +Alternatively you can use :py:meth:`rsa.PrivateKey.load_pkcs1` and +:py:meth:`rsa.PublicKey.load_pkcs1` to load keys from a file: + + >>> import rsa + >>> with open('private.pem', mode='rb') as privatefile: + ... keydata = privatefile.read() + >>> privkey = rsa.PrivateKey.load_pkcs1(keydata) + + +Time to generate a key +++++++++++++++++++++++ + +Generating a keypair may take a long time, depending on the number of +bits required. The number of bits determines the cryptographic +strength of the key, as well as the size of the message you can +encrypt. If you don't mind having a slightly smaller key than you +requested, you can pass ``accurate=False`` to speed up the key +generation process. + +Another way to speed up the key generation process is to use multiple +processes in parallel to speed up the key generation. Use no more than +the number of processes that your machine can run in parallel; a +dual-core machine should use ``poolsize=2``; a quad-core +hyperthreading machine can run two threads on each core, and thus can +use ``poolsize=8``. + + >>> (pubkey, privkey) = rsa.newkeys(512, poolsize=8) + +These are some average timings from my desktop machine (Linux 2.6, +2.93 GHz quad-core Intel Core i7, 16 GB RAM) using 64-bit CPython 2.7. +Since key generation is a random process, times may differ even on +similar hardware. On all tests, we used the default ``accurate=True``. + ++----------------+------------------+------------------+ +| Keysize (bits) | single process | eight processes | ++================+==================+==================+ +| 128 | 0.01 sec. | 0.01 sec. | ++----------------+------------------+------------------+ +| 256 | 0.03 sec. | 0.02 sec. | ++----------------+------------------+------------------+ +| 384 | 0.09 sec. | 0.04 sec. | ++----------------+------------------+------------------+ +| 512 | 0.11 sec. | 0.07 sec. | ++----------------+------------------+------------------+ +| 1024 | 0.79 sec. | 0.30 sec. | ++----------------+------------------+------------------+ +| 2048 | 6.55 sec. | 1.60 sec. | ++----------------+------------------+------------------+ +| 3072 | 23.4 sec. | 7.14 sec. | ++----------------+------------------+------------------+ +| 4096 | 72.0 sec. | 24.4 sec. | ++----------------+------------------+------------------+ + +If key generation is too slow for you, you could use OpenSSL to +generate them for you, then load them in your Python code. OpenSSL +generates a 4096-bit key in 3.5 seconds on the same machine as used +above. See :ref:`openssl` for more information. + +Key size requirements +--------------------- + +Python-RSA version 3.0 introduced PKCS#1-style random padding. This +means that 11 bytes (88 bits) of your key are no longer usable for +encryption, so keys smaller than this are unusable. The larger the +key, the higher the security. + +Creating signatures also requires a key of a certain size, depending +on the used hash method: + ++-------------+-----------------------------------+ +| Hash method | Suggested minimum key size (bits) | ++=============+===================================+ +| MD5 | 360 | ++-------------+-----------------------------------+ +| SHA-1 | 368 | ++-------------+-----------------------------------+ +| SHA-256 | 496 | ++-------------+-----------------------------------+ +| SHA-384 | 624 | ++-------------+-----------------------------------+ +| SHA-512 | 752 | ++-------------+-----------------------------------+ + + + +Encryption and decryption +------------------------- + +To encrypt or decrypt a message, use :py:func:`rsa.encrypt` resp. +:py:func:`rsa.decrypt`. Let's say that Alice wants to send a message +that only Bob can read. + +#. Bob generates a keypair, and gives the public key to Alice. This is + done such that Alice knows for sure that the key is really Bob's + (for example by handing over a USB stick that contains the key). + + >>> import rsa + >>> (bob_pub, bob_priv) = rsa.newkeys(512) + +#. Alice writes a message, and encodes it in UTF-8. The RSA module + only operates on bytes, and not on strings, so this step is + necessary. + + >>> message = 'hello Bob!'.encode('utf8') + +#. Alice encrypts the message using Bob's public key, and sends the + encrypted message. + + >>> import rsa + >>> crypto = rsa.encrypt(message, bob_pub) + +#. Bob receives the message, and decrypts it with his private key. + + >>> message = rsa.decrypt(crypto, bob_priv) + >>> print(message.decode('utf8')) + hello Bob! + +Since Bob kept his private key *private*, Alice can be sure that he is +the only one who can read the message. Bob does *not* know for sure +that it was Alice that sent the message, since she didn't sign it. + + +RSA can only encrypt messages that are smaller than the key. A couple +of bytes are lost on random padding, and the rest is available for the +message itself. For example, a 512-bit key can encode a 53-byte +message (512 bit = 64 bytes, 11 bytes are used for random padding and +other stuff). See :ref:`bigfiles` for information on how to work with +larger files. + +Altering the encrypted information will *likely* cause a +:py:class:`rsa.pkcs1.DecryptionError`. If you want to be *sure*, use +:py:func:`rsa.sign`. + + >>> crypto = rsa.encrypt(b'hello', bob_pub) + >>> crypto = crypto[:-1] + b'X' # change the last byte + >>> rsa.decrypt(crypto, bob_priv) + Traceback (most recent call last): + ... + rsa.pkcs1.DecryptionError: Decryption failed + + +.. warning:: + + Never display the stack trace of a + :py:class:`rsa.pkcs1.DecryptionError` exception. It shows where + in the code the exception occurred, and thus leaks information + about the key. It’s only a tiny bit of information, but every bit + makes cracking the keys easier. + +Low-level operations +++++++++++++++++++++ + +The core RSA algorithm operates on large integers. These operations +are considered low-level and are supported by the +:py:func:`rsa.core.encrypt_int` and :py:func:`rsa.core.decrypt_int` +functions. + +Signing and verification +------------------------ + +You can create a detached signature for a message using the +:py:func:`rsa.sign` function: + + >>> (pubkey, privkey) = rsa.newkeys(512) + >>> message = 'Go left at the blue tree' + >>> signature = rsa.sign(message, privkey, 'SHA-1') + +This hashes the message using SHA-1. Other hash methods are also +possible, check the :py:func:`rsa.sign` function documentation for +details. The hash is then signed with the private key. + +In order to verify the signature, use the :py:func:`rsa.verify` +function. This function returns True if the verification is successful: + + >>> message = 'Go left at the blue tree' + >>> rsa.verify(message, signature, pubkey) + True + +Modify the message, and the signature is no longer valid and a +:py:class:`rsa.pkcs1.VerificationError` is thrown: + + >>> message = 'Go right at the blue tree' + >>> rsa.verify(message, signature, pubkey) + Traceback (most recent call last): + File "<stdin>", line 1, in <module> + File "/home/sybren/workspace/python-rsa/rsa/pkcs1.py", line 289, in verify + raise VerificationError('Verification failed') + rsa.pkcs1.VerificationError: Verification failed + +.. warning:: + + Never display the stack trace of a + :py:class:`rsa.pkcs1.VerificationError` exception. It shows where + in the code the exception occurred, and thus leaks information + about the key. It's only a tiny bit of information, but every bit + makes cracking the keys easier. + +Instead of a message you can also call :py:func:`rsa.sign` and +:py:func:`rsa.verify` with a :py:class:`file`-like object. If the +message object has a ``read(int)`` method it is assumed to be a file. +In that case the file is hashed in 1024-byte blocks at the time. + + >>> with open('somefile', 'rb') as msgfile: + ... signature = rsa.sign(msgfile, privkey, 'SHA-1') + + >>> with open('somefile', 'rb') as msgfile: + ... rsa.verify(msgfile, signature, pubkey) + + +.. _bigfiles: + +Working with big files +---------------------- + +RSA can only encrypt messages that are smaller than the key. A couple +of bytes are lost on random padding, and the rest is available for the +message itself. For example, a 512-bit key can encode a 53-byte +message (512 bit = 64 bytes, 11 bytes are used for random padding and +other stuff). + +How it usually works +++++++++++++++++++++ + +The most common way to use RSA with larger files uses a block cypher +like AES or DES3 to encrypt the file with a random key, then encrypt +the random key with RSA. You would send the encrypted file along with +the encrypted key to the recipient. The complete flow is: + +#. Generate a random key + + >>> import rsa.randnum + >>> aes_key = rsa.randnum.read_random_bits(128) + +#. Use that key to encrypt the file with AES. +#. :py:func:`Encrypt <rsa.encrypt>` the AES key with RSA + + >>> encrypted_aes_key = rsa.encrypt(aes_key, public_rsa_key) + +#. Send the encrypted file together with ``encrypted_aes_key`` +#. The recipient now reverses this process to obtain the encrypted + file. + +.. note:: + + The Python-RSA module does not contain functionality to do the AES + encryption for you. + +Only using Python-RSA: the VARBLOCK format +++++++++++++++++++++++++++++++++++++++++++ + +.. warning:: + + The VARBLOCK format is NOT recommended for general use, has been deprecated since + Python-RSA 3.4, and will be removed in a future release. It's vulnerable to a + number of attacks: + + 1. decrypt/encrypt_bigfile() does not implement `Authenticated encryption`_ nor + uses MACs to verify messages before decrypting public key encrypted messages. + + 2. decrypt/encrypt_bigfile() does not use hybrid encryption (it uses plain RSA) + and has no method for chaining, so block reordering is possible. + + See `issue #19 on Github`_ for more information. + +.. _Authenticated encryption: https://en.wikipedia.org/wiki/Authenticated_encryption +.. _issue #19 on Github: https://github.com/sybrenstuvel/python-rsa/issues/13 + + +As far as we know, there is no pure-Python AES encryption. Previous +versions of Python-RSA included functionality to encrypt large files +with just RSA, and so does this version. The format has been improved, +though. + +Encrypting works as follows: the input file is split into blocks that +are just large enough to encrypt with your RSA key. Every block is +then encrypted using RSA, and the encrypted blocks are assembled into +the output file. This file format is called the :ref:`VARBLOCK +<VARBLOCK>` format. + +Decrypting works in reverse. The encrypted file is separated into +encrypted blocks. Those are decrypted, and assembled into the original +file. + +.. note:: + + The file will get larger after encryption, as each encrypted block + has 8 bytes of random padding and 3 more bytes of overhead. + +Since these encryption/decryption functions are potentially called on +very large files, they use another approach. Where the regular +functions store the message in memory in its entirety, these functions +work on one block at the time. As a result, you should call them with +:py:class:`file`-like objects as the parameters. + +Before using we of course need a keypair: + +>>> import rsa +>>> (pub_key, priv_key) = rsa.newkeys(512) + +Encryption works on file handles using the +:py:func:`rsa.bigfile.encrypt_bigfile` function: + +>>> from rsa.bigfile import * +>>> with open('inputfile', 'rb') as infile, open('outputfile', 'wb') as outfile: +... encrypt_bigfile(infile, outfile, pub_key) + +As does decryption using the :py:func:`rsa.bigfile.decrypt_bigfile` +function: + +>>> from rsa.bigfile import * +>>> with open('inputfile', 'rb') as infile, open('outputfile', 'wb') as outfile: +... decrypt_bigfile(infile, outfile, priv_key) + +.. note:: + + :py:func:`rsa.sign` and :py:func:`rsa.verify` work on arbitrarily + long files, so they do not have a "bigfile" equivalent. + + |