From 4d08e9d72bdd63950aefe7579f15e328a5a67288 Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 4 May 2015 22:02:47 +0200 Subject: [PATCH] cleanup --- .../crypto/aes256/Aes256Cryptor.java | 61 ++++++------------- .../aes256/CounterAwareInputStream.java | 8 +-- 2 files changed, 21 insertions(+), 48 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 e6ebe8667..3b627fb59 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 @@ -389,27 +389,6 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi return fileSize; } - // private void encryptedContentLength(SeekableByteChannel encryptedFile, Long contentLength) throws IOException { - // final ByteBuffer encryptedFileSizeBuffer; - // - // // encrypt content length in ECB mode (content length is less than one block): - // try { - // final ByteBuffer fileSizeBuffer = ByteBuffer.allocate(Long.BYTES); - // fileSizeBuffer.putLong(contentLength); - // final Cipher sizeCipher = aesEcbCipher(primaryMasterKey, Cipher.ENCRYPT_MODE); - // final byte[] encryptedFileSize = sizeCipher.doFinal(fileSizeBuffer.array()); - // encryptedFileSizeBuffer = ByteBuffer.wrap(encryptedFileSize); - // } catch (IllegalBlockSizeException | BadPaddingException e) { - // throw new IllegalStateException("Block size must be valid, as padding is requested. BadPaddingException not possible in encrypt mode.", e); - // } - // - // // skip 128bit IV: - // encryptedFile.position(16l); - // - // // write result: - // encryptedFile.write(encryptedFileSizeBuffer); - // } - private long decryptContentLength(byte[] encryptedContentLengthBytes) { try { final Cipher sizeCipher = aesEcbCipher(primaryMasterKey, Cipher.DECRYPT_MODE); @@ -563,16 +542,18 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi // truncate file encryptedFile.truncate(0); - // 96 byte header buffer (16 IV, 16 size, 32 headerMac, 32 contentMac): - final ByteBuffer headerBuf = ByteBuffer.allocate(96); - encryptedFile.write(headerBuf); - // use an IV, whose last 8 bytes store a long used in counter mode and write initial value to file. final ByteBuffer iv = ByteBuffer.wrap(randomData(AES_BLOCK_LENGTH)); iv.putInt(AES_BLOCK_LENGTH - Integer.BYTES, 0); - // encrypt and write "zero length" as a placeholder, which will be read by concurrent requests, as long as encryption didn't finish: - // encryptedContentLength(encryptedFile, 0l); + // 96 byte header buffer (16 IV, 16 size, 32 headerMac, 32 contentMac) + // prefilled with "zero" content length for impatient processes, which want to know the size, before file has been completely written: + final ByteBuffer headerBuf = ByteBuffer.allocate(96); + headerBuf.position(16); + headerBuf.put(encryptContentLength(0l)); + headerBuf.flip(); + headerBuf.limit(96); + encryptedFile.write(headerBuf); // content encryption: final Cipher cipher = this.aesCtrCipher(primaryMasterKey, iv.array(), Cipher.ENCRYPT_MODE); @@ -586,25 +567,19 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi try { plaintextSize = IOUtils.copyLarge(lengthLimitingIn, blockSizeBufferedOut); } catch (CounterAwareInputLimitReachedException ex) { - encryptedFile.truncate(64l + CounterAwareInputStream.SIXTY_FOUR_GIGABYE); - // TODO - // encryptedContentLength(encryptedFile, CounterAwareInputStream.SIXTY_FOUR_GIGABYE); - // no additional padding needed here, as 64GiB is a multiple of 128bit + encryptedFile.truncate(0l); throw new CounterOverflowException("File size exceeds limit (64Gib). Aborting to prevent counter overflow."); } - // ensure total byte count is a multiple of the block size, in CTR mode: - // final int remainderToFillLastBlock = AES_BLOCK_LENGTH - (int) (plaintextSize % AES_BLOCK_LENGTH); - // blockSizeBufferedOut.write(new byte[remainderToFillLastBlock]); - - // for filesizes of up to 16GiB: append a few blocks of fake data: - if (plaintextSize < (long) (Integer.MAX_VALUE / 4) * AES_BLOCK_LENGTH) { - final int numberOfPlaintextBlocks = (int) Math.ceil(plaintextSize / AES_BLOCK_LENGTH); - final int upToTenPercentFakeBlocks = (int) Math.ceil(Math.random() * 0.1 * numberOfPlaintextBlocks); - final byte[] emptyBytes = this.randomData(AES_BLOCK_LENGTH); - for (int i = 0; i < upToTenPercentFakeBlocks; i += AES_BLOCK_LENGTH) { - blockSizeBufferedOut.write(emptyBytes); - } + // add random length padding to obfuscate file length: + final long numberOfPlaintextBlocks = (int) Math.ceil(plaintextSize / AES_BLOCK_LENGTH); + final long minAdditionalBlocks = 4; + final long maxAdditionalBlocks = Math.min(numberOfPlaintextBlocks >> 3, 1024 * 1024); // 12,5% of original blocks, but not more than 1M blocks (16MiBs) + final long availableBlocks = (1l << 32) - numberOfPlaintextBlocks; // before reaching limit of 2^32 blocks + final long additionalBlocks = (long) Math.min(Math.random() * Math.max(minAdditionalBlocks, maxAdditionalBlocks), availableBlocks); + final byte[] randomPadding = this.randomData(AES_BLOCK_LENGTH); + for (int i = 0; i < additionalBlocks; i += AES_BLOCK_LENGTH) { + blockSizeBufferedOut.write(randomPadding); } blockSizeBufferedOut.flush(); diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CounterAwareInputStream.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CounterAwareInputStream.java index ede51e3f8..a4cea2bf8 100644 --- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CounterAwareInputStream.java +++ b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CounterAwareInputStream.java @@ -5,20 +5,18 @@ import java.io.IOException; import java.io.InputStream; import java.util.concurrent.atomic.AtomicLong; -import javax.crypto.Mac; - /** - * Updates a {@link Mac} with the bytes read from this stream. + * Throws an exception, if more than (2^32)-1 16 byte blocks will be encrypted (would result in an counter overflow).
+ * From https://tools.ietf.org/html/rfc3686: Using the encryption process described in section 2.1, this construction permits each packet to consist of up to: (2^32)-1 blocks */ class CounterAwareInputStream extends FilterInputStream { - static final long SIXTY_FOUR_GIGABYE = 1024l * 1024l * 1024l * 64l; + static final long SIXTY_FOUR_GIGABYE = ((1l << 32) - 1) * 16; private final AtomicLong counter; /** * @param in Stream from which to read contents, which will update the Mac. - * @param mac Mac to be updated during writes. */ public CounterAwareInputStream(InputStream in) { super(in);