From 1ae951126a69aa629fd9f581487b0d5e4d703d8f Mon Sep 17 00:00:00 2001 From: Jan-Peter Klein Date: Fri, 21 Mar 2025 11:03:24 +0100 Subject: [PATCH] refactored key validation --- .../org/cryptomator/common/RecoverUtil.java | 56 ++++++++++++------- .../RecoveryKeyValidateController.java | 35 +++++------- 2 files changed, 49 insertions(+), 42 deletions(-) diff --git a/src/main/java/org/cryptomator/common/RecoverUtil.java b/src/main/java/org/cryptomator/common/RecoverUtil.java index b686696c2..01a5bab9d 100644 --- a/src/main/java/org/cryptomator/common/RecoverUtil.java +++ b/src/main/java/org/cryptomator/common/RecoverUtil.java @@ -16,6 +16,7 @@ import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.ResourceBundle; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Stream; import static org.cryptomator.common.Constants.DEFAULT_KEY_ID; @@ -56,19 +57,32 @@ public class RecoverUtil { public static CryptorProvider.Scheme detectCipherCombo(byte[] masterkey, Path pathToVault) { try (Stream paths = Files.walk(pathToVault.resolve(DATA_DIR_NAME))) { - return paths.filter(path -> path.toString().endsWith(".c9r")).findFirst().map(c9rFile -> determineScheme(c9rFile, masterkey)).orElseThrow(() -> new IllegalStateException("No .c9r file found.")); + Path c9rFile = paths.filter(path -> path.toString().endsWith(".c9r")) + .findFirst() + .orElseThrow(() -> new IllegalStateException("No .c9r file found. The vault might not exist or the provided masterkey does not match.")); + CryptorProvider.Scheme scheme = determineScheme(c9rFile, masterkey); + if (scheme == null) { + throw new IllegalArgumentException("Invalid masterkey: Decryption failed."); + } + return scheme; } catch (IOException e) { - throw new IllegalStateException("Failed to detect cipher combo.", e); + throw new IllegalStateException("Failed to detect cipher combo."); } } private static CryptorProvider.Scheme determineScheme(Path c9rFile, byte[] masterkey) { try { ByteBuffer header = ByteBuffer.wrap(Files.readAllBytes(c9rFile)); - return tryDecrypt(header, new Masterkey(masterkey), SIV_GCM) ? SIV_GCM : tryDecrypt(header, new Masterkey(masterkey), SIV_CTRMAC) ? SIV_CTRMAC : null; + if (tryDecrypt(header, new Masterkey(masterkey), SIV_GCM)) { + return SIV_GCM; + } + if (tryDecrypt(header, new Masterkey(masterkey), SIV_CTRMAC)) { + return SIV_CTRMAC; + } } catch (IOException e) { - return null; + throw new IllegalStateException("Failed to read .c9r file.", e); } + return null; } private static boolean tryDecrypt(ByteBuffer header, Masterkey masterkey, CryptorProvider.Scheme scheme) { @@ -129,35 +143,35 @@ public class RecoverUtil { try { return Optional.of(RecoverUtil.detectCipherCombo(masterkey.getEncoded(), vaultPath)); } catch (Exception e) { - LOG.warn("Failed to detect cipher combo", e); + LOG.info("Failed to detect cipher combo."); return Optional.empty(); } } - public static Optional validateRecoveryKeyAndGetCombo(RecoveryKeyFactory recoveryKeyFactory, Vault vault, StringProperty recoveryKey, MasterkeyFileAccess masterkeyFileAccess) { - - Path tempRecoveryPath = null; - CharSequence tmpPass = "asdasdasd"; + public static Optional validateRecoveryKeyAndGetCombo(RecoveryKeyFactory recoveryKeyFactory, Vault vault, StringProperty recoveryKey, MasterkeyFileAccess masterkeyFileAccess, AtomicBoolean illegalArgumentExceptionOccurred) { + var tmpPass = "asdasdasd"; try { - tempRecoveryPath = createRecoveryDirectory(vault.getPath()); - createNewMasterkeyFile(recoveryKeyFactory, tempRecoveryPath, recoveryKey.get(), tmpPass); - Path masterkeyFilePath = tempRecoveryPath.resolve(MASTERKEY_FILENAME); + var tempRecoveryPath = createRecoveryDirectory(vault.getPath()); + try { + createNewMasterkeyFile(recoveryKeyFactory, tempRecoveryPath, recoveryKey.get(), tmpPass); + var masterkeyFilePath = tempRecoveryPath.resolve(MASTERKEY_FILENAME); - try (Masterkey masterkey = loadMasterkey(masterkeyFileAccess, masterkeyFilePath, tmpPass)) { - return getCipherCombo(vault.getPath(), masterkey); - } - } catch (IOException | CryptoException e) { - LOG.warn("Recovery key validation failed", e); - return Optional.empty(); - } finally { - if (tempRecoveryPath != null) { + try (var masterkey = loadMasterkey(masterkeyFileAccess, masterkeyFilePath, tmpPass)) { + return getCipherCombo(vault.getPath(), masterkey); + } + } finally { deleteRecoveryDirectory(tempRecoveryPath); } + } catch (IOException | CryptoException e) { + LOG.info("Recovery key validation failed"); + } catch (IllegalArgumentException e) { + LOG.info("Recovery key has an illegal argument"); + illegalArgumentExceptionOccurred.set(true); } + return Optional.empty(); } - public static void moveRecoveredFiles(Path recoveryPath, Path vaultPath) throws IOException { Files.move(recoveryPath.resolve(MASTERKEY_FILENAME), vaultPath.resolve(MASTERKEY_FILENAME), StandardCopyOption.REPLACE_EXISTING); Files.move(recoveryPath.resolve(VAULTCONFIG_FILENAME), vaultPath.resolve(VAULTCONFIG_FILENAME)); diff --git a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyValidateController.java b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyValidateController.java index c0a898c54..5b6d58d1c 100644 --- a/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyValidateController.java +++ b/src/main/java/org/cryptomator/ui/recoverykey/RecoveryKeyValidateController.java @@ -26,6 +26,7 @@ import javafx.scene.control.TextArea; import javafx.scene.control.TextFormatter; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; +import java.util.concurrent.atomic.AtomicBoolean; public class RecoveryKeyValidateController implements FxController { @@ -133,34 +134,26 @@ public class RecoveryKeyValidateController implements FxController { } private void validateRecoveryKey() { - isWrongKey = false; - var valid = false; - switch (recoverType.get()) { + AtomicBoolean illegalArgumentExceptionOccurred = new AtomicBoolean(false); + boolean valid = switch (recoverType.get()) { case RESTORE_VAULT_CONFIG -> { - try { - var combo = RecoverUtil.validateRecoveryKeyAndGetCombo(recoveryKeyFactory, vault, recoveryKey, masterkeyFileAccess); - valid = combo.isPresent(); - combo.ifPresent(cipherCombo::set); - } catch (IllegalStateException e) { - isWrongKey = true; - } catch (IllegalArgumentException e) { - isWrongKey = false; - } + var combo = RecoverUtil.validateRecoveryKeyAndGetCombo( + recoveryKeyFactory, vault, recoveryKey, masterkeyFileAccess, illegalArgumentExceptionOccurred); + combo.ifPresent(cipherCombo::set); + yield combo.isPresent(); } - case RESTORE_MASTERKEY, RESET_PASSWORD, SHOW_KEY, CONVERT_VAULT -> { - valid = recoveryKeyFactory.validateRecoveryKey(recoveryKey.get(), unverifiedVaultConfig != null ? this::checkKeyAgainstVaultConfig : null); - } - } + case RESTORE_MASTERKEY, RESET_PASSWORD, SHOW_KEY, CONVERT_VAULT -> + recoveryKeyFactory.validateRecoveryKey(recoveryKey.get(), + unverifiedVaultConfig != null ? this::checkKeyAgainstVaultConfig : null); + }; + if (valid) { recoveryKeyState.set(RecoveryKeyState.CORRECT); - } else if (isWrongKey) { //set via side effect in checkKeyAgainstVaultConfig() - recoveryKeyState.set(RecoveryKeyState.WRONG); - } else { - recoveryKeyState.set(RecoveryKeyState.INVALID); + return; } + recoveryKeyState.set(illegalArgumentExceptionOccurred.get() ? RecoveryKeyState.INVALID : RecoveryKeyState.WRONG); } - /* Getter/Setter */ public Vault getVault() {