Skip to content

Commit

Permalink
Add a new special case for PrivateKeys missing 3 characters with unkn…
Browse files Browse the repository at this point in the history
…own positions
  • Loading branch information
Coding-Enthusiast committed Feb 18, 2020
1 parent 842875e commit 73e8596
Show file tree
Hide file tree
Showing 3 changed files with 312 additions and 4 deletions.
285 changes: 285 additions & 0 deletions Src/FinderOuter/Services/Base58Sevice.cs
Expand Up @@ -12,6 +12,7 @@
using System.Diagnostics;
using System.Linq;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;

namespace FinderOuter.Services
Expand Down Expand Up @@ -334,6 +335,258 @@ private unsafe bool LoopUncomp()
return success;
}

private unsafe bool SpecialLoopComp(string key)
{
byte[] padded;
int uLen;

// Maximum result (58^52) is 39 bytes = 39/4 = 10 uint
uLen = 10;
uint[] powers58 = new uint[Constants.CompPrivKeyLen * uLen];
padded = new byte[4 * uLen];

for (int i = 0, j = 0; i < Constants.CompPrivKeyLen; i++)
{
BigInteger val = BigInteger.Pow(58, i);
byte[] temp = val.ToByteArray(true, false);

Array.Clear(padded, 0, padded.Length);
Buffer.BlockCopy(temp, 0, padded, 0, temp.Length);

for (int k = 0; k < padded.Length; j++, k += 4)
{
powers58[j] = (uint)(padded[k] << 0 | padded[k + 1] << 8 | padded[k + 2] << 16 | padded[k + 3] << 24);
}
}

int[] values = new int[key.Length];
for (int i = 0; i < values.Length; i++)
{
values[i] = Constants.Base58Chars.IndexOf(key[i]);
}

uint[] precomputed = new uint[uLen];

// i starts from 1 becaue it is compressed (K or L)
for (int i = 1; i < Constants.CompPrivKeyLen - 2; i++)
{
for (int j = i + 1; j < Constants.CompPrivKeyLen - 1; j++)
{
for (int k = j + 1; k < Constants.CompPrivKeyLen; k++)
{
Array.Clear(precomputed, 0, precomputed.Length);

for (int index = 0; index < i; index++)
{
ulong carry = 0;
ulong val = (ulong)values[index];
int powIndex = (Constants.CompPrivKeyLen - 1 - index) * uLen;
for (int m = uLen - 1; m >= 0; m--, powIndex++)
{
ulong result = checked((powers58[powIndex] * val) + precomputed[m] + carry);
precomputed[m] = (uint)result;
carry = (uint)(result >> 32);
}
}

for (int index = i + 1; index < j; index++)
{
ulong carry = 0;
ulong val = (ulong)values[index - 1];
int powIndex = (Constants.CompPrivKeyLen - 1 - index) * uLen;
for (int m = uLen - 1; m >= 0; m--, powIndex++)
{
ulong result = checked((powers58[powIndex] * val) + precomputed[m] + carry);
precomputed[m] = (uint)result;
carry = (uint)(result >> 32);
}
}

for (int index = j + 1; index < k; index++)
{
ulong carry = 0;
ulong val = (ulong)values[index - 2];
int powIndex = (Constants.CompPrivKeyLen - 1 - index) * uLen;
for (int m = uLen - 1; m >= 0; m--, powIndex++)
{
ulong result = checked((powers58[powIndex] * val) + precomputed[m] + carry);
precomputed[m] = (uint)result;
carry = (uint)(result >> 32);
}
}

for (int index = k + 1; index < Constants.CompPrivKeyLen; index++)
{
ulong carry = 0;
ulong val = (ulong)values[index - 3];
int powIndex = (Constants.CompPrivKeyLen - 1 - index) * uLen;
for (int m = uLen - 1; m >= 0; m--, powIndex++)
{
ulong result = checked((powers58[powIndex] * val) + precomputed[m] + carry);
precomputed[m] = (uint)result;
carry = (uint)(result >> 32);
}
}

var cancelToken = new CancellationTokenSource();
var options = new ParallelOptions
{
CancellationToken = cancelToken.Token,
MaxDegreeOfParallelism = 4
};

try
{
Parallel.For(0, 58, options, (c1, loopState) =>
{
for (int c2 = 0; c2 < 58; c2++)
{
for (int c3 = 0; c3 < 58; c3++)
{
options.CancellationToken.ThrowIfCancellationRequested();
uint[] temp = new uint[precomputed.Length];
Array.Copy(precomputed, temp, precomputed.Length);
ulong carry = 0;
ulong val = (ulong)c1;
int powIndex = (Constants.CompPrivKeyLen - 1 - i) * uLen;
for (int m = uLen - 1; m >= 0; m--, powIndex++)
{
ulong result = checked((powers58[powIndex] * val) + temp[m] + carry);
temp[m] = (uint)result;
carry = (uint)(result >> 32);
}
carry = 0;
val = (ulong)c2;
powIndex = (Constants.CompPrivKeyLen - 1 - j) * uLen;
for (int m = uLen - 1; m >= 0; m--, powIndex++)
{
ulong result = checked((powers58[powIndex] * val) + temp[m] + carry);
temp[m] = (uint)result;
carry = (uint)(result >> 32);
}
carry = 0;
val = (ulong)c3;
powIndex = (Constants.CompPrivKeyLen - 1 - k) * uLen;
for (int m = uLen - 1; m >= 0; m--, powIndex++)
{
ulong result = checked((powers58[powIndex] * val) + temp[m] + carry);
temp[m] = (uint)result;
carry = (uint)(result >> 32);
}
if (Compute(temp))
{
string foundRes = key.Insert(i, $"{Constants.Base58Chars[c1]}")
.Insert(j, $"{Constants.Base58Chars[c2]}")
.Insert(k, $"{Constants.Base58Chars[c3]}");
AddQueue($"Found a key: {foundRes}");
Task.Run(() => cancelToken.Cancel());
}
}
}
});
}
catch (Exception)
{
return true;
}
}
}
}

return false;
}


private unsafe bool Compute(uint[] keyValueInts)
{
// Sha must be defined here for this method to be thread safe
Sha256 sha = new Sha256();

fixed (uint* hPt = &sha.hashState[0], wPt = &sha.w[0])
fixed (uint* keyPt = &keyValueInts[0])
{
wPt[0] = (keyPt[0] << 16) | (keyPt[1] >> 16);
wPt[1] = (keyPt[1] << 16) | (keyPt[2] >> 16);
wPt[2] = (keyPt[2] << 16) | (keyPt[3] >> 16);
wPt[3] = (keyPt[3] << 16) | (keyPt[4] >> 16);
wPt[4] = (keyPt[4] << 16) | (keyPt[5] >> 16);
wPt[5] = (keyPt[5] << 16) | (keyPt[6] >> 16);
wPt[6] = (keyPt[6] << 16) | (keyPt[7] >> 16);
wPt[7] = (keyPt[7] << 16) | (keyPt[8] >> 16);
wPt[8] = (keyPt[8] << 16) | 0b00000000_00000000_10000000_00000000U;
// from 9 to 14 =0
wPt[15] = 272; // 34 *8 = 272

//for (int i = 16; i < w.Length; i++)
//{
// wPt[i] = SSIG1(wPt[i - 2]) + wPt[i - 7] + SSIG0(wPt[i - 15]) + wPt[i - 16];
//}

wPt[16] = sha.SSIG0(wPt[1]) + wPt[0];
wPt[17] = 11141120 + sha.SSIG0(wPt[2]) + wPt[1];
wPt[18] = sha.SSIG1(wPt[16]) + sha.SSIG0(wPt[3]) + wPt[2];
wPt[19] = sha.SSIG1(wPt[17]) + sha.SSIG0(wPt[4]) + wPt[3];
wPt[20] = sha.SSIG1(wPt[18]) + sha.SSIG0(wPt[5]) + wPt[4];
wPt[21] = sha.SSIG1(wPt[19]) + sha.SSIG0(wPt[6]) + wPt[5];
wPt[22] = sha.SSIG1(wPt[20]) + 272 + sha.SSIG0(wPt[7]) + wPt[6];
wPt[23] = sha.SSIG1(wPt[21]) + wPt[16] + sha.SSIG0(wPt[8]) + wPt[7];
wPt[24] = sha.SSIG1(wPt[22]) + wPt[17] + wPt[8];
wPt[25] = sha.SSIG1(wPt[23]) + wPt[18];
wPt[26] = sha.SSIG1(wPt[24]) + wPt[19];
wPt[27] = sha.SSIG1(wPt[25]) + wPt[20];
wPt[28] = sha.SSIG1(wPt[26]) + wPt[21];
wPt[29] = sha.SSIG1(wPt[27]) + wPt[22];
wPt[30] = sha.SSIG1(wPt[28]) + wPt[23] + 541327392;
wPt[31] = sha.SSIG1(wPt[29]) + wPt[24] + sha.SSIG0(wPt[16]) + 272;
wPt[32] = sha.SSIG1(wPt[30]) + wPt[25] + sha.SSIG0(wPt[17]) + wPt[16];
wPt[33] = sha.SSIG1(wPt[31]) + wPt[26] + sha.SSIG0(wPt[18]) + wPt[17];
wPt[34] = sha.SSIG1(wPt[32]) + wPt[27] + sha.SSIG0(wPt[19]) + wPt[18];
wPt[35] = sha.SSIG1(wPt[33]) + wPt[28] + sha.SSIG0(wPt[20]) + wPt[19];
wPt[36] = sha.SSIG1(wPt[34]) + wPt[29] + sha.SSIG0(wPt[21]) + wPt[20];
wPt[37] = sha.SSIG1(wPt[35]) + wPt[30] + sha.SSIG0(wPt[22]) + wPt[21];
wPt[38] = sha.SSIG1(wPt[36]) + wPt[31] + sha.SSIG0(wPt[23]) + wPt[22];
wPt[39] = sha.SSIG1(wPt[37]) + wPt[32] + sha.SSIG0(wPt[24]) + wPt[23];
wPt[40] = sha.SSIG1(wPt[38]) + wPt[33] + sha.SSIG0(wPt[25]) + wPt[24];
wPt[41] = sha.SSIG1(wPt[39]) + wPt[34] + sha.SSIG0(wPt[26]) + wPt[25];
wPt[42] = sha.SSIG1(wPt[40]) + wPt[35] + sha.SSIG0(wPt[27]) + wPt[26];
wPt[43] = sha.SSIG1(wPt[41]) + wPt[36] + sha.SSIG0(wPt[28]) + wPt[27];
wPt[44] = sha.SSIG1(wPt[42]) + wPt[37] + sha.SSIG0(wPt[29]) + wPt[28];
wPt[45] = sha.SSIG1(wPt[43]) + wPt[38] + sha.SSIG0(wPt[30]) + wPt[29];
wPt[46] = sha.SSIG1(wPt[44]) + wPt[39] + sha.SSIG0(wPt[31]) + wPt[30];
wPt[47] = sha.SSIG1(wPt[45]) + wPt[40] + sha.SSIG0(wPt[32]) + wPt[31];
wPt[48] = sha.SSIG1(wPt[46]) + wPt[41] + sha.SSIG0(wPt[33]) + wPt[32];
wPt[49] = sha.SSIG1(wPt[47]) + wPt[42] + sha.SSIG0(wPt[34]) + wPt[33];
wPt[50] = sha.SSIG1(wPt[48]) + wPt[43] + sha.SSIG0(wPt[35]) + wPt[34];
wPt[51] = sha.SSIG1(wPt[49]) + wPt[44] + sha.SSIG0(wPt[36]) + wPt[35];
wPt[52] = sha.SSIG1(wPt[50]) + wPt[45] + sha.SSIG0(wPt[37]) + wPt[36];
wPt[53] = sha.SSIG1(wPt[51]) + wPt[46] + sha.SSIG0(wPt[38]) + wPt[37];
wPt[54] = sha.SSIG1(wPt[52]) + wPt[47] + sha.SSIG0(wPt[39]) + wPt[38];
wPt[55] = sha.SSIG1(wPt[53]) + wPt[48] + sha.SSIG0(wPt[40]) + wPt[39];
wPt[56] = sha.SSIG1(wPt[54]) + wPt[49] + sha.SSIG0(wPt[41]) + wPt[40];
wPt[57] = sha.SSIG1(wPt[55]) + wPt[50] + sha.SSIG0(wPt[42]) + wPt[41];
wPt[58] = sha.SSIG1(wPt[56]) + wPt[51] + sha.SSIG0(wPt[43]) + wPt[42];
wPt[59] = sha.SSIG1(wPt[57]) + wPt[52] + sha.SSIG0(wPt[44]) + wPt[43];
wPt[60] = sha.SSIG1(wPt[58]) + wPt[53] + sha.SSIG0(wPt[45]) + wPt[44];
wPt[61] = sha.SSIG1(wPt[59]) + wPt[54] + sha.SSIG0(wPt[46]) + wPt[45];
wPt[62] = sha.SSIG1(wPt[60]) + wPt[55] + sha.SSIG0(wPt[47]) + wPt[46];
wPt[63] = sha.SSIG1(wPt[61]) + wPt[56] + sha.SSIG0(wPt[48]) + wPt[47];

sha.Init(hPt);
sha.CompressBlockWithWSet(hPt, wPt);

// Perform second hash
sha.DoSecondHash(hPt, wPt);

return hPt[0] == keyPt[9];
}
}



public async Task<bool> Find(string key, char missingChar)
{
Expand Down Expand Up @@ -435,5 +688,37 @@ public async Task<bool> Find(string key, char missingChar)
return CopyQueueToMessage(success);
}


public async Task<bool> FindUnknownLocation(string key)
{
InitReport();

if (string.IsNullOrEmpty(key) || key.Any(c => !Constants.Base58Chars.Contains(c)))
return Fail("Input contains invalid base-58 character(s).");
if (key.Length != Constants.CompPrivKeyLen - 3 ||
(!key.StartsWith(Constants.CompPrivKeyChar2) && key.StartsWith(Constants.CompPrivKeyChar2)))
{
// Only this special case is supported for now
return Fail("This option is only defined for compressed private keys that " +
"have 3 missing characters with unknown locations.");
}

// 51! / 3! *((51-3)!)
BigInteger total = ((51 * 50 * 49) / (3 * 2 * 1)) * BigInteger.Pow(58, 3);
AddMessage($"Start searching.{Environment.NewLine}Total number of keys to check: {total:n0}");

Stopwatch watch = Stopwatch.StartNew();
bool success = await Task.Run(() =>
{
return SpecialLoopComp(key);
}
);

watch.Stop();
AddQueue($"Elapsed time: {watch.Elapsed}");
AddQueue(GetKeyPerSec(total, watch.Elapsed.TotalSeconds));

return CopyQueueToMessage(success);
}
}
}
18 changes: 17 additions & 1 deletion Src/FinderOuter/ViewModels/MissingBase58ViewModel.cs
Expand Up @@ -52,11 +52,27 @@ public char MissingChar
set => this.RaiseAndSetIfChanged(ref _mis, value);
}

private bool _isSpecial;
public bool IsSpecialCase
{
get => _isSpecial;
set => this.RaiseAndSetIfChanged(ref _isSpecial, value);
}

public string MissingToolTip => $"Choose one of these symbols {Constants.Symbols} to use instead of the missing characters";
public string SpecialToolTip => "Select this for a special case where you have a compressed private key that is missing " +
"exactly 3 characters and you don't know their locations.";

public override void Find()
{
_ = b58Service.Find(Input, MissingChar);
if (IsSpecialCase)
{
_ = b58Service.FindUnknownLocation(Input);
}
else
{
_ = b58Service.Find(Input, MissingChar);
}
}
}
}
13 changes: 10 additions & 3 deletions Src/FinderOuter/Views/MissingBase58View.xaml
Expand Up @@ -13,8 +13,15 @@
<TextBox Text="{Binding Input}" Watermark="Base-58 encoded string" UseFloatingWatermark="True"
TextWrapping="Wrap" AcceptsReturn="True" ScrollViewer.VerticalScrollBarVisibility="Auto"
Margin="3" Grid.Column="0"/>
<TextBox Text="{Binding MissingChar}" TextAlignment="Center" VerticalAlignment="Top"
ToolTip.Tip="{Binding MissingToolTip}"
Height="40" Width="40" Margin="3" Grid.Column="1"/>

<StackPanel Orientation="Vertical" Spacing="5" Margin="3" Grid.Column="1">
<TextBox Text="{Binding MissingChar}" TextAlignment="Center" VerticalAlignment="Top"
ToolTip.Tip="{Binding MissingToolTip}"
Height="40" Width="40" Margin="3"/>
<TextBlock Text="Special case:" Margin="0,15,0,0"/>
<CheckBox IsChecked="{Binding IsSpecialCase}" ToolTip.Tip="{Binding SpecialToolTip}"
HorizontalAlignment="Center"/>
</StackPanel>

</Grid>
</UserControl>

0 comments on commit 73e8596

Please sign in to comment.