From ccbeb94aeefd3ed487e233ca31132d9746b7a4cc Mon Sep 17 00:00:00 2001 From: Stone_Red <56473591+Stone-Red-Code@users.noreply.github.com> Date: Sun, 19 Jul 2026 19:24:01 +0200 Subject: [PATCH] Add retry prompt for wrong passphrase when unlocking encrypted channels --- src/Decho/Services/ChannelService.cs | 15 ++--- src/Decho/ViewModels/MainWindowViewModel.cs | 66 ++++++++++++++------- 2 files changed, 47 insertions(+), 34 deletions(-) diff --git a/src/Decho/Services/ChannelService.cs b/src/Decho/Services/ChannelService.cs index 14f9d7e..e7f3b2e 100644 --- a/src/Decho/Services/ChannelService.cs +++ b/src/Decho/Services/ChannelService.cs @@ -37,18 +37,11 @@ internal sealed class ChannelService : IChannelService if (isEncrypted && !HasChannelKey(serverUrl, channelName) && password is not null) { - try + ChannelJoinResult unlockResult = await UnlockRoomKeyAsync( + serverUrl, channelName, password, crypto!.EncryptionSalt!, result.WrappedRoomKey ?? ""); + if (unlockResult.History.Count > 0) { - ChannelJoinResult unlockResult = await UnlockRoomKeyAsync( - serverUrl, channelName, password, crypto!.EncryptionSalt!, result.WrappedRoomKey ?? ""); - if (unlockResult.History.Count > 0) - { - result = unlockResult; - } - } - catch (Exception ex) - { - Debug.WriteLine($"Unlock failed: {ex.Message}"); + result = unlockResult; } } diff --git a/src/Decho/ViewModels/MainWindowViewModel.cs b/src/Decho/ViewModels/MainWindowViewModel.cs index 00754ea..7b1f0e8 100644 --- a/src/Decho/ViewModels/MainWindowViewModel.cs +++ b/src/Decho/ViewModels/MainWindowViewModel.cs @@ -235,32 +235,46 @@ public sealed class MainWindowViewModel : ViewModelBase return; } - ChannelCryptoDto? crypto = await ChannelService.GetChannelCryptoAsync(serverUrl, channelName); - bool isEncrypted = crypto is not null && crypto.IsEncrypted; - - if (isEncrypted && !ChannelService.HasChannelKey(serverUrl, channelName) && password is null) + string? promptMessage = null; + while (true) { - password = await ShowPromptWindowAsync("Unlock Channel", "Enter the passphrase to unlock messages:", "Unlock"); - if (string.IsNullOrEmpty(password)) + try { + ChannelCryptoDto? crypto = await ChannelService.GetChannelCryptoAsync(serverUrl, channelName); + bool isEncrypted = crypto is not null && crypto.IsEncrypted; + + if (isEncrypted && !ChannelService.HasChannelKey(serverUrl, channelName) && password is null) + { + password = await ShowPromptWindowAsync("Unlock Channel", promptMessage ?? "Enter the passphrase to unlock messages:", "Unlock"); + if (string.IsNullOrEmpty(password)) + { + return; + } + } + + ChannelJoinResult result = await ChannelService.JoinWithCryptoAsync(serverUrl, channelName, password); + EnsureChannelInList(serverUrl, channelName); + + ChannelModel? channelModel = FindChannel(serverUrl, channelName); + if (channelModel is not null) + { + ChannelViewModel? channelVm = Sidebar.GetServer(serverUrl)?.Channels + .FirstOrDefault(c => c.Name == channelName); + if (channelVm is not null) + { + foreach (MessageModel msg in result.History) + { + channelVm.AddMessage(msg); + } + } + } + return; } - } - - ChannelJoinResult result = await ChannelService.JoinWithCryptoAsync(serverUrl, channelName, password); - EnsureChannelInList(serverUrl, channelName); - - ChannelModel? channelModel = FindChannel(serverUrl, channelName); - if (channelModel is not null) - { - ChannelViewModel? channelVm = Sidebar.GetServer(serverUrl)?.Channels - .FirstOrDefault(c => c.Name == channelName); - if (channelVm is not null) + catch (InvalidOperationException ex) when (ex.Message == "Wrong passphrase") { - foreach (MessageModel msg in result.History) - { - channelVm.AddMessage(msg); - } + password = null; + promptMessage = "Wrong passphrase - try again."; } } }; @@ -1050,6 +1064,7 @@ public sealed class MainWindowViewModel : ViewModelBase _ = Task.Run(async () => { string? password = null; + string? promptMessage = null; while (true) { try @@ -1057,9 +1072,9 @@ public sealed class MainWindowViewModel : ViewModelBase ChannelCryptoDto? crypto = await ChannelService.GetChannelCryptoAsync(serverUrl, channel.Name); bool isEncrypted = crypto is not null && crypto.IsEncrypted; - if (isEncrypted && !ChannelService.HasChannelKey(serverUrl, channel.Name) && password is null) + if (isEncrypted && !ChannelService.HasChannelKey(serverUrl, channel.Name)) { - string? passphrase = await ShowPromptWindowAsync("Unlock Channel", "Enter the passphrase to unlock messages:", "Unlock"); + string? passphrase = await ShowPromptWindowAsync("Unlock Channel", promptMessage ?? "Enter the passphrase to unlock messages:", "Unlock"); if (string.IsNullOrEmpty(passphrase)) { Avalonia.Threading.Dispatcher.UIThread.Post(() => @@ -1121,6 +1136,11 @@ public sealed class MainWindowViewModel : ViewModelBase password = pwd; } + catch (InvalidOperationException ex) when (ex.Message == "Wrong passphrase") + { + password = null; + promptMessage = "Wrong passphrase - try again."; + } catch { Avalonia.Threading.Dispatcher.UIThread.Post(() =>