aboutsummaryrefslogtreecommitdiff
path: root/doc/usage.rst
diff options
context:
space:
mode:
authorherbertxue <herbertxue@google.com>2018-06-04 02:50:00 +0000
committerherbertxue <herbertxue@google.com>2018-06-12 06:38:20 +0000
commitd8ac8301abc7ccb1a099ceb7e4d435ea55bf4a46 (patch)
tree75545e9a8e9a5fce6df78d519c65628720ec47b5 /doc/usage.rst
parentdbe61a7ce8c6b3a1ffbf632590ea05c61231b687 (diff)
parentfd70d79610ac1af8b072aa27fadf660b4a64797c (diff)
downloadrsa-d8ac8301abc7ccb1a099ceb7e4d435ea55bf4a46.tar.gz
Merge commit 'fd70d79' into rsa_3.4.2android-p-preview-5android-p-preview-4
Inital commit of rsa 3.4.2 with history Added: - Android.bp - MODULE_LICENSE_APACHE2 - NOTICE - METADATA Bug: b/80314772 Test: Complied acloud with rsa and was able to import rsa. Change-Id: Iec6de78cc16267eac9d5f999728a1797c20eb8e2
Diffstat (limited to 'doc/usage.rst')
-rw-r--r--doc/usage.rst353
1 files changed, 353 insertions, 0 deletions
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.
+
+