Skip to content

Add initial support for crypto callbacks.#114

Open
roberthdevries wants to merge 3 commits into
wolfSSL:masterfrom
roberthdevries:add-support-for-crypto-callbacks
Open

Add initial support for crypto callbacks.#114
roberthdevries wants to merge 3 commits into
wolfSSL:masterfrom
roberthdevries:add-support-for-crypto-callbacks

Conversation

@roberthdevries

Copy link
Copy Markdown
Contributor

This patch does not add support for types of callbacks. Adding support for callbacks for cipher functions is complicated by issues with cffi code generation for the wc_CryptoInfo structure with two layers of anonymous unions.
Uncommenting more parts of the cipher struct causes errors regarding conflicting struct sizes of other parts of the wc_CryptoInfo struct.
Cffi cdef and the compiler seem to disagree.

However as it is, callbacks work for random and hash functions.

@JeremiahM37 JeremiahM37 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skoll Code Review

Scan type: review
Overall recommendation: REQUEST_CHANGES
Findings: 14 total — 9 posted, 5 skipped
7 finding(s) posted as inline comments (see file-level comments below)

Posted findings

  • [High] Importing wolfcrypt fails when CRYPTO_CB is disabled at build timewolfcrypt/__init__.py:49 and wolfcrypt/cryptocb.py:33-58
  • [High] Double unregister of device on context-manager exit + GCwolfcrypt/cryptocb.py:73-77, 117-118
  • [Medium] Return value of wolfCrypt_Init not checkedwolfcrypt/__init__.py:52
  • [Medium] Debug print() statements left in production callback dispatcherwolfcrypt/cryptocb.py:80, 84, 85, 107, 111
  • [Medium] Hash digest dict KeyError on unmapped hash typeswolfcrypt/cryptocb.py:84, 90
  • [Medium] Digest output buffer not size-validatedwolfcrypt/cryptocb.py:90
  • [Medium] RNG output length not validatedwolfcrypt/cryptocb.py:96-97
  • [Medium] No tests for hash callbacks despite PR claimtests/test_cryptocb.py
  • [Low] Free-floating string literal used as 'comment' in build_ffi.py is misleadingscripts/build_ffi.py:1376-1412

Skipped findings

  • [Medium] Anonymous-union cdef relies on '...' partial layout — may not match reality
  • [Low] ctx==NULL fallback would never be reached — wrong return code
  • [Low] Type annotations on overridable methods declare positional args with leading underscores
  • [Low] rng_callback signature passes raw cffi pointer with no type annotation
  • [Info] Stale exception import in init.py guard

Review generated by Skoll

Comment thread wolfcrypt/cryptocb.py Outdated
Comment thread wolfcrypt/__init__.py Outdated
Comment thread wolfcrypt/cryptocb.py Outdated
Comment thread wolfcrypt/cryptocb.py
Comment thread wolfcrypt/cryptocb.py
Comment thread wolfcrypt/cryptocb.py
Comment thread scripts/build_ffi.py Outdated
Comment thread wolfcrypt/cryptocb.py Outdated
@dgarske

dgarske commented May 5, 2026

Copy link
Copy Markdown
Member

Hi @roberthdevries nice work on this. See peer review feedback and also fix merge conflicts. Thanks

* Comment out code in build_ffi.py instead of quoted string.
* Check return value of wolfCrypt_init()
* Complete algorithm type hash table
* Change dicts into defaultdicts with a sensible value when key is missing.
* Convert debug print to debug log messages
* Add length checks to produce nicer exceptions.
* Add if _lib.CRYPT_CB_ENABLED guards around code that depends on that
  configuration option being enabled.
@roberthdevries roberthdevries force-pushed the add-support-for-crypto-callbacks branch from e10a1d3 to 1f974ad Compare May 18, 2026 18:08

@dgarske dgarske left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Skoll Code Review

Scan type: reviewOverall recommendation: REQUEST_CHANGES
Findings: 7 total — 7 posted, 0 skipped
6 finding(s) posted as inline comments (see file-level comments below)

Posted findings

  • [High] Callback exceptions are converted to 0 (success) with unwritten output bufferwolfcrypt/__init__.py:57-63, wolfcrypt/cryptocb.py:103-138
  • [Medium] cipher_callback path returns success (0) without performing any cipher operationwolfcrypt/cryptocb.py:125-127,149-150
  • [Medium] Hash callbacks and error paths are untested; default-device-id test asserts nothingtests/test_cryptocb.py:33-53
  • [Medium] del unregister not guarded against interpreter-shutdown errorswolfcrypt/cryptocb.py:100-101,152-153
  • [Medium] CRYPTO_CB cdef hash union depends on SHA/SHA256/SHA384/SHA512/SHA3 cdefs being presentscripts/build_ffi.py:1424-1440
  • [Low] Inconsistent logging style in callback (f-string vs lazy %-formatting)wolfcrypt/cryptocb.py:104,109
  • [Low] wolfCrypt_Init() added with no corresponding cleanupwolfcrypt/__init__.py:53-55

Review generated by Skoll

Comment thread wolfcrypt/cryptocb.py
)
_ffi.buffer(info.hash.digest, DIGEST_SIZE[info.hash.type])[:] = digest
return 0
if info.algo_type == _lib.WC_ALGO_TYPE_CIPHER:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] cipher_callback path returns success (0) without performing any cipher operation

For WC_ALGO_TYPE_CIPHER, callback() calls self.cipher_callback(device_id) and then unconditionally return 0. The cipher fields of wc_CryptoInfo are intentionally not exposed in the cdef (commented out per the PR description), so a subclass overriding cipher_callback has no way to read inputs or write outputs, yet a no-op override would cause wolfCrypt to be told the cipher succeeded while no encryption/decryption was actually performed. Out of the box this is masked because the default cipher_callback raises NotImplementedError (caught -> CRYPTOCB_UNAVAILABLE), but exposing an overridable cipher_callback hook that returns success-without-effect is a footgun, especially for a crypto library where a silent no-op cipher is dangerous.

Fix: Until the cipher struct is actually exposed, either remove the cipher_callback hook or always return CRYPTOCB_UNAVAILABLE for cipher so wolfCrypt falls back to software, rather than returning 0/success for an operation that did nothing.

Comment thread tests/test_cryptocb.py
from wolfcrypt.cryptocb import CryptoCallback


def test_default_device_id():

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] Hash callbacks and error paths are untested; default-device-id test asserts nothing

Only the RNG callback path is exercised. There is no coverage for hash_update_callback / hash_finalize_callback (the most complex branch in callback(), including the NULL-digest update vs finalize distinction and the digest-length validation), no coverage for the length-mismatch error branches (which is exactly where the success-on-exception bug above manifests), and no coverage for the cipher / unknown-algo fallback to CRYPTOCB_UNAVAILABLE. test_default_device_id only print()s the value and asserts nothing, so it cannot fail meaningfully.

Fix: Add tests for the hash update/finalize paths, the length-mismatch/error paths, and the unknown-algo fallback; make test_default_device_id assert on the return value.

Comment thread wolfcrypt/cryptocb.py
self._unregister()
return False

def __del__(self) -> None:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] del unregister not guarded against interpreter-shutdown errors

__del__ calls self._unregister() which dereferences module-level _lib. During interpreter shutdown _lib/wc_CryptoCb_UnRegisterDevice may already be torn down, raising AttributeError (or worse) from a destructor, producing noisy 'Exception ignored in del' output. The existing Random.__del__ in wolfcrypt/random.py explicitly guards this exact scenario with a try/except for shutdown. The new code should follow the same established pattern. (Also note __exit__ followed by __del__ calls _unregister twice; this is harmless since unregister is idempotent, but the shutdown guard is still warranted.)

Fix: Guard the destructor's unregister against shutdown-time teardown, matching the pattern already used in Random.__del__.

Comment thread scripts/build_ffi.py
# } cipher;
# """

cdef += """

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 [Medium] CRYPTO_CB cdef hash union depends on SHA/SHA256/SHA384/SHA512/SHA3 cdefs being present

The wc_CryptoInfo.hash.u union added under if features["CRYPTO_CB"] references the C types wc_Sha, wc_Sha256, wc_Sha384, wc_Sha512, and wc_Sha3*. Those typedef struct {...} declarations are emitted only under their own feature guards (if features["SHA"], ["SHA256"], etc., lines 845-908). If a build enables CRYPTO_CB while any of those hash features is disabled, the cdef will reference an undeclared type and ffibuilder.cdef()/compile will fail. The default build enables all of them so it works today, but the implicit coupling is fragile and undocumented.

Fix: Make the CRYPTO_CB hash-union cdef robust to disabled hash features, or document/assert the dependency so a non-default feature combination fails with a clear message instead of an opaque cdef error.

Comment thread wolfcrypt/cryptocb.py
self._unregister()

def callback(self, device_id: int, info: _ffi.CData) -> int:
log.debug(f"{device_id=} algo = {ALGO_TYPE_NAME[info.algo_type]}")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] Inconsistent logging style in callback (f-string vs lazy %-formatting)

Line 104 uses an eagerly-interpolated f-string inside log.debug(...), while line 109 uses lazy %s formatting. Mixing the two is inconsistent and the f-string form is what pylint's logging-fstring-interpolation flags (the file already disables several pylint checks at the top). Pick one style; lazy %-args avoid formatting cost when debug logging is disabled.

Fix: Use lazy %-style logging consistently across the callback.

Comment thread wolfcrypt/__init__.py
from wolfcrypt.cryptocb import CryptoCallback
from wolfcrypt.exceptions import WolfCryptApiError

ret = _lib.wolfCrypt_Init()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] wolfCrypt_Init() added with no corresponding cleanup

wolfCrypt_Init() is now called unconditionally on package import (a reasonable change, and required for the cryptocb registration to work), but there is no matching wolfCrypt_Cleanup() registered (e.g. via atexit). For a process-lifetime library this is generally acceptable, but it is worth a deliberate decision/comment so it is clear cleanup was intentionally omitted rather than forgotten.

Fix: Confirm the omission of wolfCrypt_Cleanup() is intentional; add a brief comment or an atexit cleanup if appropriate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants