diff --git a/lib/Crypto/Cipher/_mode_ccm.py b/lib/Crypto/Cipher/_mode_ccm.py index 1c1105bc..7f78fbe5 100644 --- a/lib/Crypto/Cipher/_mode_ccm.py +++ b/lib/Crypto/Cipher/_mode_ccm.py @@ -189,9 +189,13 @@ def _start_mac(self): # Formatting control information and nonce (A.2.1) q = 15 - len(self.nonce) # length of Q, the encoded message length + # Limit on plaintext length imposed by choice of nonce (A.1) + if self._msg_len >= 2**(8*q): + raise OverflowError("Combined plaintext and nonce too long") flags = (64 * (self._assoc_len > 0) + 8 * ((self._mac_len - 2) // 2) + (q - 1)) b_0 = struct.pack("B", flags) + self.nonce + long_to_bytes(self._msg_len, q) + assert len(b_0) == 16 # Formatting associated data (A.2.2) # Encoded 'a' is concatenated with the associated data 'A' diff --git a/lib/Crypto/SelfTest/Cipher/test_CCM.py b/lib/Crypto/SelfTest/Cipher/test_CCM.py index e8ebc0b1..9d9c2177 100644 --- a/lib/Crypto/SelfTest/Cipher/test_CCM.py +++ b/lib/Crypto/SelfTest/Cipher/test_CCM.py @@ -241,6 +241,19 @@ def test_shorter_ciphertext_than_declared(self): msg_len=DATA_LEN - 1) self.assertRaises(ValueError, cipher.decrypt, ct) + def test_plaintext_too_long_for_nonce(self): + nonce = get_tag_random("nonce", 13) + # The octet length of the binary representation of the octet length + # of the payload. + q = 15 - len(nonce) + + # The plaintext must be shorter than this + # See Appendix A.1 of NIST SP 800-38C + data_len_limit = 2**(8 * q) + data = bytearray(data_len_limit) + cipher = AES.new(self.key_128, AES.MODE_CCM, nonce=nonce) + self.assertRaises(OverflowError, cipher.encrypt_and_digest, data) + def test_message_chunks(self): # Validate that both associated data and plaintext/ciphertext # can be broken up in chunks of arbitrary length