aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cryptography/exceptions.py1
-rw-r--r--src/cryptography/hazmat/backends/interfaces.py18
-rw-r--r--src/cryptography/hazmat/backends/multibackend.py72
-rw-r--r--src/cryptography/hazmat/backends/openssl/backend.py118
-rw-r--r--src/cryptography/hazmat/backends/openssl/dh.py182
-rw-r--r--src/cryptography/hazmat/primitives/asymmetric/dh.py23
6 files changed, 399 insertions, 15 deletions
diff --git a/src/cryptography/exceptions.py b/src/cryptography/exceptions.py
index ee43fed7..648cf9df 100644
--- a/src/cryptography/exceptions.py
+++ b/src/cryptography/exceptions.py
@@ -18,6 +18,7 @@ class _Reasons(Enum):
UNSUPPORTED_SERIALIZATION = 7
UNSUPPORTED_X509 = 8
UNSUPPORTED_EXCHANGE_ALGORITHM = 9
+ UNSUPPORTED_DIFFIE_HELLMAN = 10
class UnsupportedAlgorithm(Exception):
diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py
index e15a7ca4..c5f2951c 100644
--- a/src/cryptography/hazmat/backends/interfaces.py
+++ b/src/cryptography/hazmat/backends/interfaces.py
@@ -322,9 +322,10 @@ class X509Backend(object):
@six.add_metaclass(abc.ABCMeta)
class DHBackend(object):
@abc.abstractmethod
- def generate_dh_parameters(self, key_size):
+ def generate_dh_parameters(self, generator, key_size):
"""
Generate a DHParameters instance with a modulus of key_size bits.
+ Using the given generator. Often 2 or 5.
"""
@abc.abstractmethod
@@ -335,33 +336,28 @@ class DHBackend(object):
"""
@abc.abstractmethod
- def generate_dh_private_key_and_parameters(self, key_size):
+ def generate_dh_private_key_and_parameters(self, generator, key_size):
"""
Generate a DHPrivateKey instance using key size only.
+ Using the given generator. Often 2 or 5.
"""
@abc.abstractmethod
def load_dh_private_numbers(self, numbers):
"""
- Returns a DHPrivateKey provider.
+ Load a DHPrivateKey from DHPrivateNumbers
"""
@abc.abstractmethod
def load_dh_public_numbers(self, numbers):
"""
- Returns a DHPublicKey provider.
+ Load a DHPublicKey from DHPublicNumbers.
"""
@abc.abstractmethod
def load_dh_parameter_numbers(self, numbers):
"""
- Returns a DHParameters provider.
- """
-
- @abc.abstractmethod
- def dh_exchange_algorithm_supported(self, exchange_algorithm):
- """
- Returns whether the exchange algorithm is supported by this backend.
+ Load DHParameters from DHParameterNumbers.
"""
@abc.abstractmethod
diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py
index bcd9c520..097b4908 100644
--- a/src/cryptography/hazmat/backends/multibackend.py
+++ b/src/cryptography/hazmat/backends/multibackend.py
@@ -7,9 +7,10 @@ from __future__ import absolute_import, division, print_function
from cryptography import utils
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.backends.interfaces import (
- CMACBackend, CipherBackend, DERSerializationBackend, DSABackend,
- EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend,
- PEMSerializationBackend, RSABackend, ScryptBackend, X509Backend
+ CMACBackend, CipherBackend, DERSerializationBackend, DHBackend,
+ DSABackend, EllipticCurveBackend, HMACBackend, HashBackend,
+ PBKDF2HMACBackend, PEMSerializationBackend, RSABackend, ScryptBackend,
+ X509Backend
)
@@ -24,6 +25,7 @@ from cryptography.hazmat.backends.interfaces import (
@utils.register_interface(EllipticCurveBackend)
@utils.register_interface(PEMSerializationBackend)
@utils.register_interface(X509Backend)
+@utils.register_interface(DHBackend)
@utils.register_interface(ScryptBackend)
class MultiBackend(object):
name = "multibackend"
@@ -424,6 +426,70 @@ class MultiBackend(object):
_Reasons.UNSUPPORTED_X509
)
+ def generate_dh_parameters(self, generator, key_size):
+ for b in self._filtered_backends(DHBackend):
+ return b.generate_dh_parameters(generator, key_size)
+
+ raise UnsupportedAlgorithm(
+ "This backend does not support Diffie-Hellman",
+ _Reasons.UNSUPPORTED_DIFFIE_HELLMAN
+ )
+
+ def load_dh_parameter_numbers(self, numbers):
+ for b in self._filtered_backends(DHBackend):
+ return b.load_dh_parameter_numbers(numbers)
+
+ raise UnsupportedAlgorithm(
+ "This backend does not support Diffie-Hellman",
+ _Reasons.UNSUPPORTED_DIFFIE_HELLMAN
+ )
+
+ def generate_dh_private_key(self, parameters):
+ for b in self._filtered_backends(DHBackend):
+ return b.generate_dh_private_key(parameters)
+
+ raise UnsupportedAlgorithm(
+ "This backend does not support Diffie-Hellman",
+ _Reasons.UNSUPPORTED_DIFFIE_HELLMAN
+ )
+
+ def load_dh_private_numbers(self, numbers):
+ for b in self._filtered_backends(DHBackend):
+ return b.load_dh_private_numbers(numbers)
+
+ raise UnsupportedAlgorithm(
+ "This backend does not support Diffie-Hellman",
+ _Reasons.UNSUPPORTED_DIFFIE_HELLMAN
+ )
+
+ def load_dh_public_numbers(self, numbers):
+ for b in self._filtered_backends(DHBackend):
+ return b.load_dh_public_numbers(numbers)
+
+ raise UnsupportedAlgorithm(
+ "This backend does not support Diffie-Hellman",
+ _Reasons.UNSUPPORTED_DIFFIE_HELLMAN
+ )
+
+ def generate_dh_private_key_and_parameters(self, generator, key_size):
+ for b in self._filtered_backends(DHBackend):
+ return b.generate_dh_private_key_and_parameters(generator,
+ key_size)
+
+ raise UnsupportedAlgorithm(
+ "This backend does not support Diffie-Hellman",
+ _Reasons.UNSUPPORTED_DIFFIE_HELLMAN
+ )
+
+ def dh_parameters_supported(self, p, g):
+ for b in self._filtered_backends(DHBackend):
+ return b.dh_parameters_supported(p, g)
+
+ raise UnsupportedAlgorithm(
+ "This backend does not support Diffie-Hellman",
+ _Reasons.UNSUPPORTED_DIFFIE_HELLMAN
+ )
+
def x509_name_bytes(self, name):
for b in self._filtered_backends(X509Backend):
return b.x509_name_bytes(name)
diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py
index 4a341fc2..9df113b6 100644
--- a/src/cryptography/hazmat/backends/openssl/backend.py
+++ b/src/cryptography/hazmat/backends/openssl/backend.py
@@ -16,7 +16,7 @@ import six
from cryptography import utils, x509
from cryptography.exceptions import UnsupportedAlgorithm, _Reasons
from cryptography.hazmat.backends.interfaces import (
- CMACBackend, CipherBackend, DERSerializationBackend, DSABackend,
+ CMACBackend, CipherBackend, DERSerializationBackend, DHBackend, DSABackend,
EllipticCurveBackend, HMACBackend, HashBackend, PBKDF2HMACBackend,
PEMSerializationBackend, RSABackend, ScryptBackend, X509Backend
)
@@ -24,6 +24,9 @@ from cryptography.hazmat.backends.openssl.ciphers import (
_AESCTRCipherContext, _CipherContext
)
from cryptography.hazmat.backends.openssl.cmac import _CMACContext
+from cryptography.hazmat.backends.openssl.dh import (
+ _DHParameters, _DHPrivateKey, _DHPublicKey
+)
from cryptography.hazmat.backends.openssl.dsa import (
_DSAParameters, _DSAPrivateKey, _DSAPublicKey
)
@@ -107,6 +110,7 @@ def _pem_password_cb(buf, size, writing, userdata_handle):
@utils.register_interface(CipherBackend)
@utils.register_interface(CMACBackend)
@utils.register_interface(DERSerializationBackend)
+@utils.register_interface(DHBackend)
@utils.register_interface(DSABackend)
@utils.register_interface(EllipticCurveBackend)
@utils.register_interface(HashBackend)
@@ -329,6 +333,7 @@ class Backend(object):
def _bn_to_int(self, bn):
assert bn != self._ffi.NULL
+
if six.PY3:
# Python 3 has constant time from_bytes, so use that.
bn_num_bytes = self._lib.BN_num_bytes(bn)
@@ -1734,6 +1739,117 @@ class Backend(object):
serialization._ssh_write_string(public_numbers.encode_point())
)
+ def generate_dh_parameters(self, generator, key_size):
+ if key_size < 512:
+ raise ValueError("DH key_size must be at least 512 bits")
+
+ if generator not in (2, 5):
+ raise ValueError("DH generator must be 2 or 5")
+
+ dh_param_cdata = self._lib.DH_new()
+ self.openssl_assert(dh_param_cdata != self._ffi.NULL)
+ dh_param_cdata = self._ffi.gc(dh_param_cdata, self._lib.DH_free)
+
+ res = self._lib.DH_generate_parameters_ex(
+ dh_param_cdata,
+ key_size,
+ generator,
+ self._ffi.NULL
+ )
+ self.openssl_assert(res == 1)
+
+ return _DHParameters(self, dh_param_cdata)
+
+ def generate_dh_private_key(self, parameters):
+ dh_key_cdata = self._lib.DHparams_dup(parameters._dh_cdata)
+ self.openssl_assert(dh_key_cdata != self._ffi.NULL)
+ dh_key_cdata = self._ffi.gc(dh_key_cdata, self._lib.DH_free)
+
+ res = self._lib.DH_generate_key(dh_key_cdata)
+ self.openssl_assert(res == 1)
+
+ return _DHPrivateKey(self, dh_key_cdata)
+
+ def generate_dh_private_key_and_parameters(self, generator, key_size):
+ return self.generate_dh_private_key(
+ self.generate_dh_parameters(generator, key_size))
+
+ def load_dh_private_numbers(self, numbers):
+ parameter_numbers = numbers.public_numbers.parameter_numbers
+
+ dh_cdata = self._lib.DH_new()
+ self.openssl_assert(dh_cdata != self._ffi.NULL)
+ dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
+
+ p = self._int_to_bn(parameter_numbers.p)
+ g = self._int_to_bn(parameter_numbers.g)
+ pub_key = self._int_to_bn(numbers.public_numbers.y)
+ priv_key = self._int_to_bn(numbers.x)
+
+ res = self._lib.DH_set0_pqg(dh_cdata, p, self._ffi.NULL, g)
+ self.openssl_assert(res == 1)
+
+ res = self._lib.DH_set0_key(dh_cdata, pub_key, priv_key)
+ self.openssl_assert(res == 1)
+
+ codes = self._ffi.new("int[]", 1)
+ res = self._lib.DH_check(dh_cdata, codes)
+ self.openssl_assert(res == 1)
+
+ if codes[0] != 0:
+ raise ValueError("DH private numbers did not pass safety checks.")
+
+ return _DHPrivateKey(self, dh_cdata)
+
+ def load_dh_public_numbers(self, numbers):
+ dh_cdata = self._lib.DH_new()
+ self.openssl_assert(dh_cdata != self._ffi.NULL)
+ dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
+
+ parameter_numbers = numbers.parameter_numbers
+
+ p = self._int_to_bn(parameter_numbers.p)
+ g = self._int_to_bn(parameter_numbers.g)
+ pub_key = self._int_to_bn(numbers.y)
+
+ res = self._lib.DH_set0_pqg(dh_cdata, p, self._ffi.NULL, g)
+ self.openssl_assert(res == 1)
+
+ res = self._lib.DH_set0_key(dh_cdata, pub_key, self._ffi.NULL)
+ self.openssl_assert(res == 1)
+
+ return _DHPublicKey(self, dh_cdata)
+
+ def load_dh_parameter_numbers(self, numbers):
+ dh_cdata = self._lib.DH_new()
+ self.openssl_assert(dh_cdata != self._ffi.NULL)
+ dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
+
+ p = self._int_to_bn(numbers.p)
+ g = self._int_to_bn(numbers.g)
+
+ res = self._lib.DH_set0_pqg(dh_cdata, p, self._ffi.NULL, g)
+ self.openssl_assert(res == 1)
+
+ return _DHParameters(self, dh_cdata)
+
+ def dh_parameters_supported(self, p, g):
+ dh_cdata = self._lib.DH_new()
+ self.openssl_assert(dh_cdata != self._ffi.NULL)
+ dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free)
+
+ p = self._int_to_bn(p)
+ g = self._int_to_bn(g)
+
+ res = self._lib.DH_set0_pqg(dh_cdata, p, self._ffi.NULL, g)
+ self.openssl_assert(res == 1)
+
+ codes = self._ffi.new("int[]", 1)
+ res = self._lib.DH_check(dh_cdata, codes)
+ self.openssl_assert(res == 1)
+
+ return codes[0] == 0
+
def x509_name_bytes(self, name):
x509_name = _encode_name_gc(self, name)
pp = self._ffi.new("unsigned char **")
diff --git a/src/cryptography/hazmat/backends/openssl/dh.py b/src/cryptography/hazmat/backends/openssl/dh.py
new file mode 100644
index 00000000..666429f2
--- /dev/null
+++ b/src/cryptography/hazmat/backends/openssl/dh.py
@@ -0,0 +1,182 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import absolute_import, division, print_function
+
+from cryptography import utils
+from cryptography.hazmat.primitives.asymmetric import dh
+
+
+def _dh_cdata_to_parameters(dh_cdata, backend):
+ lib = backend._lib
+ ffi = backend._ffi
+
+ param_cdata = lib.DHparams_dup(dh_cdata)
+ backend.openssl_assert(param_cdata != ffi.NULL)
+ param_cdata = ffi.gc(param_cdata, lib.DH_free)
+
+ return _DHParameters(backend, param_cdata)
+
+
+@utils.register_interface(dh.DHParametersWithSerialization)
+class _DHParameters(object):
+ def __init__(self, backend, dh_cdata):
+ self._backend = backend
+ self._dh_cdata = dh_cdata
+
+ def parameter_numbers(self):
+ p = self._backend._ffi.new("BIGNUM **")
+ g = self._backend._ffi.new("BIGNUM **")
+ self._backend._lib.DH_get0_pqg(self._dh_cdata,
+ p, self._backend._ffi.NULL, g)
+ self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
+ self._backend.openssl_assert(g[0] != self._backend._ffi.NULL)
+ return dh.DHParameterNumbers(
+ p=self._backend._bn_to_int(p[0]),
+ g=self._backend._bn_to_int(g[0])
+ )
+
+ def generate_private_key(self):
+ return self._backend.generate_dh_private_key(self)
+
+
+def _handle_dh_compute_key_error(errors, backend):
+ lib = backend._lib
+
+ backend.openssl_assert(errors[0][1:] == (
+ lib.ERR_LIB_DH,
+ lib.DH_F_COMPUTE_KEY,
+ lib.DH_R_INVALID_PUBKEY
+ ))
+
+ raise ValueError("Public key value is invalid for this exchange.")
+
+
+def _get_dh_num_bits(backend, dh_cdata):
+ p = backend._ffi.new("BIGNUM **")
+ backend._lib.DH_get0_pqg(dh_cdata, p,
+ backend._ffi.NULL,
+ backend._ffi.NULL)
+ backend.openssl_assert(p[0] != backend._ffi.NULL)
+ return backend._lib.BN_num_bits(p[0])
+
+
+@utils.register_interface(dh.DHPrivateKeyWithSerialization)
+class _DHPrivateKey(object):
+ def __init__(self, backend, dh_cdata):
+ self._backend = backend
+ self._dh_cdata = dh_cdata
+ self._key_size_bytes = self._backend._lib.DH_size(dh_cdata)
+
+ @property
+ def key_size(self):
+ return _get_dh_num_bits(self._backend, self._dh_cdata)
+
+ def private_numbers(self):
+ p = self._backend._ffi.new("BIGNUM **")
+ g = self._backend._ffi.new("BIGNUM **")
+ self._backend._lib.DH_get0_pqg(self._dh_cdata,
+ p, self._backend._ffi.NULL, g)
+ self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
+ self._backend.openssl_assert(g[0] != self._backend._ffi.NULL)
+ pub_key = self._backend._ffi.new("BIGNUM **")
+ priv_key = self._backend._ffi.new("BIGNUM **")
+ self._backend._lib.DH_get0_key(self._dh_cdata, pub_key, priv_key)
+ self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
+ self._backend.openssl_assert(priv_key[0] != self._backend._ffi.NULL)
+ return dh.DHPrivateNumbers(
+ public_numbers=dh.DHPublicNumbers(
+ parameter_numbers=dh.DHParameterNumbers(
+ p=self._backend._bn_to_int(p[0]),
+ g=self._backend._bn_to_int(g[0])
+ ),
+ y=self._backend._bn_to_int(pub_key[0])
+ ),
+ x=self._backend._bn_to_int(priv_key[0])
+ )
+
+ def exchange(self, peer_public_key):
+
+ buf = self._backend._ffi.new("unsigned char[]", self._key_size_bytes)
+ pub_key = self._backend._ffi.new("BIGNUM **")
+ self._backend._lib.DH_get0_key(peer_public_key._dh_cdata, pub_key,
+ self._backend._ffi.NULL)
+ self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
+ res = self._backend._lib.DH_compute_key(
+ buf,
+ pub_key[0],
+ self._dh_cdata
+ )
+
+ if res == -1:
+ errors = self._backend._consume_errors()
+ return _handle_dh_compute_key_error(errors, self._backend)
+ else:
+ self._backend.openssl_assert(res >= 1)
+
+ key = self._backend._ffi.buffer(buf)[:res]
+ pad = self._key_size_bytes - len(key)
+
+ if pad > 0:
+ key = (b"\x00" * pad) + key
+
+ return key
+
+ def public_key(self):
+ dh_cdata = self._backend._lib.DHparams_dup(self._dh_cdata)
+ self._backend.openssl_assert(dh_cdata != self._backend._ffi.NULL)
+ dh_cdata = self._backend._ffi.gc(
+ dh_cdata, self._backend._lib.DH_free
+ )
+
+ pub_key = self._backend._ffi.new("BIGNUM **")
+ self._backend._lib.DH_get0_key(self._dh_cdata,
+ pub_key, self._backend._ffi.NULL)
+ self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
+ pub_key_dup = self._backend._lib.BN_dup(pub_key[0])
+ self._backend.openssl_assert(pub_key_dup != self._backend._ffi.NULL)
+
+ res = self._backend._lib.DH_set0_key(dh_cdata,
+ pub_key_dup,
+ self._backend._ffi.NULL)
+ self._backend.openssl_assert(res == 1)
+
+ return _DHPublicKey(self._backend, dh_cdata)
+
+ def parameters(self):
+ return _dh_cdata_to_parameters(self._dh_cdata, self._backend)
+
+
+@utils.register_interface(dh.DHPublicKeyWithSerialization)
+class _DHPublicKey(object):
+ def __init__(self, backend, dh_cdata):
+ self._backend = backend
+ self._dh_cdata = dh_cdata
+ self._key_size_bits = _get_dh_num_bits(self._backend, self._dh_cdata)
+
+ @property
+ def key_size(self):
+ return self._key_size_bits
+
+ def public_numbers(self):
+ p = self._backend._ffi.new("BIGNUM **")
+ g = self._backend._ffi.new("BIGNUM **")
+ self._backend._lib.DH_get0_pqg(self._dh_cdata,
+ p, self._backend._ffi.NULL, g)
+ self._backend.openssl_assert(p[0] != self._backend._ffi.NULL)
+ self._backend.openssl_assert(g[0] != self._backend._ffi.NULL)
+ pub_key = self._backend._ffi.new("BIGNUM **")
+ self._backend._lib.DH_get0_key(self._dh_cdata,
+ pub_key, self._backend._ffi.NULL)
+ self._backend.openssl_assert(pub_key[0] != self._backend._ffi.NULL)
+ return dh.DHPublicNumbers(
+ parameter_numbers=dh.DHParameterNumbers(
+ p=self._backend._bn_to_int(p[0]),
+ g=self._backend._bn_to_int(g[0])
+ ),
+ y=self._backend._bn_to_int(pub_key[0])
+ )
+
+ def parameters(self):
+ return _dh_cdata_to_parameters(self._dh_cdata, self._backend)
diff --git a/src/cryptography/hazmat/primitives/asymmetric/dh.py b/src/cryptography/hazmat/primitives/asymmetric/dh.py
index 12d53eed..ec044ddd 100644
--- a/src/cryptography/hazmat/primitives/asymmetric/dh.py
+++ b/src/cryptography/hazmat/primitives/asymmetric/dh.py
@@ -11,6 +11,10 @@ import six
from cryptography import utils
+def generate_parameters(generator, key_size, backend):
+ return backend.generate_dh_parameters(generator, key_size)
+
+
class DHPrivateNumbers(object):
def __init__(self, x, public_numbers):
if not isinstance(x, six.integer_types):
@@ -35,6 +39,9 @@ class DHPrivateNumbers(object):
def __ne__(self, other):
return not self == other
+ def private_key(self, backend):
+ return backend.load_dh_private_numbers(self)
+
public_numbers = utils.read_only_property("_public_numbers")
x = utils.read_only_property("_x")
@@ -63,6 +70,9 @@ class DHPublicNumbers(object):
def __ne__(self, other):
return not self == other
+ def public_key(self, backend):
+ return backend.load_dh_public_numbers(self)
+
y = utils.read_only_property("_y")
parameter_numbers = utils.read_only_property("_parameter_numbers")
@@ -75,6 +85,9 @@ class DHParameterNumbers(object):
):
raise TypeError("p and g must be integers")
+ if g not in (2, 5):
+ raise ValueError("DH generator must be 2 or 5")
+
self._p = p
self._g = g
@@ -90,6 +103,9 @@ class DHParameterNumbers(object):
def __ne__(self, other):
return not self == other
+ def parameters(self, backend):
+ return backend.load_dh_parameter_numbers(self)
+
p = utils.read_only_property("_p")
g = utils.read_only_property("_g")
@@ -141,6 +157,13 @@ class DHPrivateKeyWithSerialization(DHPrivateKey):
Returns a DHPrivateNumbers.
"""
+ @abc.abstractmethod
+ def exchange(self, peer_public_key):
+ """
+ Given peer's DHPublicKey, carry out the key exchange and
+ return shared key as bytes.
+ """
+
@six.add_metaclass(abc.ABCMeta)
class DHPublicKey(object):