summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSybren A. St?vel <sybren@stuvel.eu>2011-07-31 17:44:44 +0200
committerSybren A. St?vel <sybren@stuvel.eu>2011-07-31 17:44:44 +0200
commitdc13538d6a72ac84c21e05e9a79fb3df044b24b8 (patch)
tree2ece358fec55413981b7fedd7df49d01d453f54f
parent71ac29641511f10874f741e15fe0712dabf3d1a7 (diff)
downloadrsa-dc13538d6a72ac84c21e05e9a79fb3df044b24b8.tar.gz
More documentation
-rw-r--r--doc/compatibility.rst1
-rw-r--r--doc/conf.py6
-rw-r--r--doc/index.rst1
-rw-r--r--doc/intro.rst2
-rw-r--r--doc/reference.rst29
-rw-r--r--doc/usage.rst80
-rw-r--r--rsa/key.py60
-rw-r--r--rsa/pkcs1.py71
8 files changed, 182 insertions, 68 deletions
diff --git a/doc/compatibility.rst b/doc/compatibility.rst
index cbc0eb3..0cb2ba6 100644
--- a/doc/compatibility.rst
+++ b/doc/compatibility.rst
@@ -42,3 +42,4 @@ through the ``pyrsa-priv2pub`` command::
--out=OUTFILENAME Output filename. Writes to stdout of not specified
--inform=INFORM key format of input - default PEM
--outform=OUTFORM key format of output - default PEM
+
diff --git a/doc/conf.py b/doc/conf.py
index 3ee83f1..ad49886 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -26,7 +26,9 @@ import sys, os
# 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.viewcode', 'sphinx.ext.pngmath']
+ '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']
@@ -35,7 +37,7 @@ templates_path = ['_templates']
source_suffix = '.rst'
# The encoding of source files.
-#source_encoding = 'utf-8-sig'
+source_encoding = 'utf-8'
# The master toctree document.
master_doc = 'index'
diff --git a/doc/index.rst b/doc/index.rst
index addc869..df81a77 100644
--- a/doc/index.rst
+++ b/doc/index.rst
@@ -39,6 +39,7 @@ Contents
licence
usage
compatibility
+ reference
Indices and tables
diff --git a/doc/intro.rst b/doc/intro.rst
index e27a7a2..cc89a19 100644
--- a/doc/intro.rst
+++ b/doc/intro.rst
@@ -14,6 +14,6 @@ numbers. It also included generating public and private keys. There
was no functionality for working with byte sequences (such as files)
yet.
-.. TODO:: write more history
+.. todo:: write more history
diff --git a/doc/reference.rst b/doc/reference.rst
new file mode 100644
index 0000000..69a89bc
--- /dev/null
+++ b/doc/reference.rst
@@ -0,0 +1,29 @@
+Reference
+==================================================
+
+Functions
+--------------------------------------------------
+
+.. autofunction:: rsa.encrypt
+
+.. autofunction:: rsa.decrypt
+
+.. autofunction:: rsa.sign
+
+.. autofunction:: rsa.verify
+
+.. autofunction:: rsa.newkeys(keysize)
+
+Classes
+--------------------------------------------------
+
+.. autoclass:: rsa.PublicKey
+ :members:
+ :inherited-members:
+
+.. autoclass:: rsa.PrivateKey
+ :members:
+ :inherited-members:
+
+
+
diff --git a/doc/usage.rst b/doc/usage.rst
index bd499a9..61121e0 100644
--- a/doc/usage.rst
+++ b/doc/usage.rst
@@ -3,10 +3,6 @@ Usage
This section describes the usage of the Python-RSA module.
-
-Generating keys
---------------------------------------------------
-
Before you can use RSA you need keys. You will receive a private key
and a public key.
@@ -15,10 +11,86 @@ and a public key.
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.
+Alternatively you can use :py:func:`rsa.PrivateKey.load_pkcs1` and
+:py:func:`rsa.PublicKey.load_pkcs1` to load keys from a file.
+
+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.
+
+These are some timings from my netbook (Linux 2.6, 1.6 GHz Intel Atom
+N270 CPU, 2 GB RAM):
+
++----------------+------------------+
+| Keysize (bits) | Time to generate |
++================+==================+
+| 32 | 0.01 sec. |
++----------------+------------------+
+| 64 | 0.03 sec. |
++----------------+------------------+
+| 96 | 0.04 sec. |
++----------------+------------------+
+| 128 | 0.08 sec. |
++----------------+------------------+
+| 256 | 0.27 sec. |
++----------------+------------------+
+| 384 | 0.93 sec. |
++----------------+------------------+
+| 512 | 1.21 sec. |
++----------------+------------------+
+| 1024 | 7.93 sec. |
++----------------+------------------+
+| 2048 | 132.97 sec. |
++----------------+------------------+
+
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).
+
+#. Alice writes a message
+
+#. Alice encrypts the message using Bob's public key, and sends the
+ encrypted message.
+
+#. Bob receives the message, and decrypts it with his private key.
+
+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.
+
+
+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
--------------------------------------------------
diff --git a/rsa/key.py b/rsa/key.py
index e8fb63f..2d2ca4d 100644
--- a/rsa/key.py
+++ b/rsa/key.py
@@ -41,15 +41,17 @@ class AbstractKey(object):
def load_pkcs1(cls, keyfile, format='PEM'):
r'''Loads a key in PKCS#1 DER or PEM format.
- @param keyfile: contents of a DER- or PEM-encoded file that contains
+ :param keyfile: contents of a DER- or PEM-encoded file that contains
the public key.
- @param format: the format of the file to load; 'PEM' or 'DER'
- @return: a PublicKey object
+ :param format: the format of the file to load; 'PEM' or 'DER'
+
+ :return: a PublicKey object
+
'''
methods = {
- 'PEM': cls.load_pkcs1_pem,
- 'DER': cls.load_pkcs1_der,
+ 'PEM': cls._load_pkcs1_pem,
+ 'DER': cls._load_pkcs1_der,
}
if format not in methods:
@@ -63,13 +65,14 @@ class AbstractKey(object):
def save_pkcs1(self, format='PEM'):
'''Saves the public key in PKCS#1 DER or PEM format.
- @param format: the format to save; 'PEM' or 'DER'
- @returns: the DER- or PEM-encoded public key.
+ :param format: the format to save; 'PEM' or 'DER'
+ :returns: the DER- or PEM-encoded public key.
+
'''
methods = {
- 'PEM': self.save_pkcs1_pem,
- 'DER': self.save_pkcs1_der,
+ 'PEM': self._save_pkcs1_pem,
+ 'DER': self._save_pkcs1_der,
}
if format not in methods:
@@ -86,7 +89,8 @@ class PublicKey(AbstractKey):
This key is also known as the 'encryption key'. It contains the 'n' and 'e'
values.
- Supports attributes as well as dictionary-like access.
+ Supports attributes as well as dictionary-like access. Attribute accesss is
+ faster, though.
>>> PublicKey(5, 3)
PublicKey(5, 3)
@@ -128,7 +132,7 @@ class PublicKey(AbstractKey):
return not (self == other)
@classmethod
- def load_pkcs1_der(cls, keyfile):
+ def _load_pkcs1_der(cls, keyfile):
r'''Loads a key in PKCS#1 DER format.
@param keyfile: contents of a DER-encoded file that contains the public
@@ -160,7 +164,7 @@ class PublicKey(AbstractKey):
as_ints = tuple(int(x) for x in priv)
return cls(*as_ints)
- def save_pkcs1_der(self):
+ def _save_pkcs1_der(self):
'''Saves the public key in PKCS#1 DER format.
@returns: the DER-encoded public key.
@@ -183,7 +187,7 @@ class PublicKey(AbstractKey):
return encoder.encode(asn_key)
@classmethod
- def load_pkcs1_pem(cls, keyfile):
+ def _load_pkcs1_pem(cls, keyfile):
'''Loads a PKCS#1 PEM-encoded public key file.
The contents of the file before the "-----BEGIN RSA PUBLIC KEY-----" and
@@ -197,7 +201,7 @@ class PublicKey(AbstractKey):
der = rsa.pem.load_pem(keyfile, 'RSA PUBLIC KEY')
return cls.load_pkcs1_der(der)
- def save_pkcs1_pem(self):
+ def _save_pkcs1_pem(self):
'''Saves a PKCS#1 PEM-encoded public key file.
@return: contents of a PEM-encoded file that contains the public key.
@@ -212,7 +216,8 @@ class PrivateKey(AbstractKey):
This key is also known as the 'decryption key'. It contains the 'n', 'e',
'd', 'p', 'q' and other values.
- Supports attributes as well as dictionary-like access.
+ Supports attributes as well as dictionary-like access. Attribute accesss is
+ faster, though.
>>> PrivateKey(3247, 65537, 833, 191, 17)
PrivateKey(3247, 65537, 833, 191, 17)
@@ -290,7 +295,7 @@ class PrivateKey(AbstractKey):
return not (self == other)
@classmethod
- def load_pkcs1_der(cls, keyfile):
+ def _load_pkcs1_der(cls, keyfile):
r'''Loads a key in PKCS#1 DER format.
@param keyfile: contents of a DER-encoded file that contains the private
@@ -334,7 +339,7 @@ class PrivateKey(AbstractKey):
as_ints = tuple(int(x) for x in priv[1:9])
return cls(*as_ints)
- def save_pkcs1_der(self):
+ def _save_pkcs1_der(self):
'''Saves the private key in PKCS#1 DER format.
@returns: the DER-encoded private key.
@@ -371,7 +376,7 @@ class PrivateKey(AbstractKey):
return encoder.encode(asn_key)
@classmethod
- def load_pkcs1_pem(cls, keyfile):
+ def _load_pkcs1_pem(cls, keyfile):
'''Loads a PKCS#1 PEM-encoded private key file.
The contents of the file before the "-----BEGIN RSA PRIVATE KEY-----" and
@@ -385,7 +390,7 @@ class PrivateKey(AbstractKey):
der = rsa.pem.load_pem(keyfile, 'RSA PRIVATE KEY')
return cls.load_pkcs1_der(der)
- def save_pkcs1_pem(self):
+ def _save_pkcs1_pem(self):
'''Saves a PKCS#1 PEM-encoded private key file.
@return: contents of a PEM-encoded file that contains the private key.
@@ -535,15 +540,16 @@ def gen_keys(nbits, accurate=True):
def newkeys(nbits, accurate=True):
"""Generates public and private keys, and returns them as (pub, priv).
- The public key is also known as the 'encryption key', and is a PublicKey
- object. The private key is also known as the 'decryption key' and is a
- PrivateKey object.
-
- @param nbits: the number of bits required to store ``n = p*q``.
- @param accurate: when True, ``n`` will have exactly the number of bits you
- asked for. However, this makes key generation much slower.
+ The public key is also known as the 'encryption key', and is a
+ :py:class:`PublicKey` object. The private key is also known as the
+ 'decryption key' and is a :py:class:`PrivateKey` object.
+
+ :param nbits: the number of bits required to store ``n = p*q``.
+ :param accurate: when True, ``n`` will have exactly the number of bits you
+ asked for. However, this makes key generation much slower. When False,
+ `n`` may have slightly less bits.
- @return: a tuple (PublicKey, PrivateKey)
+ :returns: a tuple (:py:class:`rsa.PublicKey`, :py:class:`rsa.PrivateKey`)
"""
diff --git a/rsa/pkcs1.py b/rsa/pkcs1.py
index 3955c30..7612b27 100644
--- a/rsa/pkcs1.py
+++ b/rsa/pkcs1.py
@@ -14,9 +14,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-'''Functions for PKCS1 version 1.5 encryption and signing
+'''Functions for PKCS#1 version 1.5 encryption and signing
-This module implements certain functionality from PKCS1 version 1.5. For a
+This module implements certain functionality from PKCS#1 version 1.5. For a
very clear example, read http://www.di-mgt.com.au/rsa_alg.html#pkcs1schemes
At least 8 bytes of random padding is used when encrypting a message. This makes
@@ -62,7 +62,7 @@ class VerificationError(CryptoError):
def _pad_for_encryption(message, target_length):
r'''Pads the message for encryption, returning the padded message.
- @return: 00 02 RANDOM_DATA 00 MESSAGE
+ :return: 00 02 RANDOM_DATA 00 MESSAGE
>>> block = _pad_for_encryption('hello', 16)
>>> len(block)
@@ -110,7 +110,7 @@ def _pad_for_signing(message, target_length):
The padding is always a repetition of FF bytes.
- @return: 00 01 PADDING 00 MESSAGE
+ :return: 00 01 PADDING 00 MESSAGE
>>> block = _pad_for_signing('hello', 16)
>>> len(block)
@@ -140,14 +140,13 @@ def _pad_for_signing(message, target_length):
def encrypt(message, pub_key):
- '''Encrypts the given message using PKCS1 v1.5
+ '''Encrypts the given message using PKCS#1 v1.5
- @param message: the message to encrypt. Must be a byte string no longer than
+ :param message: the message to encrypt. Must be a byte string no longer than
``k-11`` bytes, where ``k`` is the number of bytes needed to encode
the ``n`` component of the public key.
- @param pub_key: the public key to encrypt with.
-
- @raise OverflowError: when the message is too large to fit in the padded
+ :param pub_key: the :py:class:`rsa.PublicKey` to encrypt with.
+ :raise OverflowError: when the message is too large to fit in the padded
block.
>>> from rsa import key, common
@@ -156,6 +155,7 @@ def encrypt(message, pub_key):
>>> crypto = encrypt(message, pub_key)
The crypto text should be just as long as the public key 'n' component:
+
>>> len(crypto) == common.byte_size(pub_key.n)
True
@@ -171,28 +171,32 @@ def encrypt(message, pub_key):
return block
def decrypt(crypto, priv_key):
- r'''Decrypts the given message using PKCS1 v1.5
+ r'''Decrypts the given message using PKCS#1 v1.5
The decryption is considered 'failed' when the resulting cleartext doesn't
start with the bytes 00 02, or when the 00 byte between the padding and
the message cannot be found.
- @param crypto: the crypto text as returned by ``encrypt(message, pub_key)``
- @param priv_key: the private key to decrypt with.
-
- @raise DecryptionError: when the decryption fails. No details are given as
+ :param crypto: the crypto text as returned by :py:func:`rsa.encrypt`
+ :param priv_key: the :py:class:`rsa.PrivateKey` to decrypt with.
+ :raise DecryptionError: when the decryption fails. No details are given as
to why the code thinks the decryption fails, as this would leak
information about the private key.
- >>> from rsa import key, common
- >>> (pub_key, priv_key) = key.newkeys(256)
+
+ >>> import rsa
+ >>> (pub_key, priv_key) = rsa.newkeys(256)
It works with strings:
- >>> decrypt(encrypt('hello', pub_key), priv_key)
+
+ >>> crypto = encrypt('hello', pub_key)
+ >>> decrypt(crypto, priv_key)
'hello'
And with binary data:
- >>> decrypt(encrypt('\x00\x00\x00\x00\x01', pub_key), priv_key)
+
+ >>> crypto = encrypt('\x00\x00\x00\x00\x01', pub_key)
+ >>> decrypt(crypto, priv_key)
'\x00\x00\x00\x00\x01'
'''
@@ -218,16 +222,14 @@ def sign(message, priv_key, hash):
'''Signs the message with the private key.
Hashes the message, then signs the hash with the given key. This is known
- as a "detached signature", because the message itself isn't signed.
+ as a "detached signature", because the message itself isn't altered.
- @param message: the message to sign
- @param priv_key: the private key to sign with
- @param hash: the hash method used on the message. Use 'MD5', 'SHA-1',
+ :param message: the message to sign
+ :param priv_key: the :py:class:`rsa.PrivateKey` to sign with
+ :param hash: the hash method used on the message. Use 'MD5', 'SHA-1',
'SHA-256', 'SHA-384' or 'SHA-512'.
-
- @return: a message signature block.
-
- @raise OverflowError: if the private key is too small to contain the
+ :return: a message signature block.
+ :raise OverflowError: if the private key is too small to contain the
requested hash.
'''
@@ -256,11 +258,11 @@ def verify(message, signature, pub_key):
The hash method is detected automatically from the signature.
- @param message: the signed message
- @param signature: the signature block, as created with ``sign(...)``.
- @param pub_key: the public key of the person signing the message.
-
- @raise VerificationError: when the signature doesn't match the message.
+ :param message: the signed message
+ :param signature: the signature block, as created with ``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.
+
'''
blocksize = common.byte_size(pub_key.n)
@@ -301,13 +303,14 @@ def _hash(message, method_name):
def _find_method_hash(method_hash):
'''Finds the hash method and the hash itself.
- @param method_hash: ASN1 code for the hash method concatenated with the
+ :param method_hash: ASN1 code for the hash method concatenated with the
hash itself.
- @return: tuple (method, hash) where ``method`` is the used hash method, and
+ :return: tuple (method, hash) where ``method`` is the used hash method, and
``hash`` is the hash itself.
- @raise VerificationFailed: when the hash method cannot be found
+ :raise VerificationFailed: when the hash method cannot be found
+
'''
for (hashname, asn1code) in HASH_ASN1.iteritems():