Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ChangeLog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ wolfCrypt-py Release next (TBD, 2026)

* Drop support for end-of-life Python versions (<= 3.9)
* Add extra nonce parameter to Random generator
* The RsaPublic key parameter is now mandatory as it is always needed by an internal function call.


wolfCrypt-py Release 5.8.4 (Jan 7, 2026)
Expand Down
17 changes: 16 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ classifiers = [
dynamic = ["version"]
dependencies = [
"cffi>=1.0.0,<2",
"typing-extensions",
]

[project.urls]
Expand Down Expand Up @@ -99,7 +100,7 @@ target-version = "py310"
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
# McCabe complexity (`C901`) by default.
select = ["E4", "E7", "E9", "F", "B", "UP"]
select = ["E4", "E7", "E9", "F", "B", "UP", "ANN"]
ignore = ["UP031", "UP025", "UP032"]

# Allow fix for all enabled rules (when `--fix`) is provided.
Expand All @@ -109,6 +110,10 @@ unfixable = []
# Allow unused variables when underscore-prefixed.
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"

[tool.ruff.lint.per-file-ignores]
"scripts/*.py" = ["ANN"]
"tests/*.py" = ["ANN"]

[tool.ruff.format]
# Like Black, use double quotes for strings.
quote-style = "double"
Expand All @@ -135,3 +140,13 @@ docstring-code-format = false
# This only has an effect when the `docstring-code-format` setting is
# enabled.
docstring-code-line-length = "dynamic"

[tool.ty.environment]
python-version = "3.10"
root = ["."]

[tool.ty.src]
exclude = ["./lib"]

[tool.ty.rules]
all = "warn"
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,5 @@
install_requires=["cffi>=1.0.0,<2"],
cffi_modules=["./scripts/build_ffi.py:ffibuilder"],

package_data={"wolfcrypt": ["*.dll", "**/*.pyi"]}
package_data={"wolfcrypt": ["*.dll", "**/*.pyi", "py.typed"]},
)
9 changes: 9 additions & 0 deletions tests/test_aesgcmstream.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA

# pylint: disable=redefined-outer-name
# ty: ignore[possibly-missing-import]

from wolfcrypt._ffi import lib as _lib

Expand All @@ -35,6 +36,7 @@ def test_encrypt():
gcm = AesGcmStream(key, iv)
buf = gcm.encrypt("hello world")
authTag = gcm.final()
assert authTag is not None
assert b2h(authTag) == bytes('ac8fcee96dc6ef8e5236da19b6197d2e', 'utf-8')
assert b2h(buf) == bytes('5ba7d42e1bf01d7998e932', "utf-8")
gcmdec = AesGcmStream(key, iv)
Expand All @@ -48,6 +50,7 @@ def test_encrypt_short_tag():
gcm = AesGcmStream(key, iv, 12)
buf = gcm.encrypt("hello world")
authTag = gcm.final()
assert authTag is not None
assert b2h(authTag) == bytes('ac8fcee96dc6ef8e5236da19', 'utf-8')
assert b2h(buf) == bytes('5ba7d42e1bf01d7998e932', "utf-8")
gcmdec = AesGcmStream(key, iv, 12)
Expand All @@ -62,6 +65,7 @@ def test_multipart():
buf = gcm.encrypt("hello")
buf += gcm.encrypt(" world")
authTag = gcm.final()
assert authTag is not None
assert b2h(authTag) == bytes('ac8fcee96dc6ef8e5236da19b6197d2e', 'utf-8')
assert b2h(buf) == bytes('5ba7d42e1bf01d7998e932', "utf-8")
gcmdec = AesGcmStream(key, iv)
Expand All @@ -78,6 +82,7 @@ def test_encrypt_aad():
gcm.set_aad(aad)
buf = gcm.encrypt("hello world")
authTag = gcm.final()
assert authTag is not None
print(b2h(authTag))
assert b2h(authTag) == bytes('8f85338aa0b13f48f8b17482dbb8acca', 'utf-8')
assert b2h(buf) == bytes('5ba7d42e1bf01d7998e932', "utf-8")
Expand All @@ -96,6 +101,7 @@ def test_multipart_aad():
buf = gcm.encrypt("hello")
buf += gcm.encrypt(" world")
authTag = gcm.final()
assert authTag is not None
assert b2h(authTag) == bytes('8f85338aa0b13f48f8b17482dbb8acca', 'utf-8')
assert b2h(buf) == bytes('5ba7d42e1bf01d7998e932', "utf-8")
gcmdec = AesGcmStream(key, iv)
Expand All @@ -114,6 +120,7 @@ def test_encrypt_aad_bad():
gcm.set_aad(aad)
buf = gcm.encrypt("hello world")
authTag = gcm.final()
assert authTag is not None
print(b2h(authTag))
assert b2h(authTag) == bytes('8f85338aa0b13f48f8b17482dbb8acca', 'utf-8')
assert b2h(buf) == bytes('5ba7d42e1bf01d7998e932', "utf-8")
Expand Down Expand Up @@ -142,6 +149,7 @@ def test_invalid_tag_bytes():
gcm = AesGcmStream(key, iv, tag_bytes=good)
gcm.encrypt("hello world")
tag = gcm.final()
assert tag is not None
assert len(tag) == good

def test_decrypt_rejects_wrong_tag_length():
Expand All @@ -150,6 +158,7 @@ def test_decrypt_rejects_wrong_tag_length():
gcm = AesGcmStream(key, iv, tag_bytes=16)
buf = gcm.encrypt("hello world")
authTag = gcm.final()
assert authTag is not None
assert len(authTag) == 16

# Truncated tag: would silently lower the verification window to
Expand Down
5 changes: 3 additions & 2 deletions tests/test_asn.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA

# pylint: disable=redefined-outer-name
# ty: ignore[possibly-missing-import]

from collections import namedtuple
import pytest
Expand Down Expand Up @@ -90,8 +91,8 @@ def signature_vectors():
"51a4edaf9f1199f93e448482f27c43a53e0bc65b04e9848128e3"
"60314e864190e6bb9812bfbf4b40994f2c1d4ca7aad9"),
hash_cls=Sha256,
pub_key=RsaPublic.from_pem(pub_key_pem),
priv_key=RsaPrivate.from_pem(priv_key_pem)
pub_key=RsaPublic.from_pem(pub_key_pem), # ty: ignore[possibly-missing-attribute]
priv_key=RsaPrivate.from_pem(priv_key_pem) # ty: ignore[possibly-missing-attribute]
))

return vectors
Expand Down
2 changes: 1 addition & 1 deletion tests/test_chacha20poly1305.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from wolfcrypt.utils import t2b
from wolfcrypt.exceptions import WolfCryptError
from binascii import unhexlify as h2b
from wolfcrypt.ciphers import ChaCha20Poly1305
from wolfcrypt.ciphers import ChaCha20Poly1305 # ty: ignore[possibly-missing-import]

def test_encrypt_decrypt():
key = h2b("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f")
Expand Down
45 changes: 22 additions & 23 deletions tests/test_ciphers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA

# pylint: disable=redefined-outer-name
# ty: ignore[possibly-missing-import]

import os
import random
Expand Down Expand Up @@ -60,10 +61,8 @@

@pytest.fixture
def vectors():
TestVector = namedtuple("TestVector", """key iv plaintext ciphertext
ciphertext_ctr raw_key
pkcs8_key pem""")
TestVector.__new__.__defaults__ = (None,) * len(TestVector._fields)
fields = ("key", "iv", "plaintext", "ciphertext", "ciphertext_ctr", "raw_key", "pkcs8_key", "pem")
TestVector = namedtuple("TestVector", fields, defaults=(None,) * len(fields))

# test vector dictionary
vectorArray = {}
Expand All @@ -75,19 +74,19 @@ def vectors():
plaintext=t2b("now is the time "),
ciphertext=h2b("959492575f4281532ccc9d4677a233cb"),
ciphertext_ctr = h2b('287528ddf484b1055debbe751eb52b8a')
)
) # ty: ignore[missing-argument]
if _lib.CHACHA_ENABLED:
vectorArray[ChaCha]=TestVector(
key="0123456789abcdef01234567890abcdef",
iv="1234567890abcdef",
)
) # ty: ignore[missing-argument]
if _lib.DES3_ENABLED:
vectorArray[Des3]=TestVector(
key=h2b("0123456789abcdeffedeba987654321089abcdef01234567"),
iv=h2b("1234567890abcdef"),
plaintext=t2b("Now is the time for all "),
ciphertext=h2b("43a0297ed184f80e8964843212d508981894157487127db0")
)
) # ty: ignore[missing-argument]
if _lib.RSA_ENABLED:
vectorArray[RsaPublic]=TestVector(
key=h2b(
Expand All @@ -98,7 +97,7 @@ def vectors():
"0E22E96BA426BA4CE8C1FD4A6F2B1FEF8AAEF69062E5641EEB2B3C67C8DC"
"2700F6916865A90203010001"),
pem=os.path.join(certs_dir, "server-keyPub.pem")
)
) # ty: ignore[missing-argument]
vectorArray[RsaPrivate]=TestVector(
key=h2b(
"3082025C02010002818100BC730EA849F374A2A9EF18A5DA559921F9C8EC"
Expand Down Expand Up @@ -164,7 +163,7 @@ def vectors():
"1666d37c742b15b4a2febf086b1a5d3f"
"9012b105863129dbd9e2"),
pem=os.path.join(certs_dir, "server-key.pem")
)
) # ty: ignore[missing-argument]

if _lib.ECC_ENABLED:
vectorArray[EccPublic]=TestVector(
Expand All @@ -178,7 +177,7 @@ def vectors():
"55bff40f44509a3dce9bb7f0c54df5707bd4ec248e1980ec5a4ca22403622c9b"
"daefa2351243847616c6569506cc01a9bdf6751a42f7bda9b236225fc75d7fb4"
)
)
) # ty: ignore[missing-argument]
vectorArray[EccPrivate]=TestVector(
key=h2b(
"30770201010420F8CF926BBD1E28F1A8ABA1234F3274188850AD7EC7EC92"
Expand All @@ -192,41 +191,41 @@ def vectors():
"daefa2351243847616c6569506cc01a9bdf6751a42f7bda9b236225fc75d7fb4"
"f8cf926bbd1e28f1a8aba1234f3274188850ad7ec7ec92f88f974daf568965c7"
)
)
) # ty: ignore[missing-argument]

if _lib.ED25519_ENABLED:
vectorArray[Ed25519Private]=TestVector(
key = h2b(
"47CD22B276161AA18BA1E0D13DBE84FE4840E4395D784F555A92E8CF739B"
"F86B"
)
)
) # ty: ignore[missing-argument]
vectorArray[Ed25519Public]=TestVector(
key=h2b(
"8498C65F4841145F9C51E8BFF4504B5527E0D5753964B7CB3C707A2B9747"
"FC96"
)
)
) # ty: ignore[missing-argument]
if _lib.ED448_ENABLED:
vectorArray[Ed448Private]=TestVector(
key=h2b("c2b29804e9a893c9e275cac1f8a3033f3d4b78b79eb427ed359fdeb8"
"82d657c129c7930936b181971b795167ad18cabeeb52b59b94f115ad"
"59"
)
)
) # ty: ignore[missing-argument]
vectorArray[Ed448Public]=TestVector(
key=h2b("89fb2b5a5ab67dd317794cc5f1700cace295b043f3ad73a66299e10a"
"d3fc0a28289ddd1c641598a354113867a42e82ad844b4d858d92e4e7"
"80"
)
)
) # ty: ignore[missing-argument]
return vectorArray

algo_params = []
if _lib.AES_ENABLED:
algo_params.append(Aes)
algo_params.append(Aes) # ty: ignore[possibly-unresolved-reference]
if _lib.DES3_ENABLED:
algo_params.append(Des3)
algo_params.append(Des3) # ty: ignore[possibly-unresolved-reference]

@pytest.fixture(params=algo_params)
def cipher_cls(request):
Expand Down Expand Up @@ -320,7 +319,7 @@ def chacha_obj(vectors):
return r

@pytest.fixture
def test_chacha_enc_dec(chacha_obj):
def test_chacha_enc_dec(chacha_obj, vectors):
plaintext = t2b("Everyone gets Friday off.")
cyt = chacha_obj.encrypt(plaintext)
chacha_obj.set_iv(vectors[ChaCha].iv)
Expand Down Expand Up @@ -372,25 +371,25 @@ def rsa_public_pss(vectors):
def rsa_private_pem(vectors):
with open(vectors[RsaPrivate].pem, "rb") as f:
pem = f.read()
return RsaPrivate.from_pem(pem)
return RsaPrivate.from_pem(pem) # ty: ignore[possibly-missing-attribute]

@pytest.fixture
def rsa_public_pem(vectors):
with open(vectors[RsaPublic].pem, "rb") as f:
pem = f.read()
return RsaPublic.from_pem(pem)
return RsaPublic.from_pem(pem) # ty: ignore[possibly-missing-attribute]

@pytest.fixture
def rsa_private_pem_rng(vectors, rng):
with open(vectors[RsaPrivate].pem, "rb") as f:
pem = f.read()
return RsaPrivate.from_pem(pem, rng=rng)
return RsaPrivate.from_pem(pem, rng=rng) # ty: ignore[possibly-missing-attribute]

@pytest.fixture
def rsa_public_pem_rng(vectors, rng):
with open(vectors[RsaPublic].pem, "rb") as f:
pem = f.read()
return RsaPublic.from_pem(pem, rng=rng)
return RsaPublic.from_pem(pem, rng=rng) # ty: ignore[possibly-missing-attribute]

def test_new_rsa_raises(vectors):
with pytest.raises(WolfCryptError):
Expand All @@ -401,7 +400,7 @@ def test_new_rsa_raises(vectors):

if _lib.KEYGEN_ENABLED:
with pytest.raises(WolfCryptError): # invalid key size
RsaPrivate.make_key(16384)
RsaPrivate.make_key(16384) # ty: ignore[possibly-missing-attribute]


def test_rsa_encrypt_decrypt(rsa_private, rsa_public):
Expand Down
21 changes: 10 additions & 11 deletions tests/test_delete_descriptor_binding.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,39 +55,39 @@ def _static_attrs():
yield Random, "_delete"

if _lib.SHA_ENABLED:
from wolfcrypt.hashes import Sha
from wolfcrypt.hashes import Sha # ty: ignore[possibly-missing-import]
yield Sha, "_delete"
yield Sha, "_copy"
if _lib.SHA256_ENABLED:
from wolfcrypt.hashes import Sha256
from wolfcrypt.hashes import Sha256 # ty: ignore[possibly-missing-import]
yield Sha256, "_delete"
yield Sha256, "_copy"
if _lib.SHA384_ENABLED:
from wolfcrypt.hashes import Sha384
from wolfcrypt.hashes import Sha384 # ty: ignore[possibly-missing-import]
yield Sha384, "_delete"
yield Sha384, "_copy"
if _lib.SHA512_ENABLED:
from wolfcrypt.hashes import Sha512
from wolfcrypt.hashes import Sha512 # ty: ignore[possibly-missing-import]
yield Sha512, "_delete"
yield Sha512, "_copy"
if _lib.HMAC_ENABLED:
from wolfcrypt.hashes import _Hmac
from wolfcrypt.hashes import _Hmac # ty: ignore[possibly-missing-import]
yield _Hmac, "_delete"

if _lib.AESGCM_STREAM_ENABLED:
from wolfcrypt.ciphers import AesGcmStream
from wolfcrypt.ciphers import AesGcmStream # ty: ignore[possibly-missing-import]
yield AesGcmStream, "_delete"
if _lib.RSA_ENABLED:
from wolfcrypt.ciphers import _Rsa
from wolfcrypt.ciphers import _Rsa # ty: ignore[possibly-missing-import]
yield _Rsa, "_delete"
if _lib.ECC_ENABLED:
from wolfcrypt.ciphers import _Ecc
from wolfcrypt.ciphers import _Ecc # ty: ignore[possibly-missing-import]
yield _Ecc, "_delete"
if _lib.ED25519_ENABLED:
from wolfcrypt.ciphers import _Ed25519
from wolfcrypt.ciphers import _Ed25519 # ty: ignore[possibly-missing-import]
yield _Ed25519, "_delete"
if _lib.ED448_ENABLED:
from wolfcrypt.ciphers import _Ed448
from wolfcrypt.ciphers import _Ed448 # ty: ignore[possibly-missing-import]
yield _Ed448, "_delete"


Expand Down Expand Up @@ -173,7 +173,6 @@ def recorder(*args, **kwargs):
r = Random()
native = r.native_object
r.__del__()
r.native_object = None # prevent real cleanup on the way out
assert received, "recorder was never called"
args, kwargs = received[-1]
assert kwargs == {}
Expand Down
Loading
Loading