diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/cryptography/hazmat/backends/commoncrypto/ciphers.py | 36 | ||||
| -rw-r--r-- | src/cryptography/hazmat/backends/openssl/ciphers.py | 16 | ||||
| -rw-r--r-- | src/cryptography/hazmat/primitives/ciphers/base.py | 42 | ||||
| -rw-r--r-- | src/cryptography/utils.py | 7 | 
4 files changed, 99 insertions, 2 deletions
| diff --git a/src/cryptography/hazmat/backends/commoncrypto/ciphers.py b/src/cryptography/hazmat/backends/commoncrypto/ciphers.py index 1ce8aec5..b59381cb 100644 --- a/src/cryptography/hazmat/backends/commoncrypto/ciphers.py +++ b/src/cryptography/hazmat/backends/commoncrypto/ciphers.py @@ -86,6 +86,24 @@ class _CipherContext(object):          self._backend._check_cipher_response(res)          return self._backend._ffi.buffer(buf)[:outlen[0]] +    def update_into(self, data, buf): +        if len(buf) < (len(data) + self._byte_block_size - 1): +            raise ValueError( +                "buffer must be at least {0} bytes for this " +                "payload".format(len(data) + self._byte_block_size - 1) +            ) +        # Count bytes processed to handle block alignment. +        self._bytes_processed += len(data) +        outlen = self._backend._ffi.new("size_t *") +        buf = self._backend._ffi.cast( +            "unsigned char *", self._backend._ffi.from_buffer(buf) +        ) +        res = self._backend._lib.CCCryptorUpdate( +            self._ctx[0], data, len(data), buf, +            len(data) + self._byte_block_size - 1, outlen) +        self._backend._check_cipher_response(res) +        return outlen[0] +      def finalize(self):          # Raise error if block alignment is wrong.          if self._bytes_processed % self._byte_block_size: @@ -161,6 +179,24 @@ class _GCMCipherContext(object):          self._backend._check_cipher_response(res)          return self._backend._ffi.buffer(buf)[:] +    def update_into(self, data, buf): +        if len(buf) < len(data): +            raise ValueError( +                "buffer must be at least {0} bytes".format(len(data)) +            ) + +        buf = self._backend._ffi.cast( +            "unsigned char *", self._backend._ffi.from_buffer(buf) +        ) +        args = (self._ctx[0], data, len(data), buf) +        if self._operation == self._backend._lib.kCCEncrypt: +            res = self._backend._lib.CCCryptorGCMEncrypt(*args) +        else: +            res = self._backend._lib.CCCryptorGCMDecrypt(*args) + +        self._backend._check_cipher_response(res) +        return len(data) +      def finalize(self):          # CommonCrypto has a yet another bug where you must make at least one          # call to update. If you pass just AAD and call finalize without a call diff --git a/src/cryptography/hazmat/backends/openssl/ciphers.py b/src/cryptography/hazmat/backends/openssl/ciphers.py index 898b3497..0e0918af 100644 --- a/src/cryptography/hazmat/backends/openssl/ciphers.py +++ b/src/cryptography/hazmat/backends/openssl/ciphers.py @@ -109,6 +109,22 @@ class _CipherContext(object):          self._backend.openssl_assert(res != 0)          return self._backend._ffi.buffer(buf)[:outlen[0]] +    def update_into(self, data, buf): +        if len(buf) < (len(data) + self._block_size_bytes - 1): +            raise ValueError( +                "buffer must be at least {0} bytes for this " +                "payload".format(len(data) + self._block_size_bytes - 1) +            ) + +        buf = self._backend._ffi.cast( +            "unsigned char *", self._backend._ffi.from_buffer(buf) +        ) +        outlen = self._backend._ffi.new("int *") +        res = self._backend._lib.EVP_CipherUpdate(self._ctx, buf, outlen, +                                                  data, len(data)) +        self._backend.openssl_assert(res != 0) +        return outlen[0] +      def finalize(self):          # OpenSSL 1.0.1 on Ubuntu 12.04 (and possibly other distributions)          # appears to have a bug where you must make at least one call to update diff --git a/src/cryptography/hazmat/primitives/ciphers/base.py b/src/cryptography/hazmat/primitives/ciphers/base.py index 496975ae..502d9804 100644 --- a/src/cryptography/hazmat/primitives/ciphers/base.py +++ b/src/cryptography/hazmat/primitives/ciphers/base.py @@ -6,6 +6,8 @@ from __future__ import absolute_import, division, print_function  import abc +import cffi +  import six  from cryptography import utils @@ -51,6 +53,13 @@ class CipherContext(object):          """      @abc.abstractmethod +    def update_into(self, data, buf): +        """ +        Processes the provided bytes and writes the resulting data into the +        provided buffer. Returns the number of bytes written. +        """ + +    @abc.abstractmethod      def finalize(self):          """          Returns the results of processing the final block as bytes. @@ -136,6 +145,20 @@ class _CipherContext(object):              raise AlreadyFinalized("Context was already finalized.")          return self._ctx.update(data) +    # cffi 1.7 supports from_buffer on bytearray, which is required. We can +    # remove this check in the future when we raise our minimum PyPy version. +    if utils._version_check(cffi.__version__, "1.7"): +        def update_into(self, data, buf): +            if self._ctx is None: +                raise AlreadyFinalized("Context was already finalized.") +            return self._ctx.update_into(data, buf) +    else: +        def update_into(self, data, buf): +            raise NotImplementedError( +                "update_into requires cffi 1.7+. To use this method please " +                "update cffi." +            ) +      def finalize(self):          if self._ctx is None:              raise AlreadyFinalized("Context was already finalized.") @@ -154,11 +177,11 @@ class _AEADCipherContext(object):          self._tag = None          self._updated = False -    def update(self, data): +    def _check_limit(self, data_size):          if self._ctx is None:              raise AlreadyFinalized("Context was already finalized.")          self._updated = True -        self._bytes_processed += len(data) +        self._bytes_processed += data_size          if self._bytes_processed > self._ctx._mode._MAX_ENCRYPTED_BYTES:              raise ValueError(                  "{0} has a maximum encrypted byte limit of {1}".format( @@ -166,8 +189,23 @@ class _AEADCipherContext(object):                  )              ) +    def update(self, data): +        self._check_limit(len(data))          return self._ctx.update(data) +    # cffi 1.7 supports from_buffer on bytearray, which is required. We can +    # remove this check in the future when we raise our minimum PyPy version. +    if utils._version_check(cffi.__version__, "1.7"): +        def update_into(self, data, buf): +            self._check_limit(len(data)) +            return self._ctx.update_into(data, buf) +    else: +        def update_into(self, data, buf): +            raise NotImplementedError( +                "update_into requires cffi 1.7+. To use this method please " +                "update cffi." +            ) +      def finalize(self):          if self._ctx is None:              raise AlreadyFinalized("Context was already finalized.") diff --git a/src/cryptography/utils.py b/src/cryptography/utils.py index f16b7efa..8183bdaf 100644 --- a/src/cryptography/utils.py +++ b/src/cryptography/utils.py @@ -10,6 +10,8 @@ import inspect  import sys  import warnings +from packaging.version import parse +  # the functions deprecated in 1.0 and 1.4 are on an arbitrarily extended  # deprecation cycle and should not be removed until we agree on when that cycle @@ -98,6 +100,11 @@ else:          return len(bin(x)) - (2 + (x <= 0)) +def _version_check(version, required_version): +    # This is used to check if we support update_into on CipherContext. +    return parse(version) >= parse(required_version) + +  class _DeprecatedValue(object):      def __init__(self, value, message, warning_class):          self.value = value | 
