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:
1 parent
0e1190ffad
commit
326af72a25
1 file changed
+15
-9
+15
-9
@@ -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
|
||||
|
||||
Reference in new issue
Block a user