diff --git a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/resources/FileTimeUtils.java b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/resources/FileTimeUtils.java
index cc48ad3d7..a49f28eb6 100644
--- a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/resources/FileTimeUtils.java
+++ b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/resources/FileTimeUtils.java
@@ -21,7 +21,7 @@ public final class FileTimeUtils {
}
public static String toRfc1123String(FileTime time) {
- final Temporal date = OffsetDateTime.ofInstant(time.toInstant(), ZoneOffset.UTC.normalized());
+ final Temporal date = OffsetDateTime.ofInstant(time.toInstant(), ZoneOffset.UTC);
return DateTimeFormatter.RFC_1123_DATE_TIME.format(date);
}
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java
index 405c1d122..0f05b5a7d 100644
--- a/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java
+++ b/main/crypto-aes/src/main/java/org/cryptomator/crypto/aes256/Aes256Cryptor.java
@@ -42,7 +42,6 @@ import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.crypto.AbstractCryptor;
import org.cryptomator.crypto.CryptorIOSupport;
@@ -78,11 +77,6 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
*/
private static final int AES_KEY_LENGTH;
- /**
- *
- */
- private static final byte[] EMPTY_MASTER_KEY = new byte[MASTER_KEY_LENGTH];
-
/**
* Jackson JSON-Mapper.
*/
@@ -92,7 +86,7 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
* The decrypted master key. Its lifecycle starts with {@link #randomData(int)} or {@link #encryptMasterKey(Path, CharSequence)}. Its
* lifecycle ends with {@link #swipeSensitiveData()}.
*/
- private final byte[] masterKey = Arrays.copyOf(EMPTY_MASTER_KEY, MASTER_KEY_LENGTH);
+ private final byte[] masterKey = new byte[MASTER_KEY_LENGTH];
private static final int SIZE_OF_LONG = Long.SIZE / Byte.SIZE;
private static final int SIZE_OF_INT = Integer.SIZE / Byte.SIZE;
@@ -108,10 +102,7 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
}
}
- /**
- * Fills the masterkey with new random bytes.
- */
- public void randomizeMasterKey() {
+ public Aes256Cryptor() {
SECURE_PRNG.setSeed(SECURE_PRNG.generateSeed(PRNG_SEED_LENGTH));
SECURE_PRNG.nextBytes(this.masterKey);
}
@@ -119,10 +110,8 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
/**
* Encrypts the current masterKey with the given password and writes the result to the given output stream.
*/
+ @Override
public void encryptMasterKey(OutputStream out, CharSequence password) throws IOException {
- if (ArrayUtils.isEquals(this.masterKey, EMPTY_MASTER_KEY)) {
- throw new IllegalStateException("Masterkey not yet initialized.");
- }
try {
// derive key:
final byte[] userSalt = randomData(SALT_LENGTH);
@@ -157,6 +146,7 @@ public class Aes256Cryptor extends AbstractCryptor implements AesCryptographicCo
* @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.
*/
+ @Override
public void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException {
byte[] decrypted = new byte[0];
try {
diff --git a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java b/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java
index dd5cc9d39..00a934884 100644
--- a/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java
+++ b/main/crypto-aes/src/test/java/org/cryptomator/crypto/aes256/Aes256CryptorTest.java
@@ -50,20 +50,11 @@ public class Aes256CryptorTest {
/* ------------------------------------------------------------------------------- */
- @Test(expected = IllegalStateException.class)
- public void testUninitializedMasterKey() throws IOException {
- final String pw = "asd";
- final Aes256Cryptor cryptor = new Aes256Cryptor();
- final OutputStream out = Files.newOutputStream(masterKey, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
- cryptor.encryptMasterKey(out, pw);
- }
-
@Test
public void testCorrectPassword() throws IOException, WrongPasswordException, DecryptFailedException, UnsupportedKeyLengthException {
final String pw = "asd";
final Aes256Cryptor cryptor = new Aes256Cryptor();
final OutputStream out = Files.newOutputStream(masterKey, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
- cryptor.randomizeMasterKey();
cryptor.encryptMasterKey(out, pw);
cryptor.swipeSensitiveData();
@@ -77,7 +68,6 @@ public class Aes256CryptorTest {
final String pw = "asd";
final Aes256Cryptor cryptor = new Aes256Cryptor();
final OutputStream out = Files.newOutputStream(masterKey, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
- cryptor.randomizeMasterKey();
cryptor.encryptMasterKey(out, pw);
cryptor.swipeSensitiveData();
@@ -92,7 +82,6 @@ public class Aes256CryptorTest {
final String pw = "asd";
final Aes256Cryptor cryptor = new Aes256Cryptor();
final OutputStream out = Files.newOutputStream(masterKey, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
- cryptor.randomizeMasterKey();
cryptor.encryptMasterKey(out, pw);
cryptor.swipeSensitiveData();
@@ -107,7 +96,6 @@ public class Aes256CryptorTest {
final String pw = "asd";
final Aes256Cryptor cryptor = new Aes256Cryptor();
final OutputStream out = Files.newOutputStream(masterKey, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
- cryptor.randomizeMasterKey();
cryptor.encryptMasterKey(out, pw);
cryptor.swipeSensitiveData();
@@ -120,7 +108,6 @@ public class Aes256CryptorTest {
public void testEncryptionOfFilenames() throws IOException {
final CryptorIOSupport ioSupportMock = new CryptoIOSupportMock();
final Aes256Cryptor cryptor = new Aes256Cryptor();
- cryptor.randomizeMasterKey();
// short path components
final String originalPath1 = "foo/bar/baz";
diff --git a/main/crypto-api/pom.xml b/main/crypto-api/pom.xml
index 6ddff5e71..c343b8fd9 100644
--- a/main/crypto-api/pom.xml
+++ b/main/crypto-api/pom.xml
@@ -22,5 +22,9 @@
commons-io
commons-io
+
+ org.apache.commons
+ commons-lang3
+
\ No newline at end of file
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/AbstractCryptor.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/AbstractCryptor.java
index 3f40ef34f..d51ad70c3 100644
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/AbstractCryptor.java
+++ b/main/crypto-api/src/main/java/org/cryptomator/crypto/AbstractCryptor.java
@@ -1,3 +1,11 @@
+/*******************************************************************************
+ * 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.util.HashSet;
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
index 7c2ee14fb..8e416b867 100644
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/Cryptor.java
+++ b/main/crypto-api/src/main/java/org/cryptomator/crypto/Cryptor.java
@@ -15,11 +15,31 @@ import java.nio.channels.SeekableByteChannel;
import java.nio.file.DirectoryStream.Filter;
import java.nio.file.Path;
+import org.cryptomator.crypto.exceptions.DecryptFailedException;
+import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
+import org.cryptomator.crypto.exceptions.WrongPasswordException;
+
/**
* Provides access to cryptographic functions. All methods are threadsafe.
*/
public interface Cryptor extends SensitiveDataSwipeListener {
+ /**
+ * 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.
+ */
+ void decryptMasterKey(InputStream in, CharSequence password) throws DecryptFailedException, WrongPasswordException, UnsupportedKeyLengthException, IOException;
+
/**
* Encrypts each plaintext path component for its own.
*
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
new file mode 100644
index 000000000..360e07cb7
--- /dev/null
+++ b/main/crypto-api/src/main/java/org/cryptomator/crypto/CryptorIOSampling.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * 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/CryptorIOSupport.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/CryptorIOSupport.java
index afed0e506..2709441bc 100644
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/CryptorIOSupport.java
+++ b/main/crypto-api/src/main/java/org/cryptomator/crypto/CryptorIOSupport.java
@@ -1,3 +1,11 @@
+/*******************************************************************************
+ * 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;
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/SamplingDecorator.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/SamplingDecorator.java
new file mode 100644
index 000000000..d81dd9903
--- /dev/null
+++ b/main/crypto-api/src/main/java/org/cryptomator/crypto/SamplingDecorator.java
@@ -0,0 +1,161 @@
+package org.cryptomator.crypto;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.DirectoryStream.Filter;
+import java.nio.file.Path;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.commons.lang3.StringUtils;
+import org.cryptomator.crypto.exceptions.DecryptFailedException;
+import org.cryptomator.crypto.exceptions.UnsupportedKeyLengthException;
+import org.cryptomator.crypto.exceptions.WrongPasswordException;
+
+public class SamplingDecorator implements Cryptor, CryptorIOSampling {
+
+ private final Cryptor cryptor;
+ private final AtomicLong encryptedBytes;
+ private final AtomicLong decryptedBytes;
+
+ private SamplingDecorator(Cryptor cryptor) {
+ this.cryptor = cryptor;
+ encryptedBytes = new AtomicLong();
+ decryptedBytes = new AtomicLong();
+ }
+
+ public static Cryptor decorate(Cryptor cryptor) {
+ return new SamplingDecorator(cryptor);
+ }
+
+ @Override
+ public void swipeSensitiveData() {
+ cryptor.swipeSensitiveData();
+ }
+
+ @Override
+ public Long pollEncryptedBytes(boolean resetCounter) {
+ if (resetCounter) {
+ return encryptedBytes.getAndSet(0);
+ } else {
+ return encryptedBytes.get();
+ }
+ }
+
+ @Override
+ public Long pollDecryptedBytes(boolean resetCounter) {
+ if (resetCounter) {
+ return decryptedBytes.getAndSet(0);
+ } else {
+ return decryptedBytes.get();
+ }
+ }
+
+ /* Cryptor */
+
+ @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 {
+ cryptor.decryptMasterKey(in, password);
+ }
+
+ @Override
+ public String encryptPath(String cleartextPath, char encryptedPathSep, char cleartextPathSep, CryptorIOSupport ioSupport) {
+ encryptedBytes.addAndGet(StringUtils.length(cleartextPath));
+ return cryptor.encryptPath(cleartextPath, encryptedPathSep, cleartextPathSep, ioSupport);
+ }
+
+ @Override
+ public String decryptPath(String encryptedPath, char encryptedPathSep, char cleartextPathSep, CryptorIOSupport ioSupport) {
+ decryptedBytes.addAndGet(StringUtils.length(encryptedPath));
+ return cryptor.decryptPath(encryptedPath, encryptedPathSep, cleartextPathSep, ioSupport);
+ }
+
+ @Override
+ public Long decryptedContentLength(SeekableByteChannel encryptedFile) throws IOException {
+ return cryptor.decryptedContentLength(encryptedFile);
+ }
+
+ @Override
+ public Long decryptedFile(SeekableByteChannel encryptedFile, OutputStream plaintextFile) throws IOException {
+ final OutputStream countingInputStream = new CountingOutputStream(decryptedBytes, plaintextFile);
+ return cryptor.decryptedFile(encryptedFile, countingInputStream);
+ }
+
+ @Override
+ public Long encryptFile(InputStream plaintextFile, SeekableByteChannel encryptedFile) throws IOException {
+ final InputStream countingInputStream = new CountingInputStream(encryptedBytes, plaintextFile);
+ return cryptor.encryptFile(countingInputStream, encryptedFile);
+ }
+
+ @Override
+ public Filter getPayloadFilesFilter() {
+ return cryptor.getPayloadFilesFilter();
+ }
+
+ @Override
+ public void addSensitiveDataSwipeListener(SensitiveDataSwipeListener listener) {
+ cryptor.addSensitiveDataSwipeListener(listener);
+ }
+
+ @Override
+ public void removeSensitiveDataSwipeListener(SensitiveDataSwipeListener listener) {
+ cryptor.removeSensitiveDataSwipeListener(listener);
+ }
+
+ private class CountingInputStream extends InputStream {
+
+ private final InputStream in;
+ private final AtomicLong counter;
+
+ private CountingInputStream(AtomicLong counter, InputStream in) {
+ this.in = in;
+ this.counter = counter;
+ }
+
+ @Override
+ public int read() throws IOException {
+ int count = in.read();
+ counter.addAndGet(count);
+ return count;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int count = in.read(b, off, len);
+ counter.addAndGet(count);
+ return count;
+ }
+
+ }
+
+ private class CountingOutputStream extends OutputStream {
+
+ private final OutputStream out;
+ private final AtomicLong counter;
+
+ private CountingOutputStream(AtomicLong counter, OutputStream out) {
+ this.out = out;
+ this.counter = counter;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ counter.incrementAndGet();
+ out.write(b);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ counter.addAndGet(len);
+ out.write(b, off, len);
+ }
+
+ }
+
+}
diff --git a/main/crypto-api/src/main/java/org/cryptomator/crypto/SensitiveDataSwipeListener.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/SensitiveDataSwipeListener.java
index bc02282a5..c98cceb99 100644
--- a/main/crypto-api/src/main/java/org/cryptomator/crypto/SensitiveDataSwipeListener.java
+++ b/main/crypto-api/src/main/java/org/cryptomator/crypto/SensitiveDataSwipeListener.java
@@ -1,3 +1,11 @@
+/*******************************************************************************
+ * 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;
public interface SensitiveDataSwipeListener {
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/exceptions/DecryptFailedException.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/DecryptFailedException.java
similarity index 100%
rename from main/crypto-aes/src/main/java/org/cryptomator/crypto/exceptions/DecryptFailedException.java
rename to main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/DecryptFailedException.java
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/exceptions/StorageCryptingException.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/StorageCryptingException.java
similarity index 100%
rename from main/crypto-aes/src/main/java/org/cryptomator/crypto/exceptions/StorageCryptingException.java
rename to main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/StorageCryptingException.java
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/exceptions/UnsupportedKeyLengthException.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/UnsupportedKeyLengthException.java
similarity index 100%
rename from main/crypto-aes/src/main/java/org/cryptomator/crypto/exceptions/UnsupportedKeyLengthException.java
rename to main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/UnsupportedKeyLengthException.java
diff --git a/main/crypto-aes/src/main/java/org/cryptomator/crypto/exceptions/WrongPasswordException.java b/main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/WrongPasswordException.java
similarity index 100%
rename from main/crypto-aes/src/main/java/org/cryptomator/crypto/exceptions/WrongPasswordException.java
rename to main/crypto-api/src/main/java/org/cryptomator/crypto/exceptions/WrongPasswordException.java
diff --git a/main/ui/src/main/java/org/cryptomator/ui/InitializeController.java b/main/ui/src/main/java/org/cryptomator/ui/InitializeController.java
index 0e1e677b7..93094b40e 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/InitializeController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/InitializeController.java
@@ -132,7 +132,6 @@ public class InitializeController implements Initializable {
OutputStream masterKeyOutputStream = null;
try {
masterKeyOutputStream = Files.newOutputStream(masterKeyPath, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
- directory.getCryptor().randomizeMasterKey();
directory.getCryptor().encryptMasterKey(masterKeyOutputStream, password);
encryptExistingContents();
directory.getCryptor().swipeSensitiveData();
diff --git a/main/ui/src/main/java/org/cryptomator/ui/UnlockedController.java b/main/ui/src/main/java/org/cryptomator/ui/UnlockedController.java
index ec1e9170e..d14408bc7 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/UnlockedController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/UnlockedController.java
@@ -11,26 +11,44 @@ package org.cryptomator.ui;
import java.net.URL;
import java.util.ResourceBundle;
+import javafx.animation.Animation;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
+import javafx.scene.chart.LineChart;
+import javafx.scene.chart.NumberAxis;
+import javafx.scene.chart.XYChart.Data;
+import javafx.scene.chart.XYChart.Series;
import javafx.scene.control.Label;
+import javafx.util.Duration;
+import org.cryptomator.crypto.CryptorIOSampling;
import org.cryptomator.ui.model.Directory;
public class UnlockedController implements Initializable {
+ private static final int IO_SAMPLING_STEPS = 100;
+ private static final double IO_SAMPLING_INTERVAL = 0.25;
private ResourceBundle rb;
private LockListener listener;
private Directory directory;
+ private Timeline ioAnimation;
@FXML
private Label messageLabel;
+ @FXML
+ private LineChart ioGraph;
+
+ @FXML
+ private NumberAxis xAxis;
+
@Override
public void initialize(URL url, ResourceBundle rb) {
this.rb = rb;
-
}
@FXML
@@ -43,6 +61,60 @@ public class UnlockedController implements Initializable {
}
}
+ // ****************************************
+ // IO Graph
+ // ****************************************
+
+ private void startIoSampling(final CryptorIOSampling sampler) {
+ final Series decryptedBytes = new Series<>();
+ decryptedBytes.setName("decrypted");
+ final Series encryptedBytes = new Series<>();
+ encryptedBytes.setName("encrypted");
+
+ ioGraph.getData().add(decryptedBytes);
+ ioGraph.getData().add(encryptedBytes);
+
+ ioAnimation = new Timeline();
+ ioAnimation.getKeyFrames().add(new KeyFrame(Duration.seconds(IO_SAMPLING_INTERVAL), new IoSamplingAnimationHandler(sampler, decryptedBytes, encryptedBytes)));
+ ioAnimation.setCycleCount(Animation.INDEFINITE);
+ ioAnimation.play();
+ }
+
+ private class IoSamplingAnimationHandler implements EventHandler {
+
+ private static final double BYTES_TO_MEGABYTES_FACTOR = IO_SAMPLING_INTERVAL / 1024.0 / 1024.0;
+ private final CryptorIOSampling sampler;
+ private final Series decryptedBytes;
+ private final Series encryptedBytes;
+ private int step = 0;
+
+ public IoSamplingAnimationHandler(CryptorIOSampling sampler, Series decryptedBytes, Series encryptedBytes) {
+ this.sampler = sampler;
+ this.decryptedBytes = decryptedBytes;
+ this.encryptedBytes = encryptedBytes;
+ }
+
+ @Override
+ public void handle(ActionEvent event) {
+ step++;
+
+ final double decryptedMb = sampler.pollDecryptedBytes(true) * BYTES_TO_MEGABYTES_FACTOR;
+ decryptedBytes.getData().add(new Data(step, decryptedMb));
+ if (decryptedBytes.getData().size() > IO_SAMPLING_STEPS) {
+ decryptedBytes.getData().remove(0);
+ }
+
+ final double encrypteddMb = sampler.pollEncryptedBytes(true) * BYTES_TO_MEGABYTES_FACTOR;
+ encryptedBytes.getData().add(new Data(step, encrypteddMb));
+ if (encryptedBytes.getData().size() > IO_SAMPLING_STEPS) {
+ encryptedBytes.getData().remove(0);
+ }
+
+ xAxis.setLowerBound(step - IO_SAMPLING_STEPS);
+ xAxis.setUpperBound(step);
+ }
+ }
+
/* Getter/Setter */
public Directory getDirectory() {
@@ -53,6 +125,12 @@ public class UnlockedController implements Initializable {
this.directory = directory;
final String msg = String.format(rb.getString("unlocked.messageLabel.runningOnPort"), directory.getServer().getPort());
messageLabel.setText(msg);
+
+ if (directory.getCryptor() instanceof CryptorIOSampling) {
+ startIoSampling((CryptorIOSampling) directory.getCryptor());
+ } else {
+ ioGraph.setVisible(false);
+ }
}
public LockListener getListener() {
diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/Directory.java b/main/ui/src/main/java/org/cryptomator/ui/model/Directory.java
index de7056ddd..0364d8022 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/model/Directory.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/model/Directory.java
@@ -6,6 +6,8 @@ import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.commons.lang3.StringUtils;
+import org.cryptomator.crypto.Cryptor;
+import org.cryptomator.crypto.SamplingDecorator;
import org.cryptomator.crypto.aes256.Aes256Cryptor;
import org.cryptomator.ui.MainApplication;
import org.cryptomator.ui.util.MasterKeyFilter;
@@ -26,7 +28,7 @@ public class Directory implements Serializable {
private static final Logger LOG = LoggerFactory.getLogger(Directory.class);
private final WebDAVServer server = new WebDAVServer();
- private final Aes256Cryptor cryptor = new Aes256Cryptor();
+ private final Cryptor cryptor = SamplingDecorator.decorate(new Aes256Cryptor());
private final Path path;
private boolean unlocked;
private String unmountCommand;
@@ -97,7 +99,7 @@ public class Directory implements Serializable {
return path.getFileName().toString();
}
- public Aes256Cryptor getCryptor() {
+ public Cryptor getCryptor() {
return cryptor;
}
diff --git a/main/ui/src/main/resources/localization.properties b/main/ui/src/main/resources/localization.properties
index 7bf687998..ab0bdb71a 100644
--- a/main/ui/src/main/resources/localization.properties
+++ b/main/ui/src/main/resources/localization.properties
@@ -36,6 +36,7 @@ unlock.messageLabel.startServerFailed=Starting WebDAV server failed.
# unlocked.fxml
unlocked.messageLabel.runningOnPort=Vault is accessible via WebDAV on local port %d.
unlocked.button.lock=Lock vault
+unlocked.ioGraph.yAxis.label=Throughput (MiB/s)
# tray icon
diff --git a/main/ui/src/main/resources/unlocked.fxml b/main/ui/src/main/resources/unlocked.fxml
index 1dbcb79b4..96ef9ad0a 100644
--- a/main/ui/src/main/resources/unlocked.fxml
+++ b/main/ui/src/main/resources/unlocked.fxml
@@ -14,6 +14,8 @@
+
+
@@ -32,6 +34,12 @@
+
+
+
+
+
+