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 @@