aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cryptography/hazmat/backends/interfaces.py7
-rw-r--r--cryptography/hazmat/backends/openssl/backend.py58
-rw-r--r--cryptography/hazmat/primitives/asymmetric/dsa.py9
-rw-r--r--docs/hazmat/backends/interfaces.rst13
-rw-r--r--docs/hazmat/primitives/asymmetric/dsa.rst43
-rw-r--r--docs/hazmat/primitives/interfaces.rst17
-rw-r--r--tests/hazmat/primitives/test_dsa.py54
7 files changed, 201 insertions, 0 deletions
diff --git a/cryptography/hazmat/backends/interfaces.py b/cryptography/hazmat/backends/interfaces.py
index e63b079b..264c5afb 100644
--- a/cryptography/hazmat/backends/interfaces.py
+++ b/cryptography/hazmat/backends/interfaces.py
@@ -146,6 +146,13 @@ class DSABackend(object):
"""
@abc.abstractmethod
+ def create_dsa_signature_ctx(self, private_key, algorithm):
+ """
+ Returns an object conforming to the AsymmetricSignatureContext
+ interface.
+ """
+
+ @abc.abstractmethod
def create_dsa_verification_ctx(self, public_key, signature, algorithm):
"""
Returns an object conforming to the AsymmetricVerificationContext
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index 37deb2ae..348e4151 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -474,6 +474,9 @@ class Backend(object):
y=self._bn_to_int(ctx.pub_key)
)
+ def create_dsa_signature_ctx(self, private_key, algorithm):
+ return _DSASignatureContext(self, private_key, algorithm)
+
def create_dsa_verification_ctx(self, public_key, signature,
algorithm):
return _DSAVerificationContext(self, public_key, signature,
@@ -491,6 +494,19 @@ class Backend(object):
ctx.pub_key = self._int_to_bn(public_key.y)
return ctx
+ def _dsa_cdata_from_private_key(self, private_key):
+ # Does not GC the DSA cdata. You *must* make sure it's freed
+ # correctly yourself!
+ ctx = self._lib.DSA_new()
+ assert ctx != self._ffi.NULL
+ parameters = private_key.parameters()
+ ctx.p = self._int_to_bn(parameters.p)
+ ctx.q = self._int_to_bn(parameters.q)
+ ctx.g = self._int_to_bn(parameters.g)
+ ctx.priv_key = self._int_to_bn(private_key.x)
+ ctx.pub_key = self._int_to_bn(private_key.y)
+ return ctx
+
def dsa_hash_supported(self, algorithm):
if self._lib.OPENSSL_VERSION_NUMBER < 0x1000000f:
return isinstance(algorithm, hashes.SHA1)
@@ -1369,6 +1385,48 @@ class _DSAVerificationContext(object):
raise InvalidSignature
+@utils.register_interface(interfaces.AsymmetricSignatureContext)
+class _DSASignatureContext(object):
+ def __init__(self, backend, private_key, algorithm):
+ self._backend = backend
+ self._private_key = private_key
+ self._algorithm = algorithm
+ self._hash_ctx = _HashContext(backend, self._algorithm)
+ self._dsa_cdata = self._backend._dsa_cdata_from_private_key(
+ self._private_key)
+ self._dsa_cdata = self._backend._ffi.gc(self._dsa_cdata,
+ self._backend._lib.DSA_free)
+
+ def update(self, data):
+ if self._hash_ctx is None:
+ raise AlreadyFinalized("Context has already been finalized")
+
+ self._hash_ctx.update(data)
+
+ def finalize(self):
+ if self._hash_ctx is None:
+ raise AlreadyFinalized("Context has already been finalized")
+
+ data_to_sign = self._hash_ctx.finalize()
+ self._hash_ctx = None
+ sig_buf_len = self._backend._lib.DSA_size(self._dsa_cdata)
+ sig_buf = self._backend._ffi.new("unsigned char[]", sig_buf_len)
+ buflen = self._backend._ffi.new("unsigned int *")
+
+ # The first parameter passed to DSA_sign is unused by OpenSSL but
+ # must be an integer.
+ res = self._backend._lib.DSA_sign(
+ 0, data_to_sign, len(data_to_sign), sig_buf,
+ buflen, self._dsa_cdata)
+
+ if res != 1:
+ errors = self._backend._consume_errors()
+ assert errors
+ raise InvalidSignature
+
+ return self._backend._ffi.buffer(sig_buf)[:]
+
+
@utils.register_interface(interfaces.CMACContext)
class _CMACContext(object):
def __init__(self, backend, algorithm, ctx=None):
diff --git a/cryptography/hazmat/primitives/asymmetric/dsa.py b/cryptography/hazmat/primitives/asymmetric/dsa.py
index 57a7ef3d..aa3cdc90 100644
--- a/cryptography/hazmat/primitives/asymmetric/dsa.py
+++ b/cryptography/hazmat/primitives/asymmetric/dsa.py
@@ -118,6 +118,15 @@ class DSAPrivateKey(object):
return backend.generate_dsa_private_key(parameters)
+ def signer(self, algorithm, backend):
+ if not isinstance(backend, DSABackend):
+ raise UnsupportedAlgorithm(
+ "Backend object does not implement DSABackend",
+ _Reasons.BACKEND_MISSING_INTERFACE
+ )
+
+ return backend.create_dsa_signature_ctx(self, algorithm)
+
@property
def key_size(self):
return utils.bit_length(self._modulus)
diff --git a/docs/hazmat/backends/interfaces.rst b/docs/hazmat/backends/interfaces.rst
index 6833f221..f363b541 100644
--- a/docs/hazmat/backends/interfaces.rst
+++ b/docs/hazmat/backends/interfaces.rst
@@ -345,6 +345,19 @@ A specific ``backend`` may provide one or more of these interfaces.
1.0.0 and the key size is larger than 1024; older OpenSSL versions
do not support keys larger than 1024 bits.
+ .. method:: create_dsa_signature_ctx(private_key, algorithm)
+
+ :param private_key: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.DSAPrivateKey`
+ provider.
+
+ :param algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+ provider
+
+ :returns:
+ :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricSignatureContext`
+
.. method:: create_dsa_verification_ctx(public_key, signature, algorithm)
:param public_key: An instance of a
diff --git a/docs/hazmat/primitives/asymmetric/dsa.rst b/docs/hazmat/primitives/asymmetric/dsa.rst
index 03e476b6..98aebb6b 100644
--- a/docs/hazmat/primitives/asymmetric/dsa.rst
+++ b/docs/hazmat/primitives/asymmetric/dsa.rst
@@ -97,6 +97,49 @@ DSA
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.
+ .. method:: signer(algorithm, backend)
+
+ .. versionadded:: 0.4
+
+ Sign data which can be verified later by others using the public key.
+
+ .. code-block:: pycon
+
+ >>> from cryptography.hazmat.backends import default_backend
+ >>> from cryptography.hazmat.primitives import hashes
+ >>> from cryptography.hazmat.primitives.asymmetric import dsa
+ >>> parameters = dsa.DSAParameters.generate(
+ ... key_size=1024,
+ ... backend=default_backend()
+ ... )
+ >>> private_key = dsa.DSAPrivateKey.generate(
+ ... parameters=parameters,
+ ... backend=default_backend()
+ ... )
+ >>> signer = private_key.signer(
+ ... hashes.SHA256(),
+ ... default_backend()
+ ... )
+ >>> data= b"this is some data I'd like to sign"
+ >>> signer.update(data)
+ >>> signature = signer.finalize()
+ >>> public_key = private_key.public_key()
+
+ :param algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+ provider.
+
+ :param backend: A
+ :class:`~cryptography.hazmat.backends.interfaces.RSABackend`
+ provider.
+
+ :returns:
+ :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricSignatureContext`
+
+ :raises cryptography.exceptions.UnsupportedAlgorithm: This is raised if
+ the provided ``backend`` does not implement
+ :class:`~cryptography.hazmat.backends.interfaces.DSABackend`
+
.. class:: DSAPublicKey(modulus, subgroup_order, generator, y)
diff --git a/docs/hazmat/primitives/interfaces.rst b/docs/hazmat/primitives/interfaces.rst
index feafe941..dc09a26f 100644
--- a/docs/hazmat/primitives/interfaces.rst
+++ b/docs/hazmat/primitives/interfaces.rst
@@ -381,6 +381,23 @@ Asymmetric interfaces
The DSAParameters object associated with this private key.
+ .. method:: signer(algorithm, backend)
+
+ .. versionadded:: 0.4
+
+ Sign data which can be verified later by others using the public key.
+
+ :param algorithm: An instance of a
+ :class:`~cryptography.hazmat.primitives.interfaces.HashAlgorithm`
+ provider.
+
+ :param backend: A
+ :class:`~cryptography.hazmat.backends.interfaces.DSABackend`
+ provider.
+
+ :returns:
+ :class:`~cryptography.hazmat.primitives.interfaces.AsymmetricSignatureContext`
+
.. attribute:: key_size
:type: int
diff --git a/tests/hazmat/primitives/test_dsa.py b/tests/hazmat/primitives/test_dsa.py
index 4c3cd58a..1bfea2e3 100644
--- a/tests/hazmat/primitives/test_dsa.py
+++ b/tests/hazmat/primitives/test_dsa.py
@@ -798,6 +798,60 @@ class TestDSAVerification(object):
public_key.verifier(b"sig", hashes.SHA1(), pretend_backend)
+@pytest.mark.dsa
+class TestDSASignature(object):
+ _algorithms_dict = {
+ 'SHA1': hashes.SHA1,
+ 'SHA224': hashes.SHA224,
+ 'SHA256': hashes.SHA256,
+ 'SHA384': hashes.SHA384,
+ 'SHA512': hashes.SHA512}
+
+ @pytest.mark.parametrize(
+ "vector",
+ load_vectors_from_file(
+ os.path.join(
+ "asymmetric", "DSA", "FIPS_186-3", "SigGen.txt"),
+ load_fips_dsa_sig_vectors
+ )
+ )
+ def test_dsa_signing(self, vector, backend):
+ digest_algorithm = vector['digest_algorithm'].replace("-", "")
+ algorithm = self._algorithms_dict[digest_algorithm]
+ if (
+ not backend.dsa_parameters_supported(
+ vector['p'], vector['q'], vector['g']
+ ) or not backend.dsa_hash_supported(algorithm)
+ ):
+ pytest.skip(
+ "{0} does not support the provided parameters".format(backend)
+ )
+
+ private_key = dsa.DSAPrivateKey(
+ vector['p'], vector['q'], vector['g'], vector['x'], vector['y']
+ )
+ signer = private_key.signer(algorithm(), backend)
+ signer.update(vector['msg'])
+ signature = signer.finalize()
+ assert signature
+
+ public_key = private_key.public_key()
+ verifier = public_key.verifier(signature, algorithm(), backend)
+ verifier.update(vector['msg'])
+ verifier.verify()
+
+ def test_use_after_finalize(self, backend):
+ parameters = dsa.DSAParameters.generate(1024, backend)
+ private_key = dsa.DSAPrivateKey.generate(parameters, backend)
+ signer = private_key.signer(hashes.SHA1(), backend)
+ signer.update(b"data")
+ signer.finalize()
+ with pytest.raises(AlreadyFinalized):
+ signer.finalize()
+ with pytest.raises(AlreadyFinalized):
+ signer.update(b"more data")
+
+
def test_dsa_generate_invalid_backend():
pretend_backend = object()