diff --git a/pom.xml b/pom.xml
index 5e5387e1..d3f6a1aa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
4.0.0
org.cryptomator
cryptofs
- 2.1.1
+ 2.2.0
Cryptomator Crypto Filesystem
This library provides the Java filesystem provider used by Cryptomator.
https://github.com/cryptomator/cryptofs
diff --git a/src/main/java/org/cryptomator/cryptofs/common/MasterkeyBackupHelper.java b/src/main/java/org/cryptomator/cryptofs/common/BackupHelper.java
similarity index 50%
rename from src/main/java/org/cryptomator/cryptofs/common/MasterkeyBackupHelper.java
rename to src/main/java/org/cryptomator/cryptofs/common/BackupHelper.java
index 97a1097a..f5d2406e 100644
--- a/src/main/java/org/cryptomator/cryptofs/common/MasterkeyBackupHelper.java
+++ b/src/main/java/org/cryptomator/cryptofs/common/BackupHelper.java
@@ -17,11 +17,12 @@
import java.security.NoSuchAlgorithmException;
/**
- * Utility class for generating a suffix for the backup file to make it unique to its original master key file.
+ * Utility class for generating a suffix for the backup file to make it unique to its original file.
*/
-public final class MasterkeyBackupHelper {
+public final class BackupHelper {
- private static final Logger LOG = LoggerFactory.getLogger(MasterkeyBackupHelper.class);
+ private final static long NO_MISMATCH = -1L;
+ private static final Logger LOG = LoggerFactory.getLogger(BackupHelper.class);
/**
* Computes the SHA-256 digest of the given byte array and returns a file suffix containing the first 4 bytes in hex string format.
@@ -40,42 +41,36 @@ public static String generateFileIdSuffix(byte[] fileBytes) {
}
/**
- * Do a best-effort attempt to backup the masterkey at the given path.
+ * Do a best-effort attempt to back up the file at the given path.
* Fails silently if a _valid_ backup already exists and fails with a log entry, if any IO error occurs while creating or reading the backup file.
*
- * @param masterKeyPath The masterkey file to backup
- * @throws IOException If the masterkey cannot be read.
+ * @param path The file to back up
+ * @throws IOException If the path cannot be read.
*/
- public static Path attemptMasterKeyBackup(Path masterKeyPath) throws IOException {
- byte[] keyFileContents = Files.readAllBytes(masterKeyPath);
- String backupFileName = masterKeyPath.getFileName().toString() + generateFileIdSuffix(keyFileContents) + Constants.MASTERKEY_BACKUP_SUFFIX;
- Path backupFilePath = masterKeyPath.resolveSibling(backupFileName);
+ public static Path attemptBackup(Path path) throws IOException {
+ byte[] fileContents = Files.readAllBytes(path);
+ final String fileToBackup = path.getFileName().toString();
+ String backupFileName = fileToBackup + generateFileIdSuffix(fileContents) + Constants.BACKUP_SUFFIX;
+ Path backupFilePath = path.resolveSibling(backupFileName);
try (WritableByteChannel ch = Files.newByteChannel(backupFilePath, StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE)) {
- ch.write(ByteBuffer.wrap(keyFileContents));
+ ch.write(ByteBuffer.wrap(fileContents));
} catch (AccessDeniedException | FileAlreadyExistsException e) {
- assertExistingBackupMatchesContent(backupFilePath, ByteBuffer.wrap(keyFileContents));
+ assertSameContent(backupFilePath, path);
} catch (IOException e) {
- LOG.warn("Failed to backup valid masterkey file.");
+ LOG.warn("Failed to backup valid {} file.", fileToBackup);
}
return backupFilePath;
}
- private static void assertExistingBackupMatchesContent(Path backupFilePath, ByteBuffer expectedContent) {
- if (Files.exists(backupFilePath)) {
- // TODO replace by Files.mismatch() when using JDK > 12
- ByteBuffer buf = ByteBuffer.allocateDirect(expectedContent.remaining() + 1);
- try (ReadableByteChannel ch = Files.newByteChannel(backupFilePath, StandardOpenOption.READ)) {
- ch.read(buf);
- buf.flip();
- if (buf.compareTo(expectedContent) != 0) {
- LOG.warn("Corrupt masterkey backup {}. Please replace it manually or unlock the vault on a writable storage device.", backupFilePath);
- } else {
- LOG.debug("Verified backup file: {}", backupFilePath);
- }
- } catch (IOException e) {
- LOG.warn("Failed to compare valid masterkey with backup file.", e);
+ private static void assertSameContent(final Path backupFile, final Path originalFile) {
+ try {
+ if (Files.mismatch(backupFile, originalFile) == NO_MISMATCH) {
+ LOG.debug("Verified backup file: {}", backupFile);
+ } else {
+ LOG.warn("Corrupt {} backup for: {}. Please replace it manually or unlock the vault on a writable storage device.", backupFile.getFileName(), backupFile);
}
+ } catch (IOException e) {
+ LOG.warn("Failed to compare valid %s with backup file.".formatted(backupFile), e);
}
}
-
}
diff --git a/src/main/java/org/cryptomator/cryptofs/common/Constants.java b/src/main/java/org/cryptomator/cryptofs/common/Constants.java
index 2a02bfe6..dd95f02c 100644
--- a/src/main/java/org/cryptomator/cryptofs/common/Constants.java
+++ b/src/main/java/org/cryptomator/cryptofs/common/Constants.java
@@ -14,7 +14,7 @@ private Constants() {
}
public static final int VAULT_VERSION = 8;
- public static final String MASTERKEY_BACKUP_SUFFIX = ".bkup";
+ public static final String BACKUP_SUFFIX = ".bkup";
public static final String DATA_DIR_NAME = "d";
public static final String ROOT_DIR_ID = "";
public static final String RECOVERY_DIR_ID = "recovery";
diff --git a/src/main/java/org/cryptomator/cryptofs/migration/v6/Version6Migrator.java b/src/main/java/org/cryptomator/cryptofs/migration/v6/Version6Migrator.java
index e07e6bc8..0805a325 100644
--- a/src/main/java/org/cryptomator/cryptofs/migration/v6/Version6Migrator.java
+++ b/src/main/java/org/cryptomator/cryptofs/migration/v6/Version6Migrator.java
@@ -5,7 +5,7 @@
*******************************************************************************/
package org.cryptomator.cryptofs.migration.v6;
-import org.cryptomator.cryptofs.common.MasterkeyBackupHelper;
+import org.cryptomator.cryptofs.common.BackupHelper;
import org.cryptomator.cryptofs.migration.api.MigrationContinuationListener;
import org.cryptomator.cryptofs.migration.api.MigrationProgressListener;
import org.cryptomator.cryptofs.migration.api.Migrator;
@@ -48,11 +48,11 @@ public void migrate(Path vaultRoot, String vaultConfigFilename, String masterkey
MasterkeyFileAccess masterkeyFileAccess = new MasterkeyFileAccess(new byte[0], csprng);
try (Masterkey masterkey = masterkeyFileAccess.load(masterkeyFile, passphrase)) {
// create backup, as soon as we know the password was correct:
- Path masterkeyBackupFile = MasterkeyBackupHelper.attemptMasterKeyBackup(masterkeyFile);
+ Path masterkeyBackupFile = BackupHelper.attemptBackup(masterkeyFile);
LOG.info("Backed up masterkey from {} to {}.", masterkeyFile.getFileName(), masterkeyBackupFile.getFileName());
progressListener.update(MigrationProgressListener.ProgressState.FINALIZING, 0.0);
-
+
// rewrite masterkey file with normalized passphrase:
masterkeyFileAccess.persist(masterkey, masterkeyFile, Normalizer.normalize(passphrase, Form.NFC), 6);
LOG.info("Updated masterkey.");
diff --git a/src/main/java/org/cryptomator/cryptofs/migration/v7/Version7Migrator.java b/src/main/java/org/cryptomator/cryptofs/migration/v7/Version7Migrator.java
index e61c3cab..14e25f43 100644
--- a/src/main/java/org/cryptomator/cryptofs/migration/v7/Version7Migrator.java
+++ b/src/main/java/org/cryptomator/cryptofs/migration/v7/Version7Migrator.java
@@ -8,7 +8,8 @@
import org.cryptomator.cryptofs.FileNameTooLongException;
import org.cryptomator.cryptofs.common.DeletingFileVisitor;
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
-import org.cryptomator.cryptofs.common.MasterkeyBackupHelper;
+import org.cryptomator.cryptofs.common.BackupHelper;
+import org.cryptomator.cryptofs.migration.Migrators;
import org.cryptomator.cryptofs.migration.api.MigrationContinuationListener;
import org.cryptomator.cryptofs.migration.api.MigrationContinuationListener.ContinuationEvent;
import org.cryptomator.cryptofs.migration.api.MigrationContinuationListener.ContinuationResult;
@@ -58,11 +59,11 @@ public Version7Migrator(SecureRandom csprng) {
public void migrate(Path vaultRoot, String vaultConfigFilename, String masterkeyFilename, CharSequence passphrase, MigrationProgressListener progressListener, MigrationContinuationListener continuationListener) throws CryptoException, IOException {
LOG.info("Upgrading {} from version 6 to version 7.", vaultRoot);
progressListener.update(MigrationProgressListener.ProgressState.INITIALIZING, 0.0);
- Path masterkeyFile = vaultRoot.resolve(masterkeyFilename);
+ final Path masterkeyFile = vaultRoot.resolve(masterkeyFilename);
MasterkeyFileAccess masterkeyFileAccess = new MasterkeyFileAccess(new byte[0], csprng);
try (Masterkey masterkey = masterkeyFileAccess.load(masterkeyFile, passphrase)) {
// create backup, as soon as we know the password was correct:
- Path masterkeyBackupFile = MasterkeyBackupHelper.attemptMasterKeyBackup(masterkeyFile);
+ Path masterkeyBackupFile = BackupHelper.attemptBackup(masterkeyFile);
LOG.info("Backed up masterkey from {} to {}.", masterkeyFile.getFileName(), masterkeyBackupFile.getFileName());
// check file system capabilities:
@@ -76,14 +77,12 @@ public void migrate(Path vaultRoot, String vaultConfigFilename, String masterkey
LOG.warn("Underlying file system only supports names with up to {} chars (required: 220). Asking for user feedback...", filenameLengthLimit);
ContinuationResult result = continuationListener.continueMigrationOnEvent(ContinuationEvent.REQUIRES_FULL_VAULT_DIR_SCAN);
switch (result) {
- case PROCEED:
- preMigrationVisitor = new PreMigrationVisitor(vaultRoot, true);
- break;
- case CANCEL:
+ case PROCEED -> preMigrationVisitor = new PreMigrationVisitor(vaultRoot, true);
+ case CANCEL -> {
LOG.info("Migration canceled by user.");
return;
- default:
- throw new IllegalStateException("Unexpected result " + result);
+ }
+ default -> throw new IllegalStateException("Unexpected result " + result);
}
}
diff --git a/src/main/java/org/cryptomator/cryptofs/migration/v8/Version8Migrator.java b/src/main/java/org/cryptomator/cryptofs/migration/v8/Version8Migrator.java
index afa56b10..ef3cfcff 100644
--- a/src/main/java/org/cryptomator/cryptofs/migration/v8/Version8Migrator.java
+++ b/src/main/java/org/cryptomator/cryptofs/migration/v8/Version8Migrator.java
@@ -7,7 +7,7 @@
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
-import org.cryptomator.cryptofs.common.MasterkeyBackupHelper;
+import org.cryptomator.cryptofs.common.BackupHelper;
import org.cryptomator.cryptofs.migration.api.MigrationContinuationListener;
import org.cryptomator.cryptofs.migration.api.MigrationProgressListener;
import org.cryptomator.cryptofs.migration.api.Migrator;
@@ -56,7 +56,7 @@ public void migrate(Path vaultRoot, String vaultConfigFilename, String masterkey
MasterkeyFileAccess masterkeyFileAccess = new MasterkeyFileAccess(new byte[0], csprng);
try (Masterkey masterkey = masterkeyFileAccess.load(masterkeyFile, passphrase)) {
// create backup, as soon as we know the password was correct:
- Path masterkeyBackupFile = MasterkeyBackupHelper.attemptMasterKeyBackup(masterkeyFile);
+ Path masterkeyBackupFile = BackupHelper.attemptBackup(masterkeyFile);
LOG.info("Backed up masterkey from {} to {}.", masterkeyFile.getFileName(), masterkeyBackupFile.getFileName());
// create vaultconfig.cryptomator
diff --git a/src/test/java/org/cryptomator/cryptofs/common/MasterkeyBackupHelperTest.java b/src/test/java/org/cryptomator/cryptofs/common/BackupHelperTest.java
similarity index 81%
rename from src/test/java/org/cryptomator/cryptofs/common/MasterkeyBackupHelperTest.java
rename to src/test/java/org/cryptomator/cryptofs/common/BackupHelperTest.java
index 20467f00..119c1a97 100644
--- a/src/test/java/org/cryptomator/cryptofs/common/MasterkeyBackupHelperTest.java
+++ b/src/test/java/org/cryptomator/cryptofs/common/BackupHelperTest.java
@@ -15,7 +15,7 @@
import java.util.Random;
import java.util.stream.Stream;
-public class MasterkeyBackupHelperTest {
+public class BackupHelperTest {
@EnabledOnOs({OS.LINUX, OS.MAC})
@ParameterizedTest
@@ -24,11 +24,11 @@ public void testBackupFilePosix(byte[] contents, @TempDir Path tmp) throws IOExc
Path originalFile = tmp.resolve("original");
Files.write(originalFile, contents);
- Path backupFile = MasterkeyBackupHelper.attemptMasterKeyBackup(originalFile);
+ Path backupFile = BackupHelper.attemptBackup(originalFile);
Assertions.assertArrayEquals(contents, Files.readAllBytes(backupFile));
Files.setPosixFilePermissions(backupFile, PosixFilePermissions.fromString("r--r--r--"));
- Path backupFile2 = MasterkeyBackupHelper.attemptMasterKeyBackup(originalFile);
+ Path backupFile2 = BackupHelper.attemptBackup(originalFile);
Assertions.assertEquals(backupFile, backupFile2);
}
@@ -39,16 +39,16 @@ public void testBackupFileWin(byte[] contents, @TempDir Path tmp) throws IOExcep
Path originalFile = tmp.resolve("original");
Files.write(originalFile, contents);
- Path backupFile = MasterkeyBackupHelper.attemptMasterKeyBackup(originalFile);
+ Path backupFile = BackupHelper.attemptBackup(originalFile);
Assertions.assertArrayEquals(contents, Files.readAllBytes(backupFile));
Files.getFileAttributeView(backupFile, DosFileAttributeView.class).setReadOnly(true);
- Path backupFile2 = MasterkeyBackupHelper.attemptMasterKeyBackup(originalFile);
+ Path backupFile2 = BackupHelper.attemptBackup(originalFile);
Assertions.assertEquals(backupFile, backupFile2);
}
public static Stream createRandomBytes() {
- Random rnd = new Random(42l);
+ Random rnd = new Random(42L);
return Stream.generate(() -> {
byte[] bytes = new byte[100];
rnd.nextBytes(bytes);
diff --git a/src/test/java/org/cryptomator/cryptofs/migration/v6/Version6MigratorTest.java b/src/test/java/org/cryptomator/cryptofs/migration/v6/Version6MigratorTest.java
index b099554e..0fb3daf3 100644
--- a/src/test/java/org/cryptomator/cryptofs/migration/v6/Version6MigratorTest.java
+++ b/src/test/java/org/cryptomator/cryptofs/migration/v6/Version6MigratorTest.java
@@ -3,7 +3,7 @@
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import org.cryptomator.cryptofs.common.Constants;
-import org.cryptomator.cryptofs.common.MasterkeyBackupHelper;
+import org.cryptomator.cryptofs.common.BackupHelper;
import org.cryptomator.cryptofs.migration.api.Migrator;
import org.cryptomator.cryptofs.mocks.NullSecureRandom;
import org.cryptomator.cryptolib.api.CryptoException;
@@ -58,7 +58,7 @@ public void testMigrate() throws IOException, CryptoException {
byte[] beforeMigration = Files.readAllBytes(masterkeyFile);
Files.write(masterkeyFile, beforeMigration);
- Path masterkeyBackupFile = pathToVault.resolve("masterkey.cryptomator" + MasterkeyBackupHelper.generateFileIdSuffix(beforeMigration) + Constants.MASTERKEY_BACKUP_SUFFIX);
+ Path masterkeyBackupFile = pathToVault.resolve("masterkey.cryptomator" + BackupHelper.generateFileIdSuffix(beforeMigration) + Constants.BACKUP_SUFFIX);
Migrator migrator = new Version6Migrator(csprng);
migrator.migrate(pathToVault, null, "masterkey.cryptomator", oldPassword);