From f90802efa1e080cbe7d9abb7eded2451081edb4f Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 15 Jul 2023 13:08:03 -1000 Subject: [PATCH 01/12] feat: speed up implementation - drop functions that are only called once - avoid calling into the openssl_assert when nothing is wrong --- src/chacha20poly1305_reuseable/__init__.pxd | 17 +-- src/chacha20poly1305_reuseable/__init__.py | 161 +++++++++----------- 2 files changed, 77 insertions(+), 101 deletions(-) diff --git a/src/chacha20poly1305_reuseable/__init__.pxd b/src/chacha20poly1305_reuseable/__init__.pxd index 3f67c0a..662e6be 100644 --- a/src/chacha20poly1305_reuseable/__init__.pxd +++ b/src/chacha20poly1305_reuseable/__init__.pxd @@ -6,7 +6,7 @@ cdef object _ENCRYPT cdef object _DECRYPT cdef object InvalidTag -cdef object openssl_assert +cdef object openssl_failure cdef object NULL cdef object EVP_CIPHER_CTX_ctrl @@ -33,19 +33,6 @@ cdef _check_params( object associated_data ) -cdef _create_ctx() - - -cdef _set_cipher(object ctx, object cipher_name, object operation) - -cdef _set_key_len(object ctx, object key_len) - -cdef _set_key(object ctx, object key, object operation) - -cdef _set_decrypt_tag(object ctx, object tag) - -cdef _set_nonce_len(object ctx, object nonce_len) - cdef _set_nonce(object ctx, object nonce, object operation) cdef _aead_setup_with_fixed_nonce_len(object cipher_name, object key, object nonce_len, object operation) @@ -69,8 +56,6 @@ cdef _encrypt_data( object tag_length ) -cdef _tag_from_data(object data, object tag_length) - cdef _decrypt_with_fixed_nonce_len( object ctx, object nonce, diff --git a/src/chacha20poly1305_reuseable/__init__.py b/src/chacha20poly1305_reuseable/__init__.py index 01f5596..54d976c 100644 --- a/src/chacha20poly1305_reuseable/__init__.py +++ b/src/chacha20poly1305_reuseable/__init__.py @@ -7,6 +7,7 @@ import os import typing +from functools import partial from typing import Optional, Union from cryptography import exceptions @@ -14,7 +15,7 @@ from cryptography.hazmat.backends.openssl.backend import backend from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305 -openssl_assert = backend.openssl_assert +openssl_failure = partial(backend.openssl_assert, False) EVP_CIPHER_CTX_ctrl = backend._lib.EVP_CIPHER_CTX_ctrl EVP_CTRL_AEAD_SET_TAG = backend._lib.EVP_CTRL_AEAD_SET_TAG EVP_CTRL_AEAD_SET_IVLEN = backend._lib.EVP_CTRL_AEAD_SET_IVLEN @@ -150,94 +151,85 @@ def decrypt( ) -def _create_ctx() -> object: - ctx = EVP_CIPHER_CTX_new() - ctx = ffi_gc(ctx, EVP_CIPHER_CTX_free) - return ctx - - -def _set_cipher(ctx: object, cipher_name: _bytes, operation: int) -> None: - evp_cipher = EVP_get_cipherbyname(cipher_name) - openssl_assert(evp_cipher != NULL) - res = EVP_CipherInit_ex( - ctx, - evp_cipher, - NULL, - NULL, - NULL, - int(operation == _ENCRYPT), - ) - openssl_assert(res != 0) - - -def _set_key_len(ctx: object, key_len: int) -> None: - res = EVP_CIPHER_CTX_set_key_length(ctx, key_len) - openssl_assert(res != 0) - - -def _set_key(ctx: object, key: _bytes, operation: int) -> None: - key_ptr = ffi_from_buffer(key) - res = EVP_CipherInit_ex( - ctx, - NULL, - NULL, - key_ptr, - NULL, - int(operation == _ENCRYPT), - ) - openssl_assert(res != 0) - - -def _set_decrypt_tag(ctx: object, tag: _bytes) -> None: - res = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, len(tag), tag) - openssl_assert(res != 0) - - -def _set_nonce_len(ctx: object, nonce_len: int) -> None: - res = EVP_CIPHER_CTX_ctrl( - ctx, - EVP_CTRL_AEAD_SET_IVLEN, - nonce_len, - NULL, - ) - openssl_assert(res != 0) - - def _set_nonce(ctx: object, nonce: Union[_bytes, bytearray], operation: int) -> None: nonce_ptr = ffi_from_buffer(nonce) - res = EVP_CipherInit_ex( - ctx, - NULL, - NULL, - NULL, - nonce_ptr, - int(operation == _ENCRYPT), - ) - openssl_assert(res != 0) + if ( + EVP_CipherInit_ex( + ctx, + NULL, + NULL, + NULL, + nonce_ptr, + int(operation == _ENCRYPT), + ) + == 0 + ): + openssl_failure() def _aead_setup_with_fixed_nonce_len( cipher_name: _bytes, key: Union[_bytes, bytearray], nonce_len: int, operation: int ) -> object: - ctx = _create_ctx() - _set_cipher(ctx, cipher_name, operation) - _set_key_len(ctx, len(key)) - _set_key(ctx, key, operation) - _set_nonce_len(ctx, nonce_len) + # create the ctx + ctx = EVP_CIPHER_CTX_new() + ctx = ffi_gc(ctx, EVP_CIPHER_CTX_free) + # set the cipher + evp_cipher = EVP_get_cipherbyname(cipher_name) + if evp_cipher == NULL: + openssl_failure() + if ( + EVP_CipherInit_ex( + ctx, + evp_cipher, + NULL, + NULL, + NULL, + int(operation == _ENCRYPT), + ) + == 0 + ): + openssl_failure() + # Set the key length + if EVP_CIPHER_CTX_set_key_length(ctx, len(key)) == 0: + openssl_failure() + # Set the key + if ( + EVP_CipherInit_ex( + ctx, + NULL, + NULL, + ffi_from_buffer(key), + NULL, + int(operation == _ENCRYPT), + ) + == 0 + ): + openssl_failure() + # set nonce length + if ( + EVP_CIPHER_CTX_ctrl( + ctx, + EVP_CTRL_AEAD_SET_IVLEN, + nonce_len, + NULL, + ) + == 0 + ): + openssl_failure() return ctx def _process_aad(ctx: object, associated_data: _bytes) -> None: outlen = ffi_new("int *") - res = EVP_CipherUpdate(ctx, NULL, outlen, associated_data, len(associated_data)) - openssl_assert(res != 0) + if EVP_CipherUpdate(ctx, NULL, outlen, associated_data, len(associated_data)) == 0: + openssl_failure() def _process_data(ctx: object, data: _bytes) -> _bytes: outlen = ffi_new("int *") buf = ffi_new("unsigned char[]", len(data)) - res = EVP_CipherUpdate(ctx, buf, outlen, data, len(data)) - openssl_assert(res != 0) + if EVP_CipherUpdate(ctx, buf, outlen, data, len(data)) == 0: + openssl_failure() return ffi_buffer(buf, outlen[0])[:] @@ -258,23 +250,18 @@ def _encrypt_data( _process_aad(ctx, associated_data) processed_data = _process_data(ctx, data) outlen = ffi_new("int *") - res = EVP_CipherFinal_ex(ctx, NULL, outlen) - openssl_assert(res != 0) - openssl_assert(outlen[0] == 0) + if EVP_CipherFinal_ex(ctx, NULL, outlen) == 0: + openssl_failure() + if outlen[0] != 0: + openssl_failure() tag_buf = ffi_new("unsigned char[]", tag_length) - res = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_length, tag_buf) - openssl_assert(res != 0) + if EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_length, tag_buf) == 0: + openssl_failure() tag = ffi_buffer(tag_buf)[:] return processed_data + tag -def _tag_from_data(data: _bytes, tag_length: int) -> _bytes: - if len(data) < tag_length: - raise InvalidTag - return data[-tag_length:] - - def _decrypt_with_fixed_nonce_len( ctx: object, nonce: Union[_bytes, bytearray], @@ -282,10 +269,14 @@ def _decrypt_with_fixed_nonce_len( associated_data: _bytes, tag_length: int, ) -> bytes: - tag = _tag_from_data(data, tag_length) + if len(data) < tag_length: + raise InvalidTag + tag = data[-tag_length:] data = data[:-tag_length] _set_nonce(ctx, nonce, _DECRYPT) - _set_decrypt_tag(ctx, tag) + # set the decrypted tag + if EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, len(tag), tag) == 0: + openssl_failure() return _decrypt_data(ctx, data, associated_data) From c90c4f3baccf7fc323f4450165f1971b5101f360 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 15 Jul 2023 13:20:32 -1000 Subject: [PATCH 02/12] feat: speed up implementation - drop functions that are only called once - avoid calling into the openssl_assert when nothing is wrong --- src/chacha20poly1305_reuseable/__init__.pxd | 9 +++- src/chacha20poly1305_reuseable/__init__.py | 50 +++++++++++++-------- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/src/chacha20poly1305_reuseable/__init__.pxd b/src/chacha20poly1305_reuseable/__init__.pxd index 662e6be..533c8aa 100644 --- a/src/chacha20poly1305_reuseable/__init__.pxd +++ b/src/chacha20poly1305_reuseable/__init__.pxd @@ -26,8 +26,15 @@ cdef object ffi_new cdef object ffi_from_buffer cdef object ffi_buffer +cdef object MAX_SIZE +cdef object KEY_LEN +cdef object NONCE_LEN +cdef cython.uint NONCE_LEN_UINT +cdef object TAG_LENGTH +cdef object CIPHER_NAME + cdef _check_params( - object nonce_len, + cython.uint nonce_len, object nonce, object data, object associated_data diff --git a/src/chacha20poly1305_reuseable/__init__.py b/src/chacha20poly1305_reuseable/__init__.py index 54d976c..1e37daa 100644 --- a/src/chacha20poly1305_reuseable/__init__.py +++ b/src/chacha20poly1305_reuseable/__init__.py @@ -57,6 +57,14 @@ def _check_params( raise ValueError("Nonce must be 12 bytes") +MAX_SIZE = 2**32 +KEY_LEN = 32 +NONCE_LEN = 12 +NONCE_LEN_UINT = NONCE_LEN +TAG_LENGTH = 16 +CIPHER_NAME = b"chacha20-poly1305" + + class ChaCha20Poly1305Reusable(ChaCha20Poly1305): """A reuseable version of ChaCha20Poly1305. @@ -67,10 +75,10 @@ class ChaCha20Poly1305Reusable(ChaCha20Poly1305): The primary use case for this code is HAP streams. """ - _MAX_SIZE = 2**32 - _KEY_LEN = 32 - _NONCE_LEN = 12 - _TAG_LENGTH = 16 + _MAX_SIZE = MAX_SIZE + _KEY_LEN = KEY_LEN + _NONCE_LEN = NONCE_LEN + _TAG_LENGTH = TAG_LENGTH def __init__(self, key: Union[_bytes, bytearray]) -> None: if not backend.aead_cipher_supported(self): @@ -82,10 +90,10 @@ def __init__(self, key: Union[_bytes, bytearray]) -> None: if not isinstance(key, (bytes, bytearray)): raise TypeError("key must be bytes or bytearay") - if len(key) != self._KEY_LEN: + if len(key) != KEY_LEN: raise ValueError("ChaCha20Poly1305Reusable key must be 32 bytes.") - self._cipher_name = b"chacha20-poly1305" + self._cipher_name = CIPHER_NAME self._key = key self._decrypt_ctx: Optional[object] = None self._encrypt_ctx: Optional[object] = None @@ -100,28 +108,30 @@ def encrypt( data: _bytes, associated_data: typing.Optional[bytes], ) -> bytes: - if not self._encrypt_ctx: + encrypt_ctx = self._encrypt_ctx + if encrypt_ctx: self._encrypt_ctx = _aead_setup_with_fixed_nonce_len( - self._cipher_name, + CIPHER_NAME, self._key, - self._NONCE_LEN, + NONCE_LEN, _ENCRYPT, ) + encrypt_ctx = self._encrypt_ctx if associated_data is None: associated_data = b"" - if len(data) > self._MAX_SIZE or len(associated_data) > self._MAX_SIZE: + if len(data) > MAX_SIZE or len(associated_data) > MAX_SIZE: # This is OverflowError to match what cffi would raise raise OverflowError("Data or associated data too long. Max 2**32 bytes") - _check_params(self._NONCE_LEN, nonce, data, associated_data) + _check_params(NONCE_LEN_UINT, nonce, data, associated_data) return _encrypt_with_fixed_nonce_len( - self._encrypt_ctx, + encrypt_ctx, nonce, data, associated_data, - self._TAG_LENGTH, + TAG_LENGTH, ) def decrypt( @@ -130,24 +140,26 @@ def decrypt( data: _bytes, associated_data: typing.Optional[_bytes], ) -> bytes: - if not self._decrypt_ctx: + decrypt_ctx = self._decrypt_ctx + if not decrypt_ctx: self._decrypt_ctx = _aead_setup_with_fixed_nonce_len( - self._cipher_name, + CIPHER_NAME, self._key, - self._NONCE_LEN, + NONCE_LEN, _DECRYPT, ) + decrypt_ctx = self._decrypt_ctx if associated_data is None: associated_data = b"" - _check_params(self._NONCE_LEN, nonce, data, associated_data) + _check_params(NONCE_LEN_UINT, nonce, data, associated_data) return _decrypt_with_fixed_nonce_len( - self._decrypt_ctx, + decrypt_ctx, nonce, data, associated_data, - self._TAG_LENGTH, + TAG_LENGTH, ) From a177ba31e58619e310314c72ac514a4771416133 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 15 Jul 2023 13:22:38 -1000 Subject: [PATCH 03/12] feat: speed up implementation - drop functions that are only called once - avoid calling into the openssl_assert when nothing is wrong --- src/chacha20poly1305_reuseable/__init__.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/chacha20poly1305_reuseable/__init__.py b/src/chacha20poly1305_reuseable/__init__.py index 1e37daa..9338ccc 100644 --- a/src/chacha20poly1305_reuseable/__init__.py +++ b/src/chacha20poly1305_reuseable/__init__.py @@ -75,11 +75,6 @@ class ChaCha20Poly1305Reusable(ChaCha20Poly1305): The primary use case for this code is HAP streams. """ - _MAX_SIZE = MAX_SIZE - _KEY_LEN = KEY_LEN - _NONCE_LEN = NONCE_LEN - _TAG_LENGTH = TAG_LENGTH - def __init__(self, key: Union[_bytes, bytearray]) -> None: if not backend.aead_cipher_supported(self): raise exceptions.UnsupportedAlgorithm( From 7462b95cdbdd96cea0499e8f5b0be054221d1536 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 15 Jul 2023 13:34:58 -1000 Subject: [PATCH 04/12] feat: speed up implementation - drop functions that are only called once - avoid calling into the openssl_assert when nothing is wrong --- src/chacha20poly1305_reuseable/__init__.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/chacha20poly1305_reuseable/__init__.py b/src/chacha20poly1305_reuseable/__init__.py index 9338ccc..57714e5 100644 --- a/src/chacha20poly1305_reuseable/__init__.py +++ b/src/chacha20poly1305_reuseable/__init__.py @@ -234,8 +234,9 @@ def _process_aad(ctx: object, associated_data: _bytes) -> None: def _process_data(ctx: object, data: _bytes) -> _bytes: outlen = ffi_new("int *") - buf = ffi_new("unsigned char[]", len(data)) - if EVP_CipherUpdate(ctx, buf, outlen, data, len(data)) == 0: + data_len = len(data) + buf = ffi_new("unsigned char[]", data_len) + if EVP_CipherUpdate(ctx, buf, outlen, data, data_len) == 0: openssl_failure() return ffi_buffer(buf, outlen[0])[:] @@ -278,11 +279,12 @@ def _decrypt_with_fixed_nonce_len( ) -> bytes: if len(data) < tag_length: raise InvalidTag - tag = data[-tag_length:] - data = data[:-tag_length] + negative_tag_length = -tag_length + tag = data[negative_tag_length:] + data = data[:negative_tag_length] _set_nonce(ctx, nonce, _DECRYPT) # set the decrypted tag - if EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, len(tag), tag) == 0: + if EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_length, tag) == 0: openssl_failure() return _decrypt_data(ctx, data, associated_data) From 8e831965d0438616e689bc48c3f5211fb0226a3a Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 15 Jul 2023 13:38:38 -1000 Subject: [PATCH 05/12] feat: speed up implementation - drop functions that are only called once - avoid calling into the openssl_assert when nothing is wrong --- src/chacha20poly1305_reuseable/__init__.pxd | 3 ++ src/chacha20poly1305_reuseable/__init__.py | 40 +++++++++++---------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/chacha20poly1305_reuseable/__init__.pxd b/src/chacha20poly1305_reuseable/__init__.pxd index 533c8aa..cfa89a0 100644 --- a/src/chacha20poly1305_reuseable/__init__.pxd +++ b/src/chacha20poly1305_reuseable/__init__.pxd @@ -5,6 +5,9 @@ import cython cdef object _ENCRYPT cdef object _DECRYPT +cdef object lib +cdef object ffi + cdef object InvalidTag cdef object openssl_failure cdef object NULL diff --git a/src/chacha20poly1305_reuseable/__init__.py b/src/chacha20poly1305_reuseable/__init__.py index 57714e5..ef7af42 100644 --- a/src/chacha20poly1305_reuseable/__init__.py +++ b/src/chacha20poly1305_reuseable/__init__.py @@ -16,24 +16,27 @@ from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305 openssl_failure = partial(backend.openssl_assert, False) -EVP_CIPHER_CTX_ctrl = backend._lib.EVP_CIPHER_CTX_ctrl -EVP_CTRL_AEAD_SET_TAG = backend._lib.EVP_CTRL_AEAD_SET_TAG -EVP_CTRL_AEAD_SET_IVLEN = backend._lib.EVP_CTRL_AEAD_SET_IVLEN -EVP_CipherInit_ex = backend._lib.EVP_CipherInit_ex -EVP_CIPHER_CTX_new = backend._lib.EVP_CIPHER_CTX_new -EVP_CIPHER_CTX_free = backend._lib.EVP_CIPHER_CTX_free -EVP_get_cipherbyname = backend._lib.EVP_get_cipherbyname -EVP_CIPHER_CTX_set_key_length = backend._lib.EVP_CIPHER_CTX_set_key_length -EVP_CipherUpdate = backend._lib.EVP_CipherUpdate -EVP_CipherFinal_ex = backend._lib.EVP_CipherFinal_ex -EVP_CTRL_AEAD_GET_TAG = backend._lib.EVP_CTRL_AEAD_GET_TAG - -ffi_from_buffer = backend._ffi.from_buffer -ffi_gc = backend._ffi.gc -ffi_new = backend._ffi.new -ffi_buffer = backend._ffi.buffer - -NULL = backend._ffi.NULL +lib = backend._lib +ffi = backend._ffi + +EVP_CIPHER_CTX_ctrl = lib.EVP_CIPHER_CTX_ctrl +EVP_CTRL_AEAD_SET_TAG = lib.EVP_CTRL_AEAD_SET_TAG +EVP_CTRL_AEAD_SET_IVLEN = lib.EVP_CTRL_AEAD_SET_IVLEN +EVP_CipherInit_ex = lib.EVP_CipherInit_ex +EVP_CIPHER_CTX_new = lib.EVP_CIPHER_CTX_new +EVP_CIPHER_CTX_free = lib.EVP_CIPHER_CTX_free +EVP_get_cipherbyname = lib.EVP_get_cipherbyname +EVP_CIPHER_CTX_set_key_length = lib.EVP_CIPHER_CTX_set_key_length +EVP_CipherUpdate = lib.EVP_CipherUpdate +EVP_CipherFinal_ex = lib.EVP_CipherFinal_ex +EVP_CTRL_AEAD_GET_TAG = lib.EVP_CTRL_AEAD_GET_TAG + +ffi_from_buffer = ffi.from_buffer +ffi_gc = ffi.gc +ffi_new = ffi.new +ffi_buffer = ffi.buffer + +NULL = ffi.NULL _ENCRYPT = 1 _DECRYPT = 0 @@ -88,7 +91,6 @@ def __init__(self, key: Union[_bytes, bytearray]) -> None: if len(key) != KEY_LEN: raise ValueError("ChaCha20Poly1305Reusable key must be 32 bytes.") - self._cipher_name = CIPHER_NAME self._key = key self._decrypt_ctx: Optional[object] = None self._encrypt_ctx: Optional[object] = None From efe6322b2d3b263cc957c949707ee01b4205f25c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 15 Jul 2023 13:40:02 -1000 Subject: [PATCH 06/12] feat: speed up implementation - drop functions that are only called once - avoid calling into the openssl_assert when nothing is wrong --- src/chacha20poly1305_reuseable/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chacha20poly1305_reuseable/__init__.py b/src/chacha20poly1305_reuseable/__init__.py index ef7af42..6d0b4d7 100644 --- a/src/chacha20poly1305_reuseable/__init__.py +++ b/src/chacha20poly1305_reuseable/__init__.py @@ -97,7 +97,7 @@ def __init__(self, key: Union[_bytes, bytearray]) -> None: @classmethod def generate_key(cls) -> _bytes: - return os.urandom(ChaCha20Poly1305Reusable._KEY_LEN) + return os.urandom(KEY_LEN) def encrypt( self, From e622d8444d387e5f871282f3a94536d7ed357104 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 15 Jul 2023 13:41:55 -1000 Subject: [PATCH 07/12] feat: speed up implementation - drop functions that are only called once - avoid calling into the openssl_assert when nothing is wrong --- src/chacha20poly1305_reuseable/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/chacha20poly1305_reuseable/__init__.py b/src/chacha20poly1305_reuseable/__init__.py index 6d0b4d7..0a9fa5a 100644 --- a/src/chacha20poly1305_reuseable/__init__.py +++ b/src/chacha20poly1305_reuseable/__init__.py @@ -78,6 +78,8 @@ class ChaCha20Poly1305Reusable(ChaCha20Poly1305): The primary use case for this code is HAP streams. """ + _KEY_LEN = KEY_LEN + def __init__(self, key: Union[_bytes, bytearray]) -> None: if not backend.aead_cipher_supported(self): raise exceptions.UnsupportedAlgorithm( From 6d6465e7b5f2922b983b4a8297d87dd94a7b64b8 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 15 Jul 2023 13:46:24 -1000 Subject: [PATCH 08/12] fix: refactoring errors --- src/chacha20poly1305_reuseable/__init__.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/chacha20poly1305_reuseable/__init__.py b/src/chacha20poly1305_reuseable/__init__.py index 0a9fa5a..20d1e74 100644 --- a/src/chacha20poly1305_reuseable/__init__.py +++ b/src/chacha20poly1305_reuseable/__init__.py @@ -78,8 +78,6 @@ class ChaCha20Poly1305Reusable(ChaCha20Poly1305): The primary use case for this code is HAP streams. """ - _KEY_LEN = KEY_LEN - def __init__(self, key: Union[_bytes, bytearray]) -> None: if not backend.aead_cipher_supported(self): raise exceptions.UnsupportedAlgorithm( @@ -108,14 +106,14 @@ def encrypt( associated_data: typing.Optional[bytes], ) -> bytes: encrypt_ctx = self._encrypt_ctx - if encrypt_ctx: - self._encrypt_ctx = _aead_setup_with_fixed_nonce_len( + if not encrypt_ctx: + encrypt_ctx = _aead_setup_with_fixed_nonce_len( CIPHER_NAME, self._key, NONCE_LEN, _ENCRYPT, ) - encrypt_ctx = self._encrypt_ctx + self._encrypt_ctx = encrypt_ctx if associated_data is None: associated_data = b"" @@ -141,13 +139,13 @@ def decrypt( ) -> bytes: decrypt_ctx = self._decrypt_ctx if not decrypt_ctx: - self._decrypt_ctx = _aead_setup_with_fixed_nonce_len( + decrypt_ctx = _aead_setup_with_fixed_nonce_len( CIPHER_NAME, self._key, NONCE_LEN, _DECRYPT, ) - decrypt_ctx = self._decrypt_ctx + self._decrypt_ctx = decrypt_ctx if associated_data is None: associated_data = b"" From 7562bfda0f520430ba2546aee2ec29fb1d93afc0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 15 Jul 2023 13:57:52 -1000 Subject: [PATCH 09/12] fix: cover --- src/chacha20poly1305_reuseable/__init__.pxd | 2 + src/chacha20poly1305_reuseable/__init__.py | 117 +++++++++----------- 2 files changed, 56 insertions(+), 63 deletions(-) diff --git a/src/chacha20poly1305_reuseable/__init__.pxd b/src/chacha20poly1305_reuseable/__init__.pxd index cfa89a0..4477a48 100644 --- a/src/chacha20poly1305_reuseable/__init__.pxd +++ b/src/chacha20poly1305_reuseable/__init__.pxd @@ -59,6 +59,8 @@ cdef _encrypt_with_fixed_nonce_len( object tag_length, ) +cdef _openssl_assert(int ok) + cdef _encrypt_data( object ctx, object data, diff --git a/src/chacha20poly1305_reuseable/__init__.py b/src/chacha20poly1305_reuseable/__init__.py index 20d1e74..e284971 100644 --- a/src/chacha20poly1305_reuseable/__init__.py +++ b/src/chacha20poly1305_reuseable/__init__.py @@ -162,18 +162,15 @@ def decrypt( def _set_nonce(ctx: object, nonce: Union[_bytes, bytearray], operation: int) -> None: nonce_ptr = ffi_from_buffer(nonce) - if ( - EVP_CipherInit_ex( - ctx, - NULL, - NULL, - NULL, - nonce_ptr, - int(operation == _ENCRYPT), - ) - == 0 - ): - openssl_failure() + ret = EVP_CipherInit_ex( + ctx, + NULL, + NULL, + NULL, + nonce_ptr, + int(operation == _ENCRYPT), + ) + _openssl_assert(ret != 0) def _aead_setup_with_fixed_nonce_len( @@ -184,62 +181,52 @@ def _aead_setup_with_fixed_nonce_len( ctx = ffi_gc(ctx, EVP_CIPHER_CTX_free) # set the cipher evp_cipher = EVP_get_cipherbyname(cipher_name) - if evp_cipher == NULL: - openssl_failure() - if ( - EVP_CipherInit_ex( - ctx, - evp_cipher, - NULL, - NULL, - NULL, - int(operation == _ENCRYPT), - ) - == 0 - ): - openssl_failure() + _openssl_assert(evp_cipher != NULL) + ret = EVP_CipherInit_ex( + ctx, + evp_cipher, + NULL, + NULL, + NULL, + int(operation == _ENCRYPT), + ) + _openssl_assert(ret != 0) # Set the key length - if EVP_CIPHER_CTX_set_key_length(ctx, len(key)) == 0: - openssl_failure() + ret = EVP_CIPHER_CTX_set_key_length(ctx, len(key)) + _openssl_assert(ret != 0) # Set the key - if ( - EVP_CipherInit_ex( - ctx, - NULL, - NULL, - ffi_from_buffer(key), - NULL, - int(operation == _ENCRYPT), - ) - == 0 - ): - openssl_failure() + ret = EVP_CipherInit_ex( + ctx, + NULL, + NULL, + ffi_from_buffer(key), + NULL, + int(operation == _ENCRYPT), + ) + _openssl_assert(ret != 0) # set nonce length - if ( - EVP_CIPHER_CTX_ctrl( - ctx, - EVP_CTRL_AEAD_SET_IVLEN, - nonce_len, - NULL, - ) - == 0 - ): - openssl_failure() + ret = EVP_CIPHER_CTX_ctrl( + ctx, + EVP_CTRL_AEAD_SET_IVLEN, + nonce_len, + NULL, + ) + _openssl_assert(ret != 0) return ctx def _process_aad(ctx: object, associated_data: _bytes) -> None: outlen = ffi_new("int *") - if EVP_CipherUpdate(ctx, NULL, outlen, associated_data, len(associated_data)) == 0: - openssl_failure() + ret = EVP_CipherUpdate(ctx, NULL, outlen, associated_data, len(associated_data)) + _openssl_assert(ret != 0) def _process_data(ctx: object, data: _bytes) -> _bytes: outlen = ffi_new("int *") data_len = len(data) buf = ffi_new("unsigned char[]", data_len) - if EVP_CipherUpdate(ctx, buf, outlen, data, data_len) == 0: - openssl_failure() + ret = EVP_CipherUpdate(ctx, buf, outlen, data, data_len) + _openssl_assert(ret != 0) return ffi_buffer(buf, outlen[0])[:] @@ -260,15 +247,13 @@ def _encrypt_data( _process_aad(ctx, associated_data) processed_data = _process_data(ctx, data) outlen = ffi_new("int *") - if EVP_CipherFinal_ex(ctx, NULL, outlen) == 0: - openssl_failure() - if outlen[0] != 0: - openssl_failure() + ret = EVP_CipherFinal_ex(ctx, NULL, outlen) + _openssl_assert(ret != 0) + _openssl_assert(outlen[0] == 0) tag_buf = ffi_new("unsigned char[]", tag_length) - if EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_length, tag_buf) == 0: - openssl_failure() + ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_length, tag_buf) + _openssl_assert(ret != 0) tag = ffi_buffer(tag_buf)[:] - return processed_data + tag @@ -286,8 +271,8 @@ def _decrypt_with_fixed_nonce_len( data = data[:negative_tag_length] _set_nonce(ctx, nonce, _DECRYPT) # set the decrypted tag - if EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_length, tag) == 0: - openssl_failure() + ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_length, tag) + _openssl_assert(ret != 0) return _decrypt_data(ctx, data, associated_data) @@ -301,3 +286,9 @@ def _decrypt_data(ctx: object, data: _bytes, associated_data: _bytes) -> _bytes: raise InvalidTag return processed_data + + +def _openssl_assert(ok: bool) -> None: + """Raise an exception if OpenSSL returns an error.""" + if not ok: + openssl_failure() From ccd5fee44acf76c8654450c0877e6eda16d6dafc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 15 Jul 2023 13:59:32 -1000 Subject: [PATCH 10/12] fix: avoid convert --- src/chacha20poly1305_reuseable/__init__.pxd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chacha20poly1305_reuseable/__init__.pxd b/src/chacha20poly1305_reuseable/__init__.pxd index 4477a48..b2d2f72 100644 --- a/src/chacha20poly1305_reuseable/__init__.pxd +++ b/src/chacha20poly1305_reuseable/__init__.pxd @@ -59,7 +59,7 @@ cdef _encrypt_with_fixed_nonce_len( object tag_length, ) -cdef _openssl_assert(int ok) +cdef _openssl_assert(object ok) cdef _encrypt_data( object ctx, From f49fc75089d32b9832ea3d2d3b8b098380a436dd Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 15 Jul 2023 14:01:25 -1000 Subject: [PATCH 11/12] fix: cover --- src/chacha20poly1305_reuseable/__init__.pxd | 2 +- src/chacha20poly1305_reuseable/__init__.py | 26 ++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/chacha20poly1305_reuseable/__init__.pxd b/src/chacha20poly1305_reuseable/__init__.pxd index b2d2f72..d1c978a 100644 --- a/src/chacha20poly1305_reuseable/__init__.pxd +++ b/src/chacha20poly1305_reuseable/__init__.pxd @@ -59,7 +59,7 @@ cdef _encrypt_with_fixed_nonce_len( object tag_length, ) -cdef _openssl_assert(object ok) +cdef openssl_assert(object ok) cdef _encrypt_data( object ctx, diff --git a/src/chacha20poly1305_reuseable/__init__.py b/src/chacha20poly1305_reuseable/__init__.py index e284971..29dc896 100644 --- a/src/chacha20poly1305_reuseable/__init__.py +++ b/src/chacha20poly1305_reuseable/__init__.py @@ -170,7 +170,7 @@ def _set_nonce(ctx: object, nonce: Union[_bytes, bytearray], operation: int) -> nonce_ptr, int(operation == _ENCRYPT), ) - _openssl_assert(ret != 0) + openssl_assert(ret != 0) def _aead_setup_with_fixed_nonce_len( @@ -181,7 +181,7 @@ def _aead_setup_with_fixed_nonce_len( ctx = ffi_gc(ctx, EVP_CIPHER_CTX_free) # set the cipher evp_cipher = EVP_get_cipherbyname(cipher_name) - _openssl_assert(evp_cipher != NULL) + openssl_assert(evp_cipher != NULL) ret = EVP_CipherInit_ex( ctx, evp_cipher, @@ -190,10 +190,10 @@ def _aead_setup_with_fixed_nonce_len( NULL, int(operation == _ENCRYPT), ) - _openssl_assert(ret != 0) + openssl_assert(ret != 0) # Set the key length ret = EVP_CIPHER_CTX_set_key_length(ctx, len(key)) - _openssl_assert(ret != 0) + openssl_assert(ret != 0) # Set the key ret = EVP_CipherInit_ex( ctx, @@ -203,7 +203,7 @@ def _aead_setup_with_fixed_nonce_len( NULL, int(operation == _ENCRYPT), ) - _openssl_assert(ret != 0) + openssl_assert(ret != 0) # set nonce length ret = EVP_CIPHER_CTX_ctrl( ctx, @@ -211,14 +211,14 @@ def _aead_setup_with_fixed_nonce_len( nonce_len, NULL, ) - _openssl_assert(ret != 0) + openssl_assert(ret != 0) return ctx def _process_aad(ctx: object, associated_data: _bytes) -> None: outlen = ffi_new("int *") ret = EVP_CipherUpdate(ctx, NULL, outlen, associated_data, len(associated_data)) - _openssl_assert(ret != 0) + openssl_assert(ret != 0) def _process_data(ctx: object, data: _bytes) -> _bytes: @@ -226,7 +226,7 @@ def _process_data(ctx: object, data: _bytes) -> _bytes: data_len = len(data) buf = ffi_new("unsigned char[]", data_len) ret = EVP_CipherUpdate(ctx, buf, outlen, data, data_len) - _openssl_assert(ret != 0) + openssl_assert(ret != 0) return ffi_buffer(buf, outlen[0])[:] @@ -248,11 +248,11 @@ def _encrypt_data( processed_data = _process_data(ctx, data) outlen = ffi_new("int *") ret = EVP_CipherFinal_ex(ctx, NULL, outlen) - _openssl_assert(ret != 0) - _openssl_assert(outlen[0] == 0) + openssl_assert(ret != 0) + openssl_assert(outlen[0] == 0) tag_buf = ffi_new("unsigned char[]", tag_length) ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_length, tag_buf) - _openssl_assert(ret != 0) + openssl_assert(ret != 0) tag = ffi_buffer(tag_buf)[:] return processed_data + tag @@ -272,7 +272,7 @@ def _decrypt_with_fixed_nonce_len( _set_nonce(ctx, nonce, _DECRYPT) # set the decrypted tag ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_length, tag) - _openssl_assert(ret != 0) + openssl_assert(ret != 0) return _decrypt_data(ctx, data, associated_data) @@ -288,7 +288,7 @@ def _decrypt_data(ctx: object, data: _bytes, associated_data: _bytes) -> _bytes: return processed_data -def _openssl_assert(ok: bool) -> None: +def openssl_assert(ok: bool) -> None: """Raise an exception if OpenSSL returns an error.""" if not ok: openssl_failure() From c99fcebd86439d594751f6e227f816e149412df2 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sat, 15 Jul 2023 14:03:09 -1000 Subject: [PATCH 12/12] fix: keep old name --- src/chacha20poly1305_reuseable/__init__.py | 40 +++++++++++----------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/chacha20poly1305_reuseable/__init__.py b/src/chacha20poly1305_reuseable/__init__.py index 29dc896..fbbf274 100644 --- a/src/chacha20poly1305_reuseable/__init__.py +++ b/src/chacha20poly1305_reuseable/__init__.py @@ -162,7 +162,7 @@ def decrypt( def _set_nonce(ctx: object, nonce: Union[_bytes, bytearray], operation: int) -> None: nonce_ptr = ffi_from_buffer(nonce) - ret = EVP_CipherInit_ex( + res = EVP_CipherInit_ex( ctx, NULL, NULL, @@ -170,7 +170,7 @@ def _set_nonce(ctx: object, nonce: Union[_bytes, bytearray], operation: int) -> nonce_ptr, int(operation == _ENCRYPT), ) - openssl_assert(ret != 0) + openssl_assert(res != 0) def _aead_setup_with_fixed_nonce_len( @@ -182,7 +182,7 @@ def _aead_setup_with_fixed_nonce_len( # set the cipher evp_cipher = EVP_get_cipherbyname(cipher_name) openssl_assert(evp_cipher != NULL) - ret = EVP_CipherInit_ex( + res = EVP_CipherInit_ex( ctx, evp_cipher, NULL, @@ -190,12 +190,12 @@ def _aead_setup_with_fixed_nonce_len( NULL, int(operation == _ENCRYPT), ) - openssl_assert(ret != 0) + openssl_assert(res != 0) # Set the key length - ret = EVP_CIPHER_CTX_set_key_length(ctx, len(key)) - openssl_assert(ret != 0) + res = EVP_CIPHER_CTX_set_key_length(ctx, len(key)) + openssl_assert(res != 0) # Set the key - ret = EVP_CipherInit_ex( + res = EVP_CipherInit_ex( ctx, NULL, NULL, @@ -203,30 +203,30 @@ def _aead_setup_with_fixed_nonce_len( NULL, int(operation == _ENCRYPT), ) - openssl_assert(ret != 0) + openssl_assert(res != 0) # set nonce length - ret = EVP_CIPHER_CTX_ctrl( + res = EVP_CIPHER_CTX_ctrl( ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, NULL, ) - openssl_assert(ret != 0) + openssl_assert(res != 0) return ctx def _process_aad(ctx: object, associated_data: _bytes) -> None: outlen = ffi_new("int *") - ret = EVP_CipherUpdate(ctx, NULL, outlen, associated_data, len(associated_data)) - openssl_assert(ret != 0) + res = EVP_CipherUpdate(ctx, NULL, outlen, associated_data, len(associated_data)) + openssl_assert(res != 0) def _process_data(ctx: object, data: _bytes) -> _bytes: outlen = ffi_new("int *") data_len = len(data) buf = ffi_new("unsigned char[]", data_len) - ret = EVP_CipherUpdate(ctx, buf, outlen, data, data_len) - openssl_assert(ret != 0) + res = EVP_CipherUpdate(ctx, buf, outlen, data, data_len) + openssl_assert(res != 0) return ffi_buffer(buf, outlen[0])[:] @@ -247,12 +247,12 @@ def _encrypt_data( _process_aad(ctx, associated_data) processed_data = _process_data(ctx, data) outlen = ffi_new("int *") - ret = EVP_CipherFinal_ex(ctx, NULL, outlen) - openssl_assert(ret != 0) + res = EVP_CipherFinal_ex(ctx, NULL, outlen) + openssl_assert(res != 0) openssl_assert(outlen[0] == 0) tag_buf = ffi_new("unsigned char[]", tag_length) - ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_length, tag_buf) - openssl_assert(ret != 0) + res = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_length, tag_buf) + openssl_assert(res != 0) tag = ffi_buffer(tag_buf)[:] return processed_data + tag @@ -271,8 +271,8 @@ def _decrypt_with_fixed_nonce_len( data = data[:negative_tag_length] _set_nonce(ctx, nonce, _DECRYPT) # set the decrypted tag - ret = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_length, tag) - openssl_assert(ret != 0) + res = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_length, tag) + openssl_assert(res != 0) return _decrypt_data(ctx, data, associated_data)