private unsafe bool LoopComp(string key, int missingCount, char missingChar, byte[] expectedHash) { int[] missingIndexes = new int[missingCount]; byte[] ba = new byte[32]; for (int i = 0, j = 0; i < ba.Length; i++) { int hi, lo; if (key[i * 2] == missingChar) { hi = 0; missingIndexes[j++] = i * 2; } else { hi = key[i * 2] - 65; hi = hi + 10 + ((hi >> 31) & 7); } if (key[i * 2 + 1] == '*') { lo = 0; missingIndexes[j++] = i * 2 + 1; } else { lo = key[i * 2 + 1] - 65; lo = lo + 10 + ((lo >> 31) & 7) & 0x0f; } ba[i] = (byte)(lo | hi << 4); } var cartesian = CartesianProduct.Create(Enumerable.Repeat(Enumerable.Range(0, 16), missingCount)); ECCalc calc = new ECCalc(); BigInteger smallVal = new BigInteger(ba, true, true); EllipticCurvePoint smallPub = calc.MultiplyByG(smallVal); bool foundAny = false; Parallel.ForEach(cartesian, (item, loopState) => { Span <byte> temp = new byte[32]; int mis = 0; foreach (int keyItem in item) { int misIndex = missingIndexes[mis]; if (misIndex % 2 == 0) { temp[misIndex / 2] |= (byte)(keyItem << 4); } else { temp[misIndex / 2] |= (byte)keyItem; } mis++; } BigInteger tempVal = new BigInteger(temp, true, true); EllipticCurvePoint tempPub = calc.MultiplyByG(tempVal); EllipticCurvePoint pub = calc.AddChecked(tempPub, smallPub); byte[] toHash = new byte[33]; toHash[0] = pub.Y.IsEven ? (byte)2 : (byte)3; byte[] xBytes = pub.X.ToByteArray(true, true); Buffer.BlockCopy(xBytes, 0, toHash, 33 - xBytes.Length, xBytes.Length); Hash160 hash = new Hash160(); ReadOnlySpan <byte> actual = hash.ComputeHash(toHash); if (actual.SequenceEqual(expectedHash)) { char[] origHex = key.ToCharArray(); int index = 0; foreach (var keyItem in item) { origHex[missingIndexes[index++]] = GetHex(keyItem); } report.AddMessageSafe($"Found a key: {new string(origHex)}"); foundAny = true; loopState.Break(); } }); if (!foundAny) { report.AddMessageSafe("Failed to find any key."); } return(foundAny); }
private unsafe BigInteger ComputeKey(Sha256Fo sha, uint *hPt, uint *wPt, uint *oPt, byte *kPt) { byte[] chainCode; if (hasChainCode) { chainCode = this.chainCode; } else { // Compute chain-code // h1 = SHA256( SHA256(SHA256(key)) XOR 0x36 | "Derive Chaincode from Root Key") // Chain-code = SHA256( SHA256(SHA256(key)) XOR 0x5c | h1) sha.Init(hPt); wPt[0] = (uint)((kPt[0] << 24) | (kPt[1] << 16) | (kPt[2] << 8) | kPt[3]); wPt[1] = (uint)((kPt[4] << 24) | (kPt[5] << 16) | (kPt[6] << 8) | kPt[7]); wPt[2] = (uint)((kPt[8] << 24) | (kPt[9] << 16) | (kPt[10] << 8) | kPt[11]); wPt[3] = (uint)((kPt[12] << 24) | (kPt[13] << 16) | (kPt[14] << 8) | kPt[15]); wPt[4] = (uint)((kPt[16] << 24) | (kPt[17] << 16) | (kPt[18] << 8) | kPt[19]); wPt[5] = (uint)((kPt[20] << 24) | (kPt[21] << 16) | (kPt[22] << 8) | kPt[23]); wPt[6] = (uint)((kPt[24] << 24) | (kPt[25] << 16) | (kPt[26] << 8) | kPt[27]); wPt[7] = (uint)((kPt[28] << 24) | (kPt[29] << 16) | (kPt[30] << 8) | kPt[31]); wPt[8] = 0b10000000_00000000_00000000_00000000U; // From 9 to 14 remain 0 wPt[15] = 256; sha.CompressDouble32(hPt, wPt); // Use hPt in both iPt and oPt here before it is changed oPt[0] = hPt[0] ^ 0x5c5c5c5cU; oPt[1] = hPt[1] ^ 0x5c5c5c5cU; oPt[2] = hPt[2] ^ 0x5c5c5c5cU; oPt[3] = hPt[3] ^ 0x5c5c5c5cU; oPt[4] = hPt[4] ^ 0x5c5c5c5cU; oPt[5] = hPt[5] ^ 0x5c5c5c5cU; oPt[6] = hPt[6] ^ 0x5c5c5c5cU; oPt[7] = hPt[7] ^ 0x5c5c5c5cU; wPt[0] = hPt[0] ^ 0x36363636U; wPt[1] = hPt[1] ^ 0x36363636U; wPt[2] = hPt[2] ^ 0x36363636U; wPt[3] = hPt[3] ^ 0x36363636U; wPt[4] = hPt[4] ^ 0x36363636U; wPt[5] = hPt[5] ^ 0x36363636U; wPt[6] = hPt[6] ^ 0x36363636U; wPt[7] = hPt[7] ^ 0x36363636U; wPt[8] = 0x44657269; // Deri wPt[9] = 0x76652043; // ve C wPt[10] = 0x6861696e; // hain wPt[11] = 0x636f6465; // code wPt[12] = 0x2066726f; // fro wPt[13] = 0x6d20526f; // m Ro wPt[14] = 0x6f74204b; // ot K wPt[15] = 0x65798000; // ey + 0x80 pad sha.Init(hPt); sha.CompressBlock(hPt, wPt); wPt[0] = 0; wPt[1] = 0; wPt[2] = 0; wPt[3] = 0; wPt[4] = 0; wPt[5] = 0; wPt[6] = 0; wPt[7] = 0; wPt[8] = 0; wPt[9] = 0; wPt[10] = 0; wPt[11] = 0; wPt[12] = 0; wPt[13] = 0; wPt[14] = 0; wPt[15] = 496; // 32+30 * 8 sha.Compress62SecondBlock(hPt, wPt); *(Block32 *)wPt = *(Block32 *)oPt; wPt[8] = hPt[0]; wPt[9] = hPt[1]; wPt[10] = hPt[2]; wPt[11] = hPt[3]; wPt[12] = hPt[4]; wPt[13] = hPt[5]; wPt[14] = hPt[6]; wPt[15] = hPt[7]; sha.Init(hPt); sha.CompressBlock(hPt, wPt); wPt[0] = 0b10000000_00000000_00000000_00000000U; wPt[1] = 0; wPt[2] = 0; wPt[3] = 0; wPt[4] = 0; wPt[5] = 0; wPt[6] = 0; wPt[7] = 0; wPt[8] = 0; wPt[9] = 0; wPt[10] = 0; wPt[11] = 0; wPt[12] = 0; wPt[13] = 0; wPt[14] = 0; wPt[15] = 512; // 32+32 * 8 sha.Compress64SecondBlock(hPt, wPt); // TODO: this could be improved a bit chainCode = sha.GetBytes(hPt); } // hPt is chain-code now ReadOnlySpan <byte> key = new ReadOnlySpan <byte>(kPt, 32); BigInteger k = new BigInteger(key, true, true); EllipticCurvePoint point = calc.MultiplyByG(k); byte[] pubBa = new byte[65]; pubBa[0] = 4; byte[] xBytes = point.X.ToByteArray(true, true); byte[] yBytes = point.Y.ToByteArray(true, true); Buffer.BlockCopy(xBytes, 0, pubBa, 33 - xBytes.Length, xBytes.Length); Buffer.BlockCopy(yBytes, 0, pubBa, 65 - yBytes.Length, yBytes.Length); byte[] chainXor = sha.CompressDouble65(pubBa); for (var i = 0; i < chainXor.Length; i++) { chainXor[i] ^= chainCode[i]; } BigInteger A = new BigInteger(chainXor, true, true); BigInteger B = new BigInteger(key, true, true); BigInteger secexp = (A * B).Mod(N); return(secexp); }