From 919f77126ea1f7b7f3a736acc37cab11a3596117 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Sat, 15 Aug 2026 14:54:34 +0200 Subject: [PATCH 1/2] throw if chunk number exceeds max value --- .../java/org/cryptomator/cryptolib/v3/Constants.java | 1 + .../cryptolib/v3/FileContentCryptorImpl.java | 7 +++++++ .../cryptolib/v3/FileContentCryptorImplTest.java | 11 +++++++++++ 3 files changed, 19 insertions(+) diff --git a/src/main/java/org/cryptomator/cryptolib/v3/Constants.java b/src/main/java/org/cryptomator/cryptolib/v3/Constants.java index aec90da..8516fd8 100644 --- a/src/main/java/org/cryptomator/cryptolib/v3/Constants.java +++ b/src/main/java/org/cryptomator/cryptolib/v3/Constants.java @@ -15,5 +15,6 @@ private Constants() { static final int PAYLOAD_SIZE = 32 * 1024; static final int GCM_TAG_SIZE = 16; static final int CHUNK_SIZE = GCM_NONCE_SIZE + PAYLOAD_SIZE + GCM_TAG_SIZE; + static final long MAX_CHUNK_NUMBER = 4_294_967_294L; } diff --git a/src/main/java/org/cryptomator/cryptolib/v3/FileContentCryptorImpl.java b/src/main/java/org/cryptomator/cryptolib/v3/FileContentCryptorImpl.java index 77980f5..78000f0 100644 --- a/src/main/java/org/cryptomator/cryptolib/v3/FileContentCryptorImpl.java +++ b/src/main/java/org/cryptomator/cryptolib/v3/FileContentCryptorImpl.java @@ -20,6 +20,7 @@ import static org.cryptomator.cryptolib.v3.Constants.CHUNK_SIZE; import static org.cryptomator.cryptolib.v3.Constants.GCM_NONCE_SIZE; import static org.cryptomator.cryptolib.v3.Constants.GCM_TAG_SIZE; +import static org.cryptomator.cryptolib.v3.Constants.MAX_CHUNK_NUMBER; import static org.cryptomator.cryptolib.v3.Constants.PAYLOAD_SIZE; class FileContentCryptorImpl implements FileContentCryptor { @@ -61,6 +62,9 @@ public void encryptChunk(ByteBuffer cleartextChunk, ByteBuffer ciphertextChunk, if (ciphertextChunk.remaining() < CHUNK_SIZE) { throw new IllegalArgumentException("Invalid ciphertext chunk size: " + ciphertextChunk.remaining() + ", must fit up to " + CHUNK_SIZE + " bytes."); } + if (0 > chunkNumber || chunkNumber > MAX_CHUNK_NUMBER) { + throw new IllegalArgumentException("Invalid chunk number: " + chunkNumber + "expected range [0, " + Constants.MAX_CHUNK_NUMBER + "]"); + } FileHeaderImpl headerImpl = FileHeaderImpl.cast(header); encryptChunk(cleartextChunk, ciphertextChunk, chunkNumber, headerImpl.getNonce(), headerImpl.getContentKey()); } @@ -85,6 +89,9 @@ public void decryptChunk(ByteBuffer ciphertextChunk, ByteBuffer cleartextChunk, if (!authenticate) { throw new UnsupportedOperationException("authenticate can not be false"); } + if (0 > chunkNumber || chunkNumber > MAX_CHUNK_NUMBER) { + throw new IllegalArgumentException("Invalid chunk number: " + chunkNumber + "expected range [0, " + Constants.MAX_CHUNK_NUMBER + "]"); + } FileHeaderImpl headerImpl = FileHeaderImpl.cast(header); decryptChunk(ciphertextChunk, cleartextChunk, chunkNumber, headerImpl.getNonce(), headerImpl.getContentKey()); } diff --git a/src/test/java/org/cryptomator/cryptolib/v3/FileContentCryptorImplTest.java b/src/test/java/org/cryptomator/cryptolib/v3/FileContentCryptorImplTest.java index 838b0ee..aa7bb99 100644 --- a/src/test/java/org/cryptomator/cryptolib/v3/FileContentCryptorImplTest.java +++ b/src/test/java/org/cryptomator/cryptolib/v3/FileContentCryptorImplTest.java @@ -113,6 +113,17 @@ public void testEncryptChunkOfInvalidSize(int size) { }); } + @DisplayName("encrypt chunk with invalid chunk number") + @ParameterizedTest(name = "chunk number: {0}") + @ValueSource(longs = {-1, Constants.MAX_CHUNK_NUMBER + 1}) + public void testEncryptChunkOfNumber(int size) { + ByteBuffer cleartext = ByteBuffer.allocate(size); + + Assertions.assertThrows(IllegalArgumentException.class, () -> { + fileContentCryptor.encryptChunk(cleartext, 0, header); + }); + } + @Test @DisplayName("encrypt chunk") public void testChunkEncryption() { From c9bc3079b134101e5feab4552638c9d614422003 Mon Sep 17 00:00:00 2001 From: Armin Schrenk Date: Mon, 17 Aug 2026 09:30:47 +0200 Subject: [PATCH 2/2] fix tests --- .../cryptomator/cryptolib/v3/FileContentCryptorImpl.java | 4 ++-- .../cryptolib/v3/FileContentCryptorImplTest.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/cryptomator/cryptolib/v3/FileContentCryptorImpl.java b/src/main/java/org/cryptomator/cryptolib/v3/FileContentCryptorImpl.java index 78000f0..ecdaca6 100644 --- a/src/main/java/org/cryptomator/cryptolib/v3/FileContentCryptorImpl.java +++ b/src/main/java/org/cryptomator/cryptolib/v3/FileContentCryptorImpl.java @@ -62,7 +62,7 @@ public void encryptChunk(ByteBuffer cleartextChunk, ByteBuffer ciphertextChunk, if (ciphertextChunk.remaining() < CHUNK_SIZE) { throw new IllegalArgumentException("Invalid ciphertext chunk size: " + ciphertextChunk.remaining() + ", must fit up to " + CHUNK_SIZE + " bytes."); } - if (0 > chunkNumber || chunkNumber > MAX_CHUNK_NUMBER) { + if (chunkNumber < 0 || chunkNumber > MAX_CHUNK_NUMBER) { throw new IllegalArgumentException("Invalid chunk number: " + chunkNumber + "expected range [0, " + Constants.MAX_CHUNK_NUMBER + "]"); } FileHeaderImpl headerImpl = FileHeaderImpl.cast(header); @@ -89,7 +89,7 @@ public void decryptChunk(ByteBuffer ciphertextChunk, ByteBuffer cleartextChunk, if (!authenticate) { throw new UnsupportedOperationException("authenticate can not be false"); } - if (0 > chunkNumber || chunkNumber > MAX_CHUNK_NUMBER) { + if ( chunkNumber < 0 || chunkNumber > MAX_CHUNK_NUMBER) { throw new IllegalArgumentException("Invalid chunk number: " + chunkNumber + "expected range [0, " + Constants.MAX_CHUNK_NUMBER + "]"); } FileHeaderImpl headerImpl = FileHeaderImpl.cast(header); diff --git a/src/test/java/org/cryptomator/cryptolib/v3/FileContentCryptorImplTest.java b/src/test/java/org/cryptomator/cryptolib/v3/FileContentCryptorImplTest.java index aa7bb99..adef4c0 100644 --- a/src/test/java/org/cryptomator/cryptolib/v3/FileContentCryptorImplTest.java +++ b/src/test/java/org/cryptomator/cryptolib/v3/FileContentCryptorImplTest.java @@ -115,12 +115,12 @@ public void testEncryptChunkOfInvalidSize(int size) { @DisplayName("encrypt chunk with invalid chunk number") @ParameterizedTest(name = "chunk number: {0}") - @ValueSource(longs = {-1, Constants.MAX_CHUNK_NUMBER + 1}) - public void testEncryptChunkOfNumber(int size) { - ByteBuffer cleartext = ByteBuffer.allocate(size); + @ValueSource(longs = {-1L, Constants.MAX_CHUNK_NUMBER + 1L}) + public void testEncryptChunkWithInvalidNumber(long number) { + ByteBuffer cleartext = ByteBuffer.allocate(Constants.PAYLOAD_SIZE); Assertions.assertThrows(IllegalArgumentException.class, () -> { - fileContentCryptor.encryptChunk(cleartext, 0, header); + fileContentCryptor.encryptChunk(cleartext, number, header); }); }