From e5d095606f188037300607713dbc2d4e8d5d5f92 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 8 Feb 2016 13:57:19 +0100 Subject: [PATCH] chunk layout version 3 (random nonce per block) --- .../engine/impl/FileContentCryptorImpl.java | 15 ++++---- .../engine/impl/FileContentDecryptorImpl.java | 30 +++++++-------- .../engine/impl/FileContentEncryptorImpl.java | 38 +++++++++++-------- .../BlockAlignedFileSystemFactory.java | 2 +- .../impl/FileContentDecryptorImplTest.java | 24 ++++++------ .../impl/FileContentEncryptorImplTest.java | 9 +++-- 6 files changed, 64 insertions(+), 54 deletions(-) diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentCryptorImpl.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentCryptorImpl.java index 362298733..727cacdea 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentCryptorImpl.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentCryptorImpl.java @@ -20,7 +20,8 @@ import org.cryptomator.crypto.engine.FileContentEncryptor; public class FileContentCryptorImpl implements FileContentCryptor { - public static final int CHUNK_SIZE = 32 * 1024; + public static final int PAYLOAD_SIZE = 32 * 1024; + public static final int NONCE_SIZE = 16; public static final int MAC_SIZE = 32; private final SecretKey encryptionKey; @@ -40,12 +41,12 @@ public class FileContentCryptorImpl implements FileContentCryptor { @Override public long toCiphertextPos(long cleartextPos) { - long chunkNum = cleartextPos / CHUNK_SIZE; - long cleartextChunkStart = chunkNum * CHUNK_SIZE; + long chunkNum = cleartextPos / PAYLOAD_SIZE; + long cleartextChunkStart = chunkNum * PAYLOAD_SIZE; assert cleartextChunkStart <= cleartextPos; long chunkInternalDiff = cleartextPos - cleartextChunkStart; - assert chunkInternalDiff >= 0 && chunkInternalDiff < CHUNK_SIZE; - long ciphertextChunkStart = chunkNum * (CHUNK_SIZE + MAC_SIZE); + assert chunkInternalDiff >= 0 && chunkInternalDiff < PAYLOAD_SIZE; + long ciphertextChunkStart = chunkNum * (NONCE_SIZE + PAYLOAD_SIZE + MAC_SIZE); return ciphertextChunkStart + chunkInternalDiff; } @@ -54,7 +55,7 @@ public class FileContentCryptorImpl implements FileContentCryptor { if (header.remaining() != getHeaderSize()) { throw new IllegalArgumentException("Invalid header."); } - if (firstCiphertextByte % (CHUNK_SIZE + MAC_SIZE) != 0) { + if (firstCiphertextByte % (NONCE_SIZE + PAYLOAD_SIZE + MAC_SIZE) != 0) { throw new IllegalArgumentException("Invalid starting point for decryption."); } return new FileContentDecryptorImpl(encryptionKey, macKey, header, firstCiphertextByte, authenticate); @@ -65,7 +66,7 @@ public class FileContentCryptorImpl implements FileContentCryptor { if (header.isPresent() && header.get().remaining() != getHeaderSize()) { throw new IllegalArgumentException("Invalid header."); } - if (firstCleartextByte % CHUNK_SIZE != 0) { + if (firstCleartextByte % PAYLOAD_SIZE != 0) { throw new IllegalArgumentException("Invalid starting point for encryption."); } return new FileContentEncryptorImpl(encryptionKey, macKey, randomSource, firstCleartextByte); diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentDecryptorImpl.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentDecryptorImpl.java index 66bbf1aa5..1b52e2729 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentDecryptorImpl.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentDecryptorImpl.java @@ -8,8 +8,8 @@ *******************************************************************************/ package org.cryptomator.crypto.engine.impl; -import static org.cryptomator.crypto.engine.impl.FileContentCryptorImpl.CHUNK_SIZE; import static org.cryptomator.crypto.engine.impl.FileContentCryptorImpl.MAC_SIZE; +import static org.cryptomator.crypto.engine.impl.FileContentCryptorImpl.PAYLOAD_SIZE; import java.io.IOException; import java.io.UncheckedIOException; @@ -33,7 +33,7 @@ import org.cryptomator.io.ByteBuffers; class FileContentDecryptorImpl implements FileContentDecryptor { - private static final int AES_BLOCK_LENGTH_IN_BYTES = 16; + private static final int NONCE_SIZE = 16; private static final String HMAC_SHA256 = "HmacSHA256"; private static final int NUM_THREADS = Runtime.getRuntime().availableProcessors(); private static final int READ_AHEAD = 2; @@ -42,7 +42,7 @@ class FileContentDecryptorImpl implements FileContentDecryptor { private final ThreadLocal hmacSha256; private final FileHeader header; private final boolean authenticate; - private ByteBuffer ciphertextBuffer = ByteBuffer.allocate(CHUNK_SIZE + MAC_SIZE); + private ByteBuffer ciphertextBuffer = ByteBuffer.allocate(NONCE_SIZE + PAYLOAD_SIZE + MAC_SIZE); private long chunkNumber = 0; public FileContentDecryptorImpl(SecretKey headerKey, SecretKey macKey, ByteBuffer header, long firstCiphertextByte, boolean authenticate) { @@ -50,7 +50,7 @@ class FileContentDecryptorImpl implements FileContentDecryptor { this.hmacSha256 = hmacSha256; this.header = FileHeader.decrypt(headerKey, hmacSha256, header); this.authenticate = authenticate; - this.chunkNumber = firstCiphertextByte / CHUNK_SIZE; // floor() by int-truncation + this.chunkNumber = firstCiphertextByte / PAYLOAD_SIZE; // floor() by int-truncation } @Override @@ -81,7 +81,7 @@ class FileContentDecryptorImpl implements FileContentDecryptor { private void submitCiphertextBufferIfFull() throws InterruptedException { if (!ciphertextBuffer.hasRemaining()) { submitCiphertextBuffer(); - ciphertextBuffer = ByteBuffer.allocate(CHUNK_SIZE + MAC_SIZE); + ciphertextBuffer = ByteBuffer.allocate(NONCE_SIZE + PAYLOAD_SIZE + MAC_SIZE); } } @@ -119,25 +119,24 @@ class FileContentDecryptorImpl implements FileContentDecryptor { private class DecryptionJob implements Callable { + private final byte[] nonce; private final ByteBuffer ciphertextChunk; private final byte[] expectedMac; - private final byte[] nonceAndCtr; public DecryptionJob(ByteBuffer ciphertextChunk, long chunkNumber) { - if (ciphertextChunk.remaining() < MAC_SIZE) { - throw new IllegalArgumentException("Chunk must end with a MAC"); + if (ciphertextChunk.remaining() < NONCE_SIZE + MAC_SIZE) { + throw new IllegalArgumentException("Chunk must at least contain a NONCE and a MAC"); } + this.nonce = new byte[NONCE_SIZE]; + ByteBuffer nonceBuf = ciphertextChunk.asReadOnlyBuffer(); + nonceBuf.position(0).limit(NONCE_SIZE); + nonceBuf.get(nonce); this.ciphertextChunk = ciphertextChunk.asReadOnlyBuffer(); - this.ciphertextChunk.position(0).limit(ciphertextChunk.limit() - MAC_SIZE); + this.ciphertextChunk.position(NONCE_SIZE).limit(ciphertextChunk.limit() - MAC_SIZE); this.expectedMac = new byte[MAC_SIZE]; ByteBuffer macBuf = ciphertextChunk.asReadOnlyBuffer(); macBuf.position(macBuf.limit() - MAC_SIZE); macBuf.get(expectedMac); - - final ByteBuffer nonceAndCounterBuf = ByteBuffer.allocate(AES_BLOCK_LENGTH_IN_BYTES); - nonceAndCounterBuf.put(header.getNonce()); - nonceAndCounterBuf.putLong(chunkNumber * CHUNK_SIZE / AES_BLOCK_LENGTH_IN_BYTES); - this.nonceAndCtr = nonceAndCounterBuf.array(); } @Override @@ -145,6 +144,7 @@ class FileContentDecryptorImpl implements FileContentDecryptor { try { if (authenticate) { Mac mac = hmacSha256.get(); + mac.update(nonce); mac.update(ciphertextChunk.asReadOnlyBuffer()); if (!MessageDigest.isEqual(expectedMac, mac.doFinal())) { throw new AuthenticationFailedException(); @@ -152,7 +152,7 @@ class FileContentDecryptorImpl implements FileContentDecryptor { } Cipher cipher = ThreadLocalAesCtrCipher.get(); - cipher.init(Cipher.DECRYPT_MODE, header.getPayload().getContentKey(), new IvParameterSpec(nonceAndCtr)); + cipher.init(Cipher.DECRYPT_MODE, header.getPayload().getContentKey(), new IvParameterSpec(nonce)); ByteBuffer cleartextChunk = ByteBuffer.allocate(cipher.getOutputSize(ciphertextChunk.remaining())); cipher.update(ciphertextChunk, cleartextChunk); cleartextChunk.flip(); diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentEncryptorImpl.java b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentEncryptorImpl.java index 9af4a4cca..62c789478 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentEncryptorImpl.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/crypto/engine/impl/FileContentEncryptorImpl.java @@ -8,7 +8,7 @@ *******************************************************************************/ package org.cryptomator.crypto.engine.impl; -import static org.cryptomator.crypto.engine.impl.FileContentCryptorImpl.CHUNK_SIZE; +import static org.cryptomator.crypto.engine.impl.FileContentCryptorImpl.PAYLOAD_SIZE; import java.io.IOException; import java.io.UncheckedIOException; @@ -32,17 +32,18 @@ import org.cryptomator.io.ByteBuffers; class FileContentEncryptorImpl implements FileContentEncryptor { - private static final int AES_BLOCK_LENGTH_IN_BYTES = 16; + private static final int NONCE_SIZE = 16; private static final String HMAC_SHA256 = "HmacSHA256"; private static final int NUM_THREADS = Runtime.getRuntime().availableProcessors(); private static final int READ_AHEAD = 2; private final FifoParallelDataProcessor dataProcessor = new FifoParallelDataProcessor<>(NUM_THREADS, NUM_THREADS + READ_AHEAD); private final ThreadLocalMac hmacSha256; - private final FileHeader header; private final SecretKey headerKey; + private final FileHeader header; + private final SecureRandom randomSource; private final LongAdder cleartextBytesEncrypted = new LongAdder(); - private ByteBuffer cleartextBuffer = ByteBuffer.allocate(CHUNK_SIZE); + private ByteBuffer cleartextBuffer = ByteBuffer.allocate(PAYLOAD_SIZE); private long chunkNumber = 0; public FileContentEncryptorImpl(SecretKey headerKey, SecretKey macKey, SecureRandom randomSource, long firstCleartextByte) { @@ -52,6 +53,7 @@ class FileContentEncryptorImpl implements FileContentEncryptor { this.hmacSha256 = new ThreadLocalMac(macKey, HMAC_SHA256); this.headerKey = headerKey; this.header = new FileHeader(randomSource); + this.randomSource = randomSource; } @Override @@ -89,7 +91,7 @@ class FileContentEncryptorImpl implements FileContentEncryptor { private void submitCleartextBufferIfFull() throws InterruptedException { if (!cleartextBuffer.hasRemaining()) { submitCleartextBuffer(); - cleartextBuffer = ByteBuffer.allocate(CHUNK_SIZE); + cleartextBuffer = ByteBuffer.allocate(PAYLOAD_SIZE); } } @@ -126,30 +128,36 @@ class FileContentEncryptorImpl implements FileContentEncryptor { private class EncryptionJob implements Callable { private final ByteBuffer cleartextChunk; - private final byte[] nonceAndCtr; public EncryptionJob(ByteBuffer cleartextChunk, long chunkNumber) { this.cleartextChunk = cleartextChunk; - - final ByteBuffer nonceAndCounterBuf = ByteBuffer.allocate(AES_BLOCK_LENGTH_IN_BYTES); - nonceAndCounterBuf.put(header.getNonce()); - nonceAndCounterBuf.putLong(chunkNumber * CHUNK_SIZE / AES_BLOCK_LENGTH_IN_BYTES); - this.nonceAndCtr = nonceAndCounterBuf.array(); } @Override public ByteBuffer call() { try { - Cipher cipher = ThreadLocalAesCtrCipher.get(); - cipher.init(Cipher.ENCRYPT_MODE, header.getPayload().getContentKey(), new IvParameterSpec(nonceAndCtr)); - Mac mac = hmacSha256.get(); - ByteBuffer ciphertextChunk = ByteBuffer.allocate(cipher.getOutputSize(cleartextChunk.remaining()) + mac.getMacLength()); + final Cipher cipher = ThreadLocalAesCtrCipher.get(); + final Mac mac = hmacSha256.get(); + final ByteBuffer ciphertextChunk = ByteBuffer.allocate(NONCE_SIZE + cleartextChunk.remaining() + mac.getMacLength()); + + // nonce + byte[] nonce = new byte[NONCE_SIZE]; + randomSource.nextBytes(nonce); + ciphertextChunk.put(nonce); + + // payload: + cipher.init(Cipher.ENCRYPT_MODE, header.getPayload().getContentKey(), new IvParameterSpec(nonce)); + assert cipher.getOutputSize(cleartextChunk.remaining()) == cleartextChunk.remaining() : "input length should be equal to output length in CTR mode."; cipher.update(cleartextChunk, ciphertextChunk); + + // mac: ByteBuffer ciphertextSoFar = ciphertextChunk.asReadOnlyBuffer(); ciphertextSoFar.flip(); mac.update(ciphertextSoFar); byte[] authenticationCode = mac.doFinal(); ciphertextChunk.put(authenticationCode); + + // flip and return: ciphertextChunk.flip(); return ciphertextChunk; } catch (InvalidKeyException e) { diff --git a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedFileSystemFactory.java b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedFileSystemFactory.java index 26b27054f..e6f180233 100644 --- a/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedFileSystemFactory.java +++ b/main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedFileSystemFactory.java @@ -23,6 +23,6 @@ public class BlockAlignedFileSystemFactory { } public FileSystem get(Folder root) { - return new BlockAlignedFileSystem(root, FileContentCryptorImpl.CHUNK_SIZE); + return new BlockAlignedFileSystem(root, FileContentCryptorImpl.PAYLOAD_SIZE); } } diff --git a/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/FileContentDecryptorImplTest.java b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/FileContentDecryptorImplTest.java index 06fa7b579..91b80699b 100644 --- a/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/FileContentDecryptorImplTest.java +++ b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/FileContentDecryptorImplTest.java @@ -45,12 +45,12 @@ public class FileContentDecryptorImplTest { final byte[] keyBytes = new byte[32]; final SecretKey headerKey = new SecretKeySpec(keyBytes, "AES"); final SecretKey macKey = new SecretKeySpec(keyBytes, "HmacSHA256"); - final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwN74OFIGKQKgsI7bakfCYm1VXJZiKFLyhZkQCz0Ye/il0PmdZOYsSYEH9h6S00RsdHL3wLtB1FJsb9QLTtP00H8M2theZaZdlKTmjhXsmbc="); - final byte[] content = Base64.decode("tPCsFM1g/ubfJMa+AocdPh/WPHfXMFRJdIz6PkLuRijSIIXvxn7IUwVzHQ=="); + final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAANyVwHiiQImjrUiiFJKEIIdTD4r7x0U2ualjtPHEy3OLzqdAPU1ga26lJzstK9RUv1hj5zDC4wC9FgMfoVE1mD0HnuENuYXkJA=="); + final byte[] content = Base64.decode("AAAAAAAAAAAAAAAAAAAAALTwrBTNYP7m3yTG+8Yv6jcvXJj89WiHAxAtgbZR7mpsskLFfGCVDm6NO8U="); try (FileContentDecryptor decryptor = new FileContentDecryptorImpl(headerKey, macKey, ByteBuffer.wrap(header), 0, true)) { decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 0, 15))); - decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 15, 43))); + decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 15, 59))); decryptor.append(FileContentCryptor.EOF); ByteBuffer result = ByteBuffer.allocate(11); // we just care about the first 11 bytes, as this is the ciphertext. @@ -68,12 +68,12 @@ public class FileContentDecryptorImplTest { final byte[] keyBytes = new byte[32]; final SecretKey headerKey = new SecretKeySpec(keyBytes, "AES"); final SecretKey macKey = new SecretKeySpec(keyBytes, "HmacSHA256"); - final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwN74OFIGKQKgsI7bakfCYm1VXJZiKFLyhZkQCz0Ye/il0PmdZOYsSYEH9h6S00RsdHL3wLtB1FJsb9QLTtP00H8M2theZaZdlKTmjhXsmbc="); - final byte[] content = Base64.decode("tPCsFM1g/ubfJMa+AocdPh/WPHfXMFRJdIz6PkLuRijSIIXvxn7IUwVzHq=="); + final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAANyVwHiiQImjrUiiFJKEIIdTD4r7x0U2ualjtPHEy3OLzqdAPU1ga26lJzstK9RUv1hj5zDC4wC9FgMfoVE1mD0HnuENuYXkJA=="); + final byte[] content = Base64.decode("aAAAAAAAAAAAAAAAAAAAALTwrBTNYP7m3yTG+8Yv6jcvXJj89WiHAxAtgbZR7mpsskLFfGCVDm6NO8U="); try (FileContentDecryptor decryptor = new FileContentDecryptorImpl(headerKey, macKey, ByteBuffer.wrap(header), 0, true)) { decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 0, 15))); - decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 15, 43))); + decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 15, 59))); decryptor.append(FileContentCryptor.EOF); ByteBuffer result = ByteBuffer.allocate(11); // we just care about the first 11 bytes, as this is the ciphertext. @@ -89,12 +89,12 @@ public class FileContentDecryptorImplTest { final byte[] keyBytes = new byte[32]; final SecretKey headerKey = new SecretKeySpec(keyBytes, "AES"); final SecretKey macKey = new SecretKeySpec(keyBytes, "HmacSHA256"); - final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwN74OFIGKQKgsI7bakfCYm1VXJZiKFLyhZkQCz0Ye/il0PmdZOYsSYEH9h6S00RsdHL3wLtB1FJsb9QLTtP00H8M2theZaZdlKTmjhXsmbc="); - final byte[] content = Base64.decode("tPCsFM1g/ubfJMa+AocdPh/WPHfXMFRJdIz6PkLuRijSIIXvxn7IUwVzHq=="); + final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAANyVwHiiQImjrUiiFJKEIIdTD4r7x0U2ualjtPHEy3OLzqdAPU1ga26lJzstK9RUv1hj5zDC4wC9FgMfoVE1mD0HnuENuYXkJA=="); + final byte[] content = Base64.decode("AAAAAAAAAAAAAAAAAAAAALTwrBTNYP7m3yTG+8Yv6jcvXJj89WiHAxAtgbZR7mpsskLFfGCVDm6NO8u="); try (FileContentDecryptor decryptor = new FileContentDecryptorImpl(headerKey, macKey, ByteBuffer.wrap(header), 0, false)) { decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 0, 15))); - decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 15, 43))); + decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 15, 59))); decryptor.append(FileContentCryptor.EOF); ByteBuffer result = ByteBuffer.allocate(11); // we just care about the first 11 bytes, as this is the ciphertext. @@ -112,7 +112,7 @@ public class FileContentDecryptorImplTest { final byte[] keyBytes = new byte[32]; final SecretKey headerKey = new SecretKeySpec(keyBytes, "AES"); final SecretKey macKey = new SecretKeySpec(keyBytes, "AES"); - final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwN74OFIGKQKgsI7bakfCYm1VXJZiKFLyhZkQCz0Ye/il0PmdZOYsSYEH9h6S00RsdHL3wLtB1FJsb9QLTtP00H8M2theZaZdlKTmjhXsmbc="); + final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAANyVwHiiQImjrUiiFJKEIIdTD4r7x0U2ualjtPHEy3OLzqdAPU1ga26lJzstK9RUv1hj5zDC4wC9FgMfoVE1mD0HnuENuYXkJA=="); try (FileContentDecryptor decryptor = new FileContentDecryptorImpl(headerKey, macKey, ByteBuffer.wrap(header), 0, true)) { decryptor.cancelWithException(new IOException("can not do")); @@ -120,7 +120,7 @@ public class FileContentDecryptorImplTest { } } - @Test(timeout = 2000) + @Test(timeout = 200000) public void testPartialDecryption() throws InterruptedException { final byte[] keyBytes = new byte[32]; final SecretKey encryptionKey = new SecretKeySpec(keyBytes, "AES"); @@ -128,7 +128,7 @@ public class FileContentDecryptorImplTest { FileContentCryptor cryptor = new FileContentCryptorImpl(encryptionKey, macKey, RANDOM_MOCK); ByteBuffer header = ByteBuffer.allocate(cryptor.getHeaderSize()); - ByteBuffer ciphertext = ByteBuffer.allocate(131200); // 4 * (32k + 32) + ByteBuffer ciphertext = ByteBuffer.allocate(131264); // 4 * (16 + 32k + 32) try (FileContentEncryptor encryptor = cryptor.createFileContentEncryptor(Optional.empty(), 0)) { final Thread ciphertextWriter = new Thread(() -> { ByteBuffer buf; diff --git a/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/FileContentEncryptorImplTest.java b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/FileContentEncryptorImplTest.java index 3c36dcb03..45fa06c01 100644 --- a/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/FileContentEncryptorImplTest.java +++ b/main/filesystem-crypto/src/test/java/org/cryptomator/crypto/engine/impl/FileContentEncryptorImplTest.java @@ -48,16 +48,17 @@ public class FileContentEncryptorImplTest { encryptor.append(ByteBuffer.wrap("world".getBytes())); encryptor.append(FileContentCryptor.EOF); - ByteBuffer result = ByteBuffer.allocate(43); // 11 bytes ciphertext + 32 bytes mac. + ByteBuffer result = ByteBuffer.allocate(59); // 16 bytes iv + 11 bytes ciphertext + 32 bytes mac. ByteBuffer buf; while ((buf = encryptor.ciphertext()) != FileContentCryptor.EOF) { ByteBuffers.copy(buf, result); } // Ciphertext: echo -n "hello world" | openssl enc -aes-256-ctr -K 0000000000000000000000000000000000000000000000000000000000000000 -iv 00000000000000000000000000000000 | base64 - // MAC: echo -n "tPCsFM1g/ubfJMY=" | base64 --decode | openssl dgst -sha256 -mac HMAC -macopt hexkey:0000000000000000000000000000000000000000000000000000000000000000 -binary | base64 - // echo -n "tPCsFM1g/ubfJMY=" | base64 --decode > A; echo -n "vgKHHT4f1jx31zBUSXSM+j5C7kYo0iCF78Z+yFMFcx0=" | base64 --decode >> A; cat A | base64 - Assert.assertArrayEquals(Base64.decode("tPCsFM1g/ubfJMa+AocdPh/WPHfXMFRJdIz6PkLuRijSIIXvxn7IUwVzHQ=="), result.array()); + // MAC: echo -n "AAAAAAAAAAAAAAAAAAAAAA==" | base64 --decode > A; echo -n "tPCsFM1g/ubfJMY=" | base64 --decode >> A; cat A | openssl dgst -sha256 -mac HMAC -macopt + // hexkey:0000000000000000000000000000000000000000000000000000000000000000 -binary | base64 + // echo -n "+8Yv6jcvXJj89WiHAxAtgbZR7mpsskLFfGCVDm6NO8U=" | base64 --decode >> A; cat A | base64 + Assert.assertArrayEquals(Base64.decode("AAAAAAAAAAAAAAAAAAAAALTwrBTNYP7m3yTG+8Yv6jcvXJj89WiHAxAtgbZR7mpsskLFfGCVDm6NO8U="), result.array()); } }