diff --git a/main/core/src/main/java/org/cryptomator/webdav/WebDavServer.java b/main/core/src/main/java/org/cryptomator/webdav/WebDavServer.java
index 1e066b752..9f67a4a74 100644
--- a/main/core/src/main/java/org/cryptomator/webdav/WebDavServer.java
+++ b/main/core/src/main/java/org/cryptomator/webdav/WebDavServer.java
@@ -45,7 +45,7 @@ public final class WebDavServer {
* @param cryptor A fully initialized cryptor instance ready to en- or decrypt streams.
* @return true upon success
*/
- public synchronized boolean start(final String workDir, final Cryptor cryptor) {
+ public synchronized boolean start(final String workDir, final boolean checkFileIntegrity, final Cryptor cryptor) {
final ServerConnector connector = new ServerConnector(server);
connector.setHost(LOCALHOST);
@@ -53,7 +53,7 @@ public final class WebDavServer {
final String servletPathSpec = "/*";
final ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
- context.addServlet(getWebDavServletHolder(workDir, contextPath, cryptor), servletPathSpec);
+ context.addServlet(getWebDavServletHolder(workDir, contextPath, checkFileIntegrity, cryptor), servletPathSpec);
context.setContextPath(contextPath);
server.setHandler(context);
@@ -82,10 +82,11 @@ public final class WebDavServer {
return server.isStopped();
}
- private ServletHolder getWebDavServletHolder(final String workDir, final String contextPath, final Cryptor cryptor) {
+ private ServletHolder getWebDavServletHolder(final String workDir, final String contextPath, final boolean checkFileIntegrity, final Cryptor cryptor) {
final ServletHolder result = new ServletHolder("Cryptomator-WebDAV-Servlet", new WebDavServlet(cryptor));
result.setInitParameter(WebDavServlet.CFG_FS_ROOT, workDir);
result.setInitParameter(WebDavServlet.CFG_HTTP_ROOT, contextPath);
+ result.setInitParameter(WebDavServlet.CFG_CHECK_FILE_INTEGRITY, Boolean.toString(checkFileIntegrity));
return result;
}
diff --git a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/WebDavResourceFactory.java b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/WebDavResourceFactory.java
index a4a80fe5d..d4dabcd73 100644
--- a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/WebDavResourceFactory.java
+++ b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/WebDavResourceFactory.java
@@ -34,9 +34,11 @@ class WebDavResourceFactory implements DavResourceFactory {
private final LockManager lockManager = new SimpleLockManager();
private final Cryptor cryptor;
+ private final boolean checkFileIntegrity;
- WebDavResourceFactory(Cryptor cryptor) {
+ WebDavResourceFactory(Cryptor cryptor, boolean checkFileIntegrity) {
this.cryptor = cryptor;
+ this.checkFileIntegrity = checkFileIntegrity;
}
@Override
@@ -70,11 +72,11 @@ class WebDavResourceFactory implements DavResourceFactory {
}
private EncryptedFile createFilePart(DavResourceLocator locator, DavSession session, DavServletRequest request) {
- return new EncryptedFilePart(this, locator, session, request, lockManager, cryptor);
+ return new EncryptedFilePart(this, locator, session, request, lockManager, cryptor, checkFileIntegrity);
}
private EncryptedFile createFile(DavResourceLocator locator, DavSession session) {
- return new EncryptedFile(this, locator, session, lockManager, cryptor);
+ return new EncryptedFile(this, locator, session, lockManager, cryptor, checkFileIntegrity);
}
private EncryptedDir createDirectory(DavResourceLocator locator, DavSession session) {
diff --git a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/WebDavServlet.java b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/WebDavServlet.java
index 5b0ddb9ca..8c5c6c96e 100644
--- a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/WebDavServlet.java
+++ b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/WebDavServlet.java
@@ -22,8 +22,9 @@ import org.cryptomator.crypto.Cryptor;
public class WebDavServlet extends AbstractWebdavServlet {
private static final long serialVersionUID = 7965170007048673022L;
- public static final String CFG_FS_ROOT = "oce.fs.root";
- public static final String CFG_HTTP_ROOT = "oce.http.root";
+ public static final String CFG_FS_ROOT = "cfg.fs.root";
+ public static final String CFG_HTTP_ROOT = "cfg.http.root";
+ public static final String CFG_CHECK_FILE_INTEGRITY = "cfg.checkFileIntegrity";
private DavSessionProvider davSessionProvider;
private DavLocatorFactory davLocatorFactory;
private DavResourceFactory davResourceFactory;
@@ -42,9 +43,10 @@ public class WebDavServlet extends AbstractWebdavServlet {
final String fsRoot = config.getInitParameter(CFG_FS_ROOT);
final String httpRoot = config.getInitParameter(CFG_HTTP_ROOT);
+ final boolean checkFileIntegrity = Boolean.parseBoolean(config.getInitParameter(CFG_CHECK_FILE_INTEGRITY));
this.davLocatorFactory = new WebDavLocatorFactory(fsRoot, httpRoot, cryptor);
- this.davResourceFactory = new WebDavResourceFactory(cryptor);
+ this.davResourceFactory = new WebDavResourceFactory(cryptor, checkFileIntegrity);
}
@Override
diff --git a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/resources/EncryptedFile.java b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/resources/EncryptedFile.java
index 83d017a8f..2d7590fbf 100644
--- a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/resources/EncryptedFile.java
+++ b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/resources/EncryptedFile.java
@@ -40,8 +40,11 @@ public class EncryptedFile extends AbstractEncryptedNode {
private static final Logger LOG = LoggerFactory.getLogger(EncryptedFile.class);
- public EncryptedFile(DavResourceFactory factory, DavResourceLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor) {
+ protected final boolean checkIntegrity;
+
+ public EncryptedFile(DavResourceFactory factory, DavResourceLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor, boolean checkIntegrity) {
super(factory, locator, session, lockManager, cryptor);
+ this.checkIntegrity = checkIntegrity;
}
@Override
@@ -73,6 +76,9 @@ public class EncryptedFile extends AbstractEncryptedNode {
SeekableByteChannel channel = null;
try {
channel = Files.newByteChannel(path, StandardOpenOption.READ);
+ if (checkIntegrity && !cryptor.authenticateContent(channel)) {
+ throw new DecryptFailedException("File content compromised: " + path.toString());
+ }
outputContext.setContentLength(cryptor.decryptedContentLength(channel));
if (outputContext.hasStream()) {
cryptor.decryptedFile(channel, outputContext.getOutputStream());
diff --git a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/resources/EncryptedFilePart.java b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/resources/EncryptedFilePart.java
index c257b39fb..9253f3f7d 100644
--- a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/resources/EncryptedFilePart.java
+++ b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/resources/EncryptedFilePart.java
@@ -50,8 +50,8 @@ public class EncryptedFilePart extends EncryptedFile {
private final Set> requestedContentRanges = new HashSet>();
- public EncryptedFilePart(DavResourceFactory factory, DavResourceLocator locator, DavSession session, DavServletRequest request, LockManager lockManager, Cryptor cryptor) {
- super(factory, locator, session, lockManager, cryptor);
+ public EncryptedFilePart(DavResourceFactory factory, DavResourceLocator locator, DavSession session, DavServletRequest request, LockManager lockManager, Cryptor cryptor, boolean checkIntegrity) {
+ super(factory, locator, session, lockManager, cryptor, checkIntegrity);
final String rangeHeader = request.getHeader(HttpHeader.RANGE.asString());
if (rangeHeader == null) {
throw new IllegalArgumentException("HTTP request doesn't contain a range header");
@@ -116,6 +116,9 @@ public class EncryptedFilePart extends EncryptedFile {
SeekableByteChannel channel = null;
try {
channel = Files.newByteChannel(path, StandardOpenOption.READ);
+ if (checkIntegrity && !cryptor.authenticateContent(channel)) {
+ throw new DecryptFailedException("File content compromised: " + path.toString());
+ }
final Long fileSize = cryptor.decryptedContentLength(channel);
final Pair range = getUnionRange(fileSize);
final Long rangeLength = range.getRight() - range.getLeft() + 1;
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
index f90a97a4b..5619463ee 100644
--- 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
@@ -7,7 +7,7 @@ public class DecryptFailedException extends StorageCryptingException {
super("Decryption failed.", t);
}
- protected DecryptFailedException(String msg) {
+ public DecryptFailedException(String msg) {
super(msg);
}
}
\ No newline at end of file
diff --git a/main/ui/src/main/java/org/cryptomator/ui/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/UnlockController.java
index 3aea37168..387bc1e8b 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/UnlockController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/UnlockController.java
@@ -24,6 +24,7 @@ import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
+import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
@@ -55,6 +56,9 @@ public class UnlockController implements Initializable {
@FXML
private SecPasswordField passwordField;
+ @FXML
+ private CheckBox checkIntegrity;
+
@FXML
private Button unlockButton;
@@ -96,6 +100,7 @@ public class UnlockController implements Initializable {
try {
progressIndicator.setVisible(true);
masterKeyInputStream = Files.newInputStream(masterKeyPath, StandardOpenOption.READ);
+ directory.setVerifyFileIntegrity(checkIntegrity.isSelected());
directory.getCryptor().decryptMasterKey(masterKeyInputStream, password);
if (!directory.startServer()) {
messageLabel.setText(rb.getString("unlock.messageLabel.startServerFailed"));
@@ -170,6 +175,7 @@ public class UnlockController implements Initializable {
public void setDirectory(Directory directory) {
this.directory = directory;
this.findExistingUsernames();
+ this.checkIntegrity.setSelected(directory.shouldVerifyFileIntegrity());
}
public UnlockListener 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 66112bc6e..3c3d81d18 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
@@ -34,7 +34,7 @@ public class Directory implements Serializable {
private final Cryptor cryptor = SamplingDecorator.decorate(new Aes256Cryptor());
private final ObjectProperty unlocked = new SimpleObjectProperty(this, "unlocked", Boolean.FALSE);
private final Path path;
- // private boolean unlocked;
+ private boolean verifyFileIntegrity;
private WebDavMount webDavMount;
private final Runnable shutdownTask = new ShutdownTask();
@@ -50,7 +50,7 @@ public class Directory implements Serializable {
}
public synchronized boolean startServer() {
- if (server.start(path.toString(), cryptor)) {
+ if (server.start(path.toString(), verifyFileIntegrity, cryptor)) {
MainApplication.addShutdownTask(shutdownTask);
return true;
} else {
@@ -96,6 +96,14 @@ public class Directory implements Serializable {
return path;
}
+ public boolean shouldVerifyFileIntegrity() {
+ return verifyFileIntegrity;
+ }
+
+ public void setVerifyFileIntegrity(boolean verifyFileIntegrity) {
+ this.verifyFileIntegrity = verifyFileIntegrity;
+ }
+
/**
* @return Directory name without preceeding path components
*/
diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/DirectoryDeserializer.java b/main/ui/src/main/java/org/cryptomator/ui/model/DirectoryDeserializer.java
index a5d942688..9caa74c84 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/model/DirectoryDeserializer.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/model/DirectoryDeserializer.java
@@ -17,7 +17,10 @@ public class DirectoryDeserializer extends JsonDeserializer {
final JsonNode node = jp.readValueAsTree();
final String pathStr = node.get("path").asText();
final Path path = FileSystems.getDefault().getPath(pathStr);
- return new Directory(path);
+ final Directory dir = new Directory(path);
+ final boolean verifyFileIntegrity = node.has("checkIntegrity") ? node.get("checkIntegrity").asBoolean() : false;
+ dir.setVerifyFileIntegrity(verifyFileIntegrity);
+ return dir;
}
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/DirectorySerializer.java b/main/ui/src/main/java/org/cryptomator/ui/model/DirectorySerializer.java
index 83eef3839..ad64952ae 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/model/DirectorySerializer.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/model/DirectorySerializer.java
@@ -13,6 +13,7 @@ public class DirectorySerializer extends JsonSerializer {
public void serialize(Directory value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeStringField("path", value.getPath().toString());
+ jgen.writeBooleanField("checkIntegrity", value.shouldVerifyFileIntegrity());
jgen.writeEndObject();
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java b/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java
index 3b49165f6..61831295b 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/settings/Settings.java
@@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.ObjectMapper;
-@JsonPropertyOrder(value = {"webdavWorkDir"})
+@JsonPropertyOrder(value = {"directories"})
public class Settings implements Serializable {
private static final long serialVersionUID = 7609959894417878744L;
@@ -55,7 +55,6 @@ public class Settings implements Serializable {
}
private List directories;
- private String username;
private Settings() {
// private constructor
@@ -107,12 +106,4 @@ public class Settings implements Serializable {
this.directories = directories;
}
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
}
diff --git a/main/ui/src/main/resources/css/mac_theme.css b/main/ui/src/main/resources/css/mac_theme.css
index 85327baeb..c654f688a 100644
--- a/main/ui/src/main/resources/css/mac_theme.css
+++ b/main/ui/src/main/resources/css/mac_theme.css
@@ -175,8 +175,6 @@
.button,
.toggle-button,
-.radio-button > .radio,
-.check-box > .box,
.menu-button,
.choice-box,
.color-picker.split-button > .color-picker-label,
@@ -194,7 +192,6 @@
.button:hover,
.toggle-button:hover,
.radio-button:hover > .radio,
-.check-box:hover > .box,
.menu-button:hover,
.split-menu-button > .label:hover,
.split-menu-button > .arrow-button:hover,
@@ -212,8 +209,6 @@
.button:armed,
.button:default:armed,
.toggle-button:armed,
-.radio-button:armed > .radio,
-.check-box:armed .box,
.menu-button:armed,
.split-menu-button:armed > .label,
.split-menu-button > .arrow-button:pressed,
@@ -228,8 +223,6 @@
}
.button:focused,
.toggle-button:focused,
-.radio-button:focused > .radio,
-.check-box:focused > .box,
.menu-button:focused,
.choice-box:focused,
.color-picker.split-button:focused > .color-picker-label {
@@ -242,8 +235,6 @@
.button:disabled,
.toggle-button:disabled,
-.radio-button:disabled,
-.check-box:disabled,
.hyperlink:disabled,
.menu-button:disabled,
.split-menu-button:disabled,
@@ -270,8 +261,6 @@
.button:show-mnemonics .mnemonic-underline,
.toggle-button:show-mnemonics .mnemonic-underline,
-.radio-button:show-mnemonics .mnemonic-underline,
-.check-box:show-mnemonics .mnemonic-underline,
.hyperlink:show-mnemonics > .mnemonic-underline,
.split-menu-button:show-mnemonics > .mnemonic-underline,
.menu-button:show-mnemonics > .mnemonic-underline {
@@ -334,6 +323,35 @@
-fx-text-fill: -fx-mid-text-color;
}
+/*******************************************************************************
+ * *
+ * CheckBox *
+ * *
+ ******************************************************************************/
+
+.check-box {
+ -fx-label-padding: 0 0 0 3px;
+ -fx-text-fill: -fx-text-background-color;
+}
+.check-box > .box {
+ -fx-padding: 3px;
+ -fx-background-color: linear-gradient(to bottom, #A5A5A5 0%, #B8B8B8 100%), #F3F3F3, #FFFFFF;
+ -fx-background-radius: 2.5, 2.5, 2.5;
+ -fx-background-insets: 0, 1, 2 1 1 1;
+}
+.check-box > .box > .mark {
+ -fx-background-color: transparent;
+ -fx-padding: 4px;
+ -fx-shape: "M-1,4, L-1,5.5 L3.5,8.5 L9,0 L9,-1 L7,-1 L3,6 L1,4 Z";
+}
+.check-box:selected > .box {
+ -fx-background-color: #2C90FC, #3B99FC;
+ -fx-background-insets: 0, 1;
+}
+.check-box:selected > .box > .mark {
+ -fx-background-color: white;
+}
+
/*******************************************************************************
* *
* ToolBar *
diff --git a/main/ui/src/main/resources/css/win_theme.css b/main/ui/src/main/resources/css/win_theme.css
index 4993e0184..20998db5a 100644
--- a/main/ui/src/main/resources/css/win_theme.css
+++ b/main/ui/src/main/resources/css/win_theme.css
@@ -174,8 +174,6 @@
.button,
.toggle-button,
-.radio-button > .radio,
-.check-box > .box,
.menu-button,
.choice-box,
.color-picker.split-button > .color-picker-label,
@@ -192,8 +190,6 @@
}
.button:hover,
.toggle-button:hover,
-.radio-button:hover > .radio,
-.check-box:hover > .box,
.menu-button:hover,
.split-menu-button > .label:hover,
.split-menu-button > .arrow-button:hover,
@@ -208,8 +204,6 @@
.button:armed,
.button:default:armed,
.toggle-button:armed,
-.radio-button:armed > .radio,
-.check-box:armed .box,
.menu-button:armed,
.split-menu-button:armed > .label,
.split-menu-button > .arrow-button:pressed,
@@ -235,8 +229,6 @@
.button:disabled,
.toggle-button:disabled,
-.radio-button:disabled,
-.check-box:disabled,
.hyperlink:disabled,
.menu-button:disabled,
.split-menu-button:disabled,
@@ -261,8 +253,6 @@
.button:show-mnemonics .mnemonic-underline,
.toggle-button:show-mnemonics .mnemonic-underline,
-.radio-button:show-mnemonics .mnemonic-underline,
-.check-box:show-mnemonics .mnemonic-underline,
.hyperlink:show-mnemonics > .mnemonic-underline,
.split-menu-button:show-mnemonics > .mnemonic-underline,
.menu-button:show-mnemonics > .mnemonic-underline {
@@ -323,6 +313,36 @@
-fx-text-fill: -fx-mid-text-color;
}
+/*******************************************************************************
+ * *
+ * CheckBox *
+ * *
+ ******************************************************************************/
+
+/* TODO win L&F */
+.check-box {
+ -fx-label-padding: 0 0 0 3px;
+ -fx-text-fill: -fx-text-background-color;
+}
+.check-box > .box {
+ -fx-padding: 3px;
+ -fx-background-color: linear-gradient(to bottom, #A5A5A5 0%, #B8B8B8 100%), #F3F3F3, #FFFFFF;
+ -fx-background-radius: 2.5, 2.5, 2.5;
+ -fx-background-insets: 0, 1, 2 1 1 1;
+}
+.check-box > .box > .mark {
+ -fx-background-color: transparent;
+ -fx-padding: 4px;
+ -fx-shape: "M-1,4, L-1,5.5 L3.5,8.5 L9,0 L9,-1 L7,-1 L3,6 L1,4 Z";
+}
+.check-box:selected > .box {
+ -fx-background-color: #2C90FC, #3B99FC;
+ -fx-background-insets: 0, 1;
+}
+.check-box:selected > .box > .mark {
+ -fx-background-color: white;
+}
+
/*******************************************************************************
* *
* ToolBar *
diff --git a/main/ui/src/main/resources/fxml/unlock.fxml b/main/ui/src/main/resources/fxml/unlock.fxml
index 8c5ccbe28..9f3e788dd 100644
--- a/main/ui/src/main/resources/fxml/unlock.fxml
+++ b/main/ui/src/main/resources/fxml/unlock.fxml
@@ -16,6 +16,7 @@
+
@@ -38,10 +39,14 @@
-
+
+
-
+
+
+
+
diff --git a/main/ui/src/main/resources/localization.properties b/main/ui/src/main/resources/localization.properties
index 89a25f7b3..c3424739d 100644
--- a/main/ui/src/main/resources/localization.properties
+++ b/main/ui/src/main/resources/localization.properties
@@ -32,6 +32,8 @@ initialize.alert.directoryIsNotEmpty.content=All existing files inside this dire
# unlock.fxml
unlock.label.username=Username
unlock.label.password=Password
+unlock.label.checkIntegrity=File integrity
+unlock.checkbox.checkIntegrity=Verify checksums (slower, but detects manipulation)
unlock.button.unlock=Unlock vault
unlock.errorMessage.wrongPassword=Wrong password.
unlock.errorMessage.decryptionFailed=Decryption failed.