aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Gaynor <alex.gaynor@gmail.com>2014-06-25 07:05:11 -0700
committerAlex Gaynor <alex.gaynor@gmail.com>2014-06-25 07:05:11 -0700
commit42ee73b26e18fc9a03badd885a8a3847f3907cad (patch)
tree8eac892dc5072964f689f1bb49220e1f036dc78b
parentc1154d0aee0a5a61b3f3d364163da4be1ce2bbda (diff)
parent88dd94aac8f4195de462d333938e6eb7a9b8c633 (diff)
downloadcryptography-42ee73b26e18fc9a03badd885a8a3847f3907cad.tar.gz
cryptography-42ee73b26e18fc9a03badd885a8a3847f3907cad.tar.bz2
cryptography-42ee73b26e18fc9a03badd885a8a3847f3907cad.zip
Merge pull request #1177 from reaperhulk/split-openssl-backend-cipher
Split OpenSSL backend into separate files (cipher contexts)
-rw-r--r--cryptography/hazmat/backends/openssl/backend.py205
-rw-r--r--cryptography/hazmat/backends/openssl/ciphers.py219
2 files changed, 223 insertions, 201 deletions
diff --git a/cryptography/hazmat/backends/openssl/backend.py b/cryptography/hazmat/backends/openssl/backend.py
index 183f29f7..d08cd3e4 100644
--- a/cryptography/hazmat/backends/openssl/backend.py
+++ b/cryptography/hazmat/backends/openssl/backend.py
@@ -21,13 +21,16 @@ import six
from cryptography import utils
from cryptography.exceptions import (
- InternalError, InvalidTag, UnsupportedAlgorithm, _Reasons
+ InternalError, UnsupportedAlgorithm, _Reasons
)
from cryptography.hazmat.backends.interfaces import (
CMACBackend, CipherBackend, DSABackend, EllipticCurveBackend, HMACBackend,
HashBackend, PBKDF2HMACBackend, PKCS8SerializationBackend, RSABackend,
TraditionalOpenSSLSerializationBackend
)
+from cryptography.hazmat.backends.openssl.ciphers import (
+ _AESCTRCipherContext, _CipherContext
+)
from cryptography.hazmat.backends.openssl.dsa import (
_DSASignatureContext, _DSAVerificationContext
)
@@ -1168,206 +1171,6 @@ class GetCipherByName(object):
return backend._lib.EVP_get_cipherbyname(cipher_name.encode("ascii"))
-@utils.register_interface(interfaces.CipherContext)
-@utils.register_interface(interfaces.AEADCipherContext)
-@utils.register_interface(interfaces.AEADEncryptionContext)
-class _CipherContext(object):
- _ENCRYPT = 1
- _DECRYPT = 0
-
- def __init__(self, backend, cipher, mode, operation):
- self._backend = backend
- self._cipher = cipher
- self._mode = mode
- self._operation = operation
- self._tag = None
-
- if isinstance(self._cipher, interfaces.BlockCipherAlgorithm):
- self._block_size = self._cipher.block_size
- else:
- self._block_size = 1
-
- ctx = self._backend._lib.EVP_CIPHER_CTX_new()
- ctx = self._backend._ffi.gc(
- ctx, self._backend._lib.EVP_CIPHER_CTX_free
- )
-
- registry = self._backend._cipher_registry
- try:
- adapter = registry[type(cipher), type(mode)]
- except KeyError:
- raise UnsupportedAlgorithm(
- "cipher {0} in {1} mode is not supported "
- "by this backend.".format(
- cipher.name, mode.name if mode else mode),
- _Reasons.UNSUPPORTED_CIPHER
- )
-
- evp_cipher = adapter(self._backend, cipher, mode)
- if evp_cipher == self._backend._ffi.NULL:
- raise UnsupportedAlgorithm(
- "cipher {0} in {1} mode is not supported "
- "by this backend.".format(
- cipher.name, mode.name if mode else mode),
- _Reasons.UNSUPPORTED_CIPHER
- )
-
- if isinstance(mode, interfaces.ModeWithInitializationVector):
- iv_nonce = mode.initialization_vector
- elif isinstance(mode, interfaces.ModeWithNonce):
- iv_nonce = mode.nonce
- else:
- iv_nonce = self._backend._ffi.NULL
- # begin init with cipher and operation type
- res = self._backend._lib.EVP_CipherInit_ex(ctx, evp_cipher,
- self._backend._ffi.NULL,
- self._backend._ffi.NULL,
- self._backend._ffi.NULL,
- operation)
- assert res != 0
- # set the key length to handle variable key ciphers
- res = self._backend._lib.EVP_CIPHER_CTX_set_key_length(
- ctx, len(cipher.key)
- )
- assert res != 0
- if isinstance(mode, GCM):
- res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
- ctx, self._backend._lib.EVP_CTRL_GCM_SET_IVLEN,
- len(iv_nonce), self._backend._ffi.NULL
- )
- assert res != 0
- if operation == self._DECRYPT:
- res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
- ctx, self._backend._lib.EVP_CTRL_GCM_SET_TAG,
- len(mode.tag), mode.tag
- )
- assert res != 0
-
- # pass key/iv
- res = self._backend._lib.EVP_CipherInit_ex(
- ctx,
- self._backend._ffi.NULL,
- self._backend._ffi.NULL,
- cipher.key,
- iv_nonce,
- operation
- )
- assert res != 0
- # We purposely disable padding here as it's handled higher up in the
- # API.
- self._backend._lib.EVP_CIPHER_CTX_set_padding(ctx, 0)
- self._ctx = ctx
-
- def update(self, data):
- # OpenSSL 0.9.8e has an assertion in its EVP code that causes it
- # to SIGABRT if you call update with an empty byte string. This can be
- # removed when we drop support for 0.9.8e (CentOS/RHEL 5). This branch
- # should be taken only when length is zero and mode is not GCM because
- # AES GCM can return improper tag values if you don't call update
- # with empty plaintext when authenticating AAD for ...reasons.
- if len(data) == 0 and not isinstance(self._mode, GCM):
- return b""
-
- buf = self._backend._ffi.new("unsigned char[]",
- len(data) + self._block_size - 1)
- outlen = self._backend._ffi.new("int *")
- res = self._backend._lib.EVP_CipherUpdate(self._ctx, buf, outlen, data,
- len(data))
- assert res != 0
- return self._backend._ffi.buffer(buf)[:outlen[0]]
-
- def finalize(self):
- buf = self._backend._ffi.new("unsigned char[]", self._block_size)
- outlen = self._backend._ffi.new("int *")
- res = self._backend._lib.EVP_CipherFinal_ex(self._ctx, buf, outlen)
- if res == 0:
- errors = self._backend._consume_errors()
-
- if not errors and isinstance(self._mode, GCM):
- raise InvalidTag
-
- assert errors
-
- if errors[0][1:] == (
- self._backend._lib.ERR_LIB_EVP,
- self._backend._lib.EVP_F_EVP_ENCRYPTFINAL_EX,
- self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
- ) or errors[0][1:] == (
- self._backend._lib.ERR_LIB_EVP,
- self._backend._lib.EVP_F_EVP_DECRYPTFINAL_EX,
- self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
- ):
- raise ValueError(
- "The length of the provided data is not a multiple of "
- "the block length."
- )
- else:
- raise self._backend._unknown_error(errors[0])
-
- if (isinstance(self._mode, GCM) and
- self._operation == self._ENCRYPT):
- block_byte_size = self._block_size // 8
- tag_buf = self._backend._ffi.new(
- "unsigned char[]", block_byte_size
- )
- res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
- self._ctx, self._backend._lib.EVP_CTRL_GCM_GET_TAG,
- block_byte_size, tag_buf
- )
- assert res != 0
- self._tag = self._backend._ffi.buffer(tag_buf)[:]
-
- res = self._backend._lib.EVP_CIPHER_CTX_cleanup(self._ctx)
- assert res == 1
- return self._backend._ffi.buffer(buf)[:outlen[0]]
-
- def authenticate_additional_data(self, data):
- outlen = self._backend._ffi.new("int *")
- res = self._backend._lib.EVP_CipherUpdate(
- self._ctx, self._backend._ffi.NULL, outlen, data, len(data)
- )
- assert res != 0
-
- @property
- def tag(self):
- return self._tag
-
-
-@utils.register_interface(interfaces.CipherContext)
-class _AESCTRCipherContext(object):
- """
- This is needed to provide support for AES CTR mode in OpenSSL 0.9.8. It can
- be removed when we drop 0.9.8 support (RHEL5 extended life ends 2020).
- """
- def __init__(self, backend, cipher, mode):
- self._backend = backend
-
- self._key = self._backend._ffi.new("AES_KEY *")
- assert self._key != self._backend._ffi.NULL
- res = self._backend._lib.AES_set_encrypt_key(
- cipher.key, len(cipher.key) * 8, self._key
- )
- assert res == 0
- self._ecount = self._backend._ffi.new("char[]", 16)
- self._nonce = self._backend._ffi.new("char[16]", mode.nonce)
- self._num = self._backend._ffi.new("unsigned int *", 0)
-
- def update(self, data):
- buf = self._backend._ffi.new("unsigned char[]", len(data))
- self._backend._lib.AES_ctr128_encrypt(
- data, buf, len(data), self._key, self._nonce,
- self._ecount, self._num
- )
- return self._backend._ffi.buffer(buf)[:]
-
- def finalize(self):
- self._key = None
- self._ecount = None
- self._nonce = None
- self._num = None
- return b""
-
-
@utils.register_interface(interfaces.HashContext)
class _HashContext(object):
def __init__(self, backend, algorithm, ctx=None):
diff --git a/cryptography/hazmat/backends/openssl/ciphers.py b/cryptography/hazmat/backends/openssl/ciphers.py
new file mode 100644
index 00000000..c3a5499a
--- /dev/null
+++ b/cryptography/hazmat/backends/openssl/ciphers.py
@@ -0,0 +1,219 @@
+# 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
+
+from cryptography import utils
+from cryptography.exceptions import InvalidTag, UnsupportedAlgorithm, _Reasons
+from cryptography.hazmat.primitives import interfaces
+from cryptography.hazmat.primitives.ciphers.modes import GCM
+
+
+@utils.register_interface(interfaces.CipherContext)
+@utils.register_interface(interfaces.AEADCipherContext)
+@utils.register_interface(interfaces.AEADEncryptionContext)
+class _CipherContext(object):
+ _ENCRYPT = 1
+ _DECRYPT = 0
+
+ def __init__(self, backend, cipher, mode, operation):
+ self._backend = backend
+ self._cipher = cipher
+ self._mode = mode
+ self._operation = operation
+ self._tag = None
+
+ if isinstance(self._cipher, interfaces.BlockCipherAlgorithm):
+ self._block_size = self._cipher.block_size
+ else:
+ self._block_size = 1
+
+ ctx = self._backend._lib.EVP_CIPHER_CTX_new()
+ ctx = self._backend._ffi.gc(
+ ctx, self._backend._lib.EVP_CIPHER_CTX_free
+ )
+
+ registry = self._backend._cipher_registry
+ try:
+ adapter = registry[type(cipher), type(mode)]
+ except KeyError:
+ raise UnsupportedAlgorithm(
+ "cipher {0} in {1} mode is not supported "
+ "by this backend.".format(
+ cipher.name, mode.name if mode else mode),
+ _Reasons.UNSUPPORTED_CIPHER
+ )
+
+ evp_cipher = adapter(self._backend, cipher, mode)
+ if evp_cipher == self._backend._ffi.NULL:
+ raise UnsupportedAlgorithm(
+ "cipher {0} in {1} mode is not supported "
+ "by this backend.".format(
+ cipher.name, mode.name if mode else mode),
+ _Reasons.UNSUPPORTED_CIPHER
+ )
+
+ if isinstance(mode, interfaces.ModeWithInitializationVector):
+ iv_nonce = mode.initialization_vector
+ elif isinstance(mode, interfaces.ModeWithNonce):
+ iv_nonce = mode.nonce
+ else:
+ iv_nonce = self._backend._ffi.NULL
+ # begin init with cipher and operation type
+ res = self._backend._lib.EVP_CipherInit_ex(ctx, evp_cipher,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ operation)
+ assert res != 0
+ # set the key length to handle variable key ciphers
+ res = self._backend._lib.EVP_CIPHER_CTX_set_key_length(
+ ctx, len(cipher.key)
+ )
+ assert res != 0
+ if isinstance(mode, GCM):
+ res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
+ ctx, self._backend._lib.EVP_CTRL_GCM_SET_IVLEN,
+ len(iv_nonce), self._backend._ffi.NULL
+ )
+ assert res != 0
+ if operation == self._DECRYPT:
+ res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
+ ctx, self._backend._lib.EVP_CTRL_GCM_SET_TAG,
+ len(mode.tag), mode.tag
+ )
+ assert res != 0
+
+ # pass key/iv
+ res = self._backend._lib.EVP_CipherInit_ex(
+ ctx,
+ self._backend._ffi.NULL,
+ self._backend._ffi.NULL,
+ cipher.key,
+ iv_nonce,
+ operation
+ )
+ assert res != 0
+ # We purposely disable padding here as it's handled higher up in the
+ # API.
+ self._backend._lib.EVP_CIPHER_CTX_set_padding(ctx, 0)
+ self._ctx = ctx
+
+ def update(self, data):
+ # OpenSSL 0.9.8e has an assertion in its EVP code that causes it
+ # to SIGABRT if you call update with an empty byte string. This can be
+ # removed when we drop support for 0.9.8e (CentOS/RHEL 5). This branch
+ # should be taken only when length is zero and mode is not GCM because
+ # AES GCM can return improper tag values if you don't call update
+ # with empty plaintext when authenticating AAD for ...reasons.
+ if len(data) == 0 and not isinstance(self._mode, GCM):
+ return b""
+
+ buf = self._backend._ffi.new("unsigned char[]",
+ len(data) + self._block_size - 1)
+ outlen = self._backend._ffi.new("int *")
+ res = self._backend._lib.EVP_CipherUpdate(self._ctx, buf, outlen, data,
+ len(data))
+ assert res != 0
+ return self._backend._ffi.buffer(buf)[:outlen[0]]
+
+ def finalize(self):
+ buf = self._backend._ffi.new("unsigned char[]", self._block_size)
+ outlen = self._backend._ffi.new("int *")
+ res = self._backend._lib.EVP_CipherFinal_ex(self._ctx, buf, outlen)
+ if res == 0:
+ errors = self._backend._consume_errors()
+
+ if not errors and isinstance(self._mode, GCM):
+ raise InvalidTag
+
+ assert errors
+
+ if errors[0][1:] == (
+ self._backend._lib.ERR_LIB_EVP,
+ self._backend._lib.EVP_F_EVP_ENCRYPTFINAL_EX,
+ self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
+ ) or errors[0][1:] == (
+ self._backend._lib.ERR_LIB_EVP,
+ self._backend._lib.EVP_F_EVP_DECRYPTFINAL_EX,
+ self._backend._lib.EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH
+ ):
+ raise ValueError(
+ "The length of the provided data is not a multiple of "
+ "the block length."
+ )
+ else:
+ raise self._backend._unknown_error(errors[0])
+
+ if (isinstance(self._mode, GCM) and
+ self._operation == self._ENCRYPT):
+ block_byte_size = self._block_size // 8
+ tag_buf = self._backend._ffi.new(
+ "unsigned char[]", block_byte_size
+ )
+ res = self._backend._lib.EVP_CIPHER_CTX_ctrl(
+ self._ctx, self._backend._lib.EVP_CTRL_GCM_GET_TAG,
+ block_byte_size, tag_buf
+ )
+ assert res != 0
+ self._tag = self._backend._ffi.buffer(tag_buf)[:]
+
+ res = self._backend._lib.EVP_CIPHER_CTX_cleanup(self._ctx)
+ assert res == 1
+ return self._backend._ffi.buffer(buf)[:outlen[0]]
+
+ def authenticate_additional_data(self, data):
+ outlen = self._backend._ffi.new("int *")
+ res = self._backend._lib.EVP_CipherUpdate(
+ self._ctx, self._backend._ffi.NULL, outlen, data, len(data)
+ )
+ assert res != 0
+
+ @property
+ def tag(self):
+ return self._tag
+
+
+@utils.register_interface(interfaces.CipherContext)
+class _AESCTRCipherContext(object):
+ """
+ This is needed to provide support for AES CTR mode in OpenSSL 0.9.8. It can
+ be removed when we drop 0.9.8 support (RHEL5 extended life ends 2020).
+ """
+ def __init__(self, backend, cipher, mode):
+ self._backend = backend
+
+ self._key = self._backend._ffi.new("AES_KEY *")
+ assert self._key != self._backend._ffi.NULL
+ res = self._backend._lib.AES_set_encrypt_key(
+ cipher.key, len(cipher.key) * 8, self._key
+ )
+ assert res == 0
+ self._ecount = self._backend._ffi.new("char[]", 16)
+ self._nonce = self._backend._ffi.new("char[16]", mode.nonce)
+ self._num = self._backend._ffi.new("unsigned int *", 0)
+
+ def update(self, data):
+ buf = self._backend._ffi.new("unsigned char[]", len(data))
+ self._backend._lib.AES_ctr128_encrypt(
+ data, buf, len(data), self._key, self._nonce,
+ self._ecount, self._num
+ )
+ return self._backend._ffi.buffer(buf)[:]
+
+ def finalize(self):
+ self._key = None
+ self._ecount = None
+ self._nonce = None
+ self._num = None
+ return b""