supporting change password again - now via CryptoFileSystemFactory

This commit is contained in:
Sebastian Stenzel
2016-01-27 18:21:47 +01:00
parent 091a44e65d
commit a972480e72
12 changed files with 238 additions and 192 deletions

View File

@@ -21,8 +21,11 @@ public interface Cryptor extends Destroyable {
void randomizeMasterkey();
boolean readKeysFromMasterkeyFile(byte[] masterkeyFileContents, CharSequence passphrase);
void readKeysFromMasterkeyFile(byte[] masterkeyFileContents, CharSequence passphrase) throws InvalidPassphraseException;
byte[] writeKeysToMasterkeyFile(CharSequence passphrase);
@Override
void destroy();
}

View File

@@ -24,6 +24,7 @@ import org.cryptomator.common.LazyInitializer;
import org.cryptomator.crypto.engine.Cryptor;
import org.cryptomator.crypto.engine.FileContentCryptor;
import org.cryptomator.crypto.engine.FilenameCryptor;
import org.cryptomator.crypto.engine.InvalidPassphraseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -86,7 +87,7 @@ public class CryptorImpl implements Cryptor {
}
@Override
public boolean readKeysFromMasterkeyFile(byte[] masterkeyFileContents, CharSequence passphrase) {
public void readKeysFromMasterkeyFile(byte[] masterkeyFileContents, CharSequence passphrase) {
final KeyFile keyFile;
try {
final ObjectMapper om = new ObjectMapper();
@@ -107,9 +108,8 @@ public class CryptorImpl implements Cryptor {
final SecretKey kek = new SecretKeySpec(kekBytes, ENCRYPTION_ALG);
this.encryptionKey = AesKeyWrap.unwrap(kek, keyFile.getEncryptionMasterKey(), ENCRYPTION_ALG);
this.macKey = AesKeyWrap.unwrap(kek, keyFile.getMacMasterKey(), MAC_ALG);
return true;
} catch (InvalidKeyException e) {
return false;
throw new InvalidPassphraseException();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("Hard-coded algorithm doesn't exist.", e);
} finally {
@@ -152,17 +152,20 @@ public class CryptorImpl implements Cryptor {
/* ======================= destruction ======================= */
@Override
public void destroy() throws DestroyFailedException {
public void destroy() {
destroyQuietly(encryptionKey);
destroyQuietly(macKey);
}
@Override
public boolean isDestroyed() {
return encryptionKey.isDestroyed() && macKey.isDestroyed();
return (encryptionKey == null || encryptionKey.isDestroyed()) && (macKey == null || macKey.isDestroyed());
}
private void destroyQuietly(Destroyable d) {
if (d == null) {
return;
}
try {
d.destroy();
} catch (DestroyFailedException e) {

View File

@@ -16,60 +16,26 @@ import org.cryptomator.crypto.engine.InvalidPassphraseException;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.ReadableFile;
import org.cryptomator.filesystem.WritableFile;
public class CryptoFileSystem extends CryptoFolder implements FileSystem {
private static final String DATA_ROOT_DIR = "d";
private static final String ROOT_DIR_FILE = "root";
private static final String MASTERKEY_FILENAME = "masterkey.cryptomator";
private static final String MASTERKEY_BACKUP_FILENAME = "masterkey.cryptomator.bkup";
private final Folder physicalRoot;
private final CryptoFileSystemDelegate delegate;
public CryptoFileSystem(Folder physicalRoot, Cryptor cryptor, CryptoFileSystemDelegate delegate, CharSequence passphrase) throws InvalidPassphraseException {
super(null, "", cryptor);
if (cryptor.isDestroyed()) {
throw new IllegalArgumentException("Cryptor's keys must not be destroyed.");
}
this.physicalRoot = physicalRoot;
this.delegate = delegate;
final File masterkeyFile = physicalRoot.file(MASTERKEY_FILENAME);
if (masterkeyFile.exists()) {
final boolean unlocked = decryptMasterKeyFile(cryptor, masterkeyFile, passphrase);
if (!unlocked) {
throw new InvalidPassphraseException();
}
} else {
cryptor.randomizeMasterkey();
encryptMasterKeyFile(cryptor, masterkeyFile, passphrase);
}
assert masterkeyFile.exists() : "A CryptoFileSystem can not exist without a masterkey file.";
final File backupFile = physicalRoot.file(MASTERKEY_BACKUP_FILENAME);
masterkeyFile.copyTo(backupFile);
create();
}
private static boolean decryptMasterKeyFile(Cryptor cryptor, File masterkeyFile, CharSequence passphrase) {
try (ReadableFile file = masterkeyFile.openReadable()) {
// TODO we need to read the whole file but can not be sure about the
// buffer size:
final ByteBuffer bigEnoughBuffer = ByteBuffer.allocate(500);
file.read(bigEnoughBuffer);
bigEnoughBuffer.flip();
assert bigEnoughBuffer.remaining() < bigEnoughBuffer.capacity() : "The buffer wasn't big enough.";
final byte[] fileContents = new byte[bigEnoughBuffer.remaining()];
bigEnoughBuffer.get(fileContents);
return cryptor.readKeysFromMasterkeyFile(fileContents, passphrase);
}
}
private static void encryptMasterKeyFile(Cryptor cryptor, File masterkeyFile, CharSequence passphrase) {
try (WritableFile file = masterkeyFile.openWritable()) {
final byte[] fileContents = cryptor.writeKeysToMasterkeyFile(passphrase);
file.write(ByteBuffer.wrap(fileContents));
}
}
CryptoFileSystemDelegate delegate() {
return delegate;
}

View File

@@ -1,7 +1,8 @@
package org.cryptomator.filesystem.crypto;
import java.io.UncheckedIOException;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.cryptomator.crypto.engine.Cryptor;
@@ -14,20 +15,38 @@ import org.cryptomator.filesystem.shortening.ShorteningFileSystemFactory;
@Singleton
public class CryptoFileSystemFactory {
private final Provider<Cryptor> cryptorProvider;
private final Masterkeys masterkeys;
private final ShorteningFileSystemFactory shorteningFileSystemFactory;
private final BlockAlignedFileSystemFactory blockAlignedFileSystemFactory;
@Inject
public CryptoFileSystemFactory(Provider<Cryptor> cryptorProvider, ShorteningFileSystemFactory shorteningFileSystemFactory, BlockAlignedFileSystemFactory blockAlignedFileSystemFactory) {
this.cryptorProvider = cryptorProvider;
public CryptoFileSystemFactory(Masterkeys masterkeys, ShorteningFileSystemFactory shorteningFileSystemFactory, BlockAlignedFileSystemFactory blockAlignedFileSystemFactory) {
this.masterkeys = masterkeys;
this.shorteningFileSystemFactory = shorteningFileSystemFactory;
this.blockAlignedFileSystemFactory = blockAlignedFileSystemFactory;
}
public FileSystem get(Folder root, CharSequence passphrase, CryptoFileSystemDelegate delegate) throws InvalidPassphraseException {
final FileSystem nameShorteningFs = shorteningFileSystemFactory.get(root);
final FileSystem cryptoFs = new CryptoFileSystem(nameShorteningFs, cryptorProvider.get(), delegate, passphrase);
public void initializeNew(Folder vaultLocation, CharSequence passphrase) {
masterkeys.initialize(vaultLocation, passphrase);
}
public FileSystem unlockExisting(Folder vaultLocation, CharSequence passphrase, CryptoFileSystemDelegate delegate) throws InvalidPassphraseException {
final Cryptor cryptor = masterkeys.decrypt(vaultLocation, passphrase);
masterkeys.backup(vaultLocation);
final FileSystem nameShorteningFs = shorteningFileSystemFactory.get(vaultLocation);
final FileSystem cryptoFs = new CryptoFileSystem(nameShorteningFs, cryptor, delegate, passphrase);
return blockAlignedFileSystemFactory.get(cryptoFs);
}
public void changePassphrase(Folder vaultLocation, CharSequence oldPassphrase, CharSequence newPassphrase) throws InvalidPassphraseException {
masterkeys.backup(vaultLocation);
try {
masterkeys.changePassphrase(vaultLocation, oldPassphrase, newPassphrase);
// At this point the backup is still using the old password.
// It will be changed as soon as the user unlocks the vault the next time.
// This way he can still restore the old password, if he doesn't remember the new one.
} catch (UncheckedIOException e) {
masterkeys.restoreBackup(vaultLocation);
}
}
}

View File

@@ -0,0 +1,96 @@
package org.cryptomator.filesystem.crypto;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import org.apache.commons.io.IOUtils;
import org.cryptomator.crypto.engine.Cryptor;
import org.cryptomator.crypto.engine.InvalidPassphraseException;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.WritableFile;
@Singleton
class Masterkeys {
private static final String MASTERKEY_FILENAME = "masterkey.cryptomator";
private static final String MASTERKEY_BACKUP_FILENAME = "masterkey.cryptomator.bkup";
private final Provider<Cryptor> cryptorProvider;
@Inject
public Masterkeys(Provider<Cryptor> cryptorProvider) {
this.cryptorProvider = cryptorProvider;
}
public void initialize(Folder vaultLocation, CharSequence passphrase) {
File masterkeyFile = vaultLocation.file(MASTERKEY_FILENAME);
Cryptor cryptor = cryptorProvider.get();
try {
cryptor.randomizeMasterkey();
writeMasterKey(masterkeyFile, cryptor, passphrase);
} finally {
cryptor.destroy();
}
}
public Cryptor decrypt(Folder vaultLocation, CharSequence passphrase) throws InvalidPassphraseException {
File masterkeyFile = vaultLocation.file(MASTERKEY_FILENAME);
Cryptor cryptor = cryptorProvider.get();
try {
readMasterKey(masterkeyFile, cryptor, passphrase);
} catch (UncheckedIOException e) {
cryptor.destroy();
}
return cryptor;
}
public void changePassphrase(Folder vaultLocation, CharSequence oldPassphrase, CharSequence newPassphrase) throws InvalidPassphraseException {
File masterkeyFile = vaultLocation.file(MASTERKEY_FILENAME);
Cryptor cryptor = cryptorProvider.get();
try {
readMasterKey(masterkeyFile, cryptor, oldPassphrase);
writeMasterKey(masterkeyFile, cryptor, newPassphrase);
} finally {
cryptor.destroy();
}
}
public void backup(Folder vaultLocation) {
File masterkeyFile = vaultLocation.file(MASTERKEY_FILENAME);
File backupFile = vaultLocation.file(MASTERKEY_BACKUP_FILENAME);
masterkeyFile.copyTo(backupFile);
}
public void restoreBackup(Folder vaultLocation) {
File backupFile = vaultLocation.file(MASTERKEY_BACKUP_FILENAME);
File masterkeyFile = vaultLocation.file(MASTERKEY_FILENAME);
backupFile.copyTo(masterkeyFile);
}
/* I/O */
private static void readMasterKey(File file, Cryptor cryptor, CharSequence passphrase) throws UncheckedIOException, InvalidPassphraseException {
try (InputStream in = Channels.newInputStream(file.openReadable())) {
final byte[] fileContents = IOUtils.toByteArray(in);
cryptor.readKeysFromMasterkeyFile(fileContents, passphrase);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
private static void writeMasterKey(File file, Cryptor cryptor, CharSequence passphrase) throws UncheckedIOException {
try (WritableFile writable = file.openWritable()) {
final byte[] fileContents = cryptor.writeKeysToMasterkeyFile(passphrase);
writable.write(ByteBuffer.wrap(fileContents));
}
}
}

View File

@@ -29,9 +29,8 @@ public class NoCryptor implements Cryptor {
}
@Override
public boolean readKeysFromMasterkeyFile(byte[] masterkeyFileContents, CharSequence passphrase) {
public void readKeysFromMasterkeyFile(byte[] masterkeyFileContents, CharSequence passphrase) {
// thanks, but I don't need a key, if I'm not encryption anything...
return true;
}
@Override
@@ -40,4 +39,9 @@ public class NoCryptor implements Cryptor {
return new byte[0];
}
@Override
public void destroy() {
// no-op
}
}

View File

@@ -11,19 +11,28 @@ package org.cryptomator.crypto.engine.impl;
import java.io.IOException;
import org.cryptomator.crypto.engine.Cryptor;
import org.cryptomator.crypto.engine.InvalidPassphraseException;
import org.junit.Assert;
import org.junit.Test;
public class CryptorImplTest {
@Test
public void testMasterkeyDecryption() throws IOException {
public void testMasterkeyDecryptionWithCorrectPassphrase() throws IOException {
final String testMasterKey = "{\"version\":3,\"scryptSalt\":\"AAAAAAAAAAA=\",\"scryptCostParam\":2,\"scryptBlockSize\":8," //
+ "\"primaryMasterKey\":\"mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==\"," //
+ "\"hmacMasterKey\":\"mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==\"}";
final Cryptor cryptor = TestCryptorImplFactory.insecureCryptorImpl();
Assert.assertFalse(cryptor.readKeysFromMasterkeyFile(testMasterKey.getBytes(), "qwe"));
Assert.assertTrue(cryptor.readKeysFromMasterkeyFile(testMasterKey.getBytes(), "asd"));
cryptor.readKeysFromMasterkeyFile(testMasterKey.getBytes(), "asd");
}
@Test(expected = InvalidPassphraseException.class)
public void testMasterkeyDecryptionWithWrongPassphrase() throws IOException {
final String testMasterKey = "{\"version\":3,\"scryptSalt\":\"AAAAAAAAAAA=\",\"scryptCostParam\":2,\"scryptBlockSize\":8," //
+ "\"primaryMasterKey\":\"mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==\"," //
+ "\"hmacMasterKey\":\"mM+qoQ+o0qvPTiDAZYt+flaC3WbpNAx1sTXaUzxwpy0M9Ctj6Tih/Q==\"}";
final Cryptor cryptor = TestCryptorImplFactory.insecureCryptorImpl();
cryptor.readKeysFromMasterkeyFile(testMasterKey.getBytes(), "qwe");
}
@Test

View File

@@ -1,6 +1,7 @@
package org.cryptomator.filesystem.crypto;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
@@ -33,10 +34,35 @@ public class CryptoFileSystemComponentIntegrationTest {
public void setupFileSystems() {
cryptoDelegate = Mockito.mock(CryptoFileSystemDelegate.class);
ciphertextFs = new InMemoryFileSystem();
cleartextFs = cryptoFsComp.cryptoFileSystemFactory().get(ciphertextFs, "TopSecret", cryptoDelegate);
cryptoFsComp.cryptoFileSystemFactory().initializeNew(ciphertextFs, "TopSecret");
cleartextFs = cryptoFsComp.cryptoFileSystemFactory().unlockExisting(ciphertextFs, "TopSecret", cryptoDelegate);
cleartextFs.create();
}
@Test(timeout = 1000)
public void testVaultStructureInitializationAndBackupBehaviour() throws UncheckedIOException, IOException {
final FileSystem physicalFs = new InMemoryFileSystem();
final File masterkeyFile = physicalFs.file("masterkey.cryptomator");
final File masterkeyBkupFile = physicalFs.file("masterkey.cryptomator.bkup");
final Folder physicalDataRoot = physicalFs.folder("d");
Assert.assertFalse(masterkeyFile.exists());
Assert.assertFalse(masterkeyBkupFile.exists());
Assert.assertFalse(physicalDataRoot.exists());
cryptoFsComp.cryptoFileSystemFactory().initializeNew(physicalFs, "asd");
Assert.assertTrue(masterkeyFile.exists());
Assert.assertFalse(masterkeyBkupFile.exists());
Assert.assertFalse(physicalDataRoot.exists());
@SuppressWarnings("unused")
final FileSystem cryptoFs = cryptoFsComp.cryptoFileSystemFactory().unlockExisting(physicalFs, "asd", cryptoDelegate);
Assert.assertTrue(masterkeyBkupFile.exists());
Assert.assertTrue(physicalDataRoot.exists());
Assert.assertEquals(3, physicalFs.children().count()); // d + masterkey.cryptomator + masterkey.cryptomator.bkup
Assert.assertEquals(1, physicalDataRoot.files().count()); // ROOT file
Assert.assertEquals(1, physicalDataRoot.folders().count()); // ROOT directory
}
@Test
public void testEncryptionOfLongFolderNames() {
final String shortName = "normal folder name";

View File

@@ -13,13 +13,11 @@ import static org.cryptomator.filesystem.FileSystemVisitor.fileSystemVisitor;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
import org.cryptomator.crypto.engine.Cryptor;
import org.cryptomator.crypto.engine.NoCryptor;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.Folder;
import org.cryptomator.filesystem.ReadableFile;
@@ -31,58 +29,6 @@ import org.mockito.Mockito;
public class CryptoFileSystemTest {
@Test(timeout = 1000)
public void testVaultStructureInitialization() throws UncheckedIOException, IOException {
// mock cryptor:
final Cryptor cryptor = new NoCryptor();
// some mock fs:
final FileSystem physicalFs = new InMemoryFileSystem();
final File masterkeyFile = physicalFs.file("masterkey.cryptomator");
final File masterkeyBkupFile = physicalFs.file("masterkey.cryptomator.bkup");
final Folder physicalDataRoot = physicalFs.folder("d");
Assert.assertFalse(masterkeyFile.exists());
Assert.assertFalse(masterkeyBkupFile.exists());
Assert.assertFalse(physicalDataRoot.exists());
// init crypto fs:
final FileSystem fs = new CryptoFileSystem(physicalFs, cryptor, Mockito.mock(CryptoFileSystemDelegate.class), "foo");
Assert.assertTrue(masterkeyFile.exists());
Assert.assertTrue(masterkeyBkupFile.exists());
fs.create();
Assert.assertTrue(physicalDataRoot.exists());
Assert.assertEquals(3, physicalFs.children().count()); // d + masterkey.cryptomator + masterkey.cryptomator.bkup
Assert.assertEquals(1, physicalDataRoot.files().count()); // ROOT file
Assert.assertEquals(1, physicalDataRoot.folders().count()); // ROOT directory
}
@Test(timeout = 1000)
public void testMasterkeyBackupBehaviour() throws InterruptedException {
// mock cryptor:
final Cryptor cryptor = new NoCryptor();
// some mock fs:
final FileSystem physicalFs = new InMemoryFileSystem();
final File masterkeyBkupFile = physicalFs.file("masterkey.cryptomator.bkup");
Assert.assertFalse(masterkeyBkupFile.exists());
// first initialization:
new CryptoFileSystem(physicalFs, cryptor, Mockito.mock(CryptoFileSystemDelegate.class), "foo");
Assert.assertTrue(masterkeyBkupFile.exists());
final Instant bkupDateT0 = masterkeyBkupFile.lastModified();
// make sure some time passes, as the resolution of last modified date
// is not in nanos:
Thread.sleep(1);
// second initialization:
new CryptoFileSystem(physicalFs, cryptor, Mockito.mock(CryptoFileSystemDelegate.class), "foo");
Assert.assertTrue(masterkeyBkupFile.exists());
final Instant bkupDateT1 = masterkeyBkupFile.lastModified();
Assert.assertTrue(bkupDateT1.isAfter(bkupDateT0));
}
@Test(timeout = 1000)
public void testDirectoryCreation() throws UncheckedIOException, IOException {
// mock stuff and prepare crypto FS:

View File

@@ -10,6 +10,7 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.cryptomator.crypto.engine.Cryptor;
import org.cryptomator.crypto.engine.impl.CryptorImpl;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.crypto.CryptoFileSystem;
@@ -50,11 +51,17 @@ class FileSystemFactories implements Iterable<FileSystemFactory> {
}
private FileSystem createCryptoFileSystemInMemory() {
return new CryptoFileSystem(createInMemoryFileSystem(), new CryptorImpl(RANDOM_MOCK), Mockito.mock(CryptoFileSystemDelegate.class), "aPassphrase");
return new CryptoFileSystem(createInMemoryFileSystem(), createCryptor(), Mockito.mock(CryptoFileSystemDelegate.class), "aPassphrase");
}
private FileSystem createCryptoFileSystemNio() {
return new CryptoFileSystem(createNioFileSystem(), new CryptorImpl(RANDOM_MOCK), Mockito.mock(CryptoFileSystemDelegate.class), "aPassphrase");
return new CryptoFileSystem(createNioFileSystem(), createCryptor(), Mockito.mock(CryptoFileSystemDelegate.class), "aPassphrase");
}
private Cryptor createCryptor() {
Cryptor cryptor = new CryptorImpl(RANDOM_MOCK);
cryptor.randomizeMasterkey();
return cryptor;
}
private void add(String name, FileSystemFactory factory) {

View File

@@ -1,17 +1,20 @@
package org.cryptomator.ui.controllers;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.cryptomator.crypto.engine.InvalidPassphraseException;
import org.cryptomator.ui.controls.SecPasswordField;
import org.cryptomator.ui.model.Vault;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
@@ -96,80 +99,38 @@ public class ChangePasswordController extends AbstractFXMLViewController {
@FXML
private void didClickChangePasswordButton(ActionEvent event) {
throw new UnsupportedOperationException("TODO");
/*
* downloadsPageLink.setVisible(false);
* final Path masterKeyPath = vault.getPath().resolve(Vault.VAULT_MASTERKEY_FILE);
* final Path masterKeyBackupPath = vault.getPath().resolve(Vault.VAULT_MASTERKEY_BACKUP_FILE);
*
* // decrypt with old password:
* final CharSequence oldPassword = oldPasswordField.getCharacters();
* try (final InputStream masterKeyInputStream = Files.newInputStream(masterKeyPath, StandardOpenOption.READ)) {
* vault.getCryptor().decryptMasterKey(masterKeyInputStream, oldPassword);
* Files.copy(masterKeyPath, masterKeyBackupPath, StandardCopyOption.REPLACE_EXISTING);
* } catch (IOException ex) {
* messageText.setText(resourceBundle.getString("changePassword.errorMessage.decryptionFailed"));
* LOG.error("Decryption failed for technical reasons.", ex);
* newPasswordField.swipe();
* retypePasswordField.swipe();
* return;
* } catch (WrongPasswordException e) {
* messageText.setText(resourceBundle.getString("changePassword.errorMessage.wrongPassword"));
* newPasswordField.swipe();
* retypePasswordField.swipe();
* Platform.runLater(oldPasswordField::requestFocus);
* return;
* } catch (UnsupportedKeyLengthException ex) {
* messageText.setText(resourceBundle.getString("changePassword.errorMessage.unsupportedKeyLengthInstallJCE"));
* LOG.warn("Unsupported Key-Length. Please install Oracle Java Cryptography Extension (JCE).", ex);
* newPasswordField.swipe();
* retypePasswordField.swipe();
* return;
* } catch (UnsupportedVaultException e) {
* downloadsPageLink.setVisible(true);
* if (e.isVaultOlderThanSoftware()) {
* messageText.setText(resourceBundle.getString("changePassword.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " ");
* } else if (e.isSoftwareOlderThanVault()) {
* messageText.setText(resourceBundle.getString("changePassword.errorMessage.unsupportedVersion.softwareOlderThanVault") + " ");
* }
* newPasswordField.swipe();
* retypePasswordField.swipe();
* return;
* } finally {
* oldPasswordField.swipe();
* }
*
* // when we reach this line, decryption was successful.
*
* // encrypt with new password:
* final CharSequence newPassword = newPasswordField.getCharacters();
* try (final OutputStream masterKeyOutputStream = Files.newOutputStream(masterKeyPath, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.SYNC)) {
* vault.getCryptor().encryptMasterKey(masterKeyOutputStream, newPassword);
* messageText.setText(resourceBundle.getString("changePassword.infoMessage.success"));
* Platform.runLater(this::didChangePassword);
* // At this point the backup is still using the old password.
* // It will be changed as soon as the user unlocks the vault the next time.
* // This way he can still restore the old password, if he doesn't remember the new one.
* } catch (IOException ex) {
* LOG.error("Re-encryption failed for technical reasons. Restoring Backup.", ex);
* this.restoreBackupQuietly();
* } finally {
* newPasswordField.swipe();
* retypePasswordField.swipe();
* }
*/
}
private void restoreBackupQuietly() {
/*
* final Path masterKeyPath = vault.getPath().resolve(Vault.VAULT_MASTERKEY_FILE);
* final Path masterKeyBackupPath = vault.getPath().resolve(Vault.VAULT_MASTERKEY_BACKUP_FILE);
* try {
* Files.copy(masterKeyBackupPath, masterKeyPath, StandardCopyOption.REPLACE_EXISTING);
* } catch (IOException ex) {
* LOG.error("Restoring Backup failed.", ex);
* }
*/
downloadsPageLink.setVisible(false);
try {
vault.changePassphrase(oldPasswordField.getCharacters(), newPasswordField.getCharacters());
messageText.setText(resourceBundle.getString("changePassword.infoMessage.success"));
Platform.runLater(this::didChangePassword);
} catch (InvalidPassphraseException e) {
messageText.setText(resourceBundle.getString("changePassword.errorMessage.wrongPassword"));
newPasswordField.swipe();
retypePasswordField.swipe();
Platform.runLater(oldPasswordField::requestFocus);
return;
} catch (IOException ex) {
messageText.setText(resourceBundle.getString("changePassword.errorMessage.decryptionFailed"));
LOG.error("Decryption failed for technical reasons.", ex);
newPasswordField.swipe();
retypePasswordField.swipe();
return;
// } catch (UnsupportedVaultException e) {
// downloadsPageLink.setVisible(true);
// if (e.isVaultOlderThanSoftware()) {
// messageText.setText(resourceBundle.getString("changePassword.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " ");
// } else if (e.isSoftwareOlderThanVault()) {
// messageText.setText(resourceBundle.getString("changePassword.errorMessage.unsupportedVersion.softwareOlderThanVault") + " ");
// }
// newPasswordField.swipe();
// retypePasswordField.swipe();
// return;
} finally {
oldPasswordField.swipe();
newPasswordField.swipe();
retypePasswordField.swipe();
}
}
private void didChangePassword() {

View File

@@ -16,6 +16,7 @@ import java.util.Set;
import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.cryptomator.common.Optionals;
import org.cryptomator.crypto.engine.InvalidPassphraseException;
import org.cryptomator.filesystem.FileSystem;
import org.cryptomator.filesystem.crypto.CryptoFileSystemDelegate;
import org.cryptomator.filesystem.crypto.CryptoFileSystemFactory;
@@ -90,7 +91,16 @@ public class Vault implements Serializable, CryptoFileSystemDelegate {
if (fs.children().count() > 0) {
throw new FileAlreadyExistsException(null, null, "Vault location not empty.");
}
cryptoFileSystemFactory.get(fs, passphrase, this);
cryptoFileSystemFactory.initializeNew(fs, passphrase);
} catch (UncheckedIOException e) {
throw new IOException(e);
}
}
public void changePassphrase(CharSequence oldPassphrase, CharSequence newPassphrase) throws IOException, InvalidPassphraseException {
try {
FileSystem fs = NioFileSystem.rootedAt(path);
cryptoFileSystemFactory.changePassphrase(fs, oldPassphrase, newPassphrase);
} catch (UncheckedIOException e) {
throw new IOException(e);
}
@@ -99,7 +109,7 @@ public class Vault implements Serializable, CryptoFileSystemDelegate {
public synchronized void activateFrontend(CharSequence passphrase) throws FrontendCreationFailedException {
try {
FileSystem fs = NioFileSystem.rootedAt(path);
FileSystem cryptoFs = cryptoFileSystemFactory.get(fs, passphrase, this);
FileSystem cryptoFs = cryptoFileSystemFactory.unlockExisting(fs, passphrase, this);
String contextPath = StringUtils.prependIfMissing(mountName, "/");
Frontend frontend = frontendFactory.get().create(cryptoFs, contextPath);
filesystemFrontend = closer.closeLater(frontend);
@@ -165,10 +175,6 @@ public class Vault implements Serializable, CryptoFileSystemDelegate {
return StringUtils.removeEnd(path.getFileName().toString(), VAULT_FILE_EXTENSION);
}
// public Cryptor getCryptor() {
// return cryptor;
// }
public ObjectProperty<Boolean> unlockedProperty() {
return unlocked;
}