Summary
LibTomCrypt ccm_memory accepts AES-CCM encryption when noncelen is 14 bytes. The calls return CRYPT_OK, although CCM permits nonce lengths only from 7 to 13 octets.
On the generic implementation path, nonce lengths greater than 13 are silently clamped to 13. Consequently, two 14-byte nonce values that share the same first 13 bytes but differ in the final byte produce the same ciphertext and authentication tag.
This report is intentionally narrow. It is about the remaining high-nonce case observed on current develop, reproduced locally on 2026-07-14 using the clean commit recorded below. It is not claiming that older invalid tag-length, short-nonce, or message-length-at-L=1 cases are still present.
Tested version
LibTomCrypt develop commit: a68fa19bc2b532f66a6f18ca457daec53054a312
This is the upstream develop commit checked on 2026-07-14. The local source tree used for the run had no uncommitted changes.
Minimal reproducer
A minimal standalone reproducer is attached:
repro.cpp
build.sh
run.sh
build.sh
repro.cpp
run.sh
It runs three cases:
valid_control:
noncelen = 13
expected = CRYPT_OK
invalid_high_nonce_a:
noncelen = 14
final nonce byte = 0x01
expected = CRYPT_INVALID_ARG
invalid_high_nonce_b:
noncelen = 14
final nonce byte = 0x02
expected = CRYPT_INVALID_ARG
The 13-byte control is exactly the common prefix of the two 14-byte nonce values. A conforming call should reject both 14-byte inputs before processing them. If the implementation instead clamps noncelen to 13, all three calls use the same effective nonce and produce identical ciphertext and authentication-tag outputs. The reproducer checks these comparisons explicitly and bounds-checks the returned tag length before comparing tag bytes.
An application that treats the complete 14-byte values as distinct may therefore unknowingly reuse the same effective CCM nonce.
The key, nonce, AAD, plaintext, and tag buffer are fixed test values. The reproducer does not use a fuzzing harness, adapter layer, private API, or undefined behavior.
Build and run
The checked repro can be built and run with:
export LIBTOMCRYPT_ROOT=/path/to/libtomcrypt-develop
./build.sh
./run.sh
build.sh accepts LIBTOMCRYPT_ROOT when a different local LibTomCrypt tree is needed and writes the temporary executable under ${TMPDIR:-/tmp}.
For the checked run, LIBTOMCRYPT_ROOT pointed to a clean checkout of a68fa19bc2b532f66a6f18ca457daec53054a312.
Observed behavior
Reproduced on LibTomCrypt current develop, checked locally on 2026-07-14.
Output from the attached reproducer:
case,nonce_len,return,error,expected
valid_control,13,0,CRYPT_OK,CRYPT_OK
invalid_high_nonce_a,14,0,CRYPT_OK,CRYPT_INVALID_ARG
invalid_high_nonce_b,14,0,CRYPT_OK,CRYPT_INVALID_ARG
silent_truncation_check,nonce14_tail_a,0x01
silent_truncation_check,nonce14_tail_b,0x02
silent_truncation_check,nonce13_matches_nonce14_a,true
silent_truncation_check,nonce13_matches_nonce14_b,true
silent_truncation_check,ciphertexts_equal,true
silent_truncation_check,tags_equal,true
result,silent_nonce_truncation_reproduced
Expected behavior
ccm_memory(..., nonce, noncelen > 13, ...) should reject the input, preferably with CRYPT_INVALID_ARG, because AES-CCM permits nonce lengths only from 7 to 13 bytes. It should not silently treat a caller-provided 14-byte nonce as a different, shorter nonce. NIST SP 800-38C section 5.1.1 defines the CCM nonce length domain as 7 through 13 octets.
Related issue
Issue #536 demonstrated the lower-bound case using a one-byte nonce. The current develop implementation rejects noncelen < 7, but the opposite upper-bound case remains: noncelen > 13 is accepted and clamped to 13. This report covers that remaining upper-bound validation gap.
Summary
LibTomCrypt
ccm_memoryaccepts AES-CCM encryption whennoncelenis 14 bytes. The calls returnCRYPT_OK, although CCM permits nonce lengths only from 7 to 13 octets.On the generic implementation path, nonce lengths greater than 13 are silently clamped to 13. Consequently, two 14-byte nonce values that share the same first 13 bytes but differ in the final byte produce the same ciphertext and authentication tag.
This report is intentionally narrow. It is about the remaining high-nonce case observed on current
develop, reproduced locally on 2026-07-14 using the clean commit recorded below. It is not claiming that older invalid tag-length, short-nonce, or message-length-at-L=1 cases are still present.Tested version
This is the upstream
developcommit checked on 2026-07-14. The local source tree used for the run had no uncommitted changes.Minimal reproducer
A minimal standalone reproducer is attached:
repro.cppbuild.shrun.shbuild.sh
repro.cpp
run.sh
It runs three cases:
The 13-byte control is exactly the common prefix of the two 14-byte nonce values. A conforming call should reject both 14-byte inputs before processing them. If the implementation instead clamps
noncelento 13, all three calls use the same effective nonce and produce identical ciphertext and authentication-tag outputs. The reproducer checks these comparisons explicitly and bounds-checks the returned tag length before comparing tag bytes.An application that treats the complete 14-byte values as distinct may therefore unknowingly reuse the same effective CCM nonce.
The key, nonce, AAD, plaintext, and tag buffer are fixed test values. The reproducer does not use a fuzzing harness, adapter layer, private API, or undefined behavior.
Build and run
The checked repro can be built and run with:
export LIBTOMCRYPT_ROOT=/path/to/libtomcrypt-develop ./build.sh ./run.shbuild.shacceptsLIBTOMCRYPT_ROOTwhen a different local LibTomCrypt tree is needed and writes the temporary executable under${TMPDIR:-/tmp}.For the checked run,
LIBTOMCRYPT_ROOTpointed to a clean checkout ofa68fa19bc2b532f66a6f18ca457daec53054a312.Observed behavior
Reproduced on LibTomCrypt current
develop, checked locally on 2026-07-14.Output from the attached reproducer:
Expected behavior
ccm_memory(..., nonce, noncelen > 13, ...)should reject the input, preferably withCRYPT_INVALID_ARG, because AES-CCM permits nonce lengths only from 7 to 13 bytes. It should not silently treat a caller-provided 14-byte nonce as a different, shorter nonce. NIST SP 800-38C section 5.1.1 defines the CCM nonce length domain as 7 through 13 octets.Related issue
Issue #536 demonstrated the lower-bound case using a one-byte nonce. The current
developimplementation rejectsnoncelen < 7, but the opposite upper-bound case remains:noncelen > 13is accepted and clamped to 13. This report covers that remaining upper-bound validation gap.