diff options
Diffstat (limited to 'cryptography/hazmat')
-rw-r--r-- | cryptography/hazmat/backends/interfaces.py | 15 | ||||
-rw-r--r-- | cryptography/hazmat/backends/multibackend.py | 25 | ||||
-rw-r--r-- | cryptography/hazmat/backends/openssl/backend.py | 77 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/evp.py | 35 | ||||
-rw-r--r-- | cryptography/hazmat/bindings/openssl/ssl.py | 4 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/asymmetric/dsa.py | 164 | ||||
-rw-r--r-- | cryptography/hazmat/primitives/asymmetric/padding.py | 42 |
7 files changed, 340 insertions, 22 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 2a1ec439..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" @@ -126,16 +128,31 @@ class MultiBackend(object): def generate_rsa_private_key(self, public_exponent, key_size): for b in self._filtered_backends(RSABackend): return b.generate_rsa_private_key(public_exponent, key_size) - raise UnsupportedAlgorithm("RSA is not supported by the backend") + raise UnsupportedAlgorithm("RSA is not supported by the backend", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) def create_rsa_signature_ctx(self, private_key, padding, algorithm): for b in self._filtered_backends(RSABackend): return b.create_rsa_signature_ctx(private_key, padding, algorithm) - raise UnsupportedAlgorithm("RSA is not supported by the backend") + raise UnsupportedAlgorithm("RSA is not supported by the backend", + _Reasons.UNSUPPORTED_PUBLIC_KEY_ALGORITHM) def create_rsa_verification_ctx(self, public_key, signature, padding, algorithm): for b in self._filtered_backends(RSABackend): return b.create_rsa_verification_ctx(public_key, signature, padding, algorithm) - raise UnsupportedAlgorithm("RSA is not supported by the backend") + 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 753717d4..900d25c2 100644 --- a/cryptography/hazmat/backends/openssl/backend.py +++ b/cryptography/hazmat/backends/openssl/backend.py @@ -25,11 +25,12 @@ 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 -from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.asymmetric import dsa, rsa from cryptography.hazmat.primitives.asymmetric.padding import ( MGF1, PKCS1v15, PSS ) @@ -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) @@ -415,6 +417,52 @@ class Backend(object): 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") + + 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") + + 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, 0, + self._ffi.NULL, self._ffi.NULL, self._ffi.NULL + ) + + assert res == 1 + + return dsa.DSAParameters( + modulus=self._bn_to_int(ctx.p), + subgroup_order=self._bn_to_int(ctx.q), + generator=self._bn_to_int(ctx.g) + ) + + 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) + 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) + + 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) + ) + class GetCipherByName(object): def __init__(self, fmt): @@ -701,15 +749,20 @@ class _HMACContext(object): return self._backend._ffi.buffer(buf)[:outlen[0]] -def _get_rsa_pss_salt_length(mgf, key_size, digest_size): - if mgf._salt_length is MGF1.MAX_LENGTH: +def _get_rsa_pss_salt_length(pss, key_size, digest_size): + if pss._mgf._salt_length is not None: + salt = pss._mgf._salt_length + else: + salt = pss._salt_length + + if salt is MGF1.MAX_LENGTH or salt is PSS.MAX_LENGTH: # bit length - 1 per RFC 3447 emlen = int(math.ceil((key_size - 1) / 8.0)) salt_length = emlen - digest_size - 2 assert salt_length >= 0 return salt_length else: - return mgf._salt_length + return salt @utils.register_interface(interfaces.AsymmetricSignatureContext) @@ -731,7 +784,8 @@ class _RSASignatureContext(object): elif isinstance(padding, PSS): if not isinstance(padding._mgf, MGF1): raise UnsupportedAlgorithm( - "Only MGF1 is supported by this backend" + "Only MGF1 is supported by this backend", + _Reasons.UNSUPPORTED_MGF ) # Size of key in bytes - 2 is the maximum @@ -802,7 +856,7 @@ class _RSASignatureContext(object): res = self._backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen( pkey_ctx, _get_rsa_pss_salt_length( - self._padding._mgf, + self._padding, self._private_key.key_size, self._hash_ctx.algorithm.digest_size ) @@ -870,7 +924,7 @@ class _RSASignatureContext(object): data_to_sign, evp_md, _get_rsa_pss_salt_length( - self._padding._mgf, + self._padding, self._private_key.key_size, len(data_to_sign) ) @@ -915,7 +969,8 @@ class _RSAVerificationContext(object): elif isinstance(padding, PSS): if not isinstance(padding._mgf, MGF1): raise UnsupportedAlgorithm( - "Only MGF1 is supported by this backend" + "Only MGF1 is supported by this backend", + _Reasons.UNSUPPORTED_MGF ) # Size of key in bytes - 2 is the maximum @@ -986,7 +1041,7 @@ class _RSAVerificationContext(object): res = self._backend._lib.EVP_PKEY_CTX_set_rsa_pss_saltlen( pkey_ctx, _get_rsa_pss_salt_length( - self._padding._mgf, + self._padding, self._public_key.key_size, self._hash_ctx.algorithm.digest_size ) @@ -1066,7 +1121,7 @@ class _RSAVerificationContext(object): evp_md, buf, _get_rsa_pss_salt_length( - self._padding._mgf, + self._padding, self._public_key.key_size, len(data_to_verify) ) diff --git a/cryptography/hazmat/bindings/openssl/evp.py b/cryptography/hazmat/bindings/openssl/evp.py index ad4b568e..88cf5c34 100644 --- a/cryptography/hazmat/bindings/openssl/evp.py +++ b/cryptography/hazmat/bindings/openssl/evp.py @@ -141,6 +141,8 @@ int EVP_PKEY_sign(EVP_PKEY_CTX *, unsigned char *, size_t *, int EVP_PKEY_verify_init(EVP_PKEY_CTX *); int EVP_PKEY_verify(EVP_PKEY_CTX *, const unsigned char *, size_t, const unsigned char *, size_t); +int EVP_PKEY_encrypt_init(EVP_PKEY_CTX *); +int EVP_PKEY_decrypt_init(EVP_PKEY_CTX *); /* The following were macros in 0.9.8e. Once we drop support for RHEL/CentOS 5 we should move these back to FUNCTIONS. */ @@ -148,6 +150,14 @@ const EVP_CIPHER *EVP_CIPHER_CTX_cipher(const EVP_CIPHER_CTX *); int EVP_CIPHER_block_size(const EVP_CIPHER *); const EVP_MD *EVP_MD_CTX_md(const EVP_MD_CTX *); int EVP_MD_size(const EVP_MD *); + +/* Must be in macros because EVP_PKEY_CTX is undefined in 0.9.8 */ +int Cryptography_EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, + size_t *outlen, const unsigned char *in, + size_t inlen); +int Cryptography_EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, + size_t *outlen, const unsigned char *in, + size_t inlen); """ CUSTOMIZATIONS = """ @@ -162,6 +172,21 @@ const long EVP_CTRL_GCM_SET_IVLEN = -1; #if OPENSSL_VERSION_NUMBER >= 0x10000000L const long Cryptography_HAS_PBKDF2_HMAC = 1; const long Cryptography_HAS_PKEY_CTX = 1; + +/* OpenSSL 0.9.8 defines EVP_PKEY_encrypt and EVP_PKEY_decrypt functions, + but they are a completely different signature from the ones in 1.0.0+. + These wrapper functions allows us to safely declare them on any version and + conditionally remove them on 0.9.8. */ +int Cryptography_EVP_PKEY_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, + size_t *outlen, const unsigned char *in, + size_t inlen) { + return EVP_PKEY_encrypt(ctx, out, outlen, in, inlen); +} +int Cryptography_EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, + size_t *outlen, const unsigned char *in, + size_t inlen) { + return EVP_PKEY_decrypt(ctx, out, outlen, in, inlen); +} #else const long Cryptography_HAS_PBKDF2_HMAC = 0; int (*PKCS5_PBKDF2_HMAC)(const char *, int, const unsigned char *, int, int, @@ -179,6 +204,12 @@ EVP_PKEY_CTX *(*EVP_PKEY_CTX_new)(EVP_PKEY *, ENGINE *) = NULL; EVP_PKEY_CTX *(*EVP_PKEY_CTX_new_id)(int, ENGINE *) = NULL; EVP_PKEY_CTX *(*EVP_PKEY_CTX_dup)(EVP_PKEY_CTX *) = NULL; void (*EVP_PKEY_CTX_free)(EVP_PKEY_CTX *) = NULL; +int (*EVP_PKEY_encrypt_init)(EVP_PKEY_CTX *) = NULL; +int (*EVP_PKEY_decrypt_init)(EVP_PKEY_CTX *) = NULL; +int (*Cryptography_EVP_PKEY_encrypt)(EVP_PKEY_CTX *, unsigned char *, size_t *, + const unsigned char *, size_t) = NULL; +int (*Cryptography_EVP_PKEY_decrypt)(EVP_PKEY_CTX *, unsigned char *, size_t *, + const unsigned char *, size_t) = NULL; #endif """ @@ -200,6 +231,10 @@ CONDITIONAL_NAMES = { "EVP_PKEY_sign_init", "EVP_PKEY_verify", "EVP_PKEY_verify_init", + "Cryptography_EVP_PKEY_encrypt", + "EVP_PKEY_encrypt_init", + "Cryptography_EVP_PKEY_decrypt", + "EVP_PKEY_decrypt_init", "EVP_PKEY_CTX_set_signature_md", ] } diff --git a/cryptography/hazmat/bindings/openssl/ssl.py b/cryptography/hazmat/bindings/openssl/ssl.py index ad102769..094310f3 100644 --- a/cryptography/hazmat/bindings/openssl/ssl.py +++ b/cryptography/hazmat/bindings/openssl/ssl.py @@ -180,6 +180,7 @@ int SSL_pending(const SSL *); int SSL_write(SSL *, const void *, int); int SSL_read(SSL *, void *, int); X509 *SSL_get_peer_certificate(const SSL *); +int SSL_get_ex_data_X509_STORE_CTX_idx(void); Cryptography_STACK_OF_X509 *SSL_get_peer_cert_chain(const SSL *); Cryptography_STACK_OF_X509_NAME *SSL_get_client_CA_list(const SSL *); @@ -219,6 +220,9 @@ int X509_STORE_CTX_get_error(X509_STORE_CTX *); void X509_STORE_CTX_set_error(X509_STORE_CTX *, int); int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *); X509 *X509_STORE_CTX_get_current_cert(X509_STORE_CTX *); +int X509_STORE_CTX_set_ex_data(X509_STORE_CTX *, int, void *); +void *X509_STORE_CTX_get_ex_data(X509_STORE_CTX *, int); + /* SSL_SESSION */ void SSL_SESSION_free(SSL_SESSION *); diff --git a/cryptography/hazmat/primitives/asymmetric/dsa.py b/cryptography/hazmat/primitives/asymmetric/dsa.py new file mode 100644 index 00000000..4c2de36a --- /dev/null +++ b/cryptography/hazmat/primitives/asymmetric/dsa.py @@ -0,0 +1,164 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +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 + + +def _check_dsa_parameters(modulus, subgroup_order, generator): + if ( + not isinstance(modulus, six.integer_types) or + not isinstance(subgroup_order, six.integer_types) or + not isinstance(generator, six.integer_types) + ): + raise TypeError("DSA parameters must be integers") + + if (utils.bit_length(modulus), + utils.bit_length(subgroup_order)) not in ( + (1024, 160), + (2048, 256), + (3072, 256)): + raise ValueError("modulus and subgroup_order lengths must be " + "one of these pairs (1024, 160) or (2048, 256) " + "or (3072, 256)") + + if generator <= 1 or generator >= modulus: + raise ValueError("generator must be > 1 and < modulus") + + +@utils.register_interface(interfaces.DSAParameters) +class DSAParameters(object): + def __init__(self, modulus, subgroup_order, generator): + _check_dsa_parameters(modulus, subgroup_order, generator) + + self._modulus = modulus + self._subgroup_order = subgroup_order + self._generator = generator + + @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 + def modulus(self): + return self._modulus + + @property + def subgroup_order(self): + return self._subgroup_order + + @property + def generator(self): + return self._generator + + @property + def p(self): + return self.modulus + + @property + def q(self): + return self.subgroup_order + + @property + def g(self): + return self.generator + + +@utils.register_interface(interfaces.DSAPrivateKey) +class DSAPrivateKey(object): + def __init__(self, modulus, subgroup_order, generator, x, y): + _check_dsa_parameters(modulus, subgroup_order, generator) + if ( + not isinstance(x, six.integer_types) or + not isinstance(y, six.integer_types) + ): + raise TypeError("DSAPrivateKey arguments must be integers") + + if x <= 0 or x >= subgroup_order: + raise ValueError("x must be > 0 and < subgroup_order") + + if y != pow(generator, x, modulus): + raise ValueError("y must be equal to (generator ** x % modulus)") + + self._modulus = modulus + self._subgroup_order = subgroup_order + self._generator = generator + self._x = x + self._y = y + + @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 + def key_size(self): + return utils.bit_length(self._modulus) + + def public_key(self): + return DSAPublicKey(self._modulus, self._subgroup_order, + self._generator, self.y) + + @property + def x(self): + return self._x + + @property + def y(self): + return self._y + + def parameters(self): + return DSAParameters(self._modulus, self._subgroup_order, + self._generator) + + +@utils.register_interface(interfaces.DSAPublicKey) +class DSAPublicKey(object): + def __init__(self, modulus, subgroup_order, generator, y): + _check_dsa_parameters(modulus, subgroup_order, generator) + if not isinstance(y, six.integer_types): + raise TypeError("y must be an integer") + + self._modulus = modulus + self._subgroup_order = subgroup_order + self._generator = generator + self._y = y + + @property + def key_size(self): + return utils.bit_length(self._modulus) + + @property + def y(self): + return self._y + + def parameters(self): + return DSAParameters(self._modulus, self._subgroup_order, + self._generator) diff --git a/cryptography/hazmat/primitives/asymmetric/padding.py b/cryptography/hazmat/primitives/asymmetric/padding.py index 02aff280..72806a61 100644 --- a/cryptography/hazmat/primitives/asymmetric/padding.py +++ b/cryptography/hazmat/primitives/asymmetric/padding.py @@ -13,6 +13,8 @@ from __future__ import absolute_import, division, print_function +import warnings + import six from cryptography import utils @@ -26,26 +28,52 @@ class PKCS1v15(object): @utils.register_interface(interfaces.AsymmetricPadding) class PSS(object): + MAX_LENGTH = object() name = "EMSA-PSS" - def __init__(self, mgf): + def __init__(self, mgf, salt_length=None): self._mgf = mgf + if salt_length is None: + warnings.warn( + "salt_length is deprecated on MGF1 and should be added via the" + " PSS constructor.", + utils.DeprecatedIn04 + ) + else: + if (not isinstance(salt_length, six.integer_types) and + salt_length is not self.MAX_LENGTH): + raise TypeError("salt_length must be an integer") + + if salt_length is not self.MAX_LENGTH and salt_length < 0: + raise ValueError("salt_length must be zero or greater") + + if salt_length is None and self._mgf._salt_length is None: + raise ValueError("You must supply salt_length") + + self._salt_length = salt_length + class MGF1(object): MAX_LENGTH = object() - def __init__(self, algorithm, salt_length): + def __init__(self, algorithm, salt_length=None): if not isinstance(algorithm, interfaces.HashAlgorithm): raise TypeError("Expected instance of interfaces.HashAlgorithm.") self._algorithm = algorithm - if (not isinstance(salt_length, six.integer_types) and - salt_length is not self.MAX_LENGTH): - raise TypeError("salt_length must be an integer") + if salt_length is not None: + warnings.warn( + "salt_length is deprecated on MGF1 and should be passed to " + "the PSS constructor instead.", + utils.DeprecatedIn04 + ) + if (not isinstance(salt_length, six.integer_types) and + salt_length is not self.MAX_LENGTH): + raise TypeError("salt_length must be an integer") - if salt_length is not self.MAX_LENGTH and salt_length < 0: - raise ValueError("salt_length must be zero or greater") + if salt_length is not self.MAX_LENGTH and salt_length < 0: + raise ValueError("salt_length must be zero or greater") self._salt_length = salt_length |