Skip to content

Commit

Permalink
accept ByteBuffers with non-zero position
Browse files Browse the repository at this point in the history
during encryption/decryption of chunks
  • Loading branch information
overheadhunter committed Jan 24, 2025
1 parent edd4827 commit 828251a
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE.txt.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.cryptolib.api;

import java.nio.ByteBuffer;
Expand All @@ -32,17 +24,17 @@ public interface FileContentCryptor {
/**
* Encrypts a single chunk of cleartext.
*
* @param cleartextChunk Content to be encrypted
* @param cleartextChunk Content to be encrypted (starting at the buffer's current position, ending at the buffer's limit)
* @param chunkNumber Number of the chunk to be encrypted
* @param header Header of the file, this chunk belongs to
* @return Encrypted content.
* @return Encrypted content. Position is set to <code>0</code> and limit to the end of the chunk.
*/
ByteBuffer encryptChunk(ByteBuffer cleartextChunk, long chunkNumber, FileHeader header);

/**
* Encrypts a single chunk of cleartext.
*
* @param cleartextChunk Content to be encrypted
* @param cleartextChunk Content to be encrypted (starting at the buffer's current position, ending at the buffer's limit)
* @param ciphertextChunk Encrypted content buffer (with at least {@link #ciphertextChunkSize()} remaining bytes)
* @param chunkNumber Number of the chunk to be encrypted
* @param header Header of the file, this chunk belongs to
Expand All @@ -52,7 +44,7 @@ public interface FileContentCryptor {
/**
* Decrypts a single chunk of ciphertext.
*
* @param ciphertextChunk Content to be decrypted
* @param ciphertextChunk Content to be decrypted (starting at the buffer's current position, ending at the buffer's limit)
* @param chunkNumber Number of the chunk to be decrypted
* @param header Header of the file, this chunk belongs to
* @param authenticate Skip authentication by setting this flag to <code>false</code>. Should always be <code>true</code> by default.
Expand All @@ -65,7 +57,7 @@ public interface FileContentCryptor {
/**
* Decrypts a single chunk of ciphertext.
*
* @param ciphertextChunk Content to be decrypted
* @param ciphertextChunk Content to be decrypted (starting at the buffer's current position, ending at the buffer's limit)
* @param cleartextChunk Buffer for decrypted chunk (with at least {@link #cleartextChunkSize()} remaining bytes)
* @param chunkNumber Number of the chunk to be decrypted
* @param header Header of the file, this chunk belongs to
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE.txt.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.cryptolib.v1;

import org.cryptomator.cryptolib.api.AuthenticationFailedException;
Expand Down Expand Up @@ -140,7 +132,7 @@ void decryptChunk(ByteBuffer ciphertextChunk, ByteBuffer cleartextChunk, Destroy

// payload:
final ByteBuffer payloadBuf = ciphertextChunk.duplicate();
payloadBuf.position(NONCE_SIZE).limit(ciphertextChunk.limit() - MAC_SIZE);
payloadBuf.limit(ciphertextChunk.limit() - MAC_SIZE);

// payload:
try (ObjectPool.Lease<Cipher> cipher = CipherSupplier.AES_CTR.decryptionCipher(fk, new IvParameterSpec(nonce))) {
Expand All @@ -160,7 +152,7 @@ boolean checkChunkMac(byte[] headerNonce, long chunkNumber, ByteBuffer chunkBuf)

// get nonce + payload
final ByteBuffer nonceAndPayload = chunkBuf.duplicate();
nonceAndPayload.position(0).limit(chunkBuf.limit() - MAC_SIZE);
nonceAndPayload.limit(chunkBuf.limit() - MAC_SIZE);
final ByteBuffer expectedMacBuf = chunkBuf.duplicate();
expectedMacBuf.position(chunkBuf.limit() - MAC_SIZE);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
/*******************************************************************************
* Copyright (c) 2016 Sebastian Stenzel and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the accompanying LICENSE.txt.
*
* Contributors:
* Sebastian Stenzel - initial API and implementation
*******************************************************************************/
package org.cryptomator.cryptolib.v2;

import org.cryptomator.cryptolib.api.AuthenticationFailedException;
Expand Down Expand Up @@ -131,7 +123,6 @@ void decryptChunk(ByteBuffer ciphertextChunk, ByteBuffer cleartextChunk, long ch

// payload:
final ByteBuffer payloadBuf = ciphertextChunk.duplicate();
payloadBuf.position(GCM_NONCE_SIZE);
assert payloadBuf.remaining() >= GCM_TAG_SIZE;

// payload:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,15 @@ public void testChunkEncryption() {
Assertions.assertEquals(expected, ciphertext);
}

@Test
@DisplayName("encrypt chunk with offset ByteBuffer")
public void testChunkEncryptionWithByteBufferView() {
ByteBuffer cleartext = US_ASCII.encode("12345hello world12345").position(5).limit(16);
ByteBuffer ciphertext = fileContentCryptor.encryptChunk(cleartext, 0, header);
ByteBuffer expected = ByteBuffer.wrap(BaseEncoding.base64().decode("AAAAAAAAAAAAAAAAAAAAALTwrBTNYP7m3yTGKlhka9WPvX1Lpn5EYfVxlyX1ISgRXtdRnivM7r6F3Og="));
Assertions.assertEquals(expected, ciphertext);
}

@Test
@DisplayName("encrypt chunk with too small ciphertext buffer")
public void testChunkEncryptionWithBufferUnderflow() {
Expand Down Expand Up @@ -160,6 +169,17 @@ public void testChunkDecryption() throws AuthenticationFailedException {
Assertions.assertEquals(expected, cleartext);
}

@Test
@DisplayName("decrypt chunk with offset ByteBuffer")
public void testChunkDecryptionWithByteBufferView() throws AuthenticationFailedException {
byte[] actualCiphertext = BaseEncoding.base64().decode("AAAAAAAAAAAAAAAAAAAAALTwrBTNYP7m3yTGKlhka9WPvX1Lpn5EYfVxlyX1ISgRXtdRnivM7r6F3Og=");
ByteBuffer ciphertext = ByteBuffer.allocate(100);
ciphertext.position(10).put(actualCiphertext).position(10).limit(10 + actualCiphertext.length);
ByteBuffer cleartext = fileContentCryptor.decryptChunk(ciphertext, 0, header, true);
ByteBuffer expected = US_ASCII.encode("hello world");
Assertions.assertEquals(expected, cleartext);
}

@Test
@DisplayName("decrypt chunk with too small cleartext buffer")
public void testChunkDecryptionWithBufferUnderflow() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mockito;

import javax.crypto.Cipher;
import java.io.ByteArrayInputStream;
import java.io.EOFException;
import java.io.IOException;
Expand All @@ -43,6 +44,7 @@
import java.security.SecureRandom;
import java.util.Arrays;

import static java.nio.charset.StandardCharsets.US_ASCII;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.cryptomator.cryptolib.v2.Constants.GCM_NONCE_SIZE;
import static org.cryptomator.cryptolib.v2.Constants.GCM_TAG_SIZE;
Expand Down Expand Up @@ -83,6 +85,16 @@ public void testDecryptedEncryptedEqualsPlaintext() throws AuthenticationFailedE
@Nested
public class Encryption {

@BeforeEach
public void resetGcmNonce() {
// reset cipher state to avoid InvalidAlgorithmParameterExceptions due to IV-reuse
GcmTestHelper.reset((mode, key, params) -> {
try (ObjectPool.Lease<Cipher> cipher = CipherSupplier.AES_GCM.encryptionCipher(key, params)) {
cipher.get();
}
});
}

@DisplayName("encrypt chunk with invalid size")
@ParameterizedTest(name = "cleartext size: {0}")
@ValueSource(ints = {0, org.cryptomator.cryptolib.v2.Constants.PAYLOAD_SIZE + 1})
Expand All @@ -109,6 +121,20 @@ public void testChunkEncryption() {
Assertions.assertEquals(ByteBuffer.wrap(expected), ciphertext);
}

@Test
@DisplayName("encrypt chunk with offset ByteBuffer")
public void testChunkEncryptionWithByteBufferView() {
Mockito.doAnswer(invocation -> {
byte[] nonce = invocation.getArgument(0);
Arrays.fill(nonce, (byte) 0x33);
return null;
}).when(CSPRNG).nextBytes(Mockito.any());
ByteBuffer cleartext = US_ASCII.encode("12345hello world12345").position(5).limit(16);
ByteBuffer ciphertext = fileContentCryptor.encryptChunk(cleartext, 0, header);
byte[] expected = BaseEncoding.base64().decode("MzMzMzMzMzMzMzMzbYvL7CusRmzk70Kn1QxFA5WQg/hgKeba4bln");
Assertions.assertEquals(ByteBuffer.wrap(expected), ciphertext);
}

@Test
@DisplayName("encrypt chunk with too small ciphertext buffer")
public void testChunkEncryptionWithBufferUnderflow() {
Expand Down Expand Up @@ -172,6 +198,17 @@ public void testChunkDecryption() throws AuthenticationFailedException {
Assertions.assertEquals(expected, cleartext);
}

@Test
@DisplayName("decrypt chunk with offset ByteBuffer")
public void testChunkDecryptionWithByteBufferView() throws AuthenticationFailedException {
byte[] actualCiphertext = BaseEncoding.base64().decode("VVVVVVVVVVVVVVVVnHVdh+EbedvPeiCwCdaTYpzn1CXQjhSh7PHv");
ByteBuffer ciphertext = ByteBuffer.allocate(100);
ciphertext.position(10).put(actualCiphertext).position(10).limit(10 + actualCiphertext.length);
ByteBuffer cleartext = fileContentCryptor.decryptChunk(ciphertext, 0, header, true);
ByteBuffer expected = US_ASCII.encode("hello world");
Assertions.assertEquals(expected, cleartext);
}

@Test
@DisplayName("decrypt chunk with too small cleartext buffer")
public void testChunkDecryptionWithBufferUnderflow() {
Expand Down

0 comments on commit 828251a

Please sign in to comment.