Skip to content

Commit 38d544e

Browse files
Add a new search space class for bitcoin core wallet.dat passwords
1 parent 968c7af commit 38d544e

File tree

1 file changed

+223
-0
lines changed

1 file changed

+223
-0
lines changed
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
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 Autarkysoft.Bitcoin;
7+
using Autarkysoft.Bitcoin.Encoders;
8+
using FinderOuter.Backend.Hashing;
9+
using System;
10+
using System.Diagnostics;
11+
using System.Linq;
12+
using System.Text;
13+
14+
namespace FinderOuter.Services.SearchSpaces
15+
{
16+
public class CorePassSearchSpace : SearchSpaceBase
17+
{
18+
/// <summary>
19+
/// Number of chars/words in the password
20+
/// </summary>
21+
public int PasswordLength { get; private set; }
22+
/// <summary>
23+
/// Maximum possible password size in bytes (will be padded to be divisible by 4)
24+
/// </summary>
25+
public int MaxPasswordSize { get; private set; }
26+
public byte[] Salt { get; private set; }
27+
public byte[] XOR { get; private set; }
28+
public int Iteration { get; private set; }
29+
public byte[] AllValues { get; private set; }
30+
public int[] PermutationLengths { get; private set; }
31+
public int[] PermutationSizes { get; private set; }
32+
33+
34+
public bool Process(string hex, int passLength, out string error)
35+
{
36+
if (string.IsNullOrWhiteSpace(hex))
37+
{
38+
error = "Input hex can not be null or empty.";
39+
return false;
40+
}
41+
if (!InputService.CheckChars(hex.ToLower(), Base16.CharSet, null, out error))
42+
{
43+
return false;
44+
}
45+
if (!Base16.TryDecode(hex, out byte[] result))
46+
{
47+
// Can happen if the length is not divisible by 2
48+
error = "Invalid hex string.";
49+
return false;
50+
}
51+
if (result.Length < 70)
52+
{
53+
error = $"Input hex was expected to be 70 bytes but it is {result.Length} bytes.";
54+
return false;
55+
}
56+
57+
// Start reading the stream
58+
FastStreamReader stream = new(result);
59+
if (!stream.FindAndSkip([0x43, 0x00, 0x01, 0x30]))
60+
{
61+
error = "Could not find 0x43000130 in the given hex.";
62+
return false;
63+
}
64+
stream.Skip(4);
65+
66+
if (!stream.TryReadByteArray(48, out byte[] encKey))
67+
{
68+
error = "The 48-byte encrypted key was not found in the input hex located after 0x43000130.";
69+
return false;
70+
}
71+
72+
if (!stream.TryReadByte(out byte saltLen))
73+
{
74+
error = $"{Errors.EndOfStream.Convert()} (1 byte salt length was not found).";
75+
return false;
76+
}
77+
78+
if (saltLen != 8)
79+
{
80+
error = $"Salt lengths other than 8 (input indicates {saltLen}) are not supported.";
81+
return false;
82+
}
83+
84+
if (!stream.TryReadByteArray(8, out byte[] salt))
85+
{
86+
error = $"{Errors.EndOfStream.Convert()} (8 byte salt was not found).";
87+
return false;
88+
}
89+
90+
if (!stream.TryReadInt32(out int derivationMethod))
91+
{
92+
error = $"{Errors.EndOfStream.Convert()} (4 byte derivation method was not found).";
93+
return false;
94+
}
95+
96+
if (derivationMethod != 0)
97+
{
98+
error = $"Only the derivation method 0 is supported.{Environment.NewLine}" +
99+
$"The input's derivation method in the given input is {derivationMethod} which means either " +
100+
$"the input is broken or a new algorithm was used that is not supported by FinderOuter.";
101+
return false;
102+
}
103+
104+
if (!stream.TryReadInt32(out int iteration))
105+
{
106+
error = $"{Errors.EndOfStream.Convert()} (4 byte iteration was not found).";
107+
return false;
108+
}
109+
110+
// TODO: to make writing our future loops simpler we assume iternation count is not bigger than what
111+
// fits inside an Int32. Bitcoin core's source code has to be checked to make sure how it treats
112+
// this number...
113+
if (iteration < 0)
114+
{
115+
error = $"Your iteration count is huge.{Environment.NewLine}" +
116+
$"Report this on GitHub if you want it changed: " +
117+
$"Bitcoin core iteration count =0x{iteration.ToByteArray(false).ToBase16()} was rejected.";
118+
return false;
119+
}
120+
121+
// TODO: This length may actually be a CompactInt (bitcoin core source code needs to be checked).
122+
// However, since we only support 0 it doesn't matter.
123+
if (!stream.TryReadByte(out byte extraLen))
124+
{
125+
error = $"{Errors.EndOfStream.Convert()} (1 byte extra parameter length was not found).";
126+
return false;
127+
}
128+
129+
if (extraLen != 0)
130+
{
131+
error = $"Only the extra parameter length 0 is supported.{Environment.NewLine}" +
132+
$"The extra parameter length in the given input is {derivationMethod} which means either " +
133+
$"the input is broken or a new algorithm was used that is not supported by FinderOuter.";
134+
return false;
135+
}
136+
137+
#if DEBUG
138+
AssertArray(salt, saltLen);
139+
AssertArray(encKey, 48);
140+
#endif
141+
PasswordLength = passLength;
142+
Iteration = iteration;
143+
Salt = salt;
144+
XOR = encKey.SubArray(16, 16);
145+
146+
error = string.Empty;
147+
return true;
148+
}
149+
150+
#if DEBUG
151+
private static void AssertArray(byte[] data, int size)
152+
{
153+
Debug.Assert(data != null);
154+
Debug.Assert(data.Length == size);
155+
}
156+
#endif
157+
158+
159+
public bool SetValues(string[][] result, out string error)
160+
{
161+
if (result.Length != PasswordLength || result.Any(x => x.Length < 1))
162+
{
163+
error = "Invalid array length.";
164+
return false;
165+
}
166+
167+
int totalLen = 0;
168+
for (int i = 0; i < result.Length; i++)
169+
{
170+
if (result[i].Length < 1)
171+
{
172+
error = "At least 2 possible items is needed.";
173+
return false;
174+
}
175+
totalLen += result[i].Length;
176+
}
177+
178+
PermutationLengths = new int[totalLen];
179+
PermutationCounts = new int[PasswordLength];
180+
PermutationSizes = new int[PasswordLength];
181+
182+
FastStream stream = new();
183+
int index1 = 0;
184+
int index2 = 0;
185+
MaxPasswordSize = 0;
186+
foreach (string[] item in result)
187+
{
188+
int max = 0;
189+
foreach (string s in item)
190+
{
191+
byte[] t = Encoding.UTF8.GetBytes(s);
192+
stream.Write(t);
193+
PermutationLengths[index1++] = t.Length;
194+
PermutationSizes[index2] += t.Length;
195+
196+
if (s.Length > max)
197+
{
198+
max = s.Length;
199+
}
200+
}
201+
Debug.Assert(max > 0);
202+
MaxPasswordSize += max;
203+
204+
PermutationCounts[index2++] = item.Length;
205+
}
206+
207+
while (MaxPasswordSize % 4 != 0)
208+
{
209+
MaxPasswordSize++;
210+
}
211+
if (MaxPasswordSize > Sha512Fo.BlockByteSize)
212+
{
213+
error = "Password is too long (bigger than SHA512 block size).";
214+
return false;
215+
}
216+
217+
AllValues = stream.ToByteArray();
218+
219+
error = string.Empty;
220+
return true;
221+
}
222+
}
223+
}

0 commit comments

Comments
 (0)