aboutsummaryrefslogtreecommitdiffstats
path: root/cryptography/hazmat
diff options
context:
space:
mode:
Diffstat (limited to 'cryptography/hazmat')
-rw-r--r--cryptography/hazmat/backends/interfaces.py15
-rw-r--r--cryptography/hazmat/backends/multibackend.py25
-rw-r--r--cryptography/hazmat/backends/openssl/backend.py77
-rw-r--r--cryptography/hazmat/bindings/openssl/evp.py35
-rw-r--r--cryptography/hazmat/bindings/openssl/ssl.py4
-rw-r--r--cryptography/hazmat/primitives/asymmetric/dsa.py164
-rw-r--r--cryptography/hazmat/primitives/asymmetric/padding.py42
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