diff options
author | Alex Gaynor <alex.gaynor@gmail.com> | 2014-06-30 07:57:53 -0700 |
---|---|---|
committer | Alex Gaynor <alex.gaynor@gmail.com> | 2014-06-30 07:57:53 -0700 |
commit | d1eaebde366a8131c58d35946c37a739e0d5b79f (patch) | |
tree | f5ec2dd4500637c20a45e8f2ec6c845f84aeab9b | |
parent | 2d6e91f81266129c48ae775228a18d92c2d0f2c7 (diff) | |
parent | 64fe807ebe824937db47dc43ae69e8c206e0bda8 (diff) | |
download | cryptography-d1eaebde366a8131c58d35946c37a739e0d5b79f.tar.gz cryptography-d1eaebde366a8131c58d35946c37a739e0d5b79f.tar.bz2 cryptography-d1eaebde366a8131c58d35946c37a739e0d5b79f.zip |
Merge pull request #1204 from reaperhulk/split-commoncrypto-backend-ciphers
split commoncrypto backend - ciphers
-rw-r--r-- | cryptography/hazmat/backends/commoncrypto/backend.py | 173 | ||||
-rw-r--r-- | cryptography/hazmat/backends/commoncrypto/ciphers.py | 191 |
2 files changed, 194 insertions, 170 deletions
diff --git a/cryptography/hazmat/backends/commoncrypto/backend.py b/cryptography/hazmat/backends/commoncrypto/backend.py index 6147a996..f78370c4 100644 --- a/cryptography/hazmat/backends/commoncrypto/backend.py +++ b/cryptography/hazmat/backends/commoncrypto/backend.py @@ -16,8 +16,9 @@ from __future__ import absolute_import, division, print_function from collections import namedtuple from cryptography import utils -from cryptography.exceptions import ( - InternalError, InvalidTag, UnsupportedAlgorithm, _Reasons +from cryptography.exceptions import InternalError +from cryptography.hazmat.backends.commoncrypto.ciphers import ( + _CipherContext, _GCMCipherContext ) from cryptography.hazmat.backends.commoncrypto.hashes import _HashContext from cryptography.hazmat.backends.commoncrypto.hmac import _HMACContext @@ -25,7 +26,6 @@ from cryptography.hazmat.backends.interfaces import ( CipherBackend, HMACBackend, HashBackend, PBKDF2HMACBackend ) from cryptography.hazmat.bindings.commoncrypto.binding import Binding -from cryptography.hazmat.primitives import constant_time, interfaces from cryptography.hazmat.primitives.ciphers.algorithms import ( AES, ARC4, Blowfish, CAST5, TripleDES ) @@ -250,171 +250,4 @@ class Backend(object): ctx[0] = self._ffi.NULL -@utils.register_interface(interfaces.CipherContext) -class _CipherContext(object): - def __init__(self, backend, cipher, mode, operation): - self._backend = backend - self._cipher = cipher - self._mode = mode - self._operation = operation - # There is a bug in CommonCrypto where block ciphers do not raise - # kCCAlignmentError when finalizing if you supply non-block aligned - # data. To work around this we need to keep track of the block - # alignment ourselves, but only for alg+mode combos that require - # block alignment. OFB, CFB, and CTR make a block cipher algorithm - # into a stream cipher so we don't need to track them (and thus their - # block size is effectively 1 byte just like OpenSSL/CommonCrypto - # treat RC4 and other stream cipher block sizes). - # This bug has been filed as rdar://15589470 - self._bytes_processed = 0 - if (isinstance(cipher, interfaces.BlockCipherAlgorithm) and not - isinstance(mode, (OFB, CFB, CFB8, CTR))): - self._byte_block_size = cipher.block_size // 8 - else: - self._byte_block_size = 1 - - registry = self._backend._cipher_registry - try: - cipher_enum, mode_enum = 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 - ) - - ctx = self._backend._ffi.new("CCCryptorRef *") - ctx = self._backend._ffi.gc(ctx, self._backend._release_cipher_ctx) - - 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 - - if isinstance(mode, CTR): - mode_option = self._backend._lib.kCCModeOptionCTR_BE - else: - mode_option = 0 - - res = self._backend._lib.CCCryptorCreateWithMode( - operation, - mode_enum, cipher_enum, - self._backend._lib.ccNoPadding, iv_nonce, - cipher.key, len(cipher.key), - self._backend._ffi.NULL, 0, 0, mode_option, ctx) - self._backend._check_response(res) - - self._ctx = ctx - - def update(self, data): - # Count bytes processed to handle block alignment. - self._bytes_processed += len(data) - buf = self._backend._ffi.new( - "unsigned char[]", len(data) + self._byte_block_size - 1) - outlen = self._backend._ffi.new("size_t *") - res = self._backend._lib.CCCryptorUpdate( - self._ctx[0], data, len(data), buf, - len(data) + self._byte_block_size - 1, outlen) - self._backend._check_response(res) - return self._backend._ffi.buffer(buf)[:outlen[0]] - - def finalize(self): - # Raise error if block alignment is wrong. - if self._bytes_processed % self._byte_block_size: - raise ValueError( - "The length of the provided data is not a multiple of " - "the block length." - ) - buf = self._backend._ffi.new("unsigned char[]", self._byte_block_size) - outlen = self._backend._ffi.new("size_t *") - res = self._backend._lib.CCCryptorFinal( - self._ctx[0], buf, len(buf), outlen) - self._backend._check_response(res) - self._backend._release_cipher_ctx(self._ctx) - return self._backend._ffi.buffer(buf)[:outlen[0]] - - -@utils.register_interface(interfaces.AEADCipherContext) -@utils.register_interface(interfaces.AEADEncryptionContext) -class _GCMCipherContext(object): - def __init__(self, backend, cipher, mode, operation): - self._backend = backend - self._cipher = cipher - self._mode = mode - self._operation = operation - self._tag = None - - registry = self._backend._cipher_registry - try: - cipher_enum, mode_enum = 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 - ) - - ctx = self._backend._ffi.new("CCCryptorRef *") - ctx = self._backend._ffi.gc(ctx, self._backend._release_cipher_ctx) - - self._ctx = ctx - - res = self._backend._lib.CCCryptorCreateWithMode( - operation, - mode_enum, cipher_enum, - self._backend._lib.ccNoPadding, - self._backend._ffi.NULL, - cipher.key, len(cipher.key), - self._backend._ffi.NULL, 0, 0, 0, self._ctx) - self._backend._check_response(res) - - res = self._backend._lib.CCCryptorGCMAddIV( - self._ctx[0], - mode.initialization_vector, - len(mode.initialization_vector) - ) - self._backend._check_response(res) - - def update(self, data): - buf = self._backend._ffi.new("unsigned char[]", len(data)) - 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_response(res) - return self._backend._ffi.buffer(buf)[:] - - def finalize(self): - tag_size = self._cipher.block_size // 8 - tag_buf = self._backend._ffi.new("unsigned char[]", tag_size) - tag_len = self._backend._ffi.new("size_t *", tag_size) - res = self._backend._lib.CCCryptorGCMFinal( - self._ctx[0], tag_buf, tag_len) - self._backend._check_response(res) - self._backend._release_cipher_ctx(self._ctx) - self._tag = self._backend._ffi.buffer(tag_buf)[:] - if (self._operation == self._backend._lib.kCCDecrypt and - not constant_time.bytes_eq( - self._tag[:len(self._mode.tag)], self._mode.tag - )): - raise InvalidTag - return b"" - - def authenticate_additional_data(self, data): - res = self._backend._lib.CCCryptorGCMAddAAD( - self._ctx[0], data, len(data) - ) - self._backend._check_response(res) - - @property - def tag(self): - return self._tag - - backend = Backend() diff --git a/cryptography/hazmat/backends/commoncrypto/ciphers.py b/cryptography/hazmat/backends/commoncrypto/ciphers.py new file mode 100644 index 00000000..2820fd0e --- /dev/null +++ b/cryptography/hazmat/backends/commoncrypto/ciphers.py @@ -0,0 +1,191 @@ +# 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 constant_time, interfaces +from cryptography.hazmat.primitives.ciphers.modes import ( + CFB, CFB8, CTR, OFB +) + + +@utils.register_interface(interfaces.CipherContext) +class _CipherContext(object): + def __init__(self, backend, cipher, mode, operation): + self._backend = backend + self._cipher = cipher + self._mode = mode + self._operation = operation + # There is a bug in CommonCrypto where block ciphers do not raise + # kCCAlignmentError when finalizing if you supply non-block aligned + # data. To work around this we need to keep track of the block + # alignment ourselves, but only for alg+mode combos that require + # block alignment. OFB, CFB, and CTR make a block cipher algorithm + # into a stream cipher so we don't need to track them (and thus their + # block size is effectively 1 byte just like OpenSSL/CommonCrypto + # treat RC4 and other stream cipher block sizes). + # This bug has been filed as rdar://15589470 + self._bytes_processed = 0 + if (isinstance(cipher, interfaces.BlockCipherAlgorithm) and not + isinstance(mode, (OFB, CFB, CFB8, CTR))): + self._byte_block_size = cipher.block_size // 8 + else: + self._byte_block_size = 1 + + registry = self._backend._cipher_registry + try: + cipher_enum, mode_enum = 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 + ) + + ctx = self._backend._ffi.new("CCCryptorRef *") + ctx = self._backend._ffi.gc(ctx, self._backend._release_cipher_ctx) + + 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 + + if isinstance(mode, CTR): + mode_option = self._backend._lib.kCCModeOptionCTR_BE + else: + mode_option = 0 + + res = self._backend._lib.CCCryptorCreateWithMode( + operation, + mode_enum, cipher_enum, + self._backend._lib.ccNoPadding, iv_nonce, + cipher.key, len(cipher.key), + self._backend._ffi.NULL, 0, 0, mode_option, ctx) + self._backend._check_response(res) + + self._ctx = ctx + + def update(self, data): + # Count bytes processed to handle block alignment. + self._bytes_processed += len(data) + buf = self._backend._ffi.new( + "unsigned char[]", len(data) + self._byte_block_size - 1) + outlen = self._backend._ffi.new("size_t *") + res = self._backend._lib.CCCryptorUpdate( + self._ctx[0], data, len(data), buf, + len(data) + self._byte_block_size - 1, outlen) + self._backend._check_response(res) + return self._backend._ffi.buffer(buf)[:outlen[0]] + + def finalize(self): + # Raise error if block alignment is wrong. + if self._bytes_processed % self._byte_block_size: + raise ValueError( + "The length of the provided data is not a multiple of " + "the block length." + ) + buf = self._backend._ffi.new("unsigned char[]", self._byte_block_size) + outlen = self._backend._ffi.new("size_t *") + res = self._backend._lib.CCCryptorFinal( + self._ctx[0], buf, len(buf), outlen) + self._backend._check_response(res) + self._backend._release_cipher_ctx(self._ctx) + return self._backend._ffi.buffer(buf)[:outlen[0]] + + +@utils.register_interface(interfaces.AEADCipherContext) +@utils.register_interface(interfaces.AEADEncryptionContext) +class _GCMCipherContext(object): + def __init__(self, backend, cipher, mode, operation): + self._backend = backend + self._cipher = cipher + self._mode = mode + self._operation = operation + self._tag = None + + registry = self._backend._cipher_registry + try: + cipher_enum, mode_enum = 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 + ) + + ctx = self._backend._ffi.new("CCCryptorRef *") + ctx = self._backend._ffi.gc(ctx, self._backend._release_cipher_ctx) + + self._ctx = ctx + + res = self._backend._lib.CCCryptorCreateWithMode( + operation, + mode_enum, cipher_enum, + self._backend._lib.ccNoPadding, + self._backend._ffi.NULL, + cipher.key, len(cipher.key), + self._backend._ffi.NULL, 0, 0, 0, self._ctx) + self._backend._check_response(res) + + res = self._backend._lib.CCCryptorGCMAddIV( + self._ctx[0], + mode.initialization_vector, + len(mode.initialization_vector) + ) + self._backend._check_response(res) + + def update(self, data): + buf = self._backend._ffi.new("unsigned char[]", len(data)) + 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_response(res) + return self._backend._ffi.buffer(buf)[:] + + def finalize(self): + tag_size = self._cipher.block_size // 8 + tag_buf = self._backend._ffi.new("unsigned char[]", tag_size) + tag_len = self._backend._ffi.new("size_t *", tag_size) + res = self._backend._lib.CCCryptorGCMFinal( + self._ctx[0], tag_buf, tag_len + ) + self._backend._check_response(res) + self._backend._release_cipher_ctx(self._ctx) + self._tag = self._backend._ffi.buffer(tag_buf)[:] + if (self._operation == self._backend._lib.kCCDecrypt and + not constant_time.bytes_eq( + self._tag[:len(self._mode.tag)], self._mode.tag + )): + raise InvalidTag + return b"" + + def authenticate_additional_data(self, data): + res = self._backend._lib.CCCryptorGCMAddAAD( + self._ctx[0], data, len(data) + ) + self._backend._check_response(res) + + @property + def tag(self): + return self._tag |