Skip to content

Commit b03c277

Browse files
Add bitcoin core password recovery option to UI
1 parent 994d748 commit b03c277

File tree

5 files changed

+408
-1
lines changed

5 files changed

+408
-1
lines changed
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
// The FinderOuter
2+
// Copyright (c) 2020 Coding Enthusiast
3+
// Distributed under the MIT software license, see the accompanying
4+
// file LICENCE or http://www.opensource.org/licenses/mit-license.php.
5+
6+
using FinderOuter.Backend;
7+
using FinderOuter.Models;
8+
using FinderOuter.Services;
9+
using FinderOuter.Services.SearchSpaces;
10+
using ReactiveUI;
11+
using System;
12+
using System.Collections.Generic;
13+
using System.Collections.ObjectModel;
14+
using System.Diagnostics;
15+
using System.Linq;
16+
17+
namespace FinderOuter.ViewModels
18+
{
19+
public class CorePassViewModel : OptionVmBase
20+
{
21+
// Makes designer happy!
22+
public CorePassViewModel() : this(new Settings())
23+
{
24+
}
25+
26+
public CorePassViewModel(Settings settings)
27+
{
28+
Result.Settings = settings;
29+
Service = new(Result);
30+
31+
IObservable<bool> isFindEnabled = this.WhenAnyValue(
32+
x => x.Input,
33+
x => x.Result.CurrentState,
34+
(mn, state) =>
35+
!string.IsNullOrEmpty(mn) &&
36+
state != State.Working);
37+
38+
FindCommand = ReactiveCommand.Create(Find, isFindEnabled);
39+
40+
IObservable<bool> isExampleEnable = this.WhenAnyValue(x => x.Result.CurrentState, (state) => state != State.Working);
41+
ExampleCommand = ReactiveCommand.Create(Example, isExampleEnable);
42+
43+
SetExamples(GetExampleData());
44+
45+
IObservable<bool> canAdd = this.WhenAnyValue(x => x.IsProcessed, (b) => b == true);
46+
47+
StartCommand = ReactiveCommand.Create(Start, isFindEnabled);
48+
AddCommand = ReactiveCommand.Create(Add, canAdd);
49+
AddAllCommand = ReactiveCommand.Create(AddAll, canAdd);
50+
AddLowerCommand = ReactiveCommand.Create(AddLower, canAdd);
51+
AddUpperCommand = ReactiveCommand.Create(AddUpper, canAdd);
52+
AddNumberCommand = ReactiveCommand.Create(AddNumber, canAdd);
53+
AddSymbolCommand = ReactiveCommand.Create(AddSymbol, canAdd);
54+
55+
CopyCommand = ReactiveCommand.Create(Copy, isFindEnabled);
56+
}
57+
58+
59+
public override string OptionName => "Bitcoin Core wallet pass";
60+
public override string Description => $"This option can recover bitcoin core wallet.dat encryption password." +
61+
$"{Environment.NewLine}";
62+
63+
public static string PassLenToolTip => "Number of words in the passphrase.";
64+
65+
private readonly CorePassSearchSpace searchSpace = new();
66+
67+
public CorePassService Service { get; }
68+
public IFileManager FileMan { get; set; } = new FileManager();
69+
70+
71+
private int _passLen = 1;
72+
public int PassLength
73+
{
74+
get => _passLen;
75+
set
76+
{
77+
if (value < 1)
78+
value = 1;
79+
80+
if (value != _passLen)
81+
{
82+
this.RaiseAndSetIfChanged(ref _passLen, value);
83+
isChanged = true;
84+
}
85+
}
86+
}
87+
88+
89+
private string _ores;
90+
public string OpenResult
91+
{
92+
get => _ores;
93+
set => this.RaiseAndSetIfChanged(ref _ores, value);
94+
}
95+
96+
public async void Open()
97+
{
98+
string[] res = await FileMan.OpenAsync();
99+
OpenResult = $"Number of items:{Environment.NewLine}{res.Length:n0}";
100+
searchSpace.AllWords = res;
101+
isChanged = true;
102+
}
103+
104+
private void Start()
105+
{
106+
InitSearchSpace();
107+
IsProcessed = searchSpace.Process(Input, PassLength, out string error);
108+
FinishSearchSpace(searchSpace.PasswordLength, error);
109+
}
110+
111+
private void AddToList(IEnumerable<string> items)
112+
{
113+
foreach (string item in items)
114+
{
115+
if (!CurrentItems.Contains(item))
116+
{
117+
CurrentItems.Add(item);
118+
}
119+
}
120+
}
121+
122+
public IReactiveCommand AddAllCommand { get; }
123+
private void AddAll()
124+
{
125+
if (searchSpace.AllWords is not null && searchSpace.AllWords.Length != 0)
126+
{
127+
AddToList(searchSpace.AllWords);
128+
}
129+
else
130+
{
131+
Result.AddMessage("No password list is defined yet. Use the \"Open\" button to set it.");
132+
}
133+
}
134+
135+
public IReactiveCommand AddCommand { get; }
136+
private void Add()
137+
{
138+
// TODO: should we add a warning about extra spaces here?
139+
if (!string.IsNullOrEmpty(ToAdd))
140+
{
141+
if (!CurrentItems.Contains(ToAdd))
142+
{
143+
CurrentItems.Add(ToAdd);
144+
ToAdd = string.Empty;
145+
}
146+
else
147+
{
148+
Result.AddMessage($"\"{ToAdd}\" is already in the list.");
149+
}
150+
}
151+
}
152+
153+
public IReactiveCommand AddLowerCommand { get; }
154+
private void AddLower()
155+
{
156+
AddToList(ConstantsFO.LowerCase.ToCharArray().Select(c => c.ToString()));
157+
}
158+
159+
public IReactiveCommand AddUpperCommand { get; }
160+
private void AddUpper()
161+
{
162+
AddToList(ConstantsFO.UpperCase.ToCharArray().Select(c => c.ToString()));
163+
}
164+
165+
public IReactiveCommand AddNumberCommand { get; }
166+
private void AddNumber()
167+
{
168+
AddToList(ConstantsFO.Numbers.ToCharArray().Select(c => c.ToString()));
169+
}
170+
171+
public IReactiveCommand AddSymbolCommand { get; }
172+
private void AddSymbol()
173+
{
174+
AddToList(ConstantsFO.AllSymbols.ToCharArray().Select(c => c.ToString()));
175+
}
176+
177+
178+
public override async void Find()
179+
{
180+
if (isChanged && IsProcessed)
181+
{
182+
MessageBoxResult res = await WinMan.ShowMessageBox(MessageBoxType.YesNo, ConstantsFO.ChangedMessage);
183+
if (res == MessageBoxResult.Yes)
184+
{
185+
IsProcessed = false;
186+
}
187+
else
188+
{
189+
ResetSearchSpace();
190+
return;
191+
}
192+
}
193+
194+
if (!IsProcessed)
195+
{
196+
if (searchSpace.AllWords is null || searchSpace.AllWords.Length == 0)
197+
{
198+
Result.AddMessage("No password list is defined yet. Use the \"Open\" button to set it.");
199+
return;
200+
}
201+
Start();
202+
foreach (ObservableCollection<string> item in allItems)
203+
{
204+
foreach (string word in searchSpace.AllWords)
205+
{
206+
item.Add(word);
207+
}
208+
}
209+
}
210+
211+
if (IsProcessed)
212+
{
213+
if (searchSpace.SetValues(allItems.Select(x => x.ToArray()).ToArray(), out string error))
214+
{
215+
Service.Find(searchSpace);
216+
ResetSearchSpace();
217+
}
218+
else
219+
{
220+
Result.AddMessage(error);
221+
}
222+
}
223+
}
224+
225+
226+
public void Example()
227+
{
228+
object[] ex = GetNextExample();
229+
230+
Input = (string)ex[0];
231+
PassLength = (int)ex[1];
232+
233+
Start();
234+
235+
Debug.Assert(allItems.Length == PassLength);
236+
string[][] items = (string[][])ex[2];
237+
for (int i = 0; i < items.Length; i++)
238+
{
239+
foreach (var item in items[i])
240+
{
241+
allItems[i].Add(item);
242+
}
243+
}
244+
245+
Result.Message = $"Example {exampleIndex} of {totalExampleCount}. Source: {(string)ex[3]}";
246+
}
247+
248+
private ExampleData GetExampleData()
249+
{
250+
return new ExampleData<string, int, string[][], string>()
251+
{
252+
{
253+
"43000130ac71182a748152bb788fb9deb11f2f5a55f5e848d66586747cc000826d4c0c350032153d50cbf924a2ac1dc5f6279436089ca0271b64c0e66f00000000c6fe040000",
254+
2, // Pass length
255+
new string[2][]
256+
{
257+
new string[5] { "master", "M@ster", "Master", "MaStEr", "m@ster" },
258+
new string[4] { "exploder", "ExPlOreR", "Expl0der", "Exploder" },
259+
},
260+
$"Taken from https://bitcointalk.org/index.php?topic=5511431.msg64607294#msg64607294{Environment.NewLine}" +
261+
$"This is a random encryption key with a 2 word passphrase (MasterExploder). " +
262+
$"You can see how a possible list of words for each word is set." +
263+
$"{Environment.NewLine}" +
264+
$"Estimated time: ~1 sec"
265+
},
266+
};
267+
}
268+
}
269+
}

Src/FinderOuter/ViewModels/MainWindowViewModel.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public MainWindowViewModel()
2424
new MissingBase58ViewModel(Settings),
2525
new MissingMiniPrivateKeyViewModel(Settings),
2626
new MissingBip38PassViewModel(Settings),
27+
new CorePassViewModel(Settings),
2728
new MissingMnemonicViewModel(Settings),
2829
new MissingMnemonicPassViewModel(Settings),
2930
new MissingBip32PathViewModel(),
@@ -105,7 +106,10 @@ public IStorageProvider StorageProvider
105106
if (item is MissingBip38PassViewModel b38)
106107
{
107108
b38.FileMan.StorageProvider = value;
108-
return;
109+
}
110+
else if (item is CorePassViewModel core)
111+
{
112+
core.FileMan.StorageProvider = value;
109113
}
110114
}
111115
}

0 commit comments

Comments
 (0)