> tasks = new ArrayList<>();
- final Random generator = new Random(System.currentTimeMillis());
-
- final AtomicBoolean success = new AtomicBoolean(true);
-
- // 10 full interrupted requests:
- for (int i = 0; i < 10; i++) {
- final ForkJoinTask> task = ForkJoinTask.adapt(() -> {
- try {
- final HttpMethod getMethod = new GetMethod(testResourceUrl.toString());
- final int statusCode = client.executeMethod(getMethod);
- if (statusCode != 200) {
- LOG.error("Invalid status code for interrupted full request");
- success.set(false);
- }
- getMethod.getResponseBodyAsStream().read();
- getMethod.getResponseBodyAsStream().close();
- getMethod.releaseConnection();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- });
- tasks.add(task);
- }
-
- // 50 crappy interrupted range requests:
- for (int i = 0; i < 50; i++) {
- final int lower = generator.nextInt(plaintextData.length);
- final ForkJoinTask> task = ForkJoinTask.adapt(() -> {
- try {
- final HttpMethod getMethod = new GetMethod(testResourceUrl.toString());
- getMethod.addRequestHeader("Range", "bytes=" + lower + "-");
- final int statusCode = client.executeMethod(getMethod);
- if (statusCode != 206) {
- LOG.error("Invalid status code for interrupted range request");
- success.set(false);
- }
- getMethod.getResponseBodyAsStream().read();
- getMethod.getResponseBodyAsStream().close();
- getMethod.releaseConnection();
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- });
- tasks.add(task);
- }
-
- // 50 normal open range requests:
- for (int i = 0; i < 50; i++) {
- final int lower = generator.nextInt(plaintextData.length - 512);
- final int upper = plaintextData.length - 1;
- final ForkJoinTask> task = ForkJoinTask.adapt(() -> {
- try {
- final HttpMethod getMethod = new GetMethod(testResourceUrl.toString());
- getMethod.addRequestHeader("Range", "bytes=" + lower + "-");
- final byte[] expected = Arrays.copyOfRange(plaintextData, lower, upper + 1);
- final int statusCode = client.executeMethod(getMethod);
- final byte[] responseBody = new byte[upper - lower + 10];
- final int bytesRead = IOUtils.read(getMethod.getResponseBodyAsStream(), responseBody);
- getMethod.releaseConnection();
- if (statusCode != 206) {
- LOG.error("Invalid status code for open range request");
- success.set(false);
- } else if (upper - lower + 1 != bytesRead) {
- LOG.error("Invalid response length for open range request");
- success.set(false);
- } else if (!Arrays.equals(expected, Arrays.copyOfRange(responseBody, 0, bytesRead))) {
- LOG.error("Invalid response body for open range request");
- success.set(false);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- });
- tasks.add(task);
- }
-
- // 200 normal closed range requests:
- for (int i = 0; i < 200; i++) {
- final int pos1 = generator.nextInt(plaintextData.length - 512);
- final int pos2 = pos1 + 512;
- final ForkJoinTask> task = ForkJoinTask.adapt(() -> {
- try {
- final int lower = Math.min(pos1, pos2);
- final int upper = Math.max(pos1, pos2);
- final HttpMethod getMethod = new GetMethod(testResourceUrl.toString());
- getMethod.addRequestHeader("Range", "bytes=" + lower + "-" + upper);
- final byte[] expected = Arrays.copyOfRange(plaintextData, lower, upper + 1);
- final int statusCode = client.executeMethod(getMethod);
- final byte[] responseBody = new byte[upper - lower + 1];
- final int bytesRead = IOUtils.read(getMethod.getResponseBodyAsStream(), responseBody);
- getMethod.releaseConnection();
- if (statusCode != 206) {
- LOG.error("Invalid status code for closed range request");
- success.set(false);
- } else if (upper - lower + 1 != bytesRead) {
- LOG.error("Invalid response length for closed range request");
- success.set(false);
- } else if (!Arrays.equals(expected, Arrays.copyOfRange(responseBody, 0, bytesRead))) {
- LOG.error("Invalid response body for closed range request");
- success.set(false);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- });
- tasks.add(task);
- }
-
- Collections.shuffle(tasks, generator);
-
- final ForkJoinPool pool = new ForkJoinPool(4);
- for (ForkJoinTask> task : tasks) {
- pool.execute(task);
- }
- for (ForkJoinTask> task : tasks) {
- task.join();
- }
- pool.shutdown();
- cm.shutdown();
-
- Assert.assertTrue(success.get());
- }
-
- @Test
- public void testUnsatisfiableRangeRequest() throws IOException, URISyntaxException {
- final URL testResourceUrl = new URL(VAULT_BASE_URI.toURL(), "unsatisfiableRangeRequestTestFile.txt");
- final HttpClient client = new HttpClient();
-
- // prepare file content:
- final byte[] fileContent = "This is some test file content.".getBytes();
-
- // put request:
- final EntityEnclosingMethod putMethod = new PutMethod(testResourceUrl.toString());
- putMethod.setRequestEntity(new ByteArrayRequestEntity(fileContent));
- final int putResponse = client.executeMethod(putMethod);
- putMethod.releaseConnection();
- Assert.assertEquals(201, putResponse);
-
- // get request:
- final HttpMethod getMethod = new GetMethod(testResourceUrl.toString());
- getMethod.addRequestHeader("Range", "chunks=1-2");
- final int getResponse = client.executeMethod(getMethod);
- final byte[] response = new byte[fileContent.length];
- IOUtils.read(getMethod.getResponseBodyAsStream(), response);
- getMethod.releaseConnection();
- Assert.assertEquals(416, getResponse);
- Assert.assertArrayEquals(fileContent, response);
- }
-
-}
diff --git a/main/core/src/test/resources/log4j2.xml b/main/core/src/test/resources/log4j2.xml
deleted file mode 100644
index 39c2f8545..000000000
--- a/main/core/src/test/resources/log4j2.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/main/crypto-aes/pom.xml b/main/crypto-aes/pom.xml
deleted file mode 100644
index 36b640ea9..000000000
--- a/main/crypto-aes/pom.xml
+++ /dev/null
@@ -1,95 +0,0 @@
-
-
-
- 4.0.0
-
- org.cryptomator
- main
- 0.11.0-SNAPSHOT
-
- crypto-aes
- Cryptomator cryptographic module (AES)
- Provides stream ciphers and filename pseudonymization functions.
-
-
- 1.51
-
-
-
-
- org.cryptomator
- crypto-api
-
-
-
-
- org.bouncycastle
- bcprov-jdk15on
- ${bouncycastle.version}
-
-
-
-
- org.cryptomator
- siv-mode
- 1.0.2
-
-
-
-
- commons-io
- commons-io
-
-
- org.apache.commons
- commons-collections4
-
-
- org.apache.commons
- commons-lang3
-
-
- commons-codec
- commons-codec
-
-
-
-
- com.fasterxml.jackson.core
- jackson-databind
-
-
-
-
- com.google.dagger
- dagger
-
-
- com.google.dagger
- dagger-compiler
- provided
-
-
-
-
- org.cryptomator
- commons-test
-
-
-
-
-
-
- org.jacoco
- jacoco-maven-plugin
-
-
-
-
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
deleted file mode 100644
index 46810bf32..000000000
--- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java
+++ /dev/null
@@ -1,798 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Sebastian Stenzel
- * This file is licensed under the terms of the MIT license.
- * See the LICENSE.txt file for more info.
- *
- * Contributors:
- * Sebastian Stenzel - initial API and implementation
- ******************************************************************************/
-package org.cryptomator.crypto.aes256;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.channels.Channels;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.SeekableByteChannel;
-import java.nio.charset.StandardCharsets;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.util.Arrays;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import javax.crypto.AEADBadTagException;
-import javax.crypto.BadPaddingException;
-import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.Mac;
-import javax.crypto.NoSuchPaddingException;
-import javax.crypto.SecretKey;
-import javax.crypto.ShortBufferException;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-import javax.security.auth.DestroyFailedException;
-import javax.security.auth.Destroyable;
-
-import org.bouncycastle.crypto.generators.SCrypt;
-import org.cryptomator.crypto.Cryptor;
-import org.cryptomator.crypto.exceptions.DecryptFailedException;
-import org.cryptomator.crypto.exceptions.EncryptFailedException;
-import org.cryptomator.crypto.exceptions.MacAuthenticationFailedException;
-import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
-import org.cryptomator.crypto.exceptions.UnsupportedVaultException;
-import org.cryptomator.crypto.exceptions.WrongPasswordException;
-import org.cryptomator.siv.SivMode;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration {
-
- private static final Logger LOG = LoggerFactory.getLogger(Aes256Cryptor.class);
-
- /**
- * Defined in static initializer. Defaults to 256, but falls back to maximum value possible, if JCE Unlimited Strength Jurisdiction Policy Files isn't installed. Those files can be downloaded
- * here: http://www.oracle.com/technetwork/java/javase/downloads/.
- */
- private static final int AES_KEY_LENGTH_IN_BITS;
-
- /**
- * SIV mode for deterministic filename encryption.
- */
- private static final SivMode AES_SIV = new SivMode();
-
- /**
- * PRNG for cryptographically secure random numbers. Defaults to SHA1-based number generator.
- *
- * @see http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#SecureRandom
- */
- private final SecureRandom securePrng;
-
- /**
- * Jackson JSON-Mapper.
- */
- private final ObjectMapper objectMapper = new ObjectMapper();
-
- /**
- * The decrypted master key. Its lifecycle starts with the construction of an Aes256Cryptor instance or {@link #decryptMasterKey(InputStream, CharSequence)}. Its lifecycle ends with
- * {@link #swipeSensitiveData()}.
- */
- private SecretKey primaryMasterKey;
-
- /**
- * Decrypted secondary key used for hmac operations.
- */
- private SecretKey hMacMasterKey;
-
- static {
- try {
- final int maxKeyLength = Cipher.getMaxAllowedKeyLength(AES_KEY_ALGORITHM);
- AES_KEY_LENGTH_IN_BITS = (maxKeyLength >= PREF_MASTER_KEY_LENGTH_IN_BITS) ? PREF_MASTER_KEY_LENGTH_IN_BITS : maxKeyLength;
- } catch (NoSuchAlgorithmException e) {
- throw new IllegalStateException("Algorithm should exist.", e);
- }
- }
-
- /**
- * Creates a new Cryptor with a newly initialized PRNG.
- */
-
- Aes256Cryptor(SecureRandom securePrng) {
- this.securePrng = securePrng;
- // No setSeed needed. See SecureRandom.getInstance(String):
- // The first call to nextBytes will force the SecureRandom object to seed itself
- }
-
- @Override
- public void randomizeMasterKey() {
- byte[] bytes = new byte[AES_KEY_LENGTH_IN_BITS / Byte.SIZE];
- try {
- // No setSeed needed. See SecureRandom.getInstance(String):
- // The first call to nextBytes will force the SecureRandom object to seed itself
- securePrng.nextBytes(bytes);
- this.primaryMasterKey = new SecretKeySpec(bytes, AES_KEY_ALGORITHM);
- securePrng.nextBytes(bytes);
- this.hMacMasterKey = new SecretKeySpec(bytes, HMAC_KEY_ALGORITHM);
- } finally {
- Arrays.fill(bytes, (byte) 0);
- }
- }
-
- @Override
- public void encryptMasterKey(OutputStream out, CharSequence password) throws IOException {
- try {
- // derive key:
- final byte[] kekSalt = randomData(SCRYPT_SALT_LENGTH);
- final SecretKey kek = scrypt(password, kekSalt, SCRYPT_COST_PARAM, SCRYPT_BLOCK_SIZE, AES_KEY_LENGTH_IN_BITS);
-
- // encrypt:
- final Cipher encCipher = aesKeyWrapCipher(kek, Cipher.WRAP_MODE);
- byte[] wrappedPrimaryKey = encCipher.wrap(primaryMasterKey);
- byte[] wrappedSecondaryKey = encCipher.wrap(hMacMasterKey);
-
- // save encrypted masterkey:
- final KeyFile keyfile = new KeyFile();
- keyfile.setVersion(KeyFile.CURRENT_VERSION);
- keyfile.setScryptSalt(kekSalt);
- keyfile.setScryptCostParam(SCRYPT_COST_PARAM);
- keyfile.setScryptBlockSize(SCRYPT_BLOCK_SIZE);
- keyfile.setKeyLength(AES_KEY_LENGTH_IN_BITS);
- keyfile.setPrimaryMasterKey(wrappedPrimaryKey);
- keyfile.setHMacMasterKey(wrappedSecondaryKey);
- objectMapper.writeValue(out, keyfile);
- } catch (InvalidKeyException | IllegalBlockSizeException ex) {
- throw new IllegalStateException("Invalid hard coded configuration.", ex);
- }
- }
-
- @Override
- public void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException, UnsupportedVaultException {
- try {
- // load encrypted masterkey:
- final KeyFile keyfile = objectMapper.readValue(in, KeyFile.class);
-
- // check version
- if (keyfile.getVersion() != KeyFile.CURRENT_VERSION) {
- throw new UnsupportedVaultException(keyfile.getVersion(), KeyFile.CURRENT_VERSION);
- }
-
- // check, whether the key length is supported:
- final int maxKeyLen = Cipher.getMaxAllowedKeyLength(AES_KEY_ALGORITHM);
- if (keyfile.getKeyLength() > maxKeyLen) {
- throw new UnsupportedKeyLengthException(keyfile.getKeyLength(), maxKeyLen);
- }
-
- // derive key:
- final SecretKey kek = scrypt(password, keyfile.getScryptSalt(), keyfile.getScryptCostParam(), keyfile.getScryptBlockSize(), keyfile.getKeyLength());
-
- // decrypt and check password by catching AEAD exception
- final Cipher decCipher = aesKeyWrapCipher(kek, Cipher.UNWRAP_MODE);
- SecretKey primary = (SecretKey) decCipher.unwrap(keyfile.getPrimaryMasterKey(), AES_KEY_ALGORITHM, Cipher.SECRET_KEY);
- SecretKey secondary = (SecretKey) decCipher.unwrap(keyfile.getHMacMasterKey(), HMAC_KEY_ALGORITHM, Cipher.SECRET_KEY);
-
- // everything ok, assign decrypted keys:
- this.primaryMasterKey = primary;
- this.hMacMasterKey = secondary;
- } catch (NoSuchAlgorithmException ex) {
- throw new IllegalStateException("Algorithm should exist.", ex);
- } catch (InvalidKeyException e) {
- throw new WrongPasswordException();
- }
- }
-
- @Override
- public boolean isDestroyed() {
- if (primaryMasterKey == null || hMacMasterKey == null) {
- // master keys have not been set yet, so there is nothing sensitive
- return true;
- }
- return primaryMasterKey.isDestroyed() && hMacMasterKey.isDestroyed();
- }
-
- @Override
- public void destroy() {
- destroyQuietly(primaryMasterKey);
- destroyQuietly(hMacMasterKey);
- }
-
- private void destroyQuietly(Destroyable d) {
- try {
- d.destroy();
- } catch (DestroyFailedException e) {
- // ignore
- }
- }
-
- private Cipher aesKeyWrapCipher(SecretKey key, int cipherMode) {
- try {
- final Cipher cipher = Cipher.getInstance(AES_KEYWRAP_CIPHER);
- cipher.init(cipherMode, key);
- return cipher;
- } catch (InvalidKeyException ex) {
- throw new IllegalArgumentException("Invalid key.", ex);
- } catch (NoSuchAlgorithmException | NoSuchPaddingException ex) {
- throw new IllegalStateException("Algorithm/Padding should exist.", ex);
- }
- }
-
- private Cipher aesCtrCipher(SecretKey key, byte[] iv, int cipherMode) {
- try {
- final Cipher cipher = Cipher.getInstance(AES_CTR_CIPHER);
- cipher.init(cipherMode, key, new IvParameterSpec(iv));
- return cipher;
- } catch (InvalidKeyException ex) {
- throw new IllegalArgumentException("Invalid key.", ex);
- } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException ex) {
- throw new IllegalStateException("Algorithm/Padding should exist and accept an IV.", ex);
- }
- }
-
- private Cipher aesCbcCipher(SecretKey key, byte[] iv, int cipherMode) {
- try {
- final Cipher cipher = Cipher.getInstance(AES_CBC_CIPHER);
- cipher.init(cipherMode, key, new IvParameterSpec(iv));
- return cipher;
- } catch (InvalidKeyException ex) {
- throw new IllegalArgumentException("Invalid key.", ex);
- } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException ex) {
- throw new AssertionError("Every implementation of the Java platform is required to support AES/CBC/PKCS5Padding, which accepts an IV", ex);
- }
- }
-
- private Mac hmacSha256(SecretKey key) {
- try {
- final Mac mac = Mac.getInstance(HMAC_KEY_ALGORITHM);
- mac.init(key);
- return mac;
- } catch (NoSuchAlgorithmException e) {
- throw new AssertionError("Every implementation of the Java platform is required to support HmacSHA256.", e);
- } catch (InvalidKeyException e) {
- throw new IllegalArgumentException("Invalid key", e);
- }
- }
-
- private MessageDigest sha256() {
- try {
- return MessageDigest.getInstance("SHA-256");
- } catch (NoSuchAlgorithmException e) {
- throw new AssertionError("Every implementation of the Java platform is required to support Sha-256");
- }
- }
-
- private byte[] randomData(int length) {
- final byte[] result = new byte[length];
- securePrng.nextBytes(result);
- return result;
- }
-
- private SecretKey scrypt(CharSequence password, byte[] salt, int costParam, int blockSize, int keyLengthInBits) {
- // use sb, as password.toString's implementation is unknown
- final StringBuilder sb = new StringBuilder(password);
- final byte[] pw = sb.toString().getBytes();
- try {
- final byte[] key = SCrypt.generate(pw, salt, costParam, blockSize, 1, keyLengthInBits / Byte.SIZE);
- return new SecretKeySpec(key, AES_KEY_ALGORITHM);
- } finally {
- // destroy copied bytes of the plaintext password:
- Arrays.fill(pw, (byte) 0);
- for (int i = 0; i < password.length(); i++) {
- sb.setCharAt(i, (char) 0);
- }
- }
- }
-
- @Override
- public String encryptDirectoryPath(String cleartextDirectoryId, String nativePathSep) {
- final byte[] cleartextBytes = cleartextDirectoryId.getBytes(StandardCharsets.UTF_8);
- byte[] encryptedBytes = AES_SIV.encrypt(primaryMasterKey, hMacMasterKey, cleartextBytes);
- final byte[] hashed = sha256().digest(encryptedBytes);
- final String encryptedThenHashedPath = ENCRYPTED_FILENAME_CODEC.encodeAsString(hashed);
- return encryptedThenHashedPath.substring(0, 2) + nativePathSep + encryptedThenHashedPath.substring(2);
- }
-
- @Override
- public String encryptFilename(String cleartextName) {
- final byte[] cleartextBytes = cleartextName.getBytes(StandardCharsets.UTF_8);
- final byte[] encryptedBytes = AES_SIV.encrypt(primaryMasterKey, hMacMasterKey, cleartextBytes);
- return ENCRYPTED_FILENAME_CODEC.encodeAsString(encryptedBytes);
- }
-
- @Override
- public String decryptFilename(String ciphertextName) throws DecryptFailedException {
- final byte[] encryptedBytes = ENCRYPTED_FILENAME_CODEC.decode(ciphertextName);
- try {
- final byte[] cleartextBytes = AES_SIV.decrypt(primaryMasterKey, hMacMasterKey, encryptedBytes);
- return new String(cleartextBytes, StandardCharsets.UTF_8);
- } catch (AEADBadTagException e) {
- throw new DecryptFailedException(e);
- }
- }
-
- @Override
- public Long decryptedContentLength(SeekableByteChannel encryptedFile) throws IOException, MacAuthenticationFailedException {
- // read header:
- encryptedFile.position(0);
- final ByteBuffer headerBuf = ByteBuffer.allocate(104);
- final int headerBytesRead = readFromChannel(encryptedFile, headerBuf);
- if (headerBytesRead != headerBuf.capacity()) {
- return null;
- }
-
- // read iv:
- final byte[] iv = new byte[AES_BLOCK_LENGTH];
- headerBuf.position(0);
- headerBuf.get(iv);
-
- // read sensitive header data:
- final byte[] encryptedSensitiveHeaderContentBytes = new byte[48];
- headerBuf.position(24);
- headerBuf.get(encryptedSensitiveHeaderContentBytes);
-
- // read stored header mac:
- final byte[] storedHeaderMac = new byte[32];
- headerBuf.position(72);
- headerBuf.get(storedHeaderMac);
-
- // calculate mac over first 72 bytes of header:
- final Mac headerMac = this.hmacSha256(hMacMasterKey);
- headerBuf.rewind();
- headerBuf.limit(72);
- headerMac.update(headerBuf);
-
- final boolean macMatches = MessageDigest.isEqual(storedHeaderMac, headerMac.doFinal());
- if (!macMatches) {
- throw new MacAuthenticationFailedException("MAC authentication failed.");
- }
-
- // decrypt sensitive header data:
- final byte[] decryptedSensitiveHeaderContentBytes = decryptHeaderData(encryptedSensitiveHeaderContentBytes, iv);
- final ByteBuffer sensitiveHeaderContentBuf = ByteBuffer.wrap(decryptedSensitiveHeaderContentBytes);
- final Long fileSize = sensitiveHeaderContentBuf.getLong();
-
- return fileSize;
- }
-
- private byte[] decryptHeaderData(byte[] ciphertextBytes, byte[] iv) {
- try {
- final Cipher sizeCipher = aesCbcCipher(primaryMasterKey, iv, Cipher.DECRYPT_MODE);
- return sizeCipher.doFinal(ciphertextBytes);
- } catch (IllegalBlockSizeException | BadPaddingException e) {
- throw new IllegalStateException(e);
- }
- }
-
- private byte[] encryptHeaderData(byte[] plaintextBytes, byte[] iv) {
- try {
- final Cipher sizeCipher = aesCbcCipher(primaryMasterKey, iv, Cipher.ENCRYPT_MODE);
- return sizeCipher.doFinal(plaintextBytes);
- } catch (IllegalBlockSizeException | BadPaddingException e) {
- throw new IllegalStateException("Block size must be valid, as padding is requested. BadPaddingException not possible in encrypt mode.", e);
- }
- }
-
- @Override
- public Long decryptFile(SeekableByteChannel encryptedFile, OutputStream plaintextFile, boolean authenticate) throws IOException, DecryptFailedException {
- // read header:
- encryptedFile.position(0l);
- final ByteBuffer headerBuf = ByteBuffer.allocate(104);
- final int headerBytesRead = readFromChannel(encryptedFile, headerBuf);
- if (headerBytesRead != headerBuf.capacity()) {
- throw new IOException("Failed to read file header.");
- }
-
- // read iv:
- final byte[] iv = new byte[AES_BLOCK_LENGTH];
- headerBuf.position(0);
- headerBuf.get(iv);
-
- // read nonce:
- final byte[] nonce = new byte[8];
- headerBuf.position(16);
- headerBuf.get(nonce);
-
- // read sensitive header data:
- final byte[] encryptedSensitiveHeaderContentBytes = new byte[48];
- headerBuf.position(24);
- headerBuf.get(encryptedSensitiveHeaderContentBytes);
-
- // read header mac:
- final byte[] storedHeaderMac = new byte[32];
- headerBuf.position(72);
- headerBuf.get(storedHeaderMac);
-
- // calculate mac over first 72 bytes of header:
- if (authenticate) {
- final Mac headerMac = this.hmacSha256(hMacMasterKey);
- headerBuf.position(0);
- headerBuf.limit(72);
- headerMac.update(headerBuf);
- if (!MessageDigest.isEqual(storedHeaderMac, headerMac.doFinal())) {
- throw new MacAuthenticationFailedException("Header MAC authentication failed.");
- }
- }
-
- // decrypt sensitive header data:
- final byte[] fileKeyBytes = new byte[32];
- final byte[] decryptedSensitiveHeaderContentBytes = decryptHeaderData(encryptedSensitiveHeaderContentBytes, iv);
- final ByteBuffer sensitiveHeaderContentBuf = ByteBuffer.wrap(decryptedSensitiveHeaderContentBytes);
- final Long fileSize = sensitiveHeaderContentBuf.getLong();
- sensitiveHeaderContentBuf.get(fileKeyBytes);
-
- // prepare content decryption:
- final SecretKey fileKey = new SecretKeySpec(fileKeyBytes, AES_KEY_ALGORITHM);
- final LengthLimitingOutputStream paddingRemovingOutputStream = new LengthLimitingOutputStream(plaintextFile, fileSize);
- final CryptoWorkerExecutor executor = new CryptoWorkerExecutor(Runtime.getRuntime().availableProcessors(), (lock, blockDone, currentBlock, inputQueue) -> {
- return new DecryptWorker(lock, blockDone, currentBlock, inputQueue, authenticate, Channels.newChannel(paddingRemovingOutputStream)) {
-
- @Override
- protected Cipher initCipher(long startBlockNum) {
- final ByteBuffer nonceAndCounterBuf = ByteBuffer.allocate(AES_BLOCK_LENGTH);
- nonceAndCounterBuf.put(nonce);
- nonceAndCounterBuf.putLong(startBlockNum * CONTENT_MAC_BLOCK / AES_BLOCK_LENGTH);
- final byte[] nonceAndCounter = nonceAndCounterBuf.array();
- return aesCtrCipher(fileKey, nonceAndCounter, Cipher.DECRYPT_MODE);
- }
-
- @Override
- protected Mac initMac() {
- return hmacSha256(hMacMasterKey);
- }
-
- @Override
- protected void checkMac(Mac mac, long blockNum, ByteBuffer ciphertextBuf, ByteBuffer macBuf) throws MacAuthenticationFailedException {
- mac.update(iv);
- mac.update(longToByteArray(blockNum));
- mac.update(ciphertextBuf);
- final byte[] calculatedMac = mac.doFinal();
- final byte[] storedMac = new byte[mac.getMacLength()];
- macBuf.get(storedMac);
- if (!MessageDigest.isEqual(calculatedMac, storedMac)) {
- throw new MacAuthenticationFailedException("Content MAC authentication failed.");
- }
- }
-
- @Override
- protected void decrypt(Cipher cipher, ByteBuffer ciphertextBuf, ByteBuffer plaintextBuf) throws DecryptFailedException {
- assert plaintextBuf.remaining() >= cipher.getOutputSize(ciphertextBuf.remaining());
- try {
- cipher.update(ciphertextBuf, plaintextBuf);
- } catch (ShortBufferException e) {
- throw new DecryptFailedException(e);
- }
- }
-
- };
- });
-
- // read as many blocks from file as possible, but wait if queue is full:
- encryptedFile.position(104l);
- final int maxNumBlocks = 64;
- int numBlocks = 1;
- int bytesRead = 0;
- long blockNumber = 0;
- do {
- if (numBlocks < maxNumBlocks) {
- numBlocks++;
- }
- final int inBufSize = numBlocks * (CONTENT_MAC_BLOCK + 32);
- final ByteBuffer buf = ByteBuffer.allocate(inBufSize);
- bytesRead = readFromChannel(encryptedFile, buf);
- buf.flip();
- final int blocksRead = (int) Math.ceil(bytesRead / (double) (CONTENT_MAC_BLOCK + 32));
- final boolean consumedInTime = executor.offer(new BlocksData(buf.asReadOnlyBuffer(), blockNumber, blocksRead), 1, TimeUnit.SECONDS);
- if (!consumedInTime) {
- break;
- }
- blockNumber += numBlocks;
- } while (bytesRead == numBlocks * (CONTENT_MAC_BLOCK + 32));
-
- // wait for decryption workers to finish:
- try {
- executor.waitUntilDone(1, TimeUnit.SECONDS);
- } catch (ExecutionException e) {
- Throwable cause = e;
- while (cause instanceof ExecutionException) {
- cause = cause.getCause();
- }
- if (cause instanceof IOException) {
- throw (IOException) cause;
- } else if (cause instanceof TimeoutException) {
- throw new DecryptFailedException(cause);
- } else if (cause instanceof RuntimeException) {
- throw (RuntimeException) cause;
- } else {
- LOG.error("Unexpected exception", e);
- throw new RuntimeException(cause);
- }
- } finally {
- destroyQuietly(fileKey);
- }
-
- return paddingRemovingOutputStream.getBytesWritten();
- }
-
- @Override
- public Long decryptRange(SeekableByteChannel encryptedFile, OutputStream plaintextFile, long pos, long length, boolean authenticate) throws IOException, DecryptFailedException {
- // read header:
- encryptedFile.position(0l);
- final ByteBuffer headerBuf = ByteBuffer.allocate(104);
- final int headerBytesRead = readFromChannel(encryptedFile, headerBuf);
- if (headerBytesRead != headerBuf.capacity()) {
- throw new IOException("Failed to read file header.");
- }
-
- // read iv:
- final byte[] iv = new byte[AES_BLOCK_LENGTH];
- headerBuf.position(0);
- headerBuf.get(iv);
-
- // read nonce:
- final byte[] nonce = new byte[8];
- headerBuf.position(16);
- headerBuf.get(nonce);
-
- // read sensitive header data:
- final byte[] encryptedSensitiveHeaderContentBytes = new byte[48];
- headerBuf.position(24);
- headerBuf.get(encryptedSensitiveHeaderContentBytes);
-
- // read header mac:
- final byte[] storedHeaderMac = new byte[32];
- headerBuf.position(72);
- headerBuf.get(storedHeaderMac);
-
- // calculate mac over first 72 bytes of header:
- if (authenticate) {
- final Mac headerMac = this.hmacSha256(hMacMasterKey);
- headerBuf.position(0);
- headerBuf.limit(72);
- headerMac.update(headerBuf);
- if (!MessageDigest.isEqual(storedHeaderMac, headerMac.doFinal())) {
- throw new MacAuthenticationFailedException("Header MAC authentication failed.");
- }
- }
-
- // decrypt sensitive header data:
- final byte[] fileKeyBytes = new byte[32];
- final byte[] decryptedSensitiveHeaderContentBytes = decryptHeaderData(encryptedSensitiveHeaderContentBytes, iv);
- final ByteBuffer sensitiveHeaderContentBuf = ByteBuffer.wrap(decryptedSensitiveHeaderContentBytes);
- final Long fileSize = sensitiveHeaderContentBuf.getLong();
- sensitiveHeaderContentBuf.get(fileKeyBytes);
-
- assert pos + length - 1 < fileSize;
-
- // find first relevant block:
- final long startBlock = pos / CONTENT_MAC_BLOCK; // floor
- final long startByte = startBlock * (CONTENT_MAC_BLOCK + 32) + 104;
- final long offsetFromFirstBlock = pos - startBlock * CONTENT_MAC_BLOCK;
-
- // append correct counter value to nonce:
- final ByteBuffer nonceAndCounterBuf = ByteBuffer.allocate(AES_BLOCK_LENGTH);
- nonceAndCounterBuf.put(nonce);
- nonceAndCounterBuf.putLong(startBlock * CONTENT_MAC_BLOCK / AES_BLOCK_LENGTH);
- final byte[] nonceAndCounter = nonceAndCounterBuf.array();
-
- // content decryption:
- encryptedFile.position(startByte);
- final SecretKey fileKey = new SecretKeySpec(fileKeyBytes, AES_KEY_ALGORITHM);
- final Cipher cipher = this.aesCtrCipher(fileKey, nonceAndCounter, Cipher.DECRYPT_MODE);
- final Mac contentMac = this.hmacSha256(hMacMasterKey);
-
- try {
- // reading ciphered input and MACs interleaved:
- long bytesWritten = 0;
- final ByteBuffer buf = ByteBuffer.allocate(CONTENT_MAC_BLOCK + 32);
- int n = 0;
- long blockNum = startBlock;
- while ((n = readFromChannel(encryptedFile, buf)) > 0 && bytesWritten < length) {
- if (n < 32) {
- throw new DecryptFailedException("Invalid file content, missing MAC.");
- }
-
- buf.flip();
- final ByteBuffer ciphertextBuf = buf.asReadOnlyBuffer();
- ciphertextBuf.limit(n - 32);
-
- // check MAC of current block:
- if (authenticate) {
- final byte[] storedMac = new byte[contentMac.getMacLength()];
- final ByteBuffer storedMacBuf = buf.asReadOnlyBuffer();
- storedMacBuf.position(n - 32);
- storedMacBuf.get(storedMac);
- contentMac.update(iv);
- contentMac.update(longToByteArray(blockNum));
- contentMac.update(ciphertextBuf);
- ciphertextBuf.rewind();
- final byte[] calculatedMac = contentMac.doFinal();
- if (!MessageDigest.isEqual(calculatedMac, storedMac)) {
- throw new MacAuthenticationFailedException("Content MAC authentication failed.");
- }
- }
-
- // decrypt block:
- final ByteBuffer plaintextBuf = ByteBuffer.allocate(cipher.getOutputSize(ciphertextBuf.remaining()));
- cipher.update(ciphertextBuf, plaintextBuf);
- plaintextBuf.flip();
- final int offset = (bytesWritten == 0) ? (int) offsetFromFirstBlock : 0;
- final long pending = length - bytesWritten;
- final int available = plaintextBuf.remaining() - offset;
- final int currentBatch = (int) Math.min(pending, available);
-
- plaintextFile.write(plaintextBuf.array(), offset, currentBatch);
- bytesWritten += currentBatch;
- blockNum++;
- buf.rewind();
- }
- return bytesWritten;
- } catch (ShortBufferException e) {
- throw new IllegalStateException("Output buffer size known to fit.", e);
- } finally {
- destroyQuietly(fileKey);
- }
- }
-
- /**
- * header = {16 byte iv, 8 byte nonce, 48 byte sensitive header data (file size + file key + padding), 32 byte headerMac}
- */
- @Override
- public Long encryptFile(InputStream plaintextFile, SeekableByteChannel encryptedFile) throws IOException, EncryptFailedException {
- // truncate file
- encryptedFile.truncate(0l);
-
- // choose a random header IV:
- final byte[] iv = randomData(AES_BLOCK_LENGTH);
-
- // chosse 8 byte random nonce and 8 byte counter set to zero:
- final byte[] nonce = randomData(8);
-
- // choose a random content key:
- final byte[] fileKeyBytes = randomData(32);
-
- // 104 byte header buffer (16 header IV, 8 content nonce, 48 sensitive header data, 32 headerMac), filled after writing the content
- final ByteBuffer headerBuf = ByteBuffer.allocate(104);
- headerBuf.limit(104);
- encryptedFile.write(headerBuf);
-
- // prepare content encryption:
- final SecretKey fileKey = new SecretKeySpec(fileKeyBytes, AES_KEY_ALGORITHM);
- final CryptoWorkerExecutor executor = new CryptoWorkerExecutor(Runtime.getRuntime().availableProcessors(), (lock, blockDone, currentBlock, inputQueue) -> {
- return new EncryptWorker(lock, blockDone, currentBlock, inputQueue, encryptedFile) {
-
- @Override
- protected Cipher initCipher(long startBlockNum) {
- final ByteBuffer nonceAndCounterBuf = ByteBuffer.allocate(AES_BLOCK_LENGTH);
- nonceAndCounterBuf.put(nonce);
- nonceAndCounterBuf.putLong(startBlockNum * CONTENT_MAC_BLOCK / AES_BLOCK_LENGTH);
- final byte[] nonceAndCounter = nonceAndCounterBuf.array();
- return aesCtrCipher(fileKey, nonceAndCounter, Cipher.ENCRYPT_MODE);
- }
-
- @Override
- protected Mac initMac() {
- return hmacSha256(hMacMasterKey);
- }
-
- @Override
- protected byte[] calcMac(Mac mac, long blockNum, ByteBuffer ciphertextBuf) {
- mac.update(iv);
- mac.update(longToByteArray(blockNum));
- mac.update(ciphertextBuf);
- return mac.doFinal();
- }
-
- @Override
- protected void encrypt(Cipher cipher, ByteBuffer plaintextBuf, ByteBuffer ciphertextBuf) throws EncryptFailedException {
- try {
- assert ciphertextBuf.remaining() >= cipher.getOutputSize(plaintextBuf.remaining());
- cipher.update(plaintextBuf, ciphertextBuf);
- } catch (ShortBufferException e) {
- throw new EncryptFailedException(e);
- }
- }
- };
- });
-
- // read as many blocks from file as possible, but wait if queue is full:
- final byte[] randomPadding = this.randomData(AES_BLOCK_LENGTH);
- final LengthObfuscatingInputStream in = new LengthObfuscatingInputStream(plaintextFile, randomPadding);
- final ReadableByteChannel channel = Channels.newChannel(in);
- int bytesRead = 0;
- long blockNumber = 0;
- final int maxNumBlocks = 64;
- int numBlocks = 0;
- do {
- if (numBlocks < maxNumBlocks) {
- numBlocks++;
- }
- final int inBufSize = numBlocks * CONTENT_MAC_BLOCK;
- final ByteBuffer inBuf = ByteBuffer.allocate(inBufSize);
- bytesRead = readFromChannel(channel, inBuf);
- inBuf.flip();
- final int blocksRead = (int) Math.ceil(bytesRead / (double) CONTENT_MAC_BLOCK);
- final boolean consumedInTime = executor.offer(new BlocksData(inBuf.asReadOnlyBuffer(), blockNumber, blocksRead), 1, TimeUnit.SECONDS);
- if (!consumedInTime) {
- break;
- }
- blockNumber += numBlocks;
- } while (bytesRead == numBlocks * CONTENT_MAC_BLOCK);
-
- // wait for encryption workers to finish:
- try {
- executor.waitUntilDone(1, TimeUnit.SECONDS);
- } catch (ExecutionException e) {
- Throwable cause = e;
- while (cause instanceof ExecutionException) {
- cause = cause.getCause();
- }
- if (cause instanceof IOException) {
- throw (IOException) cause;
- } else if (cause instanceof TimeoutException) {
- throw new EncryptFailedException(cause);
- } else if (cause instanceof RuntimeException) {
- throw (RuntimeException) cause;
- } else {
- LOG.error("Unexpected exception", e);
- throw new RuntimeException(cause);
- }
- } finally {
- destroyQuietly(fileKey);
- }
-
- // create and write header:
- final long plaintextSize = in.getRealInputLength();
- final ByteBuffer sensitiveHeaderContentBuf = ByteBuffer.allocate(Long.BYTES + fileKeyBytes.length);
- sensitiveHeaderContentBuf.putLong(plaintextSize);
- sensitiveHeaderContentBuf.put(fileKeyBytes);
- headerBuf.clear();
- headerBuf.put(iv);
- headerBuf.put(nonce);
- headerBuf.put(encryptHeaderData(sensitiveHeaderContentBuf.array(), iv));
- headerBuf.flip();
- final Mac headerMac = this.hmacSha256(hMacMasterKey);
- headerMac.update(headerBuf);
- headerBuf.limit(104);
- headerBuf.put(headerMac.doFinal());
- headerBuf.flip();
- encryptedFile.position(0);
- encryptedFile.write(headerBuf);
-
- return plaintextSize;
- }
-
- private byte[] longToByteArray(long lng) {
- return ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(lng).array();
- }
-
- /**
- * Reads bytes from a ReadableByteChannel.
- *
- * This implementation guarantees that it will read as many bytes
- * as possible before giving up; this may not always be the case for
- * subclasses of {@link ReadableByteChannel}.
- *
- * @param input the byte channel to read
- * @param buffer byte buffer destination
- * @return the actual length read; may be less than requested if EOF was reached
- * @throws IOException if a read error occurs
- * @see
- * Apache Commons IOUtils 2.5
- */
- public static int readFromChannel(final ReadableByteChannel input, final ByteBuffer buffer) throws IOException {
- final int length = buffer.remaining();
- while (buffer.remaining() > 0) {
- final int count = input.read(buffer);
- if (count == -1) { // EOF
- break;
- }
- }
- return length - buffer.remaining();
- }
-
-}
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/AesCryptographicConfiguration.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/AesCryptographicConfiguration.java
deleted file mode 100644
index 543919c7f..000000000
--- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/AesCryptographicConfiguration.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Sebastian Stenzel
- * This file is licensed under the terms of the MIT license.
- * See the LICENSE.txt file for more info.
- *
- * Contributors:
- * Sebastian Stenzel - initial API and implementation
- ******************************************************************************/
-package org.cryptomator.crypto.aes256;
-
-import org.apache.commons.codec.binary.Base32;
-import org.apache.commons.codec.binary.BaseNCodec;
-
-interface AesCryptographicConfiguration {
-
- /**
- * Number of bytes used as salt, where needed.
- */
- int SCRYPT_SALT_LENGTH = 8;
-
- /**
- * Scrypt CPU/Memory cost parameter.
- */
- int SCRYPT_COST_PARAM = 1 << 14;
-
- /**
- * Scrypt block size (affects memory consumption)
- */
- int SCRYPT_BLOCK_SIZE = 8;
-
- /**
- * Preferred number of bytes of the master key.
- */
- int PREF_MASTER_KEY_LENGTH_IN_BITS = 256;
-
- /**
- * Number of bytes used as seed for the PRNG.
- */
- int PRNG_SEED_LENGTH = 16;
-
- /**
- * Algorithm used for en/decryption.
- *
- * @see http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#AlgorithmParameters
- */
- String AES_KEY_ALGORITHM = "AES";
-
- /**
- * Key algorithm for keyed MAC.
- */
- String HMAC_KEY_ALGORITHM = "HmacSHA256";
-
- /**
- * Cipher specs for RFC 3394 masterkey encryption.
- *
- * @see http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher
- */
- String AES_KEYWRAP_CIPHER = "AESWrap";
-
- /**
- * Cipher specs for file content encryption. Using CTR-mode for random access.
- *
- * @see http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#Cipher
- */
- String AES_CTR_CIPHER = "AES/CTR/NoPadding";
-
- /**
- * Cipher specs for file header encryption (fixed-length block cipher).
- *
- * @see http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#impl
- */
- String AES_CBC_CIPHER = "AES/CBC/PKCS5Padding";
-
- /**
- * AES block size is 128 bit or 16 bytes.
- */
- int AES_BLOCK_LENGTH = 16;
-
- /**
- * Number of bytes, a content block over which a MAC is calculated consists of.
- */
- int CONTENT_MAC_BLOCK = 32 * 1024;
-
- /**
- * How to encode the encrypted file names safely. Base32 uses only alphanumeric characters and is case-insensitive.
- */
- BaseNCodec ENCRYPTED_FILENAME_CODEC = new Base32();
-
-}
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/BlocksData.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/BlocksData.java
deleted file mode 100644
index a1a62ba8d..000000000
--- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/BlocksData.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.cryptomator.crypto.aes256;
-
-import java.nio.ByteBuffer;
-
-class BlocksData {
-
- public static final int MAX_NUM_BLOCKS = 128;
-
- final ByteBuffer buffer;
- final long startBlockNum;
- final int numBlocks;
-
- BlocksData(ByteBuffer buffer, long startBlockNum, int numBlocks) {
- if (numBlocks > MAX_NUM_BLOCKS) {
- throw new IllegalArgumentException("Too many blocks to process at once: " + numBlocks);
- }
- this.buffer = buffer;
- this.startBlockNum = startBlockNum;
- this.numBlocks = numBlocks;
- }
-
-}
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CryptoComponent.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CryptoComponent.java
deleted file mode 100644
index a3fccd27a..000000000
--- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CryptoComponent.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.cryptomator.crypto.aes256;
-
-import javax.inject.Singleton;
-
-import org.cryptomator.crypto.Cryptor;
-
-import dagger.Component;
-
-@Singleton
-@Component(modules = CryptoModule.class)
-interface CryptoComponent {
-
- Cryptor cryptor();
-
-}
\ No newline at end of file
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CryptoModule.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CryptoModule.java
deleted file mode 100644
index 1bed4cf79..000000000
--- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CryptoModule.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.cryptomator.crypto.aes256;
-
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-
-import org.cryptomator.crypto.Cryptor;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-public class CryptoModule {
-
- @Provides
- SecureRandom provideRandomNumberGenerator() {
- try {
- return SecureRandom.getInstanceStrong();
- } catch (NoSuchAlgorithmException e) {
- // quote "Every implementation of the Java platform is required to support at least one strong SecureRandom implementation."
- throw new AssertionError("No SecureRandom implementation available.");
- }
- }
-
- @Provides
- public Cryptor provideCryptor(SecureRandom secureRandom) {
- return new Aes256Cryptor(secureRandom);
- }
-
-}
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CryptoWorker.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CryptoWorker.java
deleted file mode 100644
index ed56e0b79..000000000
--- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CryptoWorker.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package org.cryptomator.crypto.aes256;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-
-import org.cryptomator.crypto.exceptions.CryptingException;
-
-abstract class CryptoWorker implements Callable {
-
- static final BlocksData POISON = new BlocksData(ByteBuffer.allocate(0), -1L, 0);
-
- private final Lock lock;
- private final Condition blockDone;
- private final AtomicLong currentBlock;
- private final BlockingQueue queue;
-
- public CryptoWorker(Lock lock, Condition blockDone, AtomicLong currentBlock, BlockingQueue queue) {
- this.lock = lock;
- this.blockDone = blockDone;
- this.currentBlock = currentBlock;
- this.queue = queue;
- }
-
- @Override
- public final Void call() throws IOException, TimeoutException {
- try {
- while (!Thread.currentThread().isInterrupted()) {
- final BlocksData blocksData = queue.take();
- if (blocksData == POISON) {
- break;
- }
- final ByteBuffer processedBytes = this.process(blocksData);
- lock.lock();
- try {
- while (currentBlock.get() != blocksData.startBlockNum) {
- if (!blockDone.await(1, TimeUnit.SECONDS)) {
- throw new TimeoutException("Waited too long to write block " + blocksData.startBlockNum + "; Current block " + currentBlock.get());
- }
- }
- assert currentBlock.get() == blocksData.startBlockNum;
- // yay, its my turn!
- this.write(processedBytes);
- // signal worker working on next block:
- currentBlock.set(blocksData.startBlockNum + blocksData.numBlocks);
- blockDone.signalAll();
- } finally {
- lock.unlock();
- }
- }
- } catch (InterruptedException e) {
- // will happen for executorService.shutdownNow() or future.cancel()
- Thread.currentThread().interrupt();
- }
- return null;
- }
-
- protected abstract ByteBuffer process(BlocksData block) throws CryptingException;
-
- protected abstract void write(ByteBuffer processedBytes) throws IOException;
-
-}
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CryptoWorkerExecutor.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CryptoWorkerExecutor.java
deleted file mode 100644
index e63947539..000000000
--- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/CryptoWorkerExecutor.java
+++ /dev/null
@@ -1,167 +0,0 @@
-package org.cryptomator.crypto.aes256;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.Callable;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.CompletionService;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorCompletionService;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-class CryptoWorkerExecutor {
-
- private static final Logger LOG = LoggerFactory.getLogger(CryptoWorkerExecutor.class);
-
- private final int numWorkers;
- private final Lock lock;
- private final Condition blockDone;
- private final AtomicLong currentBlock;
- private final BlockingQueue inputQueue;
- private final ExecutorService executorService;
- private final Future allWork;
-
- /**
- * Starts as many {@link CryptoWorker} as specified in the constructor, that start working immediately on the items submitted via {@link #offer(BlocksData, long, TimeUnit)}.
- */
- public CryptoWorkerExecutor(int numWorkers, WorkerFactory workerFactory) {
- this.numWorkers = numWorkers;
- this.lock = new ReentrantLock();
- this.blockDone = lock.newCondition();
- this.currentBlock = new AtomicLong();
- this.inputQueue = new LinkedBlockingQueue<>(numWorkers * 2); // one cycle read-ahead
- this.executorService = Executors.newFixedThreadPool(numWorkers);
-
- // start workers:
- final CompletionService completionService = new ExecutorCompletionService<>(executorService);
- final Collection> workers = new ArrayList<>(numWorkers);
- for (int i = 0; i < numWorkers; i++) {
- final CryptoWorker worker = workerFactory.createWorker(lock, blockDone, currentBlock, inputQueue);
- workers.add(completionService.submit(worker));
- }
- final Supervisor supervisor = new Supervisor(workers, completionService);
- this.allWork = executorService.submit(supervisor);
- }
-
- /**
- * Adds work to the work queue. On timeout all workers will be shut down.
- *
- * @see BlockingQueue#offer(Object, long, TimeUnit)
- * @return true if the work has been added in time. false in any other case.
- */
- public boolean offer(BlocksData data, long timeout, TimeUnit unit) {
- if (allWork.isDone()) {
- return false;
- }
- try {
- final boolean success = inputQueue.offer(data, timeout, unit);
- if (!success) {
- LOG.warn("Cancelling crypto workers due to timeout. Apparently the work queue not being drained by the workers any longer.");
- allWork.cancel(true);
- }
- return success;
- } catch (InterruptedException e) {
- LOG.error("Interrupted thread.", e);
- executorService.shutdownNow();
- Thread.currentThread().interrupt();
- }
- return false;
- }
-
- /**
- * Graceful shutdown of this executor, waiting for all jobs to finish (normally or by throwing exceptions).
- *
- * @param timeout Maximum time spent per worker to wait for a graceful shutdown
- * @param unit Timeout unit
- * @throws ExecutionException If any of the workers failed.
- */
- public void waitUntilDone(long timeout, TimeUnit unit) throws ExecutionException {
- try {
- if (allWork.isDone()) {
- // Work is done before workers being poisoned? This will most likely throw an ExecutionException:
- allWork.get();
- } else if (!poisonWorkers(timeout, unit)) {
- // Attempt to enqueue poison pill for all workers failed:
- allWork.cancel(true);
- } else {
- // All poisons enqueued successfully. Now wait for termination by poison or exception:
- allWork.get();
- }
- } catch (InterruptedException e) {
- LOG.error("Interrupted thread.", e);
- Thread.currentThread().interrupt();
- } catch (CancellationException e) {
- throw new ExecutionException("Work canceled", e);
- } finally {
- // in any case (normal or exceptional execution): shutdown executor including all workers and supervisor:
- executorService.shutdownNow();
- }
- }
-
- private boolean poisonWorkers(long timeout, TimeUnit unit) throws InterruptedException {
- // add enough poison for each worker; each worker will consume excatly one:
- for (int i = 0; i < numWorkers; i++) {
- if (!inputQueue.offer(CryptoWorker.POISON, timeout, unit)) {
- return false;
- }
- }
- return true;
- }
-
- @FunctionalInterface
- interface WorkerFactory {
- CryptoWorker createWorker(Lock lock, Condition blockDone, AtomicLong currentBlock, BlockingQueue inputQueue);
- }
-
- /**
- * A supervisor watches the work results of a collection of workers. The supervisor waits for all workers to finish.
- * The supvervisor itself does not cause any exceptions, but if one worker fails, all other workers are cancelled immediately and the exception propagates through this supvervisor.
- * Anyone waiting for the supervisor to finish will thus effectively wait for all supvervisees to finish.
- */
- private static class Supervisor implements Callable {
-
- private final Collection> workers;
- private final CompletionService> completionService;
-
- public Supervisor(Collection> workers, CompletionService> completionService) {
- this.workers = workers;
- this.completionService = completionService;
- }
-
- @Override
- public Void call() throws ExecutionException {
- try {
- for (int i = 0; i < workers.size(); i++) {
- try {
- // any ExecutionException thrown here will propagate up (after work is canceled in finally block)
- completionService.take().get();
- } catch (CancellationException ignore) {
- }
- }
- } catch (InterruptedException e) {
- // supervisor may be interrupted when executorservice is shut down.
- Thread.currentThread().interrupt();
- } finally {
- // make sure, that at the end of the day all remaining workers leave the building.
- for (Future> worker : workers) {
- worker.cancel(true);
- }
- }
- // no exception up to this point -> all workers finished work normally.
- return null;
- }
- }
-
-}
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/DecryptWorker.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/DecryptWorker.java
deleted file mode 100644
index cd2f766f5..000000000
--- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/DecryptWorker.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package org.cryptomator.crypto.aes256;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.WritableByteChannel;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-
-import javax.crypto.Cipher;
-import javax.crypto.Mac;
-
-import org.cryptomator.crypto.exceptions.CryptingException;
-import org.cryptomator.crypto.exceptions.DecryptFailedException;
-import org.cryptomator.crypto.exceptions.MacAuthenticationFailedException;
-
-abstract class DecryptWorker extends CryptoWorker implements AesCryptographicConfiguration {
-
- private final boolean shouldAuthenticate;
- private final WritableByteChannel out;
-
- public DecryptWorker(Lock lock, Condition blockDone, AtomicLong currentBlock, BlockingQueue queue, boolean shouldAuthenticate, WritableByteChannel out) {
- super(lock, blockDone, currentBlock, queue);
- this.shouldAuthenticate = shouldAuthenticate;
- this.out = out;
- }
-
- @Override
- protected ByteBuffer process(BlocksData data) throws CryptingException {
- final Cipher cipher = initCipher(data.startBlockNum);
- final Mac mac = initMac();
-
- final ByteBuffer plaintextBuf = ByteBuffer.allocate(cipher.getOutputSize(CONTENT_MAC_BLOCK) * data.numBlocks);
-
- final ByteBuffer ciphertextBuf = data.buffer.asReadOnlyBuffer();
- final ByteBuffer macBuf = data.buffer.asReadOnlyBuffer();
-
- for (long blockNum = data.startBlockNum; blockNum < data.startBlockNum + data.numBlocks; blockNum++) {
- assert (blockNum - data.startBlockNum) < BlocksData.MAX_NUM_BLOCKS;
- assert (blockNum - data.startBlockNum) * CONTENT_MAC_BLOCK < Integer.MAX_VALUE;
- final int pos = (int) (blockNum - data.startBlockNum) * (CONTENT_MAC_BLOCK + mac.getMacLength());
- ciphertextBuf.limit(Math.min(data.buffer.limit() - mac.getMacLength(), pos + CONTENT_MAC_BLOCK));
- ciphertextBuf.position(pos);
- try {
- macBuf.limit(ciphertextBuf.limit() + mac.getMacLength());
- macBuf.position(ciphertextBuf.limit());
- } catch (IllegalArgumentException e) {
- throw new DecryptFailedException("Invalid file content, missing MAC.");
- }
- if (shouldAuthenticate) {
- checkMac(mac, blockNum, ciphertextBuf, macBuf);
- }
- ciphertextBuf.position(pos);
- decrypt(cipher, ciphertextBuf, plaintextBuf);
- }
-
- plaintextBuf.flip();
- return plaintextBuf;
- }
-
- @Override
- protected void write(ByteBuffer processedBytes) throws IOException {
- out.write(processedBytes);
- }
-
- protected abstract Cipher initCipher(long startBlockNum);
-
- protected abstract Mac initMac();
-
- protected abstract void checkMac(Mac mac, long blockNum, ByteBuffer ciphertextBuf, ByteBuffer macBuf) throws MacAuthenticationFailedException;
-
- protected abstract void decrypt(Cipher cipher, ByteBuffer ciphertextBuf, ByteBuffer plaintextBuf) throws DecryptFailedException;
-
-}
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/EncryptWorker.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/EncryptWorker.java
deleted file mode 100644
index 0a42b4d14..000000000
--- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/EncryptWorker.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package org.cryptomator.crypto.aes256;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.WritableByteChannel;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-
-import javax.crypto.Cipher;
-import javax.crypto.Mac;
-
-import org.cryptomator.crypto.exceptions.CryptingException;
-import org.cryptomator.crypto.exceptions.EncryptFailedException;
-
-abstract class EncryptWorker extends CryptoWorker implements AesCryptographicConfiguration {
-
- private final WritableByteChannel out;
-
- public EncryptWorker(Lock lock, Condition blockDone, AtomicLong currentBlock, BlockingQueue queue, WritableByteChannel out) {
- super(lock, blockDone, currentBlock, queue);
- this.out = out;
- }
-
- @Override
- protected ByteBuffer process(BlocksData data) throws CryptingException {
- final Cipher cipher = initCipher(data.startBlockNum);
- final Mac mac = initMac();
-
- final ByteBuffer ciphertextBuf = ByteBuffer.allocate((cipher.getOutputSize(CONTENT_MAC_BLOCK) + mac.getMacLength()) * data.numBlocks);
- final ByteBuffer plaintextBuf = data.buffer.asReadOnlyBuffer();
-
- for (long blockNum = data.startBlockNum; blockNum < data.startBlockNum + data.numBlocks; blockNum++) {
- final int pos = (int) (blockNum - data.startBlockNum) * CONTENT_MAC_BLOCK;
- plaintextBuf.limit(Math.min(data.buffer.limit(), pos + CONTENT_MAC_BLOCK));
- encrypt(cipher, plaintextBuf, ciphertextBuf);
- final ByteBuffer toMac = ciphertextBuf.asReadOnlyBuffer();
- toMac.limit(ciphertextBuf.position());
- toMac.position((int) (blockNum - data.startBlockNum) * (CONTENT_MAC_BLOCK + mac.getMacLength()));
- ciphertextBuf.put(calcMac(mac, blockNum, toMac));
- }
-
- ciphertextBuf.flip();
- return ciphertextBuf;
- }
-
- @Override
- protected void write(ByteBuffer processedBytes) throws IOException {
- out.write(processedBytes);
- }
-
- protected abstract Cipher initCipher(long startBlockNum);
-
- protected abstract Mac initMac();
-
- protected abstract byte[] calcMac(Mac mac, long blockNum, ByteBuffer ciphertextBuf);
-
- protected abstract void encrypt(Cipher cipher, ByteBuffer plaintextBuf, ByteBuffer ciphertextBuf) throws EncryptFailedException;
-
-}
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/KeyFile.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/KeyFile.java
deleted file mode 100644
index 3bfd40582..000000000
--- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/KeyFile.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package org.cryptomator.crypto.aes256;
-
-import java.io.Serializable;
-
-import com.fasterxml.jackson.annotation.JsonPropertyOrder;
-
-@JsonPropertyOrder(value = {"version", "scryptSalt", "scryptCostParam", "scryptBlockSize", "keyLength", "primaryMasterKey", "hMacMasterKey"})
-public class KeyFile implements Serializable {
-
- static final Integer CURRENT_VERSION = 2;
- private static final long serialVersionUID = 8578363158959619885L;
-
- private Integer version;
- private byte[] scryptSalt;
- private int scryptCostParam;
- private int scryptBlockSize;
- private int keyLength;
- private byte[] primaryMasterKey;
- private byte[] hMacMasterKey;
-
- public Integer getVersion() {
- return version;
- }
-
- public void setVersion(Integer version) {
- this.version = version;
- }
-
- public byte[] getScryptSalt() {
- return scryptSalt;
- }
-
- public void setScryptSalt(byte[] scryptSalt) {
- this.scryptSalt = scryptSalt;
- }
-
- public int getScryptCostParam() {
- return scryptCostParam;
- }
-
- public void setScryptCostParam(int scryptCostParam) {
- this.scryptCostParam = scryptCostParam;
- }
-
- public int getScryptBlockSize() {
- return scryptBlockSize;
- }
-
- public void setScryptBlockSize(int scryptBlockSize) {
- this.scryptBlockSize = scryptBlockSize;
- }
-
- public int getKeyLength() {
- return keyLength;
- }
-
- public void setKeyLength(int keyLength) {
- this.keyLength = keyLength;
- }
-
- public byte[] getPrimaryMasterKey() {
- return primaryMasterKey;
- }
-
- public void setPrimaryMasterKey(byte[] primaryMasterKey) {
- this.primaryMasterKey = primaryMasterKey;
- }
-
- public byte[] getHMacMasterKey() {
- return hMacMasterKey;
- }
-
- public void setHMacMasterKey(byte[] hMacMasterKey) {
- this.hMacMasterKey = hMacMasterKey;
- }
-
-}
\ No newline at end of file
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/LengthLimitingOutputStream.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/LengthLimitingOutputStream.java
deleted file mode 100644
index 8d52f32b8..000000000
--- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/LengthLimitingOutputStream.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package org.cryptomator.crypto.aes256;
-
-import java.io.FilterOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-public class LengthLimitingOutputStream extends FilterOutputStream {
-
- private final long limit;
- private volatile long bytesWritten;
-
- public LengthLimitingOutputStream(OutputStream out, long limit) {
- super(out);
- this.limit = limit;
- this.bytesWritten = 0;
- }
-
- @Override
- public void write(int b) throws IOException {
- if (bytesWritten < limit) {
- out.write(b);
- increaseNumberOfWrittenBytes(1);
- }
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- final long bytesAvailable = limit - bytesWritten;
- final int adjustedLen = (int) Math.min(len, bytesAvailable);
- if (adjustedLen > 0) {
- out.write(b, off, adjustedLen);
- increaseNumberOfWrittenBytes(adjustedLen);
- }
- }
-
- public long getBytesWritten() {
- return bytesWritten;
- }
-
- private void increaseNumberOfWrittenBytes(int amount) throws IOException {
- bytesWritten += amount;
- if (bytesWritten >= limit) {
- out.flush();
- }
- }
-
-}
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/LengthObfuscatingInputStream.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/LengthObfuscatingInputStream.java
deleted file mode 100644
index 498fa0f29..000000000
--- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/LengthObfuscatingInputStream.java
+++ /dev/null
@@ -1,128 +0,0 @@
-package org.cryptomator.crypto.aes256;
-
-import java.io.FilterInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
-import org.apache.commons.io.IOUtils;
-
-/**
- * Not thread-safe!
- */
-public class LengthObfuscatingInputStream extends FilterInputStream {
-
- private final byte[] padding;
- private int paddingLength = -1;
- private long inputBytesRead = 0;
- private int paddingBytesRead = 0;
-
- LengthObfuscatingInputStream(InputStream in, byte[] padding) {
- super(in);
- this.padding = padding;
- }
-
- long getRealInputLength() {
- return inputBytesRead;
- }
-
- private void choosePaddingLengthOnce() {
- if (paddingLength == -1) {
- long upperBound = Math.min(Math.max(inputBytesRead / 10, 4096), 16 * 1024 * 1024); // 10% of original bytes (at least 4KiB), but not more than 16MiBs
- paddingLength = (int) (Math.random() * upperBound);
- }
- }
-
- @Override
- public int read() throws IOException {
- final int b = in.read();
- if (b != -1) {
- // stream available:
- inputBytesRead++;
- return b;
- } else {
- choosePaddingLengthOnce();
- return readFromPadding();
- }
- }
-
- private int readFromPadding() {
- if (paddingLength == -1) {
- throw new IllegalStateException("No padding length chosen yet.");
- }
-
- if (paddingBytesRead < paddingLength) {
- // padding available:
- return padding[paddingBytesRead++ % padding.length];
- } else {
- // end of stream AND padding
- return -1;
- }
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- final int bytesRead = IOUtils.read(in, b, off, len); // 0 on EOF
- inputBytesRead += bytesRead;
-
- if (bytesRead == len) {
- return bytesRead;
- } else if (bytesRead < len) {
- choosePaddingLengthOnce();
- final int additionalBytesNeeded = len - bytesRead;
- final int additionalBytesRead = readFromPadding(b, off + bytesRead, additionalBytesNeeded);
- return (bytesRead == 0 && additionalBytesRead == 0) ? -1 : bytesRead + additionalBytesRead;
- } else {
- // bytesRead > len:
- throw new IllegalStateException("Read more bytes than requested.");
- }
- }
-
- /**
- * @return bytes read from padding (0, if fully read)
- */
- private int readFromPadding(byte[] b, int off, int len) {
- if (len < 0) {
- throw new IllegalArgumentException("Length must not be negative");
- }
- if (paddingLength == -1) {
- throw new IllegalStateException("No padding length chosen yet.");
- }
-
- final int remainingPadding = paddingLength - paddingBytesRead;
- if (remainingPadding > len) {
- // padding available:
- for (int i = 0; i < len; i++) {
- b[off + i] = padding[paddingBytesRead++ % padding.length];
- }
- return len;
- } else {
- // partly available:
- for (int i = 0; i < remainingPadding; i++) {
- b[off + i] = padding[paddingBytesRead++ % padding.length];
- }
- return remainingPadding;
- }
- }
-
- @Override
- public long skip(long n) throws IOException {
- throw new IOException("Skip not supported");
- }
-
- @Override
- public int available() throws IOException {
- if (paddingLength == -1) {
- // EOF not yet reached; delegate original stream to answer this rather complicated question:
- return in.available();
- } else {
- // EOF already reached, read from remaining padding:
- return paddingLength - paddingBytesRead;
- }
- }
-
- @Override
- public boolean markSupported() {
- return false;
- }
-
-}
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
deleted file mode 100644
index ef500daec..000000000
--- a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Sebastian Stenzel
- * This file is licensed under the terms of the MIT license.
- * See the LICENSE.txt file for more info.
- *
- * Contributors:
- * Sebastian Stenzel - initial API and implementation
- ******************************************************************************/
-package org.cryptomator.crypto.aes256;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-import java.nio.channels.SeekableByteChannel;
-import java.util.Arrays;
-
-import javax.security.auth.DestroyFailedException;
-
-import org.apache.commons.io.IOUtils;
-import org.cryptomator.crypto.Cryptor;
-import org.cryptomator.crypto.exceptions.DecryptFailedException;
-import org.cryptomator.crypto.exceptions.EncryptFailedException;
-import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
-import org.cryptomator.crypto.exceptions.UnsupportedVaultException;
-import org.cryptomator.crypto.exceptions.WrongPasswordException;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class Aes256CryptorTest {
-
- private final Cryptor cryptor;
-
- public Aes256CryptorTest() {
- cryptor = DaggerCryptoTestComponent.create().cryptor();
- cryptor.randomizeMasterKey();
- }
-
- @Test
- public void testMultipleCryptorInstances() {
- Assert.assertNotSame(DaggerCryptoTestComponent.create().cryptor(), DaggerCryptoTestComponent.create().cryptor());
- }
-
- @Test(timeout = 10000)
- public void testCorrectPassword() throws IOException, WrongPasswordException, DecryptFailedException, UnsupportedKeyLengthException, DestroyFailedException, UnsupportedVaultException {
- final String pw = "asd";
-
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
- cryptor.encryptMasterKey(out, pw);
- cryptor.destroy();
-
- final Cryptor decryptor = DaggerCryptoTestComponent.create().cryptor();
- final InputStream in = new ByteArrayInputStream(out.toByteArray());
- decryptor.decryptMasterKey(in, pw);
-
- IOUtils.closeQuietly(out);
- IOUtils.closeQuietly(in);
- }
-
- @Test(timeout = 10000)
- public void testWrongPassword() throws IOException, DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, DestroyFailedException, UnsupportedVaultException {
- final String pw = "asd";
-
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
- cryptor.encryptMasterKey(out, pw);
- cryptor.destroy();
- IOUtils.closeQuietly(out);
-
- // all these passwords are expected to fail.
- final String[] wrongPws = {"a", "as", "asdf", "sdf", "das", "dsa", "foo", "bar", "baz"};
- final Cryptor decryptor = DaggerCryptoTestComponent.create().cryptor();
- for (final String wrongPw : wrongPws) {
- final InputStream in = new ByteArrayInputStream(out.toByteArray());
- try {
- decryptor.decryptMasterKey(in, wrongPw);
- Assert.fail("should not succeed.");
- } catch (WrongPasswordException e) {
- continue;
- } finally {
- IOUtils.closeQuietly(in);
- }
- }
- }
-
- @Test(expected = DecryptFailedException.class, timeout = 10000)
- public void testIntegrityViolationDuringDecryption() throws IOException, DecryptFailedException, EncryptFailedException {
- // our test plaintext data:
- final byte[] plaintextData = "Hello World".getBytes();
- final InputStream plaintextIn = new ByteArrayInputStream(plaintextData);
-
- // encrypt:
- final ByteBuffer encryptedData = ByteBuffer.allocate(104 + plaintextData.length + 4096 + 32); // header + content + maximum possible size obfuscation padding + 32 bytes mac (per each 32k)
- final SeekableByteChannel encryptedOut = new ByteBufferBackedSeekableChannel(encryptedData);
- cryptor.encryptFile(plaintextIn, encryptedOut);
- IOUtils.closeQuietly(plaintextIn);
- IOUtils.closeQuietly(encryptedOut);
-
- encryptedData.position(0);
-
- // toggle one bit inf first content byte:
- encryptedData.position(64);
- final byte fifthByte = encryptedData.get();
- encryptedData.position(64);
- encryptedData.put((byte) (fifthByte ^ 0x01));
-
- encryptedData.position(0);
-
- // decrypt modified content (should fail with DecryptFailedException):
- final SeekableByteChannel encryptedIn = new ByteBufferBackedSeekableChannel(encryptedData);
- final ByteArrayOutputStream plaintextOut = new ByteArrayOutputStream();
- cryptor.decryptFile(encryptedIn, plaintextOut, true);
- }
-
- @Test(timeout = 10000)
- public void testEncryptionAndDecryption() throws IOException, DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, EncryptFailedException {
- // our test plaintext data:
- final byte[] plaintextData = "Hello World".getBytes();
- final InputStream plaintextIn = new ByteArrayInputStream(plaintextData);
-
- // encrypt:
- final ByteBuffer encryptedData = ByteBuffer.allocate(104 + plaintextData.length + 4096 + 32); // header + content + maximum possible size obfuscation padding + 32 bytes mac (per each 32k)
- final SeekableByteChannel encryptedOut = new ByteBufferBackedSeekableChannel(encryptedData);
- cryptor.encryptFile(plaintextIn, encryptedOut);
- IOUtils.closeQuietly(plaintextIn);
- IOUtils.closeQuietly(encryptedOut);
-
- encryptedData.position(0);
-
- // decrypt file size:
- final SeekableByteChannel encryptedIn = new ByteBufferBackedSeekableChannel(encryptedData);
- final Long filesize = cryptor.decryptedContentLength(encryptedIn);
- Assert.assertEquals(plaintextData.length, filesize.longValue());
-
- // decrypt:
- final ByteArrayOutputStream plaintextOut = new ByteArrayOutputStream();
- final Long numDecryptedBytes = cryptor.decryptFile(encryptedIn, plaintextOut, true);
- IOUtils.closeQuietly(encryptedIn);
- IOUtils.closeQuietly(plaintextOut);
- Assert.assertEquals(filesize.longValue(), numDecryptedBytes.longValue());
-
- // check decrypted data:
- final byte[] result = plaintextOut.toByteArray();
- Assert.assertArrayEquals(plaintextData, result);
- }
-
- @Test(timeout = 10000)
- public void testPartialDecryption() throws IOException, DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, EncryptFailedException {
- // 8MiB test plaintext data:
- final byte[] plaintextData = new byte[2097152 * Integer.BYTES];
- final ByteBuffer bbIn = ByteBuffer.wrap(plaintextData);
- for (int i = 0; i < 2097152; i++) {
- bbIn.putInt(i);
- }
- final InputStream plaintextIn = new ByteArrayInputStream(plaintextData);
-
- // encrypt:
- final ByteBuffer encryptedData = ByteBuffer.allocate((int) (104 + plaintextData.length * 1.2));
- final SeekableByteChannel encryptedOut = new ByteBufferBackedSeekableChannel(encryptedData);
- cryptor.encryptFile(plaintextIn, encryptedOut);
- IOUtils.closeQuietly(plaintextIn);
- IOUtils.closeQuietly(encryptedOut);
-
- encryptedData.position(0);
-
- // decrypt:
- final SeekableByteChannel encryptedIn = new ByteBufferBackedSeekableChannel(encryptedData);
- final ByteArrayOutputStream plaintextOut = new ByteArrayOutputStream();
- final Long numDecryptedBytes = cryptor.decryptRange(encryptedIn, plaintextOut, 260000 * Integer.BYTES, 4000 * Integer.BYTES, true);
- IOUtils.closeQuietly(encryptedIn);
- IOUtils.closeQuietly(plaintextOut);
- Assert.assertTrue(numDecryptedBytes > 0);
-
- // check decrypted data:
- final byte[] result = plaintextOut.toByteArray();
- final byte[] expected = Arrays.copyOfRange(plaintextData, 260000 * Integer.BYTES, 264000 * Integer.BYTES);
- Assert.assertArrayEquals(expected, result);
- }
-
- @Test(timeout = 10000)
- public void testEncryptionOfFilenames() throws IOException, DecryptFailedException {
-
- // directory paths
- final String originalPath1 = "foo/bar/baz";
- final String encryptedPath1a = cryptor.encryptDirectoryPath(originalPath1, "/");
- final String encryptedPath1b = cryptor.encryptDirectoryPath(originalPath1, "/");
- Assert.assertEquals(encryptedPath1a, encryptedPath1b);
-
- // long file names
- final String str50chars = "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee";
- final String originalPath2 = str50chars + str50chars + str50chars + str50chars + str50chars + "_isLongerThan255Chars.txt";
- final String encryptedPath2a = cryptor.encryptFilename(originalPath2);
- final String encryptedPath2b = cryptor.encryptFilename(originalPath2);
- Assert.assertEquals(encryptedPath2a, encryptedPath2b);
- final String decryptedPath2 = cryptor.decryptFilename(encryptedPath2a);
- Assert.assertEquals(originalPath2, decryptedPath2);
-
- // block size length file names
- final String originalPath3 = "aaaabbbbccccdddd";
- final String encryptedPath3a = cryptor.encryptFilename(originalPath3);
- final String encryptedPath3b = cryptor.encryptFilename(originalPath3);
- Assert.assertEquals(encryptedPath3a, encryptedPath3b);
- final String decryptedPath3 = cryptor.decryptFilename(encryptedPath3a);
- Assert.assertEquals(originalPath3, decryptedPath3);
- }
-
-}
diff --git a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/ByteBufferBackedSeekableChannel.java b/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/ByteBufferBackedSeekableChannel.java
deleted file mode 100644
index d037327e8..000000000
--- a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/ByteBufferBackedSeekableChannel.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package org.cryptomator.crypto.aes256;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.SeekableByteChannel;
-
-class ByteBufferBackedSeekableChannel implements SeekableByteChannel {
-
- private final ByteBuffer buffer;
- private boolean open = true;
-
- ByteBufferBackedSeekableChannel(ByteBuffer buffer) {
- this.buffer = buffer;
- }
-
- @Override
- public boolean isOpen() {
- return open;
- }
-
- @Override
- public void close() throws IOException {
- open = false;
- }
-
- @Override
- public int read(ByteBuffer dst) throws IOException {
- if (buffer.remaining() == 0) {
- return -1;
- }
- int num = Math.min(dst.remaining(), buffer.remaining());
- byte[] bytes = new byte[num];
- buffer.get(bytes);
- dst.put(bytes);
- return num;
- }
-
- @Override
- public int write(ByteBuffer src) throws IOException {
- int num = src.remaining();
- if (buffer.remaining() < src.remaining()) {
- buffer.limit(buffer.limit() + src.remaining());
- }
- buffer.put(src);
- return num;
- }
-
- @Override
- public long position() throws IOException {
- return buffer.position();
- }
-
- @Override
- public SeekableByteChannel position(long newPosition) throws IOException {
- if (newPosition > Integer.MAX_VALUE) {
- throw new UnsupportedOperationException();
- }
- if (newPosition > buffer.limit()) {
- buffer.limit((int) newPosition);
- }
- buffer.position((int) newPosition);
- return this;
- }
-
- @Override
- public long size() throws IOException {
- return buffer.limit();
- }
-
- @Override
- public SeekableByteChannel truncate(long size) throws IOException {
- if (size > Integer.MAX_VALUE) {
- throw new UnsupportedOperationException();
- }
- buffer.limit((int) size);
- return this;
- }
-
-}
diff --git a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/CryptoTestComponent.java b/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/CryptoTestComponent.java
deleted file mode 100644
index 45be2d67c..000000000
--- a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/CryptoTestComponent.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.cryptomator.crypto.aes256;
-
-import javax.inject.Singleton;
-
-import org.cryptomator.crypto.Cryptor;
-
-import dagger.Component;
-
-@Singleton
-@Component(modules = CryptoTestModule.class)
-interface CryptoTestComponent {
-
- Cryptor cryptor();
-
-}
diff --git a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/CryptoTestModule.java b/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/CryptoTestModule.java
deleted file mode 100644
index c927b179a..000000000
--- a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/CryptoTestModule.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package org.cryptomator.crypto.aes256;
-
-import java.security.SecureRandom;
-
-import org.cryptomator.crypto.Cryptor;
-
-import dagger.Module;
-import dagger.Provides;
-
-@Module
-class CryptoTestModule {
-
- @Provides
- @SuppressWarnings("deprecation")
- SecureRandom provideRandomNumberGenerator() {
- // we use this class for testing only, as unit tests on CI servers tend to stall, if they rely on true randomness.
- return new InsecureRandomMock();
- }
-
- @Provides
- Cryptor provideCryptor(SecureRandom secureRandom) {
- return new Aes256Cryptor(secureRandom);
- }
-
-}
diff --git a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/InsecureRandomMock.java b/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/InsecureRandomMock.java
deleted file mode 100644
index 9d54108c5..000000000
--- a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/InsecureRandomMock.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.cryptomator.crypto.aes256;
-
-import java.security.SecureRandom;
-import java.util.Random;
-
-/**
- * DO NOT USE
- *
- * This class is for testing only.
- */
-@Deprecated // marked as deprecated and made package-private inside /src/test/java to avoid accidential use.
-class InsecureRandomMock extends SecureRandom {
-
- private static final long serialVersionUID = 1505563778398085504L;
- private final Random random = new Random();
-
- @Override
- public void nextBytes(byte[] bytes) {
- // let the deterministic RNG do the work:
- this.random.nextBytes(bytes);
- }
-
-}
diff --git a/main/crypto-aes/src/test/resources/log4j2.xml b/main/crypto-aes/src/test/resources/log4j2.xml
deleted file mode 100644
index 39c2f8545..000000000
--- a/main/crypto-aes/src/test/resources/log4j2.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/main/crypto-api/.gitignore b/main/crypto-api/.gitignore
deleted file mode 100644
index b83d22266..000000000
--- a/main/crypto-api/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/target/
diff --git a/main/crypto-api/pom.xml b/main/crypto-api/pom.xml
deleted file mode 100644
index f39dce6c6..000000000
--- a/main/crypto-api/pom.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-
-
-
- 4.0.0
-
- org.cryptomator
- main
- 0.11.0-SNAPSHOT
-
- crypto-api
- Cryptomator cryptographic module API
-
-
-
-
- commons-io
- commons-io
-
-
- org.apache.commons
- commons-lang3
-
-
- org.apache.commons
- commons-collections4
-
-
-
-
- org.cryptomator
- commons-test
-
-
-
-
-
-
- org.jacoco
- jacoco-maven-plugin
-
-
-
-
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/AbstractCryptorDecorator.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/AbstractCryptorDecorator.java
deleted file mode 100644
index 0db336ec5..000000000
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/AbstractCryptorDecorator.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package org.cryptomator.crypto;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.channels.SeekableByteChannel;
-
-import javax.security.auth.DestroyFailedException;
-
-import org.cryptomator.crypto.exceptions.DecryptFailedException;
-import org.cryptomator.crypto.exceptions.EncryptFailedException;
-import org.cryptomator.crypto.exceptions.MacAuthenticationFailedException;
-import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
-import org.cryptomator.crypto.exceptions.UnsupportedVaultException;
-import org.cryptomator.crypto.exceptions.WrongPasswordException;
-
-public class AbstractCryptorDecorator implements Cryptor {
-
- protected final Cryptor cryptor;
-
- public AbstractCryptorDecorator(Cryptor cryptor) {
- this.cryptor = cryptor;
- }
-
- @Override
- public void randomizeMasterKey() {
- cryptor.randomizeMasterKey();
- }
-
- @Override
- public void encryptMasterKey(OutputStream out, CharSequence password) throws IOException {
- cryptor.encryptMasterKey(out, password);
- }
-
- @Override
- public void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException, UnsupportedVaultException {
- cryptor.decryptMasterKey(in, password);
- }
-
- @Override
- public String encryptDirectoryPath(String cleartextDirectoryId, String nativePathSep) {
- return cryptor.encryptDirectoryPath(cleartextDirectoryId, nativePathSep);
- }
-
- @Override
- public String encryptFilename(String cleartextName) {
- return cryptor.encryptFilename(cleartextName);
- }
-
- @Override
- public String decryptFilename(String ciphertextName) throws DecryptFailedException {
- return cryptor.decryptFilename(ciphertextName);
- }
-
- @Override
- public Long decryptedContentLength(SeekableByteChannel encryptedFile) throws IOException, MacAuthenticationFailedException {
- return cryptor.decryptedContentLength(encryptedFile);
- }
-
- @Override
- public Long decryptFile(SeekableByteChannel encryptedFile, OutputStream plaintextFile, boolean authenticate) throws IOException, DecryptFailedException {
- return cryptor.decryptFile(encryptedFile, plaintextFile, authenticate);
- }
-
- @Override
- public Long decryptRange(SeekableByteChannel encryptedFile, OutputStream plaintextFile, long pos, long length, boolean authenticate) throws IOException, DecryptFailedException {
- return cryptor.decryptRange(encryptedFile, plaintextFile, pos, length, authenticate);
- }
-
- @Override
- public Long encryptFile(InputStream plaintextFile, SeekableByteChannel encryptedFile) throws IOException, EncryptFailedException {
- return cryptor.encryptFile(plaintextFile, encryptedFile);
- }
-
- @Override
- public void destroy() throws DestroyFailedException {
- cryptor.destroy();
- }
-
- @Override
- public boolean isDestroyed() {
- return cryptor.isDestroyed();
- }
-
-}
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/Cryptor.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/Cryptor.java
deleted file mode 100644
index a17f26238..000000000
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/Cryptor.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Sebastian Stenzel
- * This file is licensed under the terms of the MIT license.
- * See the LICENSE.txt file for more info.
- *
- * Contributors:
- * Sebastian Stenzel - initial API and implementation
- ******************************************************************************/
-package org.cryptomator.crypto;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.channels.SeekableByteChannel;
-
-import javax.security.auth.Destroyable;
-
-import org.cryptomator.crypto.exceptions.DecryptFailedException;
-import org.cryptomator.crypto.exceptions.EncryptFailedException;
-import org.cryptomator.crypto.exceptions.MacAuthenticationFailedException;
-import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
-import org.cryptomator.crypto.exceptions.UnsupportedVaultException;
-import org.cryptomator.crypto.exceptions.WrongPasswordException;
-
-/**
- * Provides access to cryptographic functions. All methods are threadsafe.
- */
-public interface Cryptor extends Destroyable {
-
- /**
- * Assigns new random bytes to the keys in this Cryptor instance.
- */
- void randomizeMasterKey();
-
- /**
- * Encrypts the current masterKey with the given password and writes the result to the given output stream.
- */
- void encryptMasterKey(OutputStream out, CharSequence password) throws IOException;
-
- /**
- * Reads the encrypted masterkey from the given input stream and decrypts it with the given password.
- *
- * @throws DecryptFailedException If the decryption failed for various reasons (including wrong password).
- * @throws WrongPasswordException If the provided password was wrong. Note: Sometimes the algorithm itself fails due to a wrong password. In this case a DecryptFailedException will be thrown.
- * @throws UnsupportedKeyLengthException If the masterkey has been encrypted with a higher key length than supported by the system. In this case Java JCE needs to be installed.
- * @throws UnsupportedVaultException If the masterkey file is too old or too modern.
- */
- void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException, UnsupportedVaultException;
-
- /**
- * Encrypts a given plaintext path representing a directory structure. See {@link #encryptFilename(String, CryptorMetadataSupport)} for contents inside directories.
- *
- * @param cleartextDirectoryId A unique directory id
- * @param nativePathSep Path separator like "/" used on local file system. Must not be null, even if cleartextPath is a sole file name without any path separators.
- * @return Encrypted path.
- */
- String encryptDirectoryPath(String cleartextDirectoryId, String nativePathSep);
-
- /**
- * Encrypts the name of a file. See {@link #encryptDirectoryPath(String, char)} for parent dir.
- *
- * @param cleartextName A plaintext filename without any preceeding directory paths.
- * @return Encrypted filename.
- */
- String encryptFilename(String cleartextName);
-
- /**
- * Decrypts the name of a file.
- *
- * @param ciphertextName A ciphertext filename without any preceeding directory paths.
- * @return Decrypted filename.
- * @throws DecryptFailedException If the decryption failed for various reasons (including wrong password).
- */
- String decryptFilename(String ciphertextName) throws DecryptFailedException;
-
- /**
- * @param metadataSupport Support object allowing the Cryptor to read and write its own metadata to the location of the encrypted file.
- * @return Content length of the decrypted file or null if unknown.
- * @throws MacAuthenticationFailedException If the MAC auth failed.
- */
- Long decryptedContentLength(SeekableByteChannel encryptedFile) throws IOException, MacAuthenticationFailedException;
-
- /**
- * @return Number of decrypted bytes. This might not be equal to the encrypted file size due to optional metadata written to it.
- * @throws DecryptFailedException If decryption failed
- */
- Long decryptFile(SeekableByteChannel encryptedFile, OutputStream plaintextFile, boolean authenticate) throws IOException, DecryptFailedException;
-
- /**
- * @param pos First byte (inclusive)
- * @param length Number of requested bytes beginning at pos.
- * @return Number of decrypted bytes. This might not be equal to the number of bytes requested due to potential overheads.
- * @throws DecryptFailedException If decryption failed
- */
- Long decryptRange(SeekableByteChannel encryptedFile, OutputStream plaintextFile, long pos, long length, boolean authenticate) throws IOException, DecryptFailedException;
-
- /**
- * @return Number of encrypted bytes. This might not be equal to the encrypted file size due to optional metadata written to it.
- */
- Long encryptFile(InputStream plaintextFile, SeekableByteChannel encryptedFile) throws IOException, EncryptFailedException;
-
-}
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/CryptorIOSampling.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/CryptorIOSampling.java
deleted file mode 100644
index c5cd2fe28..000000000
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/CryptorIOSampling.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2014 Sebastian Stenzel
- * This file is licensed under the terms of the MIT license.
- * See the LICENSE.txt file for more info.
- *
- * Contributors:
- * Sebastian Stenzel - initial API and implementation
- ******************************************************************************/
-package org.cryptomator.crypto;
-
-/**
- * Optional monitoring interface. If a cryptor implements this interface, it counts bytes de- and encrypted in a thread-safe manner.
- */
-public interface CryptorIOSampling {
-
- /**
- * @return Number of encrypted bytes since the last reset.
- */
- long pollEncryptedBytes(boolean resetCounter);
-
- /**
- * @return Number of decrypted bytes since the last reset.
- */
- long pollDecryptedBytes(boolean resetCounter);
-
-}
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/PathCachingCryptorDecorator.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/PathCachingCryptorDecorator.java
deleted file mode 100644
index 39241da5f..000000000
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/PathCachingCryptorDecorator.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package org.cryptomator.crypto;
-
-import java.util.Map;
-
-import org.apache.commons.collections4.BidiMap;
-import org.apache.commons.collections4.bidimap.AbstractDualBidiMap;
-import org.apache.commons.collections4.map.LRUMap;
-import org.cryptomator.crypto.exceptions.DecryptFailedException;
-
-public class PathCachingCryptorDecorator extends AbstractCryptorDecorator {
-
- private static final int MAX_CACHED_PATHS = 5000;
- private static final int MAX_CACHED_NAMES = 5000;
-
- private final Map pathCache = new LRUMap<>(MAX_CACHED_PATHS); //
- private final BidiMap nameCache = new BidiLRUMap<>(MAX_CACHED_NAMES); //
-
- private PathCachingCryptorDecorator(Cryptor cryptor) {
- super(cryptor);
- }
-
- public static Cryptor decorate(Cryptor cryptor) {
- return new PathCachingCryptorDecorator(cryptor);
- }
-
- /* Cryptor */
-
- @Override
- public String encryptDirectoryPath(String cleartextDirectoryId, String nativePathSep) {
- return pathCache.computeIfAbsent(cleartextDirectoryId, id -> cryptor.encryptDirectoryPath(id, nativePathSep));
- }
-
- @Override
- public String encryptFilename(String cleartextName) {
- return nameCache.computeIfAbsent(cleartextName, name -> cryptor.encryptFilename(name));
- }
-
- @Override
- public String decryptFilename(String ciphertextName) throws DecryptFailedException {
- String cleartextName = nameCache.getKey(ciphertextName);
- if (cleartextName == null) {
- cleartextName = cryptor.decryptFilename(ciphertextName);
- nameCache.put(cleartextName, ciphertextName);
- }
- return cleartextName;
- }
-
- private static class BidiLRUMap extends AbstractDualBidiMap {
-
- BidiLRUMap(int maxSize) {
- super(new LRUMap(maxSize), new LRUMap(maxSize));
- }
-
- protected BidiLRUMap(final Map normalMap, final Map reverseMap, final BidiMap inverseBidiMap) {
- super(normalMap, reverseMap, inverseBidiMap);
- }
-
- @Override
- protected BidiMap createBidiMap(Map normalMap, Map reverseMap, BidiMap inverseMap) {
- return new BidiLRUMap(normalMap, reverseMap, inverseMap);
- }
-
- }
-
-}
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/SamplingCryptorDecorator.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/SamplingCryptorDecorator.java
deleted file mode 100644
index e99066efd..000000000
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/SamplingCryptorDecorator.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package org.cryptomator.crypto;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.channels.SeekableByteChannel;
-import java.util.concurrent.atomic.LongAdder;
-
-import org.cryptomator.crypto.exceptions.DecryptFailedException;
-import org.cryptomator.crypto.exceptions.EncryptFailedException;
-
-/**
- * Decorates the Cryptor by decorating the In- and OutputStreams used during de-/encryption.
- */
-public class SamplingCryptorDecorator extends AbstractCryptorDecorator implements CryptorIOSampling {
-
- private final LongAdder encryptedBytes;
- private final LongAdder decryptedBytes;
-
- private SamplingCryptorDecorator(Cryptor cryptor) {
- super(cryptor);
- encryptedBytes = new LongAdder();
- decryptedBytes = new LongAdder();
- }
-
- public static Cryptor decorate(Cryptor cryptor) {
- return new SamplingCryptorDecorator(cryptor);
- }
-
- @Override
- public long pollEncryptedBytes(boolean resetCounter) {
- if (resetCounter) {
- return encryptedBytes.sumThenReset();
- } else {
- return encryptedBytes.sum();
- }
- }
-
- @Override
- public long pollDecryptedBytes(boolean resetCounter) {
- if (resetCounter) {
- return decryptedBytes.sumThenReset();
- } else {
- return decryptedBytes.sum();
- }
- }
-
- /* Cryptor */
-
- @Override
- public Long decryptFile(SeekableByteChannel encryptedFile, OutputStream plaintextFile, boolean authenticate) throws IOException, DecryptFailedException {
- final OutputStream countingOutputStream = new CountingOutputStream(decryptedBytes, plaintextFile);
- return cryptor.decryptFile(encryptedFile, countingOutputStream, authenticate);
- }
-
- @Override
- public Long decryptRange(SeekableByteChannel encryptedFile, OutputStream plaintextFile, long pos, long length, boolean authenticate) throws IOException, DecryptFailedException {
- final OutputStream countingOutputStream = new CountingOutputStream(decryptedBytes, plaintextFile);
- return cryptor.decryptRange(encryptedFile, countingOutputStream, pos, length, authenticate);
- }
-
- @Override
- public Long encryptFile(InputStream plaintextFile, SeekableByteChannel encryptedFile) throws IOException, EncryptFailedException {
- final InputStream countingInputStream = new CountingInputStream(encryptedBytes, plaintextFile);
- return cryptor.encryptFile(countingInputStream, encryptedFile);
- }
-
- private class CountingInputStream extends InputStream {
-
- private final InputStream in;
- private final LongAdder counter;
-
- private CountingInputStream(LongAdder counter, InputStream in) {
- this.in = in;
- this.counter = counter;
- }
-
- @Override
- public int read() throws IOException {
- int count = in.read();
- counter.add(count);
- return count;
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- int count = in.read(b, off, len);
- counter.add(count);
- return count;
- }
-
- }
-
- private class CountingOutputStream extends OutputStream {
-
- private final OutputStream out;
- private final LongAdder counter;
-
- private CountingOutputStream(LongAdder counter, OutputStream out) {
- this.out = out;
- this.counter = counter;
- }
-
- @Override
- public void write(int b) throws IOException {
- counter.increment();
- out.write(b);
- }
-
- @Override
- public void write(byte[] b, int off, int len) throws IOException {
- counter.add(len);
- out.write(b, off, len);
- }
-
- }
-
-}
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/CryptingException.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/CryptingException.java
deleted file mode 100644
index 279c94028..000000000
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/CryptingException.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.cryptomator.crypto.exceptions;
-
-import java.io.IOException;
-
-public class CryptingException extends IOException {
- private static final long serialVersionUID = -6622699014483319376L;
-
- public CryptingException(String string) {
- super(string);
- }
-
- public CryptingException(String string, Throwable t) {
- super(string, t);
- }
-}
\ No newline at end of file
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/DecryptFailedException.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/DecryptFailedException.java
deleted file mode 100644
index d61cb9446..000000000
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/DecryptFailedException.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.cryptomator.crypto.exceptions;
-
-public class DecryptFailedException extends CryptingException {
- private static final long serialVersionUID = -3855673600374897828L;
-
- public DecryptFailedException(Throwable t) {
- super("Decryption failed.", t);
- }
-
- public DecryptFailedException(String msg) {
- super(msg);
- }
-}
\ No newline at end of file
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/EncryptFailedException.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/EncryptFailedException.java
deleted file mode 100644
index 1dbc4cbb1..000000000
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/EncryptFailedException.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.cryptomator.crypto.exceptions;
-
-public class EncryptFailedException extends CryptingException {
- private static final long serialVersionUID = -3855673600374897828L;
-
- public EncryptFailedException(Throwable t) {
- super("Encryption failed.", t);
- }
-
- public EncryptFailedException(String msg) {
- super(msg);
- }
-}
\ No newline at end of file
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/MacAuthenticationFailedException.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/MacAuthenticationFailedException.java
deleted file mode 100644
index 7535c35fe..000000000
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/MacAuthenticationFailedException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.cryptomator.crypto.exceptions;
-
-public class MacAuthenticationFailedException extends DecryptFailedException {
-
- private static final long serialVersionUID = -5577052361643658772L;
-
- public MacAuthenticationFailedException(String msg) {
- super(msg);
- }
-
-}
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/MasterkeyDecryptionException.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/MasterkeyDecryptionException.java
deleted file mode 100644
index 277215e2e..000000000
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/MasterkeyDecryptionException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.cryptomator.crypto.exceptions;
-
-public class MasterkeyDecryptionException extends Exception {
-
- private static final long serialVersionUID = -6241452734672333206L;
-
- public MasterkeyDecryptionException(String string) {
- super(string);
- }
-
-}
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/UnsupportedKeyLengthException.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/UnsupportedKeyLengthException.java
deleted file mode 100644
index 548e05b6e..000000000
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/UnsupportedKeyLengthException.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.cryptomator.crypto.exceptions;
-
-public class UnsupportedKeyLengthException extends MasterkeyDecryptionException {
- private static final long serialVersionUID = 8114147446419390179L;
-
- private final int requestedLength;
- private final int supportedLength;
-
- public UnsupportedKeyLengthException(int length, int maxLength) {
- super(String.format("Key length (%d) exceeds policy maximum (%d).", length, maxLength));
- this.requestedLength = length;
- this.supportedLength = maxLength;
- }
-
- public int getRequestedLength() {
- return requestedLength;
- }
-
- public int getSupportedLength() {
- return supportedLength;
- }
-
-}
\ No newline at end of file
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/UnsupportedVaultException.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/UnsupportedVaultException.java
deleted file mode 100644
index b2a69ac54..000000000
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/UnsupportedVaultException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.cryptomator.crypto.exceptions;
-
-public class UnsupportedVaultException extends Exception {
-
- private static final long serialVersionUID = -5147549533387945622L;
-
- private final Integer detectedVersion;
- private final Integer supportedVersion;
-
- public UnsupportedVaultException(Integer detectedVersion, Integer supportedVersion) {
- super("Tried to open vault of version " + detectedVersion + ", but can only handle version " + supportedVersion);
- this.detectedVersion = detectedVersion;
- this.supportedVersion = supportedVersion;
- }
-
- public Integer getDetectedVersion() {
- return detectedVersion;
- }
-
- public Integer getSupportedVersion() {
- return supportedVersion;
- }
-
- public boolean isVaultOlderThanSoftware() {
- return detectedVersion == null || detectedVersion < supportedVersion;
- }
-
- public boolean isSoftwareOlderThanVault() {
- return detectedVersion > supportedVersion;
- }
-
-}
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/WrongPasswordException.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/WrongPasswordException.java
deleted file mode 100644
index d9e1c83b4..000000000
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/WrongPasswordException.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.cryptomator.crypto.exceptions;
-
-public class WrongPasswordException extends MasterkeyDecryptionException {
- private static final long serialVersionUID = -602047799678568780L;
-
- public WrongPasswordException() {
- super("Wrong password.");
- }
-}
\ No newline at end of file