Address more review comments.

Tests are now also type checked as this helps verifying the correctness
of the type annotations.
pull/125/head
Robert de Vries 2026-06-11 22:42:55 +02:00
parent 1fb34e5cd8
commit 747243cc50
18 changed files with 376 additions and 59 deletions

View File

@ -27,6 +27,7 @@ wolfCrypt-py Release 5.9.2 (Jul 1, 2026)
* Fix issue in AES-GCM tag verification
* Address many small issues found by Fenrir
* Add reseed support to random number 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)

View File

@ -24,6 +24,7 @@ classifiers = [
dynamic = ["version"]
dependencies = [
"cffi>=1.17",
"typing-extensions",
]
[project.urls]
@ -143,7 +144,7 @@ python-version = "3.10"
root = ["."]
[tool.ty.src]
exclude = ["./lib", "./tests"]
exclude = ["./lib"]
[tool.ty.rules]
all = "warn"

View File

@ -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 contextlib import nullcontext
@ -38,6 +39,7 @@ if _lib.AESGCM_STREAM_ENABLED:
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)
@ -53,6 +55,7 @@ if _lib.AESGCM_STREAM_ENABLED:
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)
@ -67,6 +70,7 @@ if _lib.AESGCM_STREAM_ENABLED:
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)
@ -83,6 +87,7 @@ if _lib.AESGCM_STREAM_ENABLED:
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")
@ -101,6 +106,7 @@ if _lib.AESGCM_STREAM_ENABLED:
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)
@ -119,6 +125,7 @@ if _lib.AESGCM_STREAM_ENABLED:
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")
@ -152,6 +159,7 @@ if _lib.AESGCM_STREAM_ENABLED:
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():
@ -160,6 +168,7 @@ if _lib.AESGCM_STREAM_ENABLED:
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

View File

@ -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
@ -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

View File

@ -28,7 +28,7 @@ if _lib.CHACHA20_POLY1305_ENABLED:
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")

View File

@ -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
@ -60,10 +61,8 @@ if _lib.ED448_ENABLED:
@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 = {}
@ -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(
@ -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"
@ -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(
@ -178,7 +177,7 @@ def vectors():
"55bff40f44509a3dce9bb7f0c54df5707bd4ec248e1980ec5a4ca22403622c9b"
"daefa2351243847616c6569506cc01a9bdf6751a42f7bda9b236225fc75d7fb4"
)
)
) # ty: ignore[missing-argument]
vectorArray[EccPrivate]=TestVector(
key=h2b(
"30770201010420F8CF926BBD1E28F1A8ABA1234F3274188850AD7EC7EC92"
@ -192,7 +191,7 @@ def vectors():
"daefa2351243847616c6569506cc01a9bdf6751a42f7bda9b236225fc75d7fb4"
"f8cf926bbd1e28f1a8aba1234f3274188850ad7ec7ec92f88f974daf568965c7"
)
)
) # ty: ignore[missing-argument]
if _lib.ED25519_ENABLED:
vectorArray[Ed25519Private]=TestVector(
@ -200,33 +199,33 @@ def vectors():
"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):
@ -320,7 +319,7 @@ if _lib.CHACHA_ENABLED:
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)
@ -372,25 +371,25 @@ if _lib.RSA_ENABLED:
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):
@ -401,7 +400,7 @@ if _lib.RSA_ENABLED:
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):

View File

@ -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"

View File

@ -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
@ -116,26 +117,26 @@ def vectors():
hash_params = []
if _lib.SHA_ENABLED:
hash_params.append(Sha)
hash_params.append(Sha) # ty: ignore[possibly-unresolved-reference]
if _lib.SHA256_ENABLED:
hash_params.append(Sha256)
hash_params.append(Sha256) # ty: ignore[possibly-unresolved-reference]
if _lib.SHA384_ENABLED:
hash_params.append(Sha384)
hash_params.append(Sha384) # ty: ignore[possibly-unresolved-reference]
if _lib.SHA512_ENABLED:
hash_params.append(Sha512)
hash_params.append(Sha512) # ty: ignore[possibly-unresolved-reference]
if _lib.SHA3_ENABLED:
hash_params.append(Sha3)
hash_params.append(Sha3) # ty: ignore[possibly-unresolved-reference]
hmac_params = []
if _lib.HMAC_ENABLED:
if _lib.SHA_ENABLED:
hmac_params.append(HmacSha)
hmac_params.append(HmacSha) # ty: ignore[possibly-unresolved-reference]
if _lib.SHA256_ENABLED:
hmac_params.append(HmacSha256)
hmac_params.append(HmacSha256) # ty: ignore[possibly-unresolved-reference]
if _lib.SHA384_ENABLED:
hmac_params.append(HmacSha384)
hmac_params.append(HmacSha384) # ty: ignore[possibly-unresolved-reference]
if _lib.SHA512_ENABLED:
hmac_params.append(HmacSha512)
hmac_params.append(HmacSha512) # ty: ignore[possibly-unresolved-reference]
@pytest.fixture(params=(hash_params + hmac_params))
def hash_cls(request):

View File

@ -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 pytest

View File

@ -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
@ -202,7 +203,7 @@ if _lib.ML_DSA_ENABLED:
# test that the seed type is checked (should be bytes-like, not string)
with pytest.raises(TypeError):
_ = mldsa_priv.sign_with_seed(message, " " * ML_DSA_SIGNATURE_SEED_LENGTH)
_ = mldsa_priv.sign_with_seed(message, "") # ty: ignore[invalid-argument-type]
def test_sign_with_seed_and_context(mldsa_type, rng):
signature_seed = rng.bytes(ML_DSA_SIGNATURE_SEED_LENGTH)

View File

@ -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

View File

@ -19,6 +19,8 @@
# 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
from wolfcrypt._ffi import lib as _lib

View File

@ -1,3 +1,23 @@
# __init__.pyi
#
# Copyright (C) 2026 wolfSSL Inc.
#
# This file is part of wolfSSL. (formerly known as CyaSSL)
#
# wolfSSL is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# wolfSSL is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
import _cffi_backend
import wolfcrypt._ffi.lib as lib

View File

@ -1,3 +1,22 @@
# lib.pyi
#
# Copyright (C) 2026 wolfSSL Inc.
#
# This file is part of wolfSSL. (formerly known as CyaSSL)
#
# wolfSSL is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# wolfSSL is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
from _cffi_backend import FFI
from typing import TypeAlias
@ -34,6 +53,224 @@ SHA384_ENABLED: int
SHA512_ENABLED: int
WC_RNG_SEED_CB_ENABLED: int
# Error codes
WC_FAILURE: int
MAX_CODE_E: int
WC_FIRST_E: int
WC_SPAN1_FIRST_E: int
MP_MEM: int
MP_VAL: int
MP_WOULDBLOCK: int
MP_NOT_INF: int
OPEN_RAN_E: int
READ_RAN_E: int
WINCRYPT_E: int
CRYPTGEN_E: int
RAN_BLOCK_E: int
BAD_MUTEX_E: int
WC_TIMEOUT_E: int
WC_PENDING_E: int
WC_NO_PENDING_E: int
MP_INIT_E: int
MP_READ_E: int
MP_EXPTMOD_E: int
MP_TO_E: int
MP_SUB_E: int
MP_ADD_E: int
MP_MUL_E: int
MP_MULMOD_E: int
MP_MOD_E: int
MP_INVMOD_E: int
MP_CMP_E: int
MP_ZERO_E: int
AES_EAX_AUTH_E: int
KEY_EXHAUSTED_E: int
MEMORY_E: int
VAR_STATE_CHANGE_E: int
FIPS_DEGRADED_E: int
FIPS_CODE_SZ_E: int
FIPS_DATA_SZ_E: int
RSA_WRONG_TYPE_E: int
RSA_BUFFER_E: int
BUFFER_E: int
ALGO_ID_E: int
PUBLIC_KEY_E: int
DATE_E: int
SUBJECT_E: int
ISSUER_E: int
CA_TRUE_E: int
EXTENSIONS_E: int
ASN_PARSE_E: int
ASN_VERSION_E: int
ASN_GETINT_E: int
ASN_RSA_KEY_E: int
ASN_OBJECT_ID_E: int
ASN_TAG_NULL_E: int
ASN_EXPECT_0_E: int
ASN_BITSTR_E: int
ASN_UNKNOWN_OID_E: int
ASN_DATE_SZ_E: int
ASN_BEFORE_DATE_E: int
ASN_AFTER_DATE_E: int
ASN_SIG_OID_E: int
ASN_TIME_E: int
ASN_INPUT_E: int
ASN_SIG_CONFIRM_E: int
ASN_SIG_HASH_E: int
ASN_SIG_KEY_E: int
ASN_DH_KEY_E: int
KDF_SRTP_KAT_FIPS_E: int
ASN_CRIT_EXT_E: int
ASN_ALT_NAME_E: int
ASN_NO_PEM_HEADER: int
ED25519_KAT_FIPS_E: int
ED448_KAT_FIPS_E: int
PBKDF2_KAT_FIPS_E: int
WC_KEY_MISMATCH_E: int
ECC_BAD_ARG_E: int
ASN_ECC_KEY_E: int
ECC_CURVE_OID_E: int
BAD_FUNC_ARG: int
NOT_COMPILED_IN: int
UNICODE_SIZE_E: int
NO_PASSWORD: int
ALT_NAME_E: int
BAD_OCSP_RESPONDER: int
CRL_CERT_DATE_ERR: int
AES_GCM_AUTH_E: int
AES_CCM_AUTH_E: int
ASYNC_INIT_E: int
COMPRESS_INIT_E: int
COMPRESS_E: int
DECOMPRESS_INIT_E: int
DECOMPRESS_E: int
BAD_ALIGN_E: int
ASN_NO_SIGNER_E: int
ASN_CRL_CONFIRM_E: int
ASN_CRL_NO_SIGNER_E: int
ASN_OCSP_CONFIRM_E: int
BAD_STATE_E: int
BAD_PADDING_E: int
REQ_ATTRIBUTE_E: int
PKCS7_OID_E: int
PKCS7_RECIP_E: int
FIPS_NOT_ALLOWED_E: int
ASN_NAME_INVALID_E: int
RNG_FAILURE_E: int
HMAC_MIN_KEYLEN_E: int
RSA_PAD_E: int
LENGTH_ONLY_E: int
IN_CORE_FIPS_E: int
AES_KAT_FIPS_E: int
DES3_KAT_FIPS_E: int
HMAC_KAT_FIPS_E: int
RSA_KAT_FIPS_E: int
DRBG_KAT_FIPS_E: int
DRBG_CONT_FIPS_E: int
AESGCM_KAT_FIPS_E: int
THREAD_STORE_KEY_E: int
THREAD_STORE_SET_E: int
MAC_CMP_FAILED_E: int
IS_POINT_E: int
ECC_INF_E: int
ECC_PRIV_KEY_E: int
ECC_OUT_OF_RANGE_E: int
SRP_CALL_ORDER_E: int
SRP_VERIFY_E: int
SRP_BAD_KEY_E: int
ASN_NO_SKID: int
ASN_NO_AKID: int
ASN_NO_KEYUSAGE: int
SKID_E: int
AKID_E: int
KEYUSAGE_E: int
CERTPOLICIES_E: int
WC_INIT_E: int
SIG_VERIFY_E: int
BAD_COND_E: int
SIG_TYPE_E: int
HASH_TYPE_E: int
FIPS_INVALID_VER_E: int
WC_KEY_SIZE_E: int
ASN_COUNTRY_SIZE_E: int
MISSING_RNG_E: int
ASN_PATHLEN_SIZE_E: int
ASN_PATHLEN_INV_E: int
BAD_KEYWRAP_ALG_E: int
BAD_KEYWRAP_IV_E: int
WC_CLEANUP_E: int
ECC_CDH_KAT_FIPS_E: int
DH_CHECK_PUB_E: int
BAD_PATH_ERROR: int
ASYNC_OP_E: int
ECC_PRIVATEONLY_E: int
EXTKEYUSAGE_E: int
WC_HW_E: int
WC_HW_WAIT_E: int
PSS_SALTLEN_E: int
PRIME_GEN_E: int
BER_INDEF_E: int
RSA_OUT_OF_RANGE_E: int
RSAPSS_PAT_FIPS_E: int
ECDSA_PAT_FIPS_E: int
DH_KAT_FIPS_E: int
AESCCM_KAT_FIPS_E: int
SHA3_KAT_FIPS_E: int
ECDHE_KAT_FIPS_E: int
AES_GCM_OVERFLOW_E: int
AES_CCM_OVERFLOW_E: int
RSA_KEY_PAIR_E: int
DH_CHECK_PRIV_E: int
WC_AFALG_SOCK_E: int
WC_DEVCRYPTO_E: int
ZLIB_INIT_ERROR: int
ZLIB_COMPRESS_ERROR: int
ZLIB_DECOMPRESS_ERROR: int
PKCS7_NO_SIGNER_E: int
WC_PKCS7_WANT_READ_E: int
CRYPTOCB_UNAVAILABLE: int
PKCS7_SIGNEEDS_CHECK: int
PSS_SALTLEN_RECOVER_E: int
CHACHA_POLY_OVERFLOW: int
ASN_SELF_SIGNED_E: int
SAKKE_VERIFY_FAIL_E: int
MISSING_IV: int
MISSING_KEY: int
BAD_LENGTH_E: int
ECDSA_KAT_FIPS_E: int
RSA_PAT_FIPS_E: int
KDF_TLS12_KAT_FIPS_E: int
KDF_TLS13_KAT_FIPS_E: int
KDF_SSH_KAT_FIPS_E: int
DHE_PCT_E: int
ECC_PCT_E: int
FIPS_PRIVATE_KEY_LOCKED_E: int
PROTOCOLCB_UNAVAILABLE: int
AES_SIV_AUTH_E: int
NO_VALID_DEVID: int
IO_FAILED_E: int
SYSLIB_FAILED_E: int
USE_HW_PSK: int
ENTROPY_RT_E: int
ENTROPY_APT_E: int
ASN_DEPTH_E: int
ASN_LEN_E: int
SM4_GCM_AUTH_E: int
SM4_CCM_AUTH_E: int
WC_SPAN1_LAST_E: int
WC_SPAN1_MIN_CODE_E: int
WC_SPAN2_FIRST_E: int
DEADLOCK_AVERTED_E: int
ASCON_AUTH_E: int
WC_ACCEL_INHIBIT_E: int
BAD_INDEX_E: int
INTERRUPTED_E: int
WC_SPAN2_LAST_E: int
WC_LAST_E: int
WC_SPAN2_MIN_CODE_E: int
MIN_CODE_E: int
# end of the error codes
FIPS_VERSION: int
WC_MGF1NONE: int

View File

@ -28,6 +28,7 @@ from wolfcrypt._ffi import ffi as _ffi
from wolfcrypt._ffi import lib as _lib
from wolfcrypt.exceptions import WolfCryptError, WolfCryptApiError
from wolfcrypt.hashes import _Hash
from .types import SupportsRsaSign, SupportsRsaVerify
if _lib.SHA_ENABLED:
from wolfcrypt.hashes import Sha # ty: ignore[possibly-missing-import]
@ -78,7 +79,7 @@ if _lib.ASN_ENABLED:
else:
raise WolfCryptError(f"Unknown hash class {hash_cls.__name__}")
def make_signature(data: bytes, hash_cls: type[_Hash], key = None) -> bytes:
def make_signature(data: bytes, hash_cls: type[_Hash], key: SupportsRsaSign | None = None) -> bytes:
hash_obj = hash_cls()
hash_obj.update(data)
digest = hash_obj.digest()
@ -96,7 +97,7 @@ if _lib.ASN_ENABLED:
else:
return plaintext_sig
def check_signature(signature: bytes, data: bytes, hash_cls: type[_Hash], pub_key) -> bool:
def check_signature(signature: bytes, data: bytes, hash_cls: type[_Hash], pub_key: SupportsRsaVerify) -> bool:
computed_signature = make_signature(data, hash_cls)
decrypted_signature = pub_key.verify(signature)
return _hmac.compare_digest(computed_signature, decrypted_signature)

View File

@ -23,6 +23,7 @@
from __future__ import annotations
from abc import ABC, abstractmethod
from collections.abc import Sequence
from enum import IntEnum
from typing_extensions import override
@ -32,6 +33,7 @@ from wolfcrypt.exceptions import WolfCryptError, WolfCryptApiError
from wolfcrypt.hashes import hash_type_to_cls
from wolfcrypt.random import Random
from wolfcrypt.utils import BytesOrStr, t2b
from .types import SupportsRsaSign, SupportsRsaVerify
if _lib.ASN_ENABLED:
from wolfcrypt.asn import pem_to_der # ty: ignore[possibly-missing-import]
@ -185,7 +187,7 @@ class _Cipher(ABC):
def _decrypt(self, destination: _ffi.CData, source: bytes) -> int: ...
@classmethod
def new(cls, key: BytesOrStr, mode: int, IV: BytesOrStr | None = None) -> _Cipher: # pylint: disable=W0613
def new(cls, key: BytesOrStr, mode: int, IV: BytesOrStr | None = None, **kwargs: int) -> _Cipher: # pylint: disable=W0613
"""
Returns a ciphering object, using the secret key contained in
the string **key**, and using the feedback mode **mode**, which
@ -326,7 +328,7 @@ if _lib.AES_SIV_ENABLED:
if len(self._key) not in AesSiv._key_sizes:
raise ValueError(f"key must be {AesSiv._key_sizes} in length, not {len(self._key)}")
def encrypt(self, associated_data: BytesOrStr | list[BytesOrStr], nonce: BytesOrStr, plaintext: BytesOrStr) -> tuple[bytes, bytes]:
def encrypt(self, associated_data: BytesOrStr | Sequence[bytes] | Sequence[bytearray] | Sequence[str] | Sequence[memoryview], nonce: BytesOrStr, plaintext: BytesOrStr) -> tuple[bytes, bytes]:
"""
Encrypt plaintext data using the nonce provided. The associated
data is not encrypted but is included in the authentication tag.
@ -353,7 +355,7 @@ if _lib.AES_SIV_ENABLED:
raise WolfCryptApiError("AES-SIV encryption error", ret)
return _ffi.buffer(siv)[:], _ffi.buffer(ciphertext)[:]
def decrypt(self, associated_data: BytesOrStr | list[BytesOrStr], nonce: BytesOrStr, siv: BytesOrStr, ciphertext: BytesOrStr) -> bytes:
def decrypt(self, associated_data: BytesOrStr | Sequence[bytes] | Sequence[bytearray] | Sequence[str] | Sequence[memoryview], nonce: BytesOrStr, siv: BytesOrStr, ciphertext: BytesOrStr) -> bytes:
"""
Decrypt the ciphertext using the nonce and SIV provided.
The integrity of the associated data is checked.
@ -383,7 +385,7 @@ if _lib.AES_SIV_ENABLED:
return _ffi.buffer(plaintext)[:]
@staticmethod
def _prepare_associated_data(associated_data: BytesOrStr | list[BytesOrStr]) -> tuple[_ffi.CData, bytes | list[bytes]]:
def _prepare_associated_data(associated_data: BytesOrStr | Sequence[bytes] | Sequence[bytearray] | Sequence[str] | Sequence[memoryview]) -> tuple[_ffi.CData, bytes | list[bytes]]:
"""
Prepare associated data for sending to C library.
@ -801,7 +803,7 @@ if _lib.RSA_ENABLED:
class RsaPublic(_Rsa):
class RsaPublic(_Rsa, SupportsRsaVerify):
def __init__(self, key: BytesOrStr, hash_type: int | None = None, rng: Random | None = None) -> None:
super().__init__(rng)
@ -870,6 +872,7 @@ if _lib.RSA_ENABLED:
return _ffi.buffer(ciphertext)[:]
@override
def verify(self, signature: BytesOrStr) -> bytes:
"""
Verifies **signature**, using the public key data in the
@ -933,7 +936,7 @@ if _lib.RSA_ENABLED:
return ret == 0
class RsaPrivate(RsaPublic):
class RsaPrivate(RsaPublic, SupportsRsaSign):
if _lib.KEYGEN_ENABLED:
@classmethod
def make_key(cls, size: int, rng: Random | None = None, hash_type: int | None = None) -> RsaPrivate:
@ -1067,6 +1070,7 @@ if _lib.RSA_ENABLED:
return _ffi.buffer(plaintext, ret)[:]
@override
def sign(self, plaintext: BytesOrStr) -> bytes:
"""
Signs **plaintext**, using the private key data in the object.
@ -1424,7 +1428,7 @@ if _lib.ECC_ENABLED:
return _ffi.buffer(Qx, qx_size[0])[:], _ffi.buffer(Qy,
qy_size[0])[:], _ffi.buffer(d, d_size[0])[:]
def shared_secret(self, peer: EccPrivate) -> bytes:
def shared_secret(self, peer: EccPublic) -> bytes:
"""
Generates a new secret key using the private key data in the object
and the peer's public key.
@ -2562,6 +2566,9 @@ if _lib.ML_DSA_ENABLED:
out_size = _ffi.new("word32 *")
out_size[0] = in_size
if not isinstance(seed, (list, tuple, bytes)):
raise TypeError("seed must be bytes or list/tuple")
if len(seed) != ML_DSA_SIGNATURE_SEED_LENGTH:
raise ValueError(
f"Seed for generating a signature must be {ML_DSA_SIGNATURE_SEED_LENGTH} bytes."

View File

@ -30,7 +30,7 @@ from wolfcrypt.utils import t2b
if _lib.HKDF_ENABLED:
from wolfcrypt.hashes import _Hmac # ty: ignore[possibly-missing-import]
def HKDF(hash_cls: _Hmac, in_key: bytes | str, salt: bytes | str | None = None, info: bytes | str | None = None, out_len: int | None = None) -> bytes:
def HKDF(hash_cls: type[_Hmac], in_key: bytes | str, salt: bytes | str | None = None, info: bytes | str | None = None, out_len: int | None = None) -> bytes:
"""
Perform HKDF Extract-and-Expand in one call (wraps wc_HKDF).
@ -75,7 +75,7 @@ if _lib.HKDF_ENABLED:
return _ffi.buffer(out, out_len)[:]
def HKDF_Extract(hash_cls: _Hmac, salt: bytes | str | None, in_key: bytes | str) -> bytes:
def HKDF_Extract(hash_cls: type[_Hmac], salt: bytes | str | None, in_key: bytes | str) -> bytes:
"""
HKDF-Extract: PRK = HMAC-Hash(salt, IKM)
Wraps wc_HKDF_Extract.
@ -102,7 +102,7 @@ if _lib.HKDF_ENABLED:
return _ffi.buffer(out, out_len)[:]
def HKDF_Expand(hash_cls: _Hmac, prk: bytes | str, info: bytes | str | None, out_len: int) -> bytes:
def HKDF_Expand(hash_cls: type[_Hmac], prk: bytes | str, info: bytes | str | None, out_len: int) -> bytes:
"""
HKDF-Expand: OKM = HKDF-Expand(PRK, info, L)
Wraps wc_HKDF_Expand.

35
wolfcrypt/types.py 100644
View File

@ -0,0 +1,35 @@
# types.py
#
# Copyright (C) 2026 wolfSSL Inc.
#
# This file is part of wolfSSL. (formerly known as CyaSSL)
#
# wolfSSL is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# wolfSSL is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
from abc import abstractmethod
from typing import Protocol
from .utils import BytesOrStr
class SupportsRsaSign(Protocol):
@abstractmethod
def sign(self, plaintext: BytesOrStr) -> bytes:
raise NotImplementedError
class SupportsRsaVerify(Protocol):
@abstractmethod
def verify(self, signature: BytesOrStr) -> bytes:
raise NotImplementedError