Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CCM mode doesn't check message length #812

Open
geitda opened this issue Jun 7, 2024 · 0 comments
Open

CCM mode doesn't check message length #812

geitda opened this issue Jun 7, 2024 · 0 comments

Comments

@geitda
Copy link

geitda commented Jun 7, 2024

The notes for CCM explain the tradeoff between nonce size and maximum message length, but the programming doesn't enforce it.
See at

Note that there is a trade-off between the size of the nonce and the
for the details. If n is the nonce length, and as CCM requires 7 <= n <= 13, then q is the counter length, specifically q = 15 - n. Message lengths m < 28q always work correctly and are interoperable. Message lengths 28q <= m < 28q+4-16 appear to encipher correctly as there are no errors, but are not interoperable as they are illegal (per the CCM spec) for any given q. Notably, these decode correctly with pycryptodome itself, as the error is "symmetric" for both encrypt_and_digest and decrypt_and_verify. Message lengths m > 28q+4-16 will raise an OverflowError as the underlying CTR mode cipher will wrap. The '+4' comes from fact that a single count in CTR mode can encipher 16 bytes (the block size), so the total number of bytes is 16 times larger (or 4 bits). And the -16 is because 16 bytes (one block) of the CTR keystream is needed for the tag, so the message maximum is one block less. Not that it makes much difference as this, again, is outside the CCM spec.
I would expect a ValueError "Message is too long for given nonce length" or similar that should raise as soon as 28q bytes is "reached," whether that be from the pre-declared length being too large (that is, raise immediately on the AES.new call with the passed msg_len too big), or if one or more encrypt calls makes the total message length exceed the limit.
You can test the overflow case easily

>>> from Crypto.Cipher import AES
>>> key = AES.get_random_bytes(16)
>>> nonce = AES.get_random_bytes(13) # that is, q == 2
>>> message = b'\x00' * (64 * 1024 * 16) # 64K blocks of 16 bytes
>>> ciphertext, tag = AES.new(key, AES.MODE_CCM, nonce=nonce).encrypt_and_digest(message)
Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    ciphertext, tag = AES.new(key, AES.MODE_CCM, nonce=nonce).encrypt_and_digest(message)
  File "C:\Python311\Lib\site-packages\Crypto\Cipher\_mode_ccm.py", line 575, in encrypt_and_digest
    return self.encrypt(plaintext, output=output), self.digest()
  File "C:\Python311\Lib\site-packages\Crypto\Cipher\_mode_ccm.py", line 373, in encrypt
    return self._cipher.encrypt(plaintext, output=output)
  File "C:\Python311\Lib\site-packages\Crypto\Cipher\_mode_ctr.py", line 206, in encrypt
    raise OverflowError("The counter has wrapped around in"
OverflowError: The counter has wrapped around in CTR mode

No other correct implementation will accept the non-interoperable output from pycryptodome, so I don't think there's any need to test it directly.

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

No branches or pull requests

1 participant