Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
2e03fa6
Disarm ChaCha when set_iv() rejects the nonce length (F-10069)
julek-wolfssl Sep 24, 2026
ebd1f76
Validate ECC public keys with wc_ecc_check_key on import (F-8277)
julek-wolfssl Sep 24, 2026
e0f0bc9
Size ECC private key DER buffer with wc_EccKeyDerSize (F-10070)
julek-wolfssl Sep 24, 2026
c9685f9
Detect AES-CTR support before declaring wc_AesCtrEncrypt (F-11250)
julek-wolfssl Sep 24, 2026
e52e769
Declare AES-CBC functions only when wolfSSL provides them (F-12224)
julek-wolfssl Sep 24, 2026
4a612c7
Gate streaming AES-GCM decryption on wolfSSL decrypt support (F-12225)
julek-wolfssl Sep 24, 2026
af6c329
Declare SHA-3 functions only for sizes wolfSSL provides (F-12226)
julek-wolfssl Sep 24, 2026
c505da1
Detect HKDF only when wolfSSL also provides HMAC (F-13020)
julek-wolfssl Sep 24, 2026
d121fb3
Declare wc_PBKDF2 only when wolfSSL builds PBKDF2 with HMAC (F-12232)
julek-wolfssl Sep 24, 2026
3480917
Detect PEM-to-DER and DER-to-PEM support separately (F-12233)
julek-wolfssl Sep 24, 2026
d3b631d
Declare wc_GetPkcs8TraditionalOffset only with PKCS#8 support (F-12234)
julek-wolfssl Sep 24, 2026
78bf17e
Declare RNG functions only when wolfSSL builds its RNG (F-13021)
julek-wolfssl Sep 24, 2026
6b5f504
Declare RSA operations only when wolfSSL builds them (F-12227)
julek-wolfssl Sep 24, 2026
f9345e9
Declare ECC operations only when wolfSSL builds them (F-12228)
julek-wolfssl Sep 24, 2026
54ef5c7
Validate ECC private keys with wc_ecc_check_key on import
julek-wolfssl Sep 28, 2026
394cc0d
Declare Ed25519 operations only when wolfSSL builds them (F-12229)
julek-wolfssl Sep 24, 2026
652447f
Declare Ed448 operations only when wolfSSL builds them (F-12230)
julek-wolfssl Sep 24, 2026
25ef90f
Declare ML-KEM operations only when wolfSSL builds them (F-12231)
julek-wolfssl Sep 24, 2026
03466c1
Declare ML-DSA operations only when wolfSSL builds them (F-10071)
julek-wolfssl Sep 24, 2026
e093833
Detect ML-DSA no-context macros on the last line (F-13023)
julek-wolfssl Sep 28, 2026
c9da5a6
Detect feature macros regardless of indentation or value (F-8281)
julek-wolfssl Sep 28, 2026
5415734
Check the digest size in ECDSA sign() and verify() (F-8279)
julek-wolfssl Sep 24, 2026
7d88511
Check the digest size in ECDSA sign_raw() and verify_raw() (F-8280)
julek-wolfssl Sep 24, 2026
ed5cc03
Build bundled wolfSSL with -fPIC on every Linux platform
julek-wolfssl Sep 25, 2026
d1f4fdb
Run CI on arm64 Linux too
julek-wolfssl Sep 25, 2026
cccacb7
Address review: create RSA RNG on first use
julek-wolfssl Sep 29, 2026
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
7 changes: 6 additions & 1 deletion .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ permissions:
jobs:
build:

runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, ubuntu-24.04-arm ]

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v7
Expand Down
755 changes: 565 additions & 190 deletions scripts/build_ffi.py

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions tests/test_aesgcmstream.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
from binascii import hexlify as b2h
from wolfcrypt.ciphers import AesGcmStream

def skip_without_decrypt():
if not _lib.AESGCM_STREAM_DECRYPT_ENABLED:
pytest.skip("AES-GCM streaming decryption is not compiled in")

def test_encrypt():
"""Known answer encrypt-decrypt test with default authentication tag size of 16 bytes"""
key = "fedcba9876543210"
Expand All @@ -42,6 +46,7 @@ def test_encrypt():
assert authTag is not None
assert b2h(authTag) == bytes('ac8fcee96dc6ef8e5236da19b6197d2e', 'utf-8')
assert b2h(buf) == bytes('5ba7d42e1bf01d7998e932', "utf-8")
skip_without_decrypt()
gcmdec = AesGcmStream(key, iv)
bufdec = gcmdec.decrypt(buf)
gcmdec.final(authTag)
Expand All @@ -58,6 +63,7 @@ def test_encrypt_short_tag():
assert authTag is not None
assert b2h(authTag) == bytes('ac8fcee96dc6ef8e5236da19', 'utf-8')
assert b2h(buf) == bytes('5ba7d42e1bf01d7998e932', "utf-8")
skip_without_decrypt()
gcmdec = AesGcmStream(key, iv, 12)
bufdec = gcmdec.decrypt(buf)
gcmdec.final(authTag)
Expand All @@ -73,6 +79,7 @@ def test_multipart():
assert authTag is not None
assert b2h(authTag) == bytes('ac8fcee96dc6ef8e5236da19b6197d2e', 'utf-8')
assert b2h(buf) == bytes('5ba7d42e1bf01d7998e932', "utf-8")
skip_without_decrypt()
gcmdec = AesGcmStream(key, iv)
bufdec = gcmdec.decrypt(buf[:5])
bufdec += gcmdec.decrypt(buf[5:])
Expand All @@ -91,6 +98,7 @@ def test_encrypt_aad():
print(b2h(authTag))
assert b2h(authTag) == bytes('8f85338aa0b13f48f8b17482dbb8acca', 'utf-8')
assert b2h(buf) == bytes('5ba7d42e1bf01d7998e932', "utf-8")
skip_without_decrypt()
gcmdec = AesGcmStream(key, iv)
gcmdec.set_aad(aad)
bufdec = gcmdec.decrypt(buf)
Expand All @@ -109,6 +117,7 @@ def test_multipart_aad():
assert authTag is not None
assert b2h(authTag) == bytes('8f85338aa0b13f48f8b17482dbb8acca', 'utf-8')
assert b2h(buf) == bytes('5ba7d42e1bf01d7998e932', "utf-8")
skip_without_decrypt()
gcmdec = AesGcmStream(key, iv)
gcmdec.set_aad(aad)
bufdec = gcmdec.decrypt(buf[:5])
Expand All @@ -117,6 +126,7 @@ def test_multipart_aad():
assert bufdec == t2b("hello world")

def test_encrypt_aad_bad():
skip_without_decrypt()
key = "fedcba9876543210"
iv = "0123456789abcdef"
aad = "aad data"
Expand Down Expand Up @@ -163,6 +173,7 @@ def test_invalid_tag_bytes():
assert len(tag) == good

def test_decrypt_rejects_wrong_tag_length():
skip_without_decrypt()
key = "fedcba9876543210"
iv = "0123456789abcdef"
gcm = AesGcmStream(key, iv, tag_bytes=16)
Expand Down Expand Up @@ -200,3 +211,15 @@ def test_repeated_construction_destruction():
gcm.final()
del gcm
gc.collect()

def test_decrypt_rejected_when_not_compiled_in(monkeypatch):
"""F-12225: streaming decryption needs AES-GCM decryption in the linked wolfSSL."""
monkeypatch.setattr(_lib, "AESGCM_STREAM_DECRYPT_ENABLED", 0)
key = "fedcba9876543210"
iv = "0123456789abcdef"
gcm = AesGcmStream(key, iv)
with pytest.raises(NotImplementedError, match="AES-GCM streaming decryption"):
gcm.decrypt(bytes.fromhex("5ba7d42e1bf01d7998e932"))
# The rejected call leaves the object usable for encryption.
assert gcm.encrypt("hello world") == bytes.fromhex("5ba7d42e1bf01d7998e932")
assert gcm.final() == bytes.fromhex("ac8fcee96dc6ef8e5236da19b6197d2e")
41 changes: 35 additions & 6 deletions tests/test_asn.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@
# ty: ignore[possibly-missing-import]

from collections import namedtuple
import importlib.util
import pytest
import os
from wolfcrypt import asn
from wolfcrypt._ffi import lib as _lib
from wolfcrypt.utils import h2b

if _lib.ASN_ENABLED:
from wolfcrypt.asn import pem_to_der, der_to_pem, make_signature, check_signature
from wolfcrypt.asn import make_signature, check_signature
if _lib.PEM_TO_DER_ENABLED:
from wolfcrypt.asn import pem_to_der
if _lib.DER_TO_PEM_ENABLED:
from wolfcrypt.asn import der_to_pem
if _lib.SHA256_ENABLED:
from wolfcrypt.hashes import Sha256
if _lib.RSA_ENABLED:
Expand Down Expand Up @@ -77,7 +83,9 @@ def signature_vectors():
# Signature computed with:
# echo -n "wolfcrypt is the best crypto around" | \
# openssl dgst -hex -sha256 -sign tests/certs/server-key.pem
if _lib.ASN_ENABLED and _lib.SHA256_ENABLED and _lib.RSA_ENABLED:
# RSA key objects always create a Random.
if (_lib.PEM_TO_DER_ENABLED and _lib.SHA256_ENABLED and _lib.RSA_ENABLED and _lib.RNG_ENABLED
and _lib.RSA_SIGN_ENABLED and _lib.RSA_VERIFY_ENABLED):
vectors.append(TestVector(
data="wolfcrypt is the best crypto around",
signature=h2b("1d65f21df8fdc9f3c2351792840423481c6b0f2332105abd9248"
Expand All @@ -99,14 +107,35 @@ def signature_vectors():

def test_pem_der_conversion(pem_der_conversion_vectors):
for vector in pem_der_conversion_vectors:
computed_der = pem_to_der(vector.pem, vector.type)
assert computed_der == vector.der
if _lib.PEM_TO_DER_ENABLED:
computed_der = pem_to_der(vector.pem, vector.type)
assert computed_der == vector.der

computed_pem = der_to_pem(vector.der, vector.type)
assert computed_pem == vector.pem
if _lib.DER_TO_PEM_ENABLED:
computed_pem = der_to_pem(vector.der, vector.type)
assert computed_pem == vector.pem

def test_signature(signature_vectors):
for vector in signature_vectors:
assert make_signature(vector.data, vector.hash_cls, vector.priv_key) == vector.signature
assert check_signature(vector.signature, vector.data, vector.hash_cls,
vector.pub_key)

def test_pem_der_helpers_defined_only_when_enabled(monkeypatch):
"""F-12233: each conversion direction needs its own wolfSSL function."""
helpers = {"PEM_TO_DER_ENABLED": "pem_to_der", "DER_TO_PEM_ENABLED": "der_to_pem"}
for flag, name in helpers.items():
assert hasattr(asn, name) == bool(getattr(_lib, flag)), name

# Load fresh copies of the module as if one direction were not compiled in.
for disabled, disabled_name in helpers.items():
with monkeypatch.context() as m:
m.setattr(_lib, disabled, 0)
spec = importlib.util.find_spec("wolfcrypt.asn")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
assert not hasattr(module, disabled_name), disabled
for flag, name in helpers.items():
if flag != disabled:
assert hasattr(module, name) == bool(getattr(_lib, flag)), (disabled, name)
assert hasattr(module, "make_signature") == bool(_lib.ASN_ENABLED), disabled
Loading
Loading