From 7813a1138115b43643b6fcb5d061819e8cfd3a42 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Fri, 16 Jan 2015 19:55:33 +0100 Subject: [PATCH] - pad filenames with NULL bytes (fixes #24) --- .../crypto/aes256/Aes256Cryptor.java | 17 ++++++++++++++--- .../crypto/aes256/Aes256CryptorTest.java | 8 ++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java index d5af20da1..8be60eba7 100644 --- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java +++ b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java @@ -331,8 +331,11 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo final ByteBuffer iv = ByteBuffer.allocate(AES_BLOCK_LENGTH); iv.put(partialIv); final Cipher cipher = this.aesCtrCipher(key, iv.array(), Cipher.ENCRYPT_MODE); + // add NULL padding to the cleartext to get a multiple of the block size: final byte[] cleartextBytes = cleartext.getBytes(StandardCharsets.UTF_8); - final byte[] encryptedBytes = cipher.doFinal(cleartextBytes); + final byte[] nullBytePadding = new byte[AES_BLOCK_LENGTH - cleartextBytes.length % AES_BLOCK_LENGTH]; + final byte[] paddedCleartextBytes = ArrayUtils.addAll(cleartextBytes, nullBytePadding); + final byte[] encryptedBytes = cipher.doFinal(paddedCleartextBytes); final String ivAndCiphertext = ENCRYPTED_FILENAME_CODEC.encodeAsString(partialIv) + IV_PREFIX_SEPARATOR + ENCRYPTED_FILENAME_CODEC.encodeAsString(encryptedBytes); if (ivAndCiphertext.length() + BASIC_FILE_EXT.length() > ENCRYPTED_FILENAME_LENGTH_LIMIT) { @@ -387,8 +390,16 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo final Cipher cipher = this.aesCtrCipher(key, iv.array(), Cipher.DECRYPT_MODE); final byte[] encryptedBytes = ENCRYPTED_FILENAME_CODEC.decode(ciphertext); - final byte[] cleartextBytes = cipher.doFinal(encryptedBytes); - return new String(cleartextBytes, StandardCharsets.UTF_8); + final byte[] paddedCleartextBytes = cipher.doFinal(encryptedBytes); + + // remove NULL padding (not valid in file names anyway) + final int beginOfPadding = ArrayUtils.indexOf(paddedCleartextBytes, (byte) 0x00); + if (beginOfPadding == -1) { + return new String(paddedCleartextBytes, StandardCharsets.UTF_8); + } else { + final byte[] cleartextBytes = Arrays.copyOf(paddedCleartextBytes, beginOfPadding); + return new String(cleartextBytes, StandardCharsets.UTF_8); + } } private LongFilenameMetadata getMetadata(CryptorIOSupport ioSupport, String metadataFile) throws IOException { diff --git a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java b/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java index 59c66e1cc..bb1ff5a76 100644 --- a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java +++ b/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java @@ -235,6 +235,14 @@ public class Aes256CryptorTest { Assert.assertEquals(encryptedPath2a, encryptedPath2b); final String decryptedPath2 = cryptor.decryptPath(encryptedPath2a, '/', '/', ioSupportMock); Assert.assertEquals(originalPath2, decryptedPath2); + + // block size length path components + final String originalPath3 = "aaaabbbbccccdddd"; + final String encryptedPath3a = cryptor.encryptPath(originalPath3, '/', '/', ioSupportMock); + final String encryptedPath3b = cryptor.encryptPath(originalPath3, '/', '/', ioSupportMock); + Assert.assertEquals(encryptedPath3a, encryptedPath3b); + final String decryptedPath3 = cryptor.decryptPath(encryptedPath3a, '/', '/', ioSupportMock); + Assert.assertEquals(originalPath3, decryptedPath3); } private static class CryptoIOSupportMock implements CryptorIOSupport {