From 6e36bd3a249355e2d91c0bcd0f9cedd59221a40c Mon Sep 17 00:00:00 2001 From: Robert de Vries Date: Sat, 11 Apr 2026 17:26:22 +0200 Subject: [PATCH 1/5] Start adding typing information. Initially only random.py and utils.py. --- wolfcrypt/_ffi/__init__.pyi | 5 +++ wolfcrypt/_ffi/lib.pyi | 76 +++++++++++++++++++++++++++++++++++++ wolfcrypt/random.py | 26 +++++++------ wolfcrypt/utils.py | 6 ++- 4 files changed, 99 insertions(+), 14 deletions(-) create mode 100644 wolfcrypt/_ffi/__init__.pyi create mode 100644 wolfcrypt/_ffi/lib.pyi diff --git a/wolfcrypt/_ffi/__init__.pyi b/wolfcrypt/_ffi/__init__.pyi new file mode 100644 index 0000000..3d94536 --- /dev/null +++ b/wolfcrypt/_ffi/__init__.pyi @@ -0,0 +1,5 @@ +import _cffi_backend + +ffi: _cffi_backend.FFI + +__all__ = ["lib"] \ No newline at end of file diff --git a/wolfcrypt/_ffi/lib.pyi b/wolfcrypt/_ffi/lib.pyi new file mode 100644 index 0000000..222a534 --- /dev/null +++ b/wolfcrypt/_ffi/lib.pyi @@ -0,0 +1,76 @@ + +from _cffi_backend import FFI +from typing_extensions import TypeAlias + +INVALID_DEVID: int + +AES_ENABLED: int +AES_SIV_ENABLED: int +AESGCM_STREAM_ENABLED: int +ASN_ENABLED: int +CHACHA_ENABLED: int +CHACHA_STREAM_ENABLED: int +CHACHA20_POLY1305_ENABLED: int +DES3_ENABLED: int +ECC_ENABLED: int +ED25519_ENABLED: int +ED448_ENABLED: int +FIPS_ENABLED: int +HMAC_ENABLED: int +KEYGEN_ENABLED: int +HKDF_ENABLED: int +ML_DSA_ENABLED: int +ML_KEM_ENABLED: int +MPAPI_ENABLED: int +PWDBASED_ENABLED: int +RSA_ENABLED: int +RSA_PSS_ENABLED: int +SHA_ENABLED: int +SHA3_ENABLED: int +SHA256_ENABLED: int +SHA384_ENABLED: int +SHA512_ENABLED: int +WC_RNG_SEED_CB_ENABLED: int + +FIPS_VERSION: int + +WC_MGF1NONE: int +WC_MGF1SHA1: int +WC_MGF1SHA224: int +WC_MGF1SHA256: int +WC_MGF1SHA384: int +WC_MGF1SHA512: int + +WC_HASH_TYPE_NONE: int +WC_HASH_TYPE_MD2: int +WC_HASH_TYPE_MD4: int +WC_HASH_TYPE_MD5: int +WC_HASH_TYPE_SHA: int +WC_HASH_TYPE_SHA224: int +WC_HASH_TYPE_SHA256: int +WC_HASH_TYPE_SHA384: int +WC_HASH_TYPE_SHA512: int +WC_HASH_TYPE_MD5_SHA: int +WC_HASH_TYPE_SHA3_224: int +WC_HASH_TYPE_SHA3_256: int +WC_HASH_TYPE_SHA3_384: int +WC_HASH_TYPE_SHA3_512: int +WC_HASH_TYPE_BLAKE2B: int +WC_HASH_TYPE_BLAKE2S: int + +WC_ML_KEM_512: int +WC_ML_KEM_768: int +WC_ML_KEM_1024: int + +WC_ML_DSA_44: int +WC_ML_DSA_65: int +WC_ML_DSA_87: int + +WC_KEYTYPE_ALL: int + +RNG: TypeAlias = FFI.CData + +def wc_InitRngNonce_ex(rng: RNG, nonce: bytes, nonce_size: int, heap: FFI.CData, device_id: int) -> int: ... +def wc_RNG_GenerateByte(rng: RNG, buffer: FFI.CData) -> int: ... +def wc_RNG_GenerateBlock(rng: RNG, buffer: FFI.CData, len: int) -> int: ... +def wc_FreeRng(rng: RNG) -> None: ... diff --git a/wolfcrypt/random.py b/wolfcrypt/random.py index 17d3ae7..596b8ef 100644 --- a/wolfcrypt/random.py +++ b/wolfcrypt/random.py @@ -20,6 +20,10 @@ # pylint: disable=no-member,no-name-in-module +from __future__ import annotations + +from _cffi_backend import FFI + from wolfcrypt._ffi import ffi as _ffi from wolfcrypt._ffi import lib as _lib @@ -31,14 +35,10 @@ class Random: A Cryptographically Secure Pseudo Random Number Generator - CSPRNG """ - def __init__(self, nonce=_ffi.NULL, device_id=_lib.INVALID_DEVID): + def __init__(self, nonce=b"", device_id=_lib.INVALID_DEVID) -> None: self.native_object = _ffi.new("WC_RNG *") - if nonce == _ffi.NULL: - nonce_size = 0 - else: - nonce_size = len(nonce) - ret = _lib.wc_InitRngNonce_ex(self.native_object, nonce, nonce_size, _ffi.NULL, device_id) + ret = _lib.wc_InitRngNonce_ex(self.native_object, nonce, len(nonce), _ffi.NULL, device_id) if ret < 0: # pragma: no cover self.native_object = None raise WolfCryptError("RNG init error (%d)" % ret) @@ -46,32 +46,34 @@ def __init__(self, nonce=_ffi.NULL, device_id=_lib.INVALID_DEVID): # making sure _lib.wc_FreeRng outlives WC_RNG instances _delete = _lib.wc_FreeRng - def __del__(self): + def __del__(self) -> None: if self.native_object: try: - self._delete(self.native_object) + Random._delete(self.native_object) except AttributeError: # Can occur during interpreter shutdown pass - def byte(self): + def byte(self) -> __builtins__.bytes: """ Generate and return a random byte. """ - result = _ffi.new('byte[1]') + result = _ffi.new("byte[1]") + assert self.native_object is not None ret = _lib.wc_RNG_GenerateByte(self.native_object, result) if ret < 0: # pragma: no cover raise WolfCryptError("RNG generate byte error (%d)" % ret) return _ffi.buffer(result, 1)[:] - def bytes(self, length): + def bytes(self, length) -> __builtins__.bytes: """ Generate and return a random sequence of length bytes. """ - result = _ffi.new('byte[%d]' % length) + result = _ffi.new("byte[%d]" % length) + assert self.native_object is not None ret = _lib.wc_RNG_GenerateBlock(self.native_object, result, length) if ret < 0: # pragma: no cover raise WolfCryptError("RNG generate block error (%d)" % ret) diff --git a/wolfcrypt/utils.py b/wolfcrypt/utils.py index b5274c8..e30ef31 100644 --- a/wolfcrypt/utils.py +++ b/wolfcrypt/utils.py @@ -20,10 +20,12 @@ # pylint: disable=unused-import +from __future__ import annotations + from binascii import hexlify as b2h, unhexlify as h2b # noqa: F401 -def t2b(string): +def t2b(string: bytes | bytearray | memoryview | str) -> bytes: """ Converts text to binary. @@ -31,5 +33,5 @@ def t2b(string): Encodes str to UTF-8 bytes. """ if isinstance(string, (bytes, bytearray, memoryview)): - return string + return bytes(string) return str(string).encode("utf-8") From 8448f41b42344165d61384d87bc93d41c304a61f Mon Sep 17 00:00:00 2001 From: Robert de Vries Date: Wed, 15 Apr 2026 17:07:29 +0200 Subject: [PATCH 2/5] Added ffi to all in _ffi/__init__.py Removed unused import. --- wolfcrypt/_ffi/__init__.pyi | 2 +- wolfcrypt/random.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/wolfcrypt/_ffi/__init__.pyi b/wolfcrypt/_ffi/__init__.pyi index 3d94536..f068df3 100644 --- a/wolfcrypt/_ffi/__init__.pyi +++ b/wolfcrypt/_ffi/__init__.pyi @@ -2,4 +2,4 @@ import _cffi_backend ffi: _cffi_backend.FFI -__all__ = ["lib"] \ No newline at end of file +__all__ = ["ffi", "lib"] diff --git a/wolfcrypt/random.py b/wolfcrypt/random.py index 596b8ef..5dc7505 100644 --- a/wolfcrypt/random.py +++ b/wolfcrypt/random.py @@ -22,8 +22,6 @@ from __future__ import annotations -from _cffi_backend import FFI - from wolfcrypt._ffi import ffi as _ffi from wolfcrypt._ffi import lib as _lib From 338a4839b9f73d00f964f899aa1dab226e0a6f3b Mon Sep 17 00:00:00 2001 From: Robert de Vries Date: Thu, 16 Apr 2026 23:04:06 +0200 Subject: [PATCH 3/5] Fix various review comments. - Missing import in _ffi/__init__.py - Updated docstring of t2b - Reverted change to Random.__del__() --- wolfcrypt/_ffi/__init__.pyi | 1 + wolfcrypt/_ffi/lib.pyi | 2 +- wolfcrypt/random.py | 2 +- wolfcrypt/utils.py | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/wolfcrypt/_ffi/__init__.pyi b/wolfcrypt/_ffi/__init__.pyi index f068df3..fd4cc63 100644 --- a/wolfcrypt/_ffi/__init__.pyi +++ b/wolfcrypt/_ffi/__init__.pyi @@ -1,4 +1,5 @@ import _cffi_backend +import _ffi.lib as lib ffi: _cffi_backend.FFI diff --git a/wolfcrypt/_ffi/lib.pyi b/wolfcrypt/_ffi/lib.pyi index 222a534..047e5cf 100644 --- a/wolfcrypt/_ffi/lib.pyi +++ b/wolfcrypt/_ffi/lib.pyi @@ -1,6 +1,6 @@ from _cffi_backend import FFI -from typing_extensions import TypeAlias +from typing import TypeAlias INVALID_DEVID: int diff --git a/wolfcrypt/random.py b/wolfcrypt/random.py index 5dc7505..4c4f312 100644 --- a/wolfcrypt/random.py +++ b/wolfcrypt/random.py @@ -47,7 +47,7 @@ def __init__(self, nonce=b"", device_id=_lib.INVALID_DEVID) -> None: def __del__(self) -> None: if self.native_object: try: - Random._delete(self.native_object) + self._delete(self.native_object) except AttributeError: # Can occur during interpreter shutdown pass diff --git a/wolfcrypt/utils.py b/wolfcrypt/utils.py index e30ef31..a1c7d37 100644 --- a/wolfcrypt/utils.py +++ b/wolfcrypt/utils.py @@ -29,7 +29,8 @@ def t2b(string: bytes | bytearray | memoryview | str) -> bytes: """ Converts text to binary. - Passes through bytes, bytearray, and memoryview unchanged. + Passes through bytes unchanged. + Objects of type bytearray or memoryview are converted to bytes. Encodes str to UTF-8 bytes. """ if isinstance(string, (bytes, bytearray, memoryview)): From 917e2942ea4ad5cb7bff488c6b2665b1ab644f1e Mon Sep 17 00:00:00 2001 From: Robert de Vries Date: Fri, 17 Apr 2026 21:46:46 +0200 Subject: [PATCH 4/5] Add a few missed type annotations in random.py. --- wolfcrypt/random.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/random.py b/wolfcrypt/random.py index 4c4f312..62ed4e4 100644 --- a/wolfcrypt/random.py +++ b/wolfcrypt/random.py @@ -33,7 +33,7 @@ class Random: A Cryptographically Secure Pseudo Random Number Generator - CSPRNG """ - def __init__(self, nonce=b"", device_id=_lib.INVALID_DEVID) -> None: + def __init__(self, nonce: __builtins__.bytes = b"", device_id: int = _lib.INVALID_DEVID) -> None: self.native_object = _ffi.new("WC_RNG *") ret = _lib.wc_InitRngNonce_ex(self.native_object, nonce, len(nonce), _ffi.NULL, device_id) @@ -65,7 +65,7 @@ def byte(self) -> __builtins__.bytes: return _ffi.buffer(result, 1)[:] - def bytes(self, length) -> __builtins__.bytes: + def bytes(self, length: int) -> __builtins__.bytes: """ Generate and return a random sequence of length bytes. """ From 61c7990be01f4a69fa84ab926a05db7c1d8731c4 Mon Sep 17 00:00:00 2001 From: Robert de Vries Date: Mon, 20 Apr 2026 22:37:07 +0200 Subject: [PATCH 5/5] Addressed various review comments. --- ChangeLog.rst | 6 ++++++ requirements/test.txt | 1 + setup.py | 3 ++- wolfcrypt/_ffi/__init__.pyi | 2 +- wolfcrypt/random.py | 2 +- wolfcrypt/utils.py | 13 ++++++++++--- 6 files changed, 21 insertions(+), 6 deletions(-) diff --git a/ChangeLog.rst b/ChangeLog.rst index 92d779b..b6fec81 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -1,3 +1,9 @@ +wolfCrypt-py Release NEXT (TBD, 2026) +========================================== + +* Add extra nonce parameter to Random generator + + wolfCrypt-py Release 5.8.4 (Jan 7, 2026) ========================================== diff --git a/requirements/test.txt b/requirements/test.txt index 8c01ca8..53c4efe 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -1,3 +1,4 @@ -r prod.txt tox pytest +types-cffi diff --git a/setup.py b/setup.py index ed2836e..ba23674 100755 --- a/setup.py +++ b/setup.py @@ -74,6 +74,7 @@ u"Programming Language :: Python :: 3.7", u"Programming Language :: Python :: 3.8", u"Programming Language :: Python :: 3.9", + u"Programming Language :: Python :: 3.10", u"Topic :: Security", u"Topic :: Security :: Cryptography", u"Topic :: Software Development" @@ -83,5 +84,5 @@ install_requires=["cffi>=1.0.0"], cffi_modules=["./scripts/build_ffi.py:ffibuilder"], - package_data={"wolfcrypt": ["*.dll"]} + package_data={"wolfcrypt": ["*.dll", "**/*.pyi"]} ) diff --git a/wolfcrypt/_ffi/__init__.pyi b/wolfcrypt/_ffi/__init__.pyi index fd4cc63..f3a1ba1 100644 --- a/wolfcrypt/_ffi/__init__.pyi +++ b/wolfcrypt/_ffi/__init__.pyi @@ -1,5 +1,5 @@ import _cffi_backend -import _ffi.lib as lib +import wolfcrypt._ffi.lib as lib ffi: _cffi_backend.FFI diff --git a/wolfcrypt/random.py b/wolfcrypt/random.py index 62ed4e4..039c44f 100644 --- a/wolfcrypt/random.py +++ b/wolfcrypt/random.py @@ -34,7 +34,7 @@ class Random: """ def __init__(self, nonce: __builtins__.bytes = b"", device_id: int = _lib.INVALID_DEVID) -> None: - self.native_object = _ffi.new("WC_RNG *") + self.native_object: _lib.RNG | None = _ffi.new("WC_RNG *") ret = _lib.wc_InitRngNonce_ex(self.native_object, nonce, len(nonce), _ffi.NULL, device_id) if ret < 0: # pragma: no cover diff --git a/wolfcrypt/utils.py b/wolfcrypt/utils.py index a1c7d37..5de6363 100644 --- a/wolfcrypt/utils.py +++ b/wolfcrypt/utils.py @@ -27,12 +27,19 @@ def t2b(string: bytes | bytearray | memoryview | str) -> bytes: """ - Converts text to binary. + Converts text to bytes. Passes through bytes unchanged. Objects of type bytearray or memoryview are converted to bytes. Encodes str to UTF-8 bytes. + + :param string: text to convert to bytes. + :raises TypeError: if string is not one of the supported types. """ - if isinstance(string, (bytes, bytearray, memoryview)): + if isinstance(string, bytes): + return string + if isinstance(string, (bytearray, memoryview)): return bytes(string) - return str(string).encode("utf-8") + if isinstance(string, str): + return str(string).encode("utf-8") + raise TypeError(f"String parameter of wrong type {type(string).__name__}, expected bytes, bytearray, memoryview or str")