diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/cryptography/hazmat/backends/interfaces.py | 9 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/multibackend.py | 13 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/backend.py | 66 | ||||
-rw-r--r-- | src/cryptography/hazmat/backends/openssl/dh.py | 77 | ||||
-rw-r--r-- | src/cryptography/hazmat/primitives/asymmetric/dh.py | 11 |
5 files changed, 143 insertions, 33 deletions
diff --git a/src/cryptography/hazmat/backends/interfaces.py b/src/cryptography/hazmat/backends/interfaces.py index c5f2951c..9ed50cc4 100644 --- a/src/cryptography/hazmat/backends/interfaces.py +++ b/src/cryptography/hazmat/backends/interfaces.py @@ -361,11 +361,18 @@ class DHBackend(object): """ @abc.abstractmethod - def dh_parameters_supported(self, p, g): + def dh_parameters_supported(self, p, g, q=None): """ Returns whether the backend supports DH with these parameter values. """ + @abc.abstractmethod + def dh_x942_serialization_supported(self): + """ + Returns True if the backend supports the serialization of DH objects + with subgroup order (q). + """ + @six.add_metaclass(abc.ABCMeta) class ScryptBackend(object): diff --git a/src/cryptography/hazmat/backends/multibackend.py b/src/cryptography/hazmat/backends/multibackend.py index 097b4908..bb30c661 100644 --- a/src/cryptography/hazmat/backends/multibackend.py +++ b/src/cryptography/hazmat/backends/multibackend.py @@ -481,9 +481,18 @@ class MultiBackend(object): _Reasons.UNSUPPORTED_DIFFIE_HELLMAN ) - def dh_parameters_supported(self, p, g): + def dh_parameters_supported(self, p, g, q=None): for b in self._filtered_backends(DHBackend): - return b.dh_parameters_supported(p, g) + return b.dh_parameters_supported(p, g, q) + + raise UnsupportedAlgorithm( + "This backend does not support Diffie-Hellman", + _Reasons.UNSUPPORTED_DIFFIE_HELLMAN + ) + + def dh_x942_serialization_supported(self): + for b in self._filtered_backends(DHBackend): + return b.dh_x942_serialization_supported() raise UnsupportedAlgorithm( "This backend does not support Diffie-Hellman", diff --git a/src/cryptography/hazmat/backends/openssl/backend.py b/src/cryptography/hazmat/backends/openssl/backend.py index 77538162..446891d3 100644 --- a/src/cryptography/hazmat/backends/openssl/backend.py +++ b/src/cryptography/hazmat/backends/openssl/backend.py @@ -23,7 +23,8 @@ from cryptography.hazmat.backends.interfaces import ( from cryptography.hazmat.backends.openssl.ciphers import _CipherContext from cryptography.hazmat.backends.openssl.cmac import _CMACContext from cryptography.hazmat.backends.openssl.dh import ( - _DHParameters, _DHPrivateKey, _DHPublicKey + _DHParameters, _DHPrivateKey, _DHPublicKey, + _dh_params_dup ) from cryptography.hazmat.backends.openssl.dsa import ( _DSAParameters, _DSAPrivateKey, _DSAPublicKey @@ -99,6 +100,9 @@ class Backend(object): self._cipher_registry = {} self._register_default_ciphers() self.activate_osrandom_engine() + self._dh_types = [self._lib.EVP_PKEY_DH] + if self._lib.Cryptography_HAS_EVP_PKEY_DHX: + self._dh_types.append(self._lib.EVP_PKEY_DHX) def openssl_assert(self, ok): return binding._openssl_assert(self._lib, ok) @@ -480,7 +484,7 @@ class Backend(object): self.openssl_assert(ec_cdata != self._ffi.NULL) ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) return _EllipticCurvePrivateKey(self, ec_cdata, evp_pkey) - elif key_type == self._lib.EVP_PKEY_DH: + elif key_type in self._dh_types: dh_cdata = self._lib.EVP_PKEY_get1_DH(evp_pkey) self.openssl_assert(dh_cdata != self._ffi.NULL) dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) @@ -512,7 +516,7 @@ class Backend(object): self.openssl_assert(ec_cdata != self._ffi.NULL) ec_cdata = self._ffi.gc(ec_cdata, self._lib.EC_KEY_free) return _EllipticCurvePublicKey(self, ec_cdata, evp_pkey) - elif key_type == self._lib.EVP_PKEY_DH: + elif key_type in self._dh_types: dh_cdata = self._lib.EVP_PKEY_get1_DH(evp_pkey) self.openssl_assert(dh_cdata != self._ffi.NULL) dh_cdata = self._ffi.gc(dh_cdata, self._lib.DH_free) @@ -1205,6 +1209,23 @@ class Backend(object): _Reasons.UNSUPPORTED_CIPHER ) + elif errors[0][1:] in ( + ( + self._lib.ERR_LIB_ASN1, + self._lib.ASN1_F_ASN1_CHECK_TLEN, + self._lib.ASN1_R_WRONG_TAG + ), + ( + self._lib.ERR_LIB_PEM, + self._lib.PEM_F_PEM_READ_BIO, + self._lib.PEM_R_NO_START_LINE + ), + ): + raise UnsupportedAlgorithm( + "Unsupported public key algorithm.", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM + ) + elif any( error[1:] == ( self._lib.ERR_LIB_EVP, @@ -1660,9 +1681,7 @@ class Backend(object): return evp_pkey 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) + dh_key_cdata = _dh_params_dup(parameters._dh_cdata, self) res = self._lib.DH_generate_key(dh_key_cdata) self.openssl_assert(res == 1) @@ -1684,10 +1703,16 @@ class Backend(object): p = self._int_to_bn(parameter_numbers.p) g = self._int_to_bn(parameter_numbers.g) + + if parameter_numbers.q is not None: + q = self._int_to_bn(parameter_numbers.q) + else: + q = self._ffi.NULL + 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) + res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) self.openssl_assert(res == 1) res = self._lib.DH_set0_key(dh_cdata, pub_key, priv_key) @@ -1713,9 +1738,15 @@ class Backend(object): p = self._int_to_bn(parameter_numbers.p) g = self._int_to_bn(parameter_numbers.g) + + if parameter_numbers.q is not None: + q = self._int_to_bn(parameter_numbers.q) + else: + q = self._ffi.NULL + pub_key = self._int_to_bn(numbers.y) - res = self._lib.DH_set0_pqg(dh_cdata, p, self._ffi.NULL, g) + res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) self.openssl_assert(res == 1) res = self._lib.DH_set0_key(dh_cdata, pub_key, self._ffi.NULL) @@ -1733,12 +1764,17 @@ class Backend(object): 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) + if numbers.q is not None: + q = self._int_to_bn(numbers.q) + else: + q = self._ffi.NULL + + res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) self.openssl_assert(res == 1) return _DHParameters(self, dh_cdata) - def dh_parameters_supported(self, p, g): + def dh_parameters_supported(self, p, g, q=None): 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) @@ -1746,7 +1782,12 @@ class Backend(object): 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) + if q is not None: + q = self._int_to_bn(q) + else: + q = self._ffi.NULL + + res = self._lib.DH_set0_pqg(dh_cdata, p, q, g) self.openssl_assert(res == 1) codes = self._ffi.new("int[]", 1) @@ -1755,6 +1796,9 @@ class Backend(object): return codes[0] == 0 + def dh_x942_serialization_supported(self): + return self._lib.Cryptography_HAS_EVP_PKEY_DHX == 1 + 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 index b594d411..88c876fc 100644 --- a/src/cryptography/hazmat/backends/openssl/dh.py +++ b/src/cryptography/hazmat/backends/openssl/dh.py @@ -5,18 +5,31 @@ from __future__ import absolute_import, division, print_function from cryptography import utils +from cryptography.exceptions import UnsupportedAlgorithm, _Reasons from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import dh -def _dh_cdata_to_parameters(dh_cdata, backend): +def _dh_params_dup(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) + if lib.OPENSSL_VERSION_NUMBER < 0x10002000 or lib.CRYPTOGRAPHY_IS_LIBRESSL: + # In OpenSSL versions < 1.0.2 or libressl DHparams_dup don't copy q + q = ffi.new("BIGNUM **") + lib.DH_get0_pqg(dh_cdata, ffi.NULL, q, ffi.NULL) + q_dup = lib.BN_dup(q[0]) + res = lib.DH_set0_pqg(param_cdata, ffi.NULL, q_dup, ffi.NULL) + backend.openssl_assert(res == 1) + + return param_cdata + +def _dh_cdata_to_parameters(dh_cdata, backend): + param_cdata = _dh_params_dup(dh_cdata, backend) return _DHParameters(backend, param_cdata) @@ -29,13 +42,18 @@ class _DHParameters(object): 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) + q = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_pqg(self._dh_cdata, p, q, g) self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) + if q[0] == self._backend._ffi.NULL: + q_val = None + else: + q_val = self._backend._bn_to_int(q[0]) return dh.DHParameterNumbers( p=self._backend._bn_to_int(p[0]), - g=self._backend._bn_to_int(g[0]) + g=self._backend._bn_to_int(g[0]), + q=q_val ) def generate_private_key(self): @@ -78,10 +96,14 @@ class _DHPrivateKey(object): 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) + q = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_pqg(self._dh_cdata, p, q, g) self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) + if q[0] == self._backend._ffi.NULL: + q_val = None + else: + q_val = self._backend._bn_to_int(q[0]) 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) @@ -91,7 +113,8 @@ class _DHPrivateKey(object): public_numbers=dh.DHPublicNumbers( parameter_numbers=dh.DHParameterNumbers( p=self._backend._bn_to_int(p[0]), - g=self._backend._bn_to_int(g[0]) + g=self._backend._bn_to_int(g[0]), + q=q_val ), y=self._backend._bn_to_int(pub_key[0]) ), @@ -126,12 +149,7 @@ class _DHPrivateKey(object): 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 - ) - + dh_cdata = _dh_params_dup(self._dh_cdata, self._backend) pub_key = self._backend._ffi.new("BIGNUM **") self._backend._lib.DH_get0_key(self._dh_cdata, pub_key, self._backend._ffi.NULL) @@ -154,6 +172,17 @@ class _DHPrivateKey(object): raise ValueError( "DH private keys support only PKCS8 serialization" ) + if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX: + q = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_pqg(self._dh_cdata, + self._backend._ffi.NULL, + q, + self._backend._ffi.NULL) + if q[0] != self._backend._ffi.NULL: + raise UnsupportedAlgorithm( + "DH X9.42 serialization is not supported", + _Reasons.UNSUPPORTED_SERIALIZATION) + return self._backend._private_key_bytes( encoding, format, @@ -178,10 +207,14 @@ class _DHPublicKey(object): 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) + q = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_pqg(self._dh_cdata, p, q, g) self._backend.openssl_assert(p[0] != self._backend._ffi.NULL) self._backend.openssl_assert(g[0] != self._backend._ffi.NULL) + if q[0] == self._backend._ffi.NULL: + q_val = None + else: + q_val = self._backend._bn_to_int(q[0]) pub_key = self._backend._ffi.new("BIGNUM **") self._backend._lib.DH_get0_key(self._dh_cdata, pub_key, self._backend._ffi.NULL) @@ -189,7 +222,8 @@ class _DHPublicKey(object): return dh.DHPublicNumbers( parameter_numbers=dh.DHParameterNumbers( p=self._backend._bn_to_int(p[0]), - g=self._backend._bn_to_int(g[0]) + g=self._backend._bn_to_int(g[0]), + q=q_val ), y=self._backend._bn_to_int(pub_key[0]) ) @@ -204,6 +238,17 @@ class _DHPublicKey(object): "SubjectPublicKeyInfo serialization" ) + if not self._backend._lib.Cryptography_HAS_EVP_PKEY_DHX: + q = self._backend._ffi.new("BIGNUM **") + self._backend._lib.DH_get0_pqg(self._dh_cdata, + self._backend._ffi.NULL, + q, + self._backend._ffi.NULL) + if q[0] != self._backend._ffi.NULL: + raise UnsupportedAlgorithm( + "DH X9.42 serialization is not supported", + _Reasons.UNSUPPORTED_SERIALIZATION) + return self._backend._public_key_bytes( encoding, format, diff --git a/src/cryptography/hazmat/primitives/asymmetric/dh.py b/src/cryptography/hazmat/primitives/asymmetric/dh.py index ec044ddd..aa60a2d8 100644 --- a/src/cryptography/hazmat/primitives/asymmetric/dh.py +++ b/src/cryptography/hazmat/primitives/asymmetric/dh.py @@ -78,18 +78,21 @@ class DHPublicNumbers(object): class DHParameterNumbers(object): - def __init__(self, p, g): + def __init__(self, p, g, q=None): if ( not isinstance(p, six.integer_types) or not isinstance(g, six.integer_types) ): raise TypeError("p and g must be integers") + if q is not None and not isinstance(q, six.integer_types): + raise TypeError("q must be integer or None") - if g not in (2, 5): + if q is None and g not in (2, 5): raise ValueError("DH generator must be 2 or 5") self._p = p self._g = g + self._q = q def __eq__(self, other): if not isinstance(other, DHParameterNumbers): @@ -97,7 +100,8 @@ class DHParameterNumbers(object): return ( self._p == other._p and - self._g == other._g + self._g == other._g and + self._q == other._q ) def __ne__(self, other): @@ -108,6 +112,7 @@ class DHParameterNumbers(object): p = utils.read_only_property("_p") g = utils.read_only_property("_g") + q = utils.read_only_property("_q") @six.add_metaclass(abc.ABCMeta) |