Address Copilot review on wolfcrypt-py (F-4015, F-4463)

- ChaCha.set_iv(): only mark _iv_set after _set_key() succeeds, and clear
  it first, so a failed re-key cannot leave encrypt()/decrypt() unblocked
  with a stale or partially-applied IV. Add a regression test.
- Update _Cipher.new()/encrypt()/decrypt() docstrings that still referred
  to CFB/segment-size behavior to match the actually supported modes
  (MODE_CBC, MODE_CTR) and their IV requirements.
pull/128/head
Juliusz Sosinowicz 2026-06-23 13:39:24 +00:00
parent 2ecf721695
commit b13aeea62b
2 changed files with 37 additions and 10 deletions

View File

@ -64,3 +64,28 @@ def test_encrypt_decrypt_after_set_iv_roundtrips():
dec = ChaCha(KEY)
dec.set_iv(NONCE)
assert dec.decrypt(ciphertext) == plaintext
def test_failed_set_iv_keeps_encrypt_blocked(monkeypatch):
"""
If re-keying fails inside set_iv(), the IV must be treated as not set so
encrypt()/decrypt() stay blocked rather than running with a stale or
partially-applied IV.
"""
from wolfcrypt.ciphers import ChaCha
cipher = ChaCha(KEY)
# First, establish a valid IV so a later failure would otherwise leave
# _iv_set True under the old ordering.
cipher.set_iv(NONCE)
monkeypatch.setattr(cipher, "_set_key", lambda direction: -1)
with pytest.raises(WolfCryptError):
cipher.set_iv(NONCE)
monkeypatch.undo() # restore real _set_key
# The failed re-key must have cleared the "IV is set" state, so encrypt()
# refuses here. Under the old ordering _iv_set stayed True and this
# encrypt() would instead run with a stale IV.
with pytest.raises(WolfCryptError):
cipher.encrypt(b"A" * 16)

View File

@ -159,12 +159,11 @@ class _Cipher:
"""
Returns a ciphering object, using the secret key contained in
the string **key**, and using the feedback mode **mode**, which
must be one of MODE_* defined in this module.
must be one of the supported MODE_* values (MODE_CBC, MODE_CTR).
If **mode** is MODE_CBC or MODE_CFB, **IV** must be provided and
must be a string of the same length as the block size. Not
providing a value of **IV** will result in a ValueError exception
being raised.
Both supported modes require **IV** to be provided as a string of
the same length as the block size. Not providing a value of **IV**
will result in a ValueError exception being raised.
"""
return cls(key, mode, IV)
@ -173,8 +172,9 @@ class _Cipher:
Encrypts a non-empty string, using the key-dependent data in
the object, and with the appropriate feedback mode.
The string's length must be an exact multiple of the algorithm's
block size or, in CFB mode, of the segment size.
In MODE_CBC the string's length must be an exact multiple of the
algorithm's block size. MODE_CTR is a stream mode and imposes no
length restriction.
Returns a string containing the ciphertext.
"""
@ -205,8 +205,9 @@ class _Cipher:
Decrypts **string**, using the key-dependent data in the
object and with the appropriate feedback mode.
The string's length must be an exact multiple of the algorithm's
block size or, in CFB mode, of the segment size.
In MODE_CBC the string's length must be an exact multiple of the
algorithm's block size. MODE_CTR is a stream mode and imposes no
length restriction.
Returns a string containing the plaintext.
"""
@ -583,10 +584,11 @@ if _lib.CHACHA_ENABLED:
if len(self._IV_nonce) != self._NONCE_SIZE:
raise ValueError(f"nonce must be {self._NONCE_SIZE} bytes, got {len(self._IV_nonce)}")
self._IV_counter = counter
self._iv_set = True
self._iv_set = False
ret = self._set_key(self._REKEY_BOTH)
if ret < 0:
raise WolfCryptApiError("ChaCha set_iv error", ret)
self._iv_set = True
if _lib.CHACHA20_POLY1305_ENABLED:
class ChaCha20Poly1305: