public unsafe void CompressSingleBlockTest(int len) { byte[] data = GetRandomBytes(len); byte[] padded = new byte[Sha512Fo.BlockByteSize]; Buffer.BlockCopy(data, 0, padded, 0, data.Length); padded[len] = 0x80; padded[127] = (byte)(len << 3); padded[126] = (byte)(len >> 5); padded[125] = (byte)(len >> 13); padded[124] = (byte)(len >> 21); padded[123] = (byte)(len >> 29); ulong *hPt1 = stackalloc ulong[Sha512Fo.UBufferSize]; ulong *wPt1 = hPt1 + Sha512Fo.HashStateSize; ulong *hPt2 = stackalloc ulong[Sha512Fo.UBufferSize]; ulong *wPt2 = hPt2 + Sha512Fo.HashStateSize; fixed(byte *dPt1 = &data[0], dPt2 = &padded[0]) { Sha512Fo.Init(hPt1); Sha512Fo.CompressData(dPt1, data.Length, data.Length, hPt1, wPt1); byte[] expected = Sha512Fo.GetBytes(hPt1); Sha512Fo.Init(hPt2); Sha512Fo.CompressSingleBlock(dPt2, hPt2, wPt2); byte[] actual = Sha512Fo.GetBytes(hPt2); Assert.Equal(expected, actual); } }
public unsafe void MainLoop(ulong[] pads, byte[] salt, byte[] allValues, int passLength) { Debug.Assert(pads != null && pads.Length == Sha512Fo.HashStateSize * 2); Debug.Assert(salt != null && salt.Length == Sha512Fo.BlockByteSize); if (passLength == 1) { ulong *bigBuffer = stackalloc ulong[Sha512Fo.UBufferSize + Sha512Fo.HashStateSize + (2 * Sha512Fo.WorkingVectorSize)]; ulong *hPt = bigBuffer; ulong *wPt = hPt + Sha512Fo.HashStateSize; ulong *seedPt = wPt + Sha512Fo.WorkingVectorSize; fixed(byte *dPt = &salt[0], valPt = &allValues[0]) fixed(ulong *iPt = &pads[0], oPt = &pads[Sha512Fo.HashStateSize]) { foreach (byte val in allValues) { dPt[8] = val; // 1. SHA512(inner_pad | data) -> 2 blocks; first one is already compressed *(Block64 *)hPt = *(Block64 *)iPt; // Data length is unknown and an initial block of 128 bytes was already compressed // but we already reject anything big that needs another block (there is only one more block to compress) // The pad and data length is also already set Sha512Fo.CompressSingleBlock(dPt, hPt, wPt); // 2. SHA512(outer_pad | hash) -> 2 blocks; first one is already compressed // Copy hashstate into next block before changing it *(Block64 *)wPt = *(Block64 *)hPt; wPt[8] = 0b10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000UL; wPt[9] = 0; wPt[10] = 0; wPt[11] = 0; wPt[12] = 0; wPt[13] = 0; wPt[14] = 0; wPt[15] = 1536; // oPad.Length(=128) + hashState.Lengh(=64) = 192 byte *8 = 1,536 bit *(Block64 *)hPt = *(Block64 *)oPt; Sha512Fo.Compress192SecondBlock(hPt, wPt); // Copy u1 to result of F() to be XOR'ed with each result on iterations, and result of F() is the seed *(Block64 *)seedPt = *(Block64 *)hPt; // Compute u2 to u(c-1) where c is iteration and each u is the HMAC of previous u for (int j = 1; j < 2048; j++) { // Each u is calculated by computing HMAC(previous_u) where previous_u is 64 bytes hPt // Start by making a copy of hPt so Init() can be called *(Block64 *)wPt = *(Block64 *)hPt; // Final result is SHA512(outer_pad | SHA512(inner_pad | 64_byte_data)) // 1. Compute SHA512(inner_pad | 64_byte_data) // 2. Compute SHA512(outer_pad | hash) // Since pads don't change and each step is Init() then Compress(pad) the hashState is always the same // after these 2 steps and is already computed and stored in temp arrays above // by doing this 2*2047=4094 SHA512 block compressions are skipped // Replace: sha.Init(hPt); sha.CompressBlockWithWSet(hPt, iPt); with line below: *(Block64 *)hPt = *(Block64 *)iPt; Sha512Fo.Compress192SecondBlock(hPt, wPt); // 2. Compute SHA512(outer_pad | hash) *(Block64 *)wPt = *(Block64 *)hPt; // The rest of wPt is set above and is unchanged // Replace: sha.Init(hPt); sha.CompressBlock(hPt, oPt); with line below: *(Block64 *)hPt = *(Block64 *)oPt; Sha512Fo.Compress192SecondBlock(hPt, wPt); // result of F() is XOR sum of all u arrays seedPt[0] ^= hPt[0]; seedPt[1] ^= hPt[1]; seedPt[2] ^= hPt[2]; seedPt[3] ^= hPt[3]; seedPt[4] ^= hPt[4]; seedPt[5] ^= hPt[5]; seedPt[6] ^= hPt[6]; seedPt[7] ^= hPt[7]; } if (SetBip32(bigBuffer, comparer)) { report.FoundAnyResult = true; string finalResult = Encoding.UTF8.GetString(new byte[] { val }); report.AddMessageSafe($"Passphrase is: {finalResult}"); return; } } } } else { report.SetProgressStep(allValues.Length); Parallel.For(0, allValues.Length, (firstItem, state) => LoopBip39(pads, ParallelSalt(salt, allValues[firstItem]), allValues, passLength, state)); } }