Merge pull request #593 from alex/better-readme

Fixed #576 -- improve the readme
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index 6da90ce..d886932 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -20,7 +20,7 @@
     UnsupportedAlgorithm, InvalidTag, InternalError
 )
 from cryptography.hazmat.backends.interfaces import (
-    CipherBackend, HashBackend, HMACBackend, PBKDF2HMACBackend
+    CipherBackend, HashBackend, HMACBackend, PBKDF2HMACBackend, RSABackend
 )
 from cryptography.hazmat.primitives import interfaces, hashes
 from cryptography.hazmat.primitives.ciphers.algorithms import (
@@ -30,12 +30,14 @@
     CBC, CTR, ECB, OFB, CFB, GCM,
 )
 from cryptography.hazmat.bindings.openssl.binding import Binding
+from cryptography.hazmat.primitives.asymmetric import rsa
 
 
 @utils.register_interface(CipherBackend)
 @utils.register_interface(HashBackend)
 @utils.register_interface(HMACBackend)
 @utils.register_interface(PBKDF2HMACBackend)
+@utils.register_interface(RSABackend)
 class Backend(object):
     """
     OpenSSL API binding interfaces.
@@ -259,6 +261,46 @@
             )
         )
 
+    def _bn_to_int(self, bn):
+        hex_cdata = self._lib.BN_bn2hex(bn)
+        assert hex_cdata != self._ffi.NULL
+        hex_str = self._ffi.string(hex_cdata)
+        self._lib.OPENSSL_free(hex_cdata)
+        return int(hex_str, 16)
+
+    def generate_rsa_private_key(self, public_exponent, key_size):
+        if public_exponent < 3:
+            raise ValueError("public_exponent must be >= 3")
+
+        if public_exponent & 1 == 0:
+            raise ValueError("public_exponent must be odd")
+
+        if key_size < 512:
+            raise ValueError("key_size must be at least 512-bits")
+
+        ctx = backend._lib.RSA_new()
+        ctx = backend._ffi.gc(ctx, backend._lib.RSA_free)
+
+        bn = backend._lib.BN_new()
+        assert bn != self._ffi.NULL
+        bn = backend._ffi.gc(bn, backend._lib.BN_free)
+
+        res = backend._lib.BN_set_word(bn, public_exponent)
+        assert res == 1
+
+        res = backend._lib.RSA_generate_key_ex(
+            ctx, key_size, bn, backend._ffi.NULL
+        )
+        assert res == 1
+
+        return rsa.RSAPrivateKey(
+            p=self._bn_to_int(ctx.p),
+            q=self._bn_to_int(ctx.q),
+            private_exponent=self._bn_to_int(ctx.d),
+            public_exponent=self._bn_to_int(ctx.e),
+            modulus=self._bn_to_int(ctx.n),
+        )
+
 
 class GetCipherByName(object):
     def __init__(self, fmt):
diff --git a/docs/hazmat/backends/openssl.rst b/docs/hazmat/backends/openssl.rst
index ea72af9..e388087 100644
--- a/docs/hazmat/backends/openssl.rst
+++ b/docs/hazmat/backends/openssl.rst
@@ -15,6 +15,7 @@
     * :class:`~cryptography.hazmat.backends.interfaces.HashBackend`
     * :class:`~cryptography.hazmat.backends.interfaces.HMACBackend`
     * :class:`~cryptography.hazmat.backends.interfaces.PBKDF2HMACBackend`
+    * :class:`~cryptography.hazmat.backends.interfaces.RSABackend`
 
     It also exposes the following:
 
diff --git a/tests/hazmat/primitives/test_rsa.py b/tests/hazmat/primitives/test_rsa.py
index e2aca02..fdd55e7 100644
--- a/tests/hazmat/primitives/test_rsa.py
+++ b/tests/hazmat/primitives/test_rsa.py
@@ -14,6 +14,7 @@
 
 from __future__ import absolute_import, division, print_function
 
+import itertools
 import os
 
 import pytest
@@ -23,8 +24,53 @@
 from ...utils import load_pkcs1_vectors, load_vectors_from_file
 
 
+def _check_rsa_private_key(skey):
+    assert skey
+    assert skey.modulus
+    assert skey.public_exponent
+    assert skey.private_exponent
+    assert skey.p * skey.q == skey.modulus
+    assert skey.key_size
+
+    pkey = skey.public_key()
+    assert pkey
+    assert skey.modulus == pkey.modulus
+    assert skey.public_exponent == pkey.public_exponent
+    assert skey.key_size == pkey.key_size
+
+
+@pytest.mark.rsa
 class TestRSA(object):
     @pytest.mark.parametrize(
+        "public_exponent,key_size",
+        itertools.product(
+            (3, 5, 65537),
+            (1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1536, 2048)
+        )
+    )
+    def test_generate_rsa_keys(self, backend, public_exponent, key_size):
+        skey = backend.generate_rsa_private_key(public_exponent, key_size)
+        _check_rsa_private_key(skey)
+        assert skey.key_size == key_size
+        assert skey.public_exponent == public_exponent
+
+    def test_generate_bad_rsa_key(self, backend):
+        with pytest.raises(ValueError):
+            backend.generate_rsa_private_key(public_exponent=1, key_size=2048)
+
+        with pytest.raises(ValueError):
+            backend.generate_rsa_private_key(public_exponent=4, key_size=2048)
+
+    def test_cant_generate_insecure_tiny_key(self, backend):
+        with pytest.raises(ValueError):
+            backend.generate_rsa_private_key(public_exponent=65537,
+                                             key_size=511)
+
+        with pytest.raises(ValueError):
+            backend.generate_rsa_private_key(public_exponent=65537,
+                                             key_size=256)
+
+    @pytest.mark.parametrize(
         "pkcs1_example",
         load_vectors_from_file(
             os.path.join(
@@ -36,12 +82,15 @@
         secret, public = pkcs1_example
 
         skey = rsa.RSAPrivateKey(**secret)
+        assert skey
+        _check_rsa_private_key(skey)
+
         pkey = rsa.RSAPublicKey(**public)
+        assert pkey
+
         pkey2 = skey.public_key()
+        assert pkey2
 
-        assert skey and pkey and pkey2
-
-        assert skey.modulus
         assert skey.modulus == pkey.modulus
         assert skey.modulus == skey.n
         assert skey.public_exponent == pkey.public_exponent
@@ -58,8 +107,6 @@
         assert skey.key_size == pkey.key_size
         assert skey.key_size == pkey2.key_size
 
-        assert skey.p * skey.q == skey.modulus
-
     def test_invalid_private_key_argument_types(self):
         with pytest.raises(TypeError):
             rsa.RSAPrivateKey(None, None, None, None, None)
diff --git a/tests/hazmat/primitives/vectors/oath/rfc-4226.txt b/tests/hazmat/primitives/vectors/oath/rfc-4226.txt
new file mode 100644
index 0000000..35f7f8d
--- /dev/null
+++ b/tests/hazmat/primitives/vectors/oath/rfc-4226.txt
@@ -0,0 +1,80 @@
+# HOTP Test Vectors
+# RFC 4226 Appendix D
+
+COUNT = 0
+COUNTER = 0
+INTERMEDIATE = cc93cf18508d94934c64b65d8ba7667fb7cde4b0
+TRUNCATED = 4c93cf18
+HOTP = 755224
+SECRET = 12345678901234567890
+
+COUNT = 1
+COUNTER = 1
+INTERMEDIATE = 75a48a19d4cbe100644e8ac1397eea747a2d33ab
+TRUNCATED = 41397eea
+HOTP = 287082
+SECRET = 12345678901234567890
+
+
+COUNT = 2
+COUNTER = 2
+INTERMEDIATE = 0bacb7fa082fef30782211938bc1c5e70416ff44
+TRUNCATED = 82fef30
+HOTP = 359152
+SECRET = 12345678901234567890
+
+
+COUNT = 3
+COUNTER = 3
+INTERMEDIATE = 66c28227d03a2d5529262ff016a1e6ef76557ece
+TRUNCATED = 66ef7655
+HOTP = 969429
+SECRET = 12345678901234567890
+
+
+COUNT = 4
+COUNTER = 4
+INTERMEDIATE = a904c900a64b35909874b33e61c5938a8e15ed1c
+TRUNCATED = 61c5938a
+HOTP = 338314
+SECRET = 12345678901234567890
+
+
+COUNT = 5
+COUNTER = 5
+INTERMEDIATE = a37e783d7b7233c083d4f62926c7a25f238d0316
+TRUNCATED = 33c083d4
+HOTP = 254676
+SECRET = 12345678901234567890
+
+
+COUNT = 6
+COUNTER = 6
+INTERMEDIATE = bc9cd28561042c83f219324d3c607256c03272ae
+TRUNCATED = 7256c032
+HOTP = 287922
+SECRET = 12345678901234567890
+
+
+COUNT = 7
+COUNTER = 7
+INTERMEDIATE = a4fb960c0bc06e1eabb804e5b397cdc4b45596fa
+TRUNCATED = 4e5b397
+HOTP = 162583
+SECRET = 12345678901234567890
+
+
+COUNT = 8
+COUNTER = 8
+INTERMEDIATE = 1b3c89f65e6c9e883012052823443f048b4332db
+TRUNCATED = 2823443f
+HOTP = 399871
+SECRET = 12345678901234567890
+
+
+COUNT = 9
+COUNTER = 9
+INTERMEDIATE = 1637409809a679dc698207310c8c7fc07290d9e5
+TRUNCATED = 2679dc69
+HOTP = 520489
+SECRET = 12345678901234567890
diff --git a/tests/hazmat/primitives/vectors/oath/rfc-6238.txt b/tests/hazmat/primitives/vectors/oath/rfc-6238.txt
new file mode 100644
index 0000000..cc20995
--- /dev/null
+++ b/tests/hazmat/primitives/vectors/oath/rfc-6238.txt
@@ -0,0 +1,110 @@
+# TOTP Test Vectors
+# RFC 6238 Appendix B
+
+COUNT = 0
+TIME = 59
+TOTP = 94287082
+MODE = SHA1
+SECRET = 12345678901234567890
+
+COUNT = 1
+TIME = 59
+TOTP = 46119246
+MODE = SHA256
+SECRET = 12345678901234567890
+
+COUNT = 2
+TIME = 59
+TOTP = 90693936
+MODE = SHA512
+SECRET = 12345678901234567890
+
+COUNT = 3
+TIME = 1111111109
+TOTP = 07081804
+MODE = SHA1
+SECRET = 12345678901234567890
+
+COUNT = 4
+TIME = 1111111109
+TOTP = 68084774
+MODE = SHA256
+SECRET = 12345678901234567890
+
+COUNT = 5
+TIME = 1111111109
+TOTP = 25091201
+MODE = SHA512
+SECRET = 12345678901234567890
+
+COUNT = 6
+TIME = 1111111111
+TOTP = 14050471
+MODE = SHA1
+SECRET = 12345678901234567890
+
+COUNT = 7
+TIME = 1111111111
+TOTP = 67062674
+MODE = SHA256
+SECRET = 12345678901234567890
+
+COUNT = 8
+TIME = 1111111111
+TOTP = 99943326
+MODE = SHA512
+SECRET = 12345678901234567890
+
+COUNT = 9
+TIME = 1234567890
+TOTP = 89005924
+MODE = SHA1
+SECRET = 12345678901234567890
+
+COUNT = 10
+TIME = 1234567890
+TOTP = 91819424
+MODE = SHA256
+SECRET = 12345678901234567890
+
+COUNT = 11
+TIME = 1234567890
+TOTP = 93441116
+MODE = SHA512
+SECRET = 12345678901234567890
+
+COUNT = 12
+TIME = 2000000000
+TOTP = 69279037
+MODE = SHA1
+SECRET = 12345678901234567890
+
+COUNT = 13
+TIME = 2000000000
+TOTP = 90698825
+MODE = SHA256
+SECRET = 12345678901234567890
+
+COUNT = 14
+TIME = 2000000000
+TOTP = 38618901
+MODE = SHA512
+SECRET = 12345678901234567890
+
+COUNT = 15
+TIME = 20000000000
+TOTP = 65353130
+MODE = SHA1
+SECRET = 12345678901234567890
+
+COUNT = 16
+TIME = 20000000000
+TOTP = 77737706
+MODE = SHA256
+SECRET = 12345678901234567890
+
+COUNT = 17
+TIME = 20000000000
+TOTP = 47863826
+MODE = SHA512
+SECRET = 12345678901234567890
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 2f4a43c..a0571ad 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -826,3 +826,117 @@
         )
     )
     assert vectors == expected
+
+
+def test_load_hotp_vectors():
+    vector_data = textwrap.dedent("""
+    # HOTP Test Vectors
+    # RFC 4226 Appendix D
+
+    COUNT = 0
+    COUNTER = 0
+    INTERMEDIATE = cc93cf18508d94934c64b65d8ba7667fb7cde4b0
+    TRUNCATED = 4c93cf18
+    HOTP = 755224
+    SECRET = 12345678901234567890
+
+    COUNT = 1
+    COUNTER = 1
+    INTERMEDIATE = 75a48a19d4cbe100644e8ac1397eea747a2d33ab
+    TRUNCATED = 41397eea
+    HOTP = 287082
+    SECRET = 12345678901234567890
+
+
+    COUNT = 2
+    COUNTER = 2
+    INTERMEDIATE = 0bacb7fa082fef30782211938bc1c5e70416ff44
+    TRUNCATED = 82fef30
+    HOTP = 359152
+    SECRET = 12345678901234567890
+
+
+    COUNT = 3
+    COUNTER = 3
+    INTERMEDIATE = 66c28227d03a2d5529262ff016a1e6ef76557ece
+    TRUNCATED = 66ef7655
+    HOTP = 969429
+    SECRET = 12345678901234567890
+    """).splitlines()
+
+    assert load_nist_vectors(vector_data) == [
+        {
+            "counter": b"0",
+            "intermediate": b"cc93cf18508d94934c64b65d8ba7667fb7cde4b0",
+            "truncated": b"4c93cf18",
+            "hotp": b"755224",
+            "secret": b"12345678901234567890",
+        },
+        {
+            "counter": b"1",
+            "intermediate": b"75a48a19d4cbe100644e8ac1397eea747a2d33ab",
+            "truncated": b"41397eea",
+            "hotp": b"287082",
+            "secret": b"12345678901234567890",
+        },
+        {
+            "counter": b"2",
+            "intermediate": b"0bacb7fa082fef30782211938bc1c5e70416ff44",
+            "truncated": b"82fef30",
+            "hotp": b"359152",
+            "secret": b"12345678901234567890",
+        },
+        {
+            "counter": b"3",
+            "intermediate": b"66c28227d03a2d5529262ff016a1e6ef76557ece",
+            "truncated": b"66ef7655",
+            "hotp": b"969429",
+            "secret": b"12345678901234567890",
+        },
+    ]
+
+
+def test_load_totp_vectors():
+    vector_data = textwrap.dedent("""
+    # TOTP Test Vectors
+    # RFC 6238 Appendix B
+
+    COUNT = 0
+    TIME = 59
+    TOTP = 94287082
+    MODE = SHA1
+    SECRET = 12345678901234567890
+
+    COUNT = 1
+    TIME = 59
+    TOTP = 46119246
+    MODE = SHA256
+    SECRET = 12345678901234567890
+
+    COUNT = 2
+    TIME = 59
+    TOTP = 90693936
+    MODE = SHA512
+    SECRET = 12345678901234567890
+    """).splitlines()
+
+    assert load_nist_vectors(vector_data) == [
+        {
+            "time": b"59",
+            "totp": b"94287082",
+            "mode": b"SHA1",
+            "secret": b"12345678901234567890",
+        },
+        {
+            "time": b"59",
+            "totp": b"46119246",
+            "mode": b"SHA256",
+            "secret": b"12345678901234567890",
+        },
+        {
+            "time": b"59",
+            "totp": b"90693936",
+            "mode": b"SHA512",
+            "secret": b"12345678901234567890",
+        },
+    ]