Add initial support for crypto callbacks.#114
Conversation
JeremiahM37
left a comment
There was a problem hiding this comment.
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 time —
wolfcrypt/__init__.py:49 and wolfcrypt/cryptocb.py:33-58 - [High] Double unregister of device on context-manager exit + GC —
wolfcrypt/cryptocb.py:73-77, 117-118 - [Medium] Return value of wolfCrypt_Init not checked —
wolfcrypt/__init__.py:52 - [Medium] Debug print() statements left in production callback dispatcher —
wolfcrypt/cryptocb.py:80, 84, 85, 107, 111 - [Medium] Hash digest dict KeyError on unmapped hash types —
wolfcrypt/cryptocb.py:84, 90 - [Medium] Digest output buffer not size-validated —
wolfcrypt/cryptocb.py:90 - [Medium] RNG output length not validated —
wolfcrypt/cryptocb.py:96-97 - [Medium] No tests for hash callbacks despite PR claim —
tests/test_cryptocb.py - [Low] Free-floating string literal used as 'comment' in build_ffi.py is misleading —
scripts/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
|
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.
e10a1d3 to
1f974ad
Compare
dgarske
left a comment
There was a problem hiding this comment.
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 buffer —
wolfcrypt/__init__.py:57-63, wolfcrypt/cryptocb.py:103-138 - [Medium] cipher_callback path returns success (0) without performing any cipher operation —
wolfcrypt/cryptocb.py:125-127,149-150 - [Medium] Hash callbacks and error paths are untested; default-device-id test asserts nothing —
tests/test_cryptocb.py:33-53 - [Medium] del unregister not guarded against interpreter-shutdown errors —
wolfcrypt/cryptocb.py:100-101,152-153 - [Medium] CRYPTO_CB cdef hash union depends on SHA/SHA256/SHA384/SHA512/SHA3 cdefs being present —
scripts/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 cleanup —
wolfcrypt/__init__.py:53-55
Review generated by Skoll
| ) | ||
| _ffi.buffer(info.hash.digest, DIGEST_SIZE[info.hash.type])[:] = digest | ||
| return 0 | ||
| if info.algo_type == _lib.WC_ALGO_TYPE_CIPHER: |
There was a problem hiding this comment.
🟠 [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.
| from wolfcrypt.cryptocb import CryptoCallback | ||
|
|
||
|
|
||
| def test_default_device_id(): |
There was a problem hiding this comment.
🟠 [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.
| self._unregister() | ||
| return False | ||
|
|
||
| def __del__(self) -> None: |
There was a problem hiding this comment.
🟠 [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__.
| # } cipher; | ||
| # """ | ||
|
|
||
| cdef += """ |
There was a problem hiding this comment.
🟠 [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.
| self._unregister() | ||
|
|
||
| def callback(self, device_id: int, info: _ffi.CData) -> int: | ||
| log.debug(f"{device_id=} algo = {ALGO_TYPE_NAME[info.algo_type]}") |
There was a problem hiding this comment.
🔵 [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.
| from wolfcrypt.cryptocb import CryptoCallback | ||
| from wolfcrypt.exceptions import WolfCryptApiError | ||
|
|
||
| ret = _lib.wolfCrypt_Init() |
There was a problem hiding this comment.
🔵 [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.
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_CryptoInfostructure 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_CryptoInfostruct.Cffi
cdefand the compiler seem to disagree.However as it is, callbacks work for random and hash functions.