Refine chab3 exception types: add WireError(ValueError) for too short or invalid ciphertext, reserving TypeError for caller errors (wrong key/nonce sizes, too small into buffer, non-buffer types).

This commit is contained in:
LeoVasanko committed 2026-10-07 19:59:23 +00:00
1 parent 0e1190ffad
commit 326af72a25
1 file changed
+15 -9
+15 -9
View File
@@ -18,6 +18,12 @@ NONCE_LEN = 12
_MAC_KEY_BLOCK = 64
class WireError(ValueError):
"""Failure an unauthenticated peer can cause with crafted packets:
truncated ciphertext, bad authentication tag. Distinct from TypeError,
which signals caller misuse (wrong buffer sizes, wrong key length)."""
def nonce_increment(nonce: Buffer) -> None:
"""Increment the nonce in place, little-endian."""
n = memoryview(nonce).cast("B")
@@ -33,11 +39,11 @@ def _session(key: Buffer, nonce: Buffer, ad: Buffer | None, maclen: int):
key = memoryview(key).cast("B")
nonce = memoryview(nonce).cast("B")
if len(key) != KEY_LEN:
raise ValueError("key must be 32 bytes")
raise TypeError("key must be 32 bytes")
if len(nonce) != NONCE_LEN:
raise ValueError("nonce must be 12 bytes")
raise TypeError("nonce must be 12 bytes")
if not 0 <= maclen <= 32:
raise ValueError("maclen must be in 0..32")
raise TypeError("maclen must be in 0..32")
# IETF layout: 32-bit little-endian block counter || 96-bit nonce
stream = Cipher(ChaCha20(key, bytes(4) + nonce), mode=None).encryptor()
if maclen == 0:
@@ -98,10 +104,10 @@ def decrypt_into(
ct = memoryview(ct).cast("B")
if nonce is None:
if len(ct) < NONCE_LEN + maclen:
raise ValueError("ciphertext buffer too short")
raise WireError("ciphertext buffer too short")
nonce, ct = ct[:NONCE_LEN], ct[NONCE_LEN:]
elif len(ct) < maclen:
raise ValueError("ciphertext buffer too short")
raise WireError("ciphertext buffer too short")
used = len(ct) - maclen
mv = memoryview(out).cast("B")
if len(mv) < used:
@@ -111,7 +117,7 @@ def decrypt_into(
if mac is not None:
mac.update(ct[:used])
if not hmac.compare_digest(bytes(ct[used:]), _tag(mac, used, maclen)):
raise ValueError("invalid authentication tag")
raise WireError("invalid authentication tag")
stream.update_into(ct[:used], out)
return out
@@ -143,7 +149,7 @@ def decrypt(
ct = memoryview(ct).cast("B")
hdr = NONCE_LEN if nonce is None else 0
if len(ct) < hdr + maclen:
raise ValueError("ciphertext buffer too short")
raise WireError("ciphertext buffer too short")
out = bytearray(len(ct) - hdr - maclen)
decrypt_into(out, ct, key, nonce, ad=ad, maclen=maclen)
return out
@@ -153,7 +159,7 @@ class _Sequence:
def __init__(self, key: Buffer, *, maclen: int = 16):
key = memoryview(key).cast("B")
if len(key) != KEY_LEN:
raise ValueError("key must be 32 bytes")
raise TypeError("key must be 32 bytes")
self._key = key
self._maclen = maclen
self._nonce = bytearray(NONCE_LEN)
@@ -191,7 +197,7 @@ class Receiver(_Sequence):
def decrypt(self, ct: Buffer, *, ad: Buffer | None = None) -> bytearray:
ct = memoryview(ct).cast("B")
if len(ct) < self._maclen:
raise ValueError("ciphertext buffer too short")
raise WireError("ciphertext buffer too short")
out = bytearray(len(ct) - self._maclen)
self.decrypt_into(out, ct, ad=ad)
return out