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..ecdaca6 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 (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); 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 ( 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); 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..adef4c0 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 = {-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, number, header); + }); + } + @Test @DisplayName("encrypt chunk") public void testChunkEncryption() {