diff options
author | Sybren A. Stüvel <sybren@stuvel.eu> | 2011-08-03 13:31:57 +0200 |
---|---|---|
committer | Sybren A. Stüvel <sybren@stuvel.eu> | 2011-08-03 13:31:57 +0200 |
commit | dbea213e8875d53087b5b3adf85c7004f13b05d8 (patch) | |
tree | 01770c1edff43ec14835682c18866e10eae2e278 | |
parent | fc9c786aca72401ec1a879f27ab99bde6b795736 (diff) | |
download | rsa-git-dbea213e8875d53087b5b3adf85c7004f13b05d8.tar.gz |
more documentation
-rw-r--r-- | doc/Makefile | 2 | ||||
-rw-r--r-- | doc/compatibility.rst | 10 | ||||
-rw-r--r-- | doc/index.rst | 2 | ||||
-rw-r--r-- | doc/reference.rst | 37 | ||||
-rw-r--r-- | doc/upgrading.rst | 67 | ||||
-rw-r--r-- | doc/usage.rst | 65 | ||||
-rw-r--r-- | rsa/pkcs1.py | 32 |
7 files changed, 183 insertions, 32 deletions
diff --git a/doc/Makefile b/doc/Makefile index f19a957..397d629 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -14,6 +14,8 @@ 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" diff --git a/doc/compatibility.rst b/doc/compatibility.rst index c9dac39..ab9e2e4 100644 --- a/doc/compatibility.rst +++ b/doc/compatibility.rst @@ -11,20 +11,20 @@ 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: +Encryption: PKCS#1 v1.5 with at least 8 bytes of random padding -:Signatures: +Signatures: PKCS#1 v1.5 using the following hash methods: MD5, SHA-1, SHA-256, SHA-384, SHA-512 -:Private keys: +Private keys: PKCS#1 v1.5 in PEM and DER format, ASN.1 type RSAPrivateKey -:Public keys: +Public keys: PKCS#1 v1.5 in PEM and DER format, ASN.1 type RSAPublicKey -:VARBLOCK encryption: +:ref:`VARBLOCK <bigfiles>` encryption: Python-RSA only, not compatible with any other known application. diff --git a/doc/index.rst b/doc/index.rst index 9c84331..02577ab 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -36,9 +36,11 @@ Contents .. toctree:: :maxdepth: 2 + :numbered: intro installation + upgrading licence usage cli diff --git a/doc/reference.rst b/doc/reference.rst index a1a7d35..53dee63 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -1,6 +1,9 @@ Reference ================================================== +This is the class and function reference. For more usage information +see the :ref:`usage` page. + Functions -------------------------------------------------- @@ -14,10 +17,6 @@ Functions .. autofunction:: rsa.newkeys(keysize) -.. autofunction:: rsa.bigfile.encrypt_bigfile - -.. autofunction:: rsa.bigfile.decrypt_bigfile - Classes -------------------------------------------------- @@ -42,9 +41,22 @@ Exceptions .. index:: VARBLOCK (file format) -The VARBLOCK file format +Module: rsa.bigfile -------------------------------------------------- +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 +++++++++++++++++++++++++++++++++++++++++++++++++++ + 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:: @@ -55,7 +67,7 @@ concatenation:: BLOCK := LENGTH || DATA - LENGTH := varint-encoded length of the followng data, in bytes + LENGTH := varint-encoded length of the following data, in bytes DATA := the data to store in the block @@ -65,3 +77,16 @@ efficiently encode an arbitrarily long integer. .. _Protobuf: http://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..c8a2d82 --- /dev/null +++ b/doc/upgrading.rst @@ -0,0 +1,67 @@ +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 are still available. + +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. + +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 index 09954e2..9b5fc17 100644 --- a/doc/usage.rst +++ b/doc/usage.rst @@ -1,3 +1,5 @@ +.. _usage: + Usage ================================================== @@ -6,7 +8,7 @@ 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. -.. note:: +.. important:: The private key is called *private* for a reason. Never share this key with anyone. @@ -97,23 +99,37 @@ that only Bob can read. hello Bob! Since Bob kept his private key *private*, Alice can be sure that he is -the only one who can read the message. - -.. note:: - - Bob does *not* know for sure that it was Alice that sent the - message, since she didn't sign it. +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 `Working with big files`_ for information on how to work with +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 = encrypt('hello', pub_key) + >>> crypto = 'X' + crypto[1:] # change the first byte + >>> decrypt(crypto, priv_key) + 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 ++++++++++++++++++++++++++++++ @@ -153,7 +169,7 @@ Modify the message, and the signature is no longer valid and a raise VerificationError('Verification failed') rsa.pkcs1.VerificationError: Verification failed -.. note:: +.. warning:: Never display the stack trace of a :py:class:`rsa.pkcs1.VerificationError` exception. It shows where @@ -173,6 +189,8 @@ In that case the file is hashed in 1024-byte blocks at the time. ... rsa.verify(msgfile, signature, pubkey) +.. _bigfiles: + Working with big files -------------------------------------------------- @@ -196,33 +214,39 @@ the encrypted key to the recipient. The complete flow is: >>> aes_key = rsa.randnum.read_random_bits(128) #. Use that key to encrypt the file with AES. -#. Encrypt the AES key with RSA +#. :py:func:`Encrypt <rsa.encrypt>` the AES key with RSA - >>> encrypted_aes_key = rsa.encrypt(aes_key, public_key) + >>> 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:: -Only using Python-RSA -++++++++++++++++++++++++++++++++++++++++ + The Python-RSA module does not contain functionality to do the AES + encryption for you. + +Only using Python-RSA: the VARBLOCK format ++++++++++++++++++++++++++++++++++++++++++++ As far as we know, there is no pure-Python AES encryption. Previous -versions of Python-RSA included functionality to encrypt large files, +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 VARBLOCK format. +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. @@ -237,19 +261,22 @@ Before using we of course need a keypair: >>> import rsa >>> (pub_key, priv_key) = rsa.newkeys(512) -Encryption works on file handles: +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: +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. diff --git a/rsa/pkcs1.py b/rsa/pkcs1.py index b81629e..4ca0f96 100644 --- a/rsa/pkcs1.py +++ b/rsa/pkcs1.py @@ -198,7 +198,27 @@ def decrypt(crypto, priv_key): >>> crypto = encrypt('\x00\x00\x00\x00\x01', pub_key) >>> decrypt(crypto, priv_key) '\x00\x00\x00\x00\x01' - + + Altering the encrypted information will *likely* cause a + :py:class:`rsa.pkcs1.DecryptionError`. If you want to be *sure*, use + :py:func:`rsa.sign`. + + + .. 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. + + >>> crypto = encrypt('hello', pub_key) + >>> crypto = 'X' + crypto[1:] # change the first byte + >>> decrypt(crypto, priv_key) + Traceback (most recent call last): + ... + rsa.pkcs1.DecryptionError: Decryption failed + ''' blocksize = common.byte_size(priv_key.n) @@ -263,10 +283,18 @@ def verify(message, signature, pub_key): :param message: the signed message. Can be an 8-bit string or a file-like object. If ``message`` has a ``read()`` method, it is assumed to be a file-like object. - :param signature: the signature block, as created with ``sign(...)``. + :param signature: the signature block, as created with :py:func:`rsa.sign`. :param pub_key: the :py:class:`rsa.PublicKey` of the person signing the message. :raise VerificationError: when the signature doesn't match the message. + .. 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. + ''' blocksize = common.byte_size(pub_key.n) |