diff options
-rw-r--r-- | cryptography/hazmat/backends/openssl/backend.py | 44 | ||||
-rw-r--r-- | docs/hazmat/backends/openssl.rst | 1 | ||||
-rw-r--r-- | tests/hazmat/primitives/test_rsa.py | 57 | ||||
-rw-r--r-- | tests/hazmat/primitives/vectors/oath/rfc-4226.txt | 80 | ||||
-rw-r--r-- | tests/hazmat/primitives/vectors/oath/rfc-6238.txt | 110 | ||||
-rw-r--r-- | tests/test_utils.py | 114 |
6 files changed, 400 insertions, 6 deletions
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 6da90cef..d8869328 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -20,7 +20,7 @@ from cryptography.exceptions import ( 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 @@ from cryptography.hazmat.primitives.ciphers.modes import ( 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 @@ class Backend(object): ) ) + 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 ea72af96..e3880875 100644 --- a/docs/hazmat/backends/openssl.rst +++ b/docs/hazmat/backends/openssl.rst @@ -15,6 +15,7 @@ The `OpenSSL`_ C library. * :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 e2aca028..fdd55e73 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 cryptography.hazmat.primitives.asymmetric import rsa 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 @@ class TestRSA(object): secret, public = pkcs1_example skey = rsa.RSAPrivateKey(**secret) + assert skey + _check_rsa_private_key(skey) + pkey = rsa.RSAPublicKey(**public) - pkey2 = skey.public_key() + assert pkey - assert skey and pkey and pkey2 + pkey2 = skey.public_key() + assert 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 @@ class TestRSA(object): 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 00000000..35f7f8d7 --- /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 00000000..cc209950 --- /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 2f4a43c8..a0571ad6 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -826,3 +826,117 @@ def test_load_pkcs1_vectors(): ) ) 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", + }, + ] |