diff options
author | herbertxue <herbertxue@google.com> | 2018-06-04 02:50:00 +0000 |
---|---|---|
committer | herbertxue <herbertxue@google.com> | 2018-06-12 06:38:20 +0000 |
commit | d8ac8301abc7ccb1a099ceb7e4d435ea55bf4a46 (patch) | |
tree | 75545e9a8e9a5fce6df78d519c65628720ec47b5 /doc/usage.rst | |
parent | dbe61a7ce8c6b3a1ffbf632590ea05c61231b687 (diff) | |
parent | fd70d79610ac1af8b072aa27fadf660b4a64797c (diff) | |
download | rsa-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.rst | 353 |
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. + + |