From 3677eaf1fcb12f2746e2444020716eb8dc223857 Mon Sep 17 00:00:00 2001 From: Mohammed Attia Date: Thu, 6 Mar 2014 02:08:07 +0200 Subject: Add basic DSA backend methods --- cryptography/hazmat/backends/openssl/backend.py | 70 ++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 3293741c..370a6515 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -29,7 +29,7 @@ from cryptography.hazmat.backends.interfaces import ( ) from cryptography.hazmat.bindings.openssl.binding import Binding from cryptography.hazmat.primitives import hashes, interfaces -from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.asymmetric import rsa, dsa from cryptography.hazmat.primitives.asymmetric.padding import ( MGF1, PKCS1v15, PSS ) @@ -409,13 +409,79 @@ class Backend(object): return _RSAVerificationContext(self, public_key, signature, padding, algorithm) + def generate_dsa_parameters(self, key_size, ctx=None): + if key_size not in [1024, 2048, 3072]: + raise ValueError("Key size must be 1024 or 2048 or" + "3072 bits") + + if ctx is None: + ctx = self._lib.DSA_new() + assert ctx != self._ffi.NULL + ctx = self._ffi.gc(ctx, self._lib.DSA_free) + + res = self._lib.DSA_generate_parameters_ex( + ctx, key_size, self._ffi.NULL, self._ffi.NULL, + self._ffi.NULL, self._ffi.NULL + ) + + assert res == 1 + + return dsa.DSAParams( + modulus=self._bn_to_int(ctx.p), + subroup_order=self._bn_to_int(ctx.q), + generator=self._bn_to_int(ctx.g) + ) + + def generate_dsa_private_key(self, parameters, key_size): + ctx = self._lib.DSA_new() + assert ctx != self._ffi.NULL + ctx = self._ffi.gc(ctx, self._lib.DSA_free) + if all([parameters.p, parameters.q, parameters.g]): + if ctx.p not in [1024, 2048, 3072]: + raise ValueError("Prime Modulus length must be 1024 or 2048 or" + "3072 bits") + + if ctx.q not in [160, 256]: + raise ValueError("Subgroup order length must be 160 or" + "256 bits") + + if (ctx.p, ctx.q) not in [ + (1024, 160), + (2048, 256), + (3072, 256)]: + raise ValueError("Prime Modulus and Subgroup order lengths" + "must be one of these pairs (1024, 160)" + "or (2048, 256) or (3072, 256)") + + if ctx.g <= 1 or ctx.g >= ctx.p: + raise ValueError("Generator must be > 1 and < Prime Modulus") + + ctx.p = self._int_to_bn(parameters.p) + ctx.q = self._int_to_bn(parameters.q) + ctx.g = self._int_to_bn(parameters.g) + + else: + if key_size not in [1024, 2048, 3072]: + raise ValueError("Key size must be 1024 or 2048 or" + "3072 bits") + self.generate_dsa_parameters(key_size, ctx) + + self._lib.DSA_generate_key(ctx) + + return dsa.DSAPrivateKey( + modulus=self._bn_to_int(ctx.p), + subgroup_order=self._bn_to_int(ctx.q), + generator=self._bn_to_int(ctx.g), + x=self._bn_to_int(ctx.priv_key), + y=self._bn_to_int(ctx.pub_key) + ) + def mgf1_hash_supported(self, algorithm): if self._lib.Cryptography_HAS_MGF1_MD: return self.hash_supported(algorithm) else: return isinstance(algorithm, hashes.SHA1) - class GetCipherByName(object): def __init__(self, fmt): self._fmt = fmt -- cgit v1.2.3 From e3fac5cafbdc57090295bad0acbdbced2821849f Mon Sep 17 00:00:00 2001 From: Mohammed Attia Date: Sat, 8 Mar 2014 09:49:03 +0200 Subject: Remove validation from the backend since it's already done through the API --- cryptography/hazmat/backends/openssl/backend.py | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 370a6515..65e3df47 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -410,7 +410,7 @@ class Backend(object): algorithm) def generate_dsa_parameters(self, key_size, ctx=None): - if key_size not in [1024, 2048, 3072]: + if key_size not in (1024, 2048, 3072): raise ValueError("Key size must be 1024 or 2048 or" "3072 bits") @@ -437,31 +437,12 @@ class Backend(object): assert ctx != self._ffi.NULL ctx = self._ffi.gc(ctx, self._lib.DSA_free) if all([parameters.p, parameters.q, parameters.g]): - if ctx.p not in [1024, 2048, 3072]: - raise ValueError("Prime Modulus length must be 1024 or 2048 or" - "3072 bits") - - if ctx.q not in [160, 256]: - raise ValueError("Subgroup order length must be 160 or" - "256 bits") - - if (ctx.p, ctx.q) not in [ - (1024, 160), - (2048, 256), - (3072, 256)]: - raise ValueError("Prime Modulus and Subgroup order lengths" - "must be one of these pairs (1024, 160)" - "or (2048, 256) or (3072, 256)") - - if ctx.g <= 1 or ctx.g >= ctx.p: - raise ValueError("Generator must be > 1 and < Prime Modulus") - ctx.p = self._int_to_bn(parameters.p) ctx.q = self._int_to_bn(parameters.q) ctx.g = self._int_to_bn(parameters.g) else: - if key_size not in [1024, 2048, 3072]: + if key_size not in (1024, 2048, 3072): raise ValueError("Key size must be 1024 or 2048 or" "3072 bits") self.generate_dsa_parameters(key_size, ctx) -- cgit v1.2.3 From 5aefc9f47f0dff59389ad847950cfd5c9ae8c023 Mon Sep 17 00:00:00 2001 From: Mohammed Attia Date: Sat, 8 Mar 2014 11:03:06 +0200 Subject: Add key_size to GC --- cryptography/hazmat/backends/openssl/backend.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 65e3df47..ca3992fe 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -419,8 +419,11 @@ class Backend(object): assert ctx != self._ffi.NULL ctx = self._ffi.gc(ctx, self._lib.DSA_free) + bn = self._int_to_bn(key_size) + bn = self._ffi.gc(bn, self._lib.BN_free) + res = self._lib.DSA_generate_parameters_ex( - ctx, key_size, self._ffi.NULL, self._ffi.NULL, + ctx, bn, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL ) @@ -445,7 +448,9 @@ class Backend(object): if key_size not in (1024, 2048, 3072): raise ValueError("Key size must be 1024 or 2048 or" "3072 bits") - self.generate_dsa_parameters(key_size, ctx) + bn = self._int_to_bn(key_size) + bn = self._ffi.gc(bn, self._lib.BN_free) + self.generate_dsa_parameters(bn, ctx) self._lib.DSA_generate_key(ctx) @@ -463,6 +468,7 @@ class Backend(object): else: return isinstance(algorithm, hashes.SHA1) + class GetCipherByName(object): def __init__(self, fmt): self._fmt = fmt -- cgit v1.2.3 From dacefbad29787833676adb04cdc91ff5bfc7b8f4 Mon Sep 17 00:00:00 2001 From: Mohammed Attia Date: Tue, 1 Apr 2014 05:28:08 +0200 Subject: Add generation methods and remove the optional arguments from the backend --- cryptography/hazmat/backends/openssl/backend.py | 55 +++++++++++------------- cryptography/hazmat/primitives/asymmetric/dsa.py | 8 ++++ 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index ca3992fe..3e6b1b5b 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -29,7 +29,7 @@ from cryptography.hazmat.backends.interfaces import ( ) from cryptography.hazmat.bindings.openssl.binding import Binding from cryptography.hazmat.primitives import hashes, interfaces -from cryptography.hazmat.primitives.asymmetric import rsa, dsa +from cryptography.hazmat.primitives.asymmetric import dsa, rsa from cryptography.hazmat.primitives.asymmetric.padding import ( MGF1, PKCS1v15, PSS ) @@ -409,22 +409,30 @@ class Backend(object): return _RSAVerificationContext(self, public_key, signature, padding, algorithm) - def generate_dsa_parameters(self, key_size, ctx=None): + def mgf1_hash_supported(self, algorithm): + if self._lib.Cryptography_HAS_MGF1_MD: + return self.hash_supported(algorithm) + else: + return isinstance(algorithm, hashes.SHA1) + + def generate_dsa_parameters(self, key_size): if key_size not in (1024, 2048, 3072): - raise ValueError("Key size must be 1024 or 2048 or" - "3072 bits") + raise ValueError( + "Key size must be 1024 or 2048 or 3072 bits") - if ctx is None: - ctx = self._lib.DSA_new() - assert ctx != self._ffi.NULL - ctx = self._ffi.gc(ctx, self._lib.DSA_free) + if backend._lib.OPENSSL_VERSION_NUMBER < 0x1000000f \ + and key_size > 1024: + raise ValueError( + "Key size must be 1024 because OpenSSL < 1.0.0 doesn't " + "support larger key sizes") - bn = self._int_to_bn(key_size) - bn = self._ffi.gc(bn, self._lib.BN_free) + ctx = self._lib.DSA_new() + assert ctx != self._ffi.NULL + ctx = self._ffi.gc(ctx, self._lib.DSA_free) res = self._lib.DSA_generate_parameters_ex( - ctx, bn, self._ffi.NULL, self._ffi.NULL, - self._ffi.NULL, self._ffi.NULL + ctx, key_size, self._ffi.NULL, self._ffi.NULL, + self._ffi.NULL, self._ffi.NULL, self._ffi.NULL ) assert res == 1 @@ -435,22 +443,13 @@ class Backend(object): generator=self._bn_to_int(ctx.g) ) - def generate_dsa_private_key(self, parameters, key_size): + def generate_dsa_private_key(self, parameters): ctx = self._lib.DSA_new() assert ctx != self._ffi.NULL ctx = self._ffi.gc(ctx, self._lib.DSA_free) - if all([parameters.p, parameters.q, parameters.g]): - ctx.p = self._int_to_bn(parameters.p) - ctx.q = self._int_to_bn(parameters.q) - ctx.g = self._int_to_bn(parameters.g) - - else: - if key_size not in (1024, 2048, 3072): - raise ValueError("Key size must be 1024 or 2048 or" - "3072 bits") - bn = self._int_to_bn(key_size) - bn = self._ffi.gc(bn, self._lib.BN_free) - self.generate_dsa_parameters(bn, ctx) + ctx.p = self._int_to_bn(parameters.p) + ctx.q = self._int_to_bn(parameters.q) + ctx.g = self._int_to_bn(parameters.g) self._lib.DSA_generate_key(ctx) @@ -462,12 +461,6 @@ class Backend(object): y=self._bn_to_int(ctx.pub_key) ) - def mgf1_hash_supported(self, algorithm): - if self._lib.Cryptography_HAS_MGF1_MD: - return self.hash_supported(algorithm) - else: - return isinstance(algorithm, hashes.SHA1) - class GetCipherByName(object): def __init__(self, fmt): diff --git a/cryptography/hazmat/primitives/asymmetric/dsa.py b/cryptography/hazmat/primitives/asymmetric/dsa.py index 974db0a6..eb4a162c 100644 --- a/cryptography/hazmat/primitives/asymmetric/dsa.py +++ b/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -49,6 +49,10 @@ class DSAParameters(object): self._subgroup_order = subgroup_order self._generator = generator + @classmethod + def generate(cls, backend, key_size): + return backend.generate_dsa_parameters(key_size) + @property def modulus(self): return self._modulus @@ -96,6 +100,10 @@ class DSAPrivateKey(object): self._x = x self._y = y + @classmethod + def generate(cls, backend, parameters): + return backend.generate_dsa_private_key(parameters) + @property def key_size(self): return utils.bit_length(self._modulus) -- cgit v1.2.3 From 9ac7c1d9032816e161b64f8e283bffac99b85c2e Mon Sep 17 00:00:00 2001 From: Mohammed Attia Date: Tue, 1 Apr 2014 14:23:27 +0200 Subject: Add tests for DSA parameters and key generation --- cryptography/hazmat/backends/openssl/backend.py | 6 ++-- cryptography/hazmat/primitives/asymmetric/dsa.py | 4 +-- tests/hazmat/backends/test_openssl.py | 13 +++++++- tests/hazmat/primitives/test_dsa.py | 41 ++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index 3e6b1b5b..f161bd4e 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -431,15 +431,15 @@ class Backend(object): ctx = self._ffi.gc(ctx, self._lib.DSA_free) res = self._lib.DSA_generate_parameters_ex( - ctx, key_size, self._ffi.NULL, self._ffi.NULL, + ctx, key_size, self._ffi.NULL, 0, self._ffi.NULL, self._ffi.NULL, self._ffi.NULL ) assert res == 1 - return dsa.DSAParams( + return dsa.DSAParameters( modulus=self._bn_to_int(ctx.p), - subroup_order=self._bn_to_int(ctx.q), + subgroup_order=self._bn_to_int(ctx.q), generator=self._bn_to_int(ctx.g) ) diff --git a/cryptography/hazmat/primitives/asymmetric/dsa.py b/cryptography/hazmat/primitives/asymmetric/dsa.py index eb4a162c..e32e05c0 100644 --- a/cryptography/hazmat/primitives/asymmetric/dsa.py +++ b/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -50,7 +50,7 @@ class DSAParameters(object): self._generator = generator @classmethod - def generate(cls, backend, key_size): + def generate(cls, key_size, backend): return backend.generate_dsa_parameters(key_size) @property @@ -101,7 +101,7 @@ class DSAPrivateKey(object): self._y = y @classmethod - def generate(cls, backend, parameters): + def generate(cls, parameters, backend): return backend.generate_dsa_private_key(parameters) @property diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index 016da0fc..86404fe9 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -21,7 +21,7 @@ from cryptography.exceptions import ( ) from cryptography.hazmat.backends.openssl.backend import Backend, backend from cryptography.hazmat.primitives import hashes, interfaces -from cryptography.hazmat.primitives.asymmetric import padding, rsa +from cryptography.hazmat.primitives.asymmetric import dsa, padding, rsa from cryptography.hazmat.primitives.ciphers import Cipher from cryptography.hazmat.primitives.ciphers.algorithms import AES from cryptography.hazmat.primitives.ciphers.modes import CBC @@ -192,6 +192,17 @@ class TestOpenSSL(object): res = backend._lib.ENGINE_free(e) assert res == 1 + @pytest.mark.skipif( + backend._lib.OPENSSL_VERSION_NUMBER >= 0x1000000f, + reason="Requires an older OpenSSL. Must be < 1.0.0" + ) + def test_large_key_size_on_old_openssl(self): + with pytest.raises(ValueError): + dsa.DSAParameters.generate(2048, backend=backend) + + with pytest.raises(ValueError): + dsa.DSAParameters.generate(3072, backend=backend) + class TestOpenSSLRandomEngine(object): def teardown_method(self, method): diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py index 2a2b9bda..6503b9d4 100644 --- a/tests/hazmat/primitives/test_dsa.py +++ b/tests/hazmat/primitives/test_dsa.py @@ -14,10 +14,18 @@ from __future__ import absolute_import, division, print_function +import os + import pytest from cryptography.hazmat.primitives.asymmetric import dsa +from cryptography.utils import bit_length + +from ...utils import ( + load_vectors_from_file, load_fips_dsa_key_pair_vectors +) + def _check_dsa_private_key(skey): assert skey @@ -157,6 +165,39 @@ class TestDSA(object): 'f90f7dff6d2bae' } + def test_generate_dsa_parameters(self, backend): + parameters = dsa.DSAParameters.generate(1024, backend) + assert bit_length(parameters.p) == 1024 + if backend._lib.OPENSSL_VERSION_NUMBER >= 0x1000000fL: + parameters = dsa.DSAParameters.generate(2048, backend) + assert bit_length(parameters.p) == 2048 + parameters = dsa.DSAParameters.generate(3072, backend) + assert bit_length(parameters.p) == 3072 + + @pytest.mark.parametrize( + "vector", + load_vectors_from_file( + os.path.join( + "asymmetric", "DSA", "FIPS_186-3", "KeyPair.rsp"), + load_fips_dsa_key_pair_vectors + ) + ) + def test_generate_dsa_keys(self, vector, backend): + class Object(object): + pass + parameters = Object() + parameters.p = vector['p'] + parameters.q = vector['q'] + parameters.g = vector['g'] + skey = dsa.DSAPrivateKey.generate(parameters, backend) + + skey_parameters = skey.parameters() + assert skey_parameters.p == vector['p'] + assert skey_parameters.q == vector['q'] + assert skey_parameters.g == vector['g'] + assert skey.key_size == bit_length(vector['p']) + assert skey.y == pow(skey_parameters.g, skey.x, skey_parameters.p) + def test_invalid_parameters_argument_types(self): with pytest.raises(TypeError): dsa.DSAParameters(None, None, None) -- cgit v1.2.3 From 97c27c698dc5325aff3887cf13e0e58bcfd1acfe Mon Sep 17 00:00:00 2001 From: Mohammed Attia Date: Wed, 2 Apr 2014 03:46:57 +0200 Subject: Add DSABackend --- cryptography/hazmat/backends/interfaces.py | 15 ++++++++++ cryptography/hazmat/backends/multibackend.py | 16 ++++++++++- cryptography/hazmat/backends/openssl/backend.py | 8 ++++-- cryptography/hazmat/primitives/asymmetric/dsa.py | 14 ++++++++++ pytest.ini | 1 + tests/conftest.py | 4 ++- tests/hazmat/backends/test_multibackend.py | 35 +++++++++++++++++++++++- tests/hazmat/backends/test_openssl.py | 12 ++++++++ tests/hazmat/primitives/test_dsa.py | 35 +++++++++++++++--------- 9 files changed, 121 insertions(+), 19 deletions(-) diff --git a/cryptography/hazmat/backends/interfaces.py b/cryptography/hazmat/backends/interfaces.py index 27b609ed..20c21118 100644 --- a/cryptography/hazmat/backends/interfaces.py +++ b/cryptography/hazmat/backends/interfaces.py @@ -113,6 +113,21 @@ class RSABackend(six.with_metaclass(abc.ABCMeta)): """ +class DSABackend(six.with_metaclass(abc.ABCMeta)): + @abc.abstractmethod + def generate_dsa_parameters(self, key_size): + """ + Generate a DSAParameters instance with a modulus of key_size bits. + """ + + @abc.abstractmethod + def generate_dsa_private_key(self, parameters): + """ + Generate an DSAPrivateKey instance with parameters as + a DSAParameters object. + """ + + class OpenSSLSerializationBackend(six.with_metaclass(abc.ABCMeta)): @abc.abstractmethod def load_openssl_pem_private_key(self, data, password): diff --git a/cryptography/hazmat/backends/multibackend.py b/cryptography/hazmat/backends/multibackend.py index aa649dd3..86cded85 100644 --- a/cryptography/hazmat/backends/multibackend.py +++ b/cryptography/hazmat/backends/multibackend.py @@ -16,7 +16,8 @@ from __future__ import absolute_import, division, print_function from cryptography import utils from cryptography.exceptions import UnsupportedAlgorithm, _Reasons from cryptography.hazmat.backends.interfaces import ( - CipherBackend, HMACBackend, HashBackend, PBKDF2HMACBackend, RSABackend + CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend, + RSABackend ) @@ -25,6 +26,7 @@ from cryptography.hazmat.backends.interfaces import ( @utils.register_interface(HMACBackend) @utils.register_interface(PBKDF2HMACBackend) @utils.register_interface(RSABackend) +@utils.register_interface(DSABackend) class MultiBackend(object): name = "multibackend" @@ -142,3 +144,15 @@ class MultiBackend(object): padding, algorithm) raise UnsupportedAlgorithm("RSA is not supported by the backend", _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + + def generate_dsa_parameters(self, key_size): + for b in self._filtered_backends(DSABackend): + return b.generate_dsa_parameters(key_size) + raise UnsupportedAlgorithm("DSA is not supported by the backend", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) + + def generate_dsa_private_key(self, parameters): + for b in self._filtered_backends(DSABackend): + return b.generate_dsa_private_key(parameters) + raise UnsupportedAlgorithm("DSA is not supported by the backend", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py index f161bd4e..82b7949e 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -25,7 +25,8 @@ from cryptography.exceptions import ( UnsupportedAlgorithm, _Reasons ) from cryptography.hazmat.backends.interfaces import ( - CipherBackend, HMACBackend, HashBackend, PBKDF2HMACBackend, RSABackend + CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend, + RSABackend ) from cryptography.hazmat.bindings.openssl.binding import Binding from cryptography.hazmat.primitives import hashes, interfaces @@ -46,6 +47,7 @@ _OpenSSLError = collections.namedtuple("_OpenSSLError", @utils.register_interface(CipherBackend) +@utils.register_interface(DSABackend) @utils.register_interface(HashBackend) @utils.register_interface(HMACBackend) @utils.register_interface(PBKDF2HMACBackend) @@ -420,8 +422,8 @@ class Backend(object): raise ValueError( "Key size must be 1024 or 2048 or 3072 bits") - if backend._lib.OPENSSL_VERSION_NUMBER < 0x1000000f \ - and key_size > 1024: + if (self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f and + key_size > 1024): raise ValueError( "Key size must be 1024 because OpenSSL < 1.0.0 doesn't " "support larger key sizes") diff --git a/cryptography/hazmat/primitives/asymmetric/dsa.py b/cryptography/hazmat/primitives/asymmetric/dsa.py index e32e05c0..4c2de36a 100644 --- a/cryptography/hazmat/primitives/asymmetric/dsa.py +++ b/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -16,6 +16,8 @@ from __future__ import absolute_import, division, print_function import six from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons +from cryptography.hazmat.backends.interfaces import DSABackend from cryptography.hazmat.primitives import interfaces @@ -51,6 +53,12 @@ class DSAParameters(object): @classmethod def generate(cls, key_size, backend): + if not isinstance(backend, DSABackend): + raise UnsupportedAlgorithm( + "Backend object does not implement DSABackend", + _Reasons.BACKEND_MISSING_INTERFACE + ) + return backend.generate_dsa_parameters(key_size) @property @@ -102,6 +110,12 @@ class DSAPrivateKey(object): @classmethod def generate(cls, parameters, backend): + if not isinstance(backend, DSABackend): + raise UnsupportedAlgorithm( + "Backend object does not implement DSABackend", + _Reasons.BACKEND_MISSING_INTERFACE + ) + return backend.generate_dsa_private_key(parameters) @property diff --git a/pytest.ini b/pytest.ini index 3f65e30e..b590d0be 100644 --- a/pytest.ini +++ b/pytest.ini @@ -2,6 +2,7 @@ addopts = -r s markers = cipher: this test requires a backend providing CipherBackend + dsa: this test requires a backend providing DSABackend hash: this test requires a backend providing HashBackend hmac: this test requires a backend providing HMACBackend pbkdf2hmac: this test requires a backend providing PBKDF2HMACBackend diff --git a/tests/conftest.py b/tests/conftest.py index 8e89af57..1ee2a993 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,7 +17,8 @@ import pytest from cryptography.hazmat.backends import _available_backends from cryptography.hazmat.backends.interfaces import ( - CipherBackend, HMACBackend, HashBackend, PBKDF2HMACBackend, RSABackend + CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend, + RSABackend ) from .utils import check_backend_support, check_for_iface, select_backends @@ -37,6 +38,7 @@ def pytest_runtest_setup(item): check_for_iface("cipher", CipherBackend, item) check_for_iface("hash", HashBackend, item) check_for_iface("pbkdf2hmac", PBKDF2HMACBackend, item) + check_for_iface("dsa", DSABackend, item) check_for_iface("rsa", RSABackend, item) check_backend_support(item) diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py index f0be72b2..4ec8a110 100644 --- a/tests/hazmat/backends/test_multibackend.py +++ b/tests/hazmat/backends/test_multibackend.py @@ -18,7 +18,8 @@ from cryptography.exceptions import ( UnsupportedAlgorithm, _Reasons ) from cryptography.hazmat.backends.interfaces import ( - CipherBackend, HMACBackend, HashBackend, PBKDF2HMACBackend, RSABackend + CipherBackend, HMACBackend, HashBackend, PBKDF2HMACBackend, RSABackend, + DSABackend ) from cryptography.hazmat.backends.multibackend import MultiBackend from cryptography.hazmat.primitives import hashes, hmac @@ -27,6 +28,8 @@ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from ...utils import raises_unsupported_algorithm +from pretend import stub + @utils.register_interface(CipherBackend) class DummyCipherBackend(object): @@ -98,6 +101,15 @@ class DummyRSABackend(object): pass +@utils.register_interface(DSABackend) +class DummyDSABackend(object): + def generate_dsa_parameters(self, key_size): + pass + + def generate_dsa_private_key(self, parameters): + pass + + class TestMultiBackend(object): def test_ciphers(self): backend = MultiBackend([ @@ -193,3 +205,24 @@ class TestMultiBackend(object): ): backend.create_rsa_verification_ctx( "public_key", "sig", padding.PKCS1v15(), hashes.MD5()) + + def test_dsa(self): + backend = MultiBackend([ + DummyDSABackend() + ]) + + backend.generate_dsa_parameters(key_size=1024) + + parameters = stub() + backend.generate_dsa_private_key(parameters) + + backend = MultiBackend([]) + with raises_unsupported_algorithm( + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM + ): + backend.generate_dsa_parameters(key_size=1024) + + with raises_unsupported_algorithm( + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM + ): + backend.generate_dsa_private_key(parameters) diff --git a/tests/hazmat/backends/test_openssl.py b/tests/hazmat/backends/test_openssl.py index 86404fe9..6ab16627 100644 --- a/tests/hazmat/backends/test_openssl.py +++ b/tests/hazmat/backends/test_openssl.py @@ -28,6 +28,8 @@ from cryptography.hazmat.primitives.ciphers.modes import CBC from ...utils import raises_unsupported_algorithm +from cryptography.utils import bit_length + @utils.register_interface(interfaces.Mode) class DummyMode(object): @@ -203,6 +205,16 @@ class TestOpenSSL(object): with pytest.raises(ValueError): dsa.DSAParameters.generate(3072, backend=backend) + @pytest.mark.skipif( + backend._lib.OPENSSL_VERSION_NUMBER < 0x1000000f, + reason="Requires a newer OpenSSL. Must be >= 1.0.0" + ) + def test_large_key_size_on_new_openssl(self): + parameters = dsa.DSAParameters.generate(2048, backend) + assert bit_length(parameters.p) == 2048 + parameters = dsa.DSAParameters.generate(3072, backend) + assert bit_length(parameters.p) == 3072 + class TestOpenSSLRandomEngine(object): def teardown_method(self, method): diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py index 6503b9d4..2b5d4bb3 100644 --- a/tests/hazmat/primitives/test_dsa.py +++ b/tests/hazmat/primitives/test_dsa.py @@ -18,12 +18,13 @@ import os import pytest +from cryptography.exceptions import _Reasons from cryptography.hazmat.primitives.asymmetric import dsa - from cryptography.utils import bit_length from ...utils import ( - load_vectors_from_file, load_fips_dsa_key_pair_vectors + load_vectors_from_file, load_fips_dsa_key_pair_vectors, + raises_unsupported_algorithm ) @@ -61,6 +62,7 @@ def _check_dsa_private_key(skey): assert skey_parameters.generator == pkey_parameters.generator +@pytest.mark.dsa class TestDSA(object): _parameters_1024 = { 'p': 'd38311e2cd388c3ed698e82fdf88eb92b5a9a483dc88005d4b725ef341eabb47' @@ -168,11 +170,10 @@ class TestDSA(object): def test_generate_dsa_parameters(self, backend): parameters = dsa.DSAParameters.generate(1024, backend) assert bit_length(parameters.p) == 1024 - if backend._lib.OPENSSL_VERSION_NUMBER >= 0x1000000fL: - parameters = dsa.DSAParameters.generate(2048, backend) - assert bit_length(parameters.p) == 2048 - parameters = dsa.DSAParameters.generate(3072, backend) - assert bit_length(parameters.p) == 3072 + + def test_generate_invalid_dsa_parameters(self, backend): + with pytest.raises(ValueError): + dsa.DSAParameters.generate(1, backend) @pytest.mark.parametrize( "vector", @@ -183,12 +184,9 @@ class TestDSA(object): ) ) def test_generate_dsa_keys(self, vector, backend): - class Object(object): - pass - parameters = Object() - parameters.p = vector['p'] - parameters.q = vector['q'] - parameters.g = vector['g'] + parameters = dsa.DSAParameters(modulus=vector['p'], + subgroup_order=vector['q'], + generator=vector['g']) skey = dsa.DSAPrivateKey.generate(parameters, backend) skey_parameters = skey.parameters() @@ -720,3 +718,14 @@ class TestDSA(object): generator=int(self._parameters_1024['g'], 16), y=None ) + + +def test_dsa_generate_invalid_backend(): + pretend_backend = object() + + with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE): + dsa.DSAParameters.generate(1024, pretend_backend) + + pretend_parameters = object() + with raises_unsupported_algorithm(_Reasons.BACKEND_MISSING_INTERFACE): + dsa.DSAPrivateKey.generate(pretend_parameters, pretend_backend) -- cgit v1.2.3 From 29474ac7dab3f5c8b664463ed28ec83b7b77250b Mon Sep 17 00:00:00 2001 From: Mohammed Attia Date: Wed, 2 Apr 2014 04:03:09 +0200 Subject: Add docs for DSA parameters and key generation --- docs/hazmat/backends/interfaces.rst | 34 +++++++++++++++++ docs/hazmat/primitives/asymmetric/dsa.rst | 59 +++++++++++++++++++++++++++++- tests/hazmat/backends/test_multibackend.py | 8 ++-- 3 files changed, 95 insertions(+), 6 deletions(-) diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst index c38f818f..9c401d28 100644 --- a/docs/hazmat/backends/interfaces.rst +++ b/docs/hazmat/backends/interfaces.rst @@ -285,3 +285,37 @@ A specific ``backend`` may provide one or more of these interfaces. :raises cryptography.exceptions.UnsupportedAlgorithm: If the data is encrypted with an unsupported algorithm. + + +.. class:: DSABackend + + .. versionadded:: 0.4 + + A backend with methods for using DSA. + + .. method:: generate_dsa_parameters(key_size) + + :param int key_size: The length of the modulus in bits. It should be + either "1024, 2048 or 3072". For keys generated in 2014 this should + be at least 2048. + Note that some applications (such as SSH) have not yet gained support + for larger key sizes specified in FIPS 186-3 and are still restricted + to only the 1024-bit keys specified in FIPS 186-2. + + :return: A new instance of a + :class:`~cryptography.hazmat.primitives.interfaces.DSAParameters` + provider. + + .. method:: generate_dsa_private_key(parameters) + + :param parameters: A + :class:`~cryptography.hazmat.primitives.interfaces.DSAParameters` + provider. + + :return: A new instance of a + :class:`~cryptography.hazmat.primitives.interfaces.DSAPrivateKey` + provider. + + :raises ValueError: This is raised if the key size is not (1024 or 2048 or 3072) + or if the OpenSSL version is older than 1.0.0 and the key size is larger than 1024 + because older OpenSSL versions don't support a key size larger than 1024. diff --git a/docs/hazmat/primitives/asymmetric/dsa.rst b/docs/hazmat/primitives/asymmetric/dsa.rst index 69e8d58e..1a6a6e0e 100644 --- a/docs/hazmat/primitives/asymmetric/dsa.rst +++ b/docs/hazmat/primitives/asymmetric/dsa.rst @@ -13,6 +13,16 @@ DSA DSA Parameters are required for generating a DSA private key. + You should use :meth:`~generate` to generate new parameters. + + .. warning:: + This method only checks a limited set of properties of its arguments. + Using DSA parameters that you do not trust or with incorrect arguments + may lead to insecure operation, crashes, and other undefined behavior. + We recommend that you only ever load parameters that were generated + with software you trust. + + This class conforms to the :class:`~cryptography.hazmat.primitives.interfaces.DSAParameters` interface. @@ -23,6 +33,23 @@ DSA ``subgroup_order``, or ``generator`` do not match the bounds specified in `FIPS 186-4`_. + .. classmethod:: generate(key_size, backend) + + Generate a new ``DSAParameters`` instance using ``backend``. + + :param int key_size: The length of the modulus in bits. It should be + either "1024, 2048 or 3072". For keys generated in 2014 this should + be `at least 2048`_ (See page 41). + Note that some applications (such as SSH) have not yet gained support + for larger key sizes specified in FIPS 186-3 and are still restricted + to only the 1024-bit keys specified in FIPS 186-2. + + :return: A new instance of ``DSAParameters`` + + :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised if + the provided ``backend`` does not implement + :class:`~cryptography.hazmat.backends.interfaces.DSABackend` + .. class:: DSAPrivateKey(modulus, subgroup_order, generator, x, y) @@ -30,6 +57,16 @@ DSA A DSA private key is required for signing messages. + You should use :meth:`~generate` to generate new keys. + + .. warning:: + This method only checks a limited set of properties of its arguments. + Using a DSA private key that you do not trust or with incorrect + parameters may lead to insecure operation, crashes, and other undefined + behavior. We recommend that you only ever load private keys that were + generated with software you trust. + + This class conforms to the :class:`~cryptography.hazmat.primitives.interfaces.DSAPrivateKey` interface. @@ -40,6 +77,26 @@ DSA ``subgroup_order``, or ``generator`` do not match the bounds specified in `FIPS 186-4`_. + .. classmethod:: generate(parameters, backend) + + Generate a new ``DSAPrivateKey`` instance using ``backend``. + + :param parameters: A + :class:`~cryptography.hazmat.primitives.interfaces.DSAParameters` + provider. + :param backend: A + :class:`~cryptography.hazmat.backends.interfaces.DSABackend` + provider. + :return: A new instance of ``DSAPrivateKey``. + + :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised if + the provided ``backend`` does not implement + :class:`~cryptography.hazmat.backends.interfaces.DSABackend` + + :raises ValueError: This is raised if the key size is not (1024 or 2048 or 3072) + or if the OpenSSL version is older than 1.0.0 and the key size is larger than 1024 + because older OpenSSL versions don't support a key size larger than 1024. + .. class:: DSAPublicKey(modulus, subgroup_order, generator, y) @@ -65,4 +122,4 @@ DSA .. _`DSA`: https://en.wikipedia.org/wiki/Digital_Signature_Algorithm .. _`public-key`: https://en.wikipedia.org/wiki/Public-key_cryptography .. _`FIPS 186-4`: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf - +.. _`at least 2048`: http://www.ecrypt.eu.org/documents/D.SPA.20.pdf diff --git a/tests/hazmat/backends/test_multibackend.py b/tests/hazmat/backends/test_multibackend.py index 4ec8a110..f46009d4 100644 --- a/tests/hazmat/backends/test_multibackend.py +++ b/tests/hazmat/backends/test_multibackend.py @@ -18,8 +18,8 @@ from cryptography.exceptions import ( UnsupportedAlgorithm, _Reasons ) from cryptography.hazmat.backends.interfaces import ( - CipherBackend, HMACBackend, HashBackend, PBKDF2HMACBackend, RSABackend, - DSABackend + CipherBackend, DSABackend, HMACBackend, HashBackend, PBKDF2HMACBackend, + RSABackend ) from cryptography.hazmat.backends.multibackend import MultiBackend from cryptography.hazmat.primitives import hashes, hmac @@ -28,8 +28,6 @@ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from ...utils import raises_unsupported_algorithm -from pretend import stub - @utils.register_interface(CipherBackend) class DummyCipherBackend(object): @@ -213,7 +211,7 @@ class TestMultiBackend(object): backend.generate_dsa_parameters(key_size=1024) - parameters = stub() + parameters = object() backend.generate_dsa_private_key(parameters) backend = MultiBackend([]) -- cgit v1.2.3