public unsafe void CompressHmacBlock_0x5c_Bitcoinseed_Test() { int extraLen = 5; byte[] extraEndBa = GetRandomBytes(extraLen); int dataLen = 128 + extraLen; byte[] data = new byte[dataLen]; ((Span <byte>)data).Fill(0x5c); Buffer.BlockCopy(extraEndBa, 0, data, 128, extraLen); // XOR "Bitcoin seed" with initial bytes byte[] xor = Encoding.UTF8.GetBytes("Bitcoin seed"); for (int i = 0; i < xor.Length; i++) { data[i] ^= xor[i]; } byte[] expected = ComputeSingleSha(data); using Sha512Fo sha = new Sha512Fo(); fixed(byte *dPt = &data[0]) fixed(ulong *hPt = &sha.hashState[0], wPt = &sha.w[0]) { sha.Init(hPt); sha.CompressHmacBlock_0x5c_Bitcoinseed(hPt, wPt); sha.CompressData(dPt + 128, extraLen, dataLen, hPt, wPt); byte[] actual = sha.GetBytes(hPt); Assert.Equal(expected, actual); } }
public void ComputeHash_AMillionATest() { byte[] actualHash = Sha512Fo.ComputeHash(HashTestCaseHelper.GetAMillionA()); byte[] expectedHash = Helper.HexToBytes("e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"); Assert.Equal(expectedHash, actualHash); }
public unsafe void Compare_Sha512HashStateTest() { PrvToPubComparer comp = new(); byte[] data = new byte[] { 1, 2, 3 }; ulong *hPt = stackalloc ulong[Sha512Fo.UBufferSize]; ulong *wPt = hPt + Sha512Fo.HashStateSize; fixed(byte *dPt = data) { // Get hashstate ready first Sha512Fo.CompressData(dPt, data.Length, data.Length, hPt, wPt); Scalar key = new(hPt, out int overflow); Assert.Equal(0, overflow); Calc calc = new(); string pubHex = calc.GetPubkey(key, true).ToArray().ToBase16(); bool b = comp.Init(pubHex); Assert.True(b); bool actual = comp.Compare(hPt); Assert.True(actual); } }
public unsafe void CompressHmacBlock_0x36_Bitcoinseed_Test() { int extraLen = 5; byte[] extraEndBa = GetRandomBytes(extraLen); int dataLen = 128 + extraLen; byte[] data = new byte[dataLen]; ((Span <byte>)data).Fill(0x36); Buffer.BlockCopy(extraEndBa, 0, data, 128, extraLen); // XOR "Bitcoin seed" with initial bytes byte[] xor = Encoding.UTF8.GetBytes("Bitcoin seed"); for (int i = 0; i < xor.Length; i++) { data[i] ^= xor[i]; } byte[] expected = ComputeSingleSha(data); ulong *hPt = stackalloc ulong[Sha512Fo.UBufferSize]; ulong *wPt = hPt + Sha512Fo.HashStateSize; fixed(byte *dPt = &data[0]) { Sha512Fo.Init(hPt); Sha512Fo.CompressHmacBlock_0x36_Bitcoinseed(hPt, wPt); Sha512Fo.CompressData(dPt + 128, extraLen, dataLen, hPt, wPt); byte[] actual = Sha512Fo.GetBytes(hPt); Assert.Equal(expected, actual); } }
private unsafe void Loop12() { if (missCount > 1) { report.AddMessageSafe("Running in parallel."); report.SetProgressStep(2048); int firstIndex = missingIndexes[0]; Parallel.For(0, 2048, (firstItem, state) => Loop12(firstItem, firstIndex, state)); } else { // We can't call the same parallel method due to usage of LoopState so we at least optimize this by // avoiding the inner loop over the IEnumerable using Sha512Fo sha512 = new Sha512Fo(); ulong[] ipad = new ulong[80]; ulong[] opad = new ulong[80]; using Sha256Fo sha256 = new Sha256Fo(); int misIndex = missingIndexes[0]; fixed(ulong *iPt = ipad, oPt = opad) fixed(uint *wPt = &sha256.w[0], hPt = &sha256.hashState[0], wrd = &wordIndexes[0]) fixed(byte *mnPt = &mnBytes[0]) { wPt[4] = 0b10000000_00000000_00000000_00000000U; wPt[15] = 128; for (uint item = 0; item < 2048; item++) { wrd[misIndex] = item; wPt[0] = wrd[0] << 21 | wrd[1] << 10 | wrd[2] >> 1; wPt[1] = wrd[2] << 31 | wrd[3] << 20 | wrd[4] << 9 | wrd[5] >> 2; wPt[2] = wrd[5] << 30 | wrd[6] << 19 | wrd[7] << 8 | wrd[8] >> 3; wPt[3] = wrd[8] << 29 | wrd[9] << 18 | wrd[10] << 7 | wrd[11] >> 4; sha256.Init(hPt); sha256.Compress16(hPt, wPt); if ((wrd[11] & 0b1111) == hPt[0] >> 28) { int mnLen = 0; for (int i = 0; i < 12; i++) { var temp = allWordsBytes[wrd[i]]; Buffer.BlockCopy(temp, 0, mnBytes, mnLen, temp.Length); mnLen += temp.Length; } if (SetBip32(sha512, mnPt, --mnLen, iPt, oPt)) { SetResultParallel(mnPt, mnLen); break; } } } } } }
public unsafe void GetSecond32BytesTest() { byte[] data = GetRandomBytes(64); byte[] expected = ((Span <byte>)data).Slice(32, 32).ToArray(); ulong[] hashState = new ulong[8]; for (int i = 0, j = 0; i < 8; i++, j += 8) { hashState[i] = ((ulong)data[j] << 56) | ((ulong)data[j + 1] << 48) | ((ulong)data[j + 2] << 40) | ((ulong)data[j + 3] << 32) | ((ulong)data[j + 4] << 24) | ((ulong)data[j + 5] << 16) | ((ulong)data[j + 6] << 8) | data[j + 7]; } using Sha512Fo sha = new Sha512Fo(); fixed(ulong *hPt = &hashState[0]) { byte[] actual = sha.GetSecond32Bytes(hPt); Assert.Equal(expected, actual); } }
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 void ComputeHash_NistMonteCarloTest() { byte[] seed = Helper.HexToBytes("5c337de5caf35d18ed90b5cddfce001ca1b8ee8602f367e7c24ccca6f893802fb1aca7a3dae32dcd60800a59959bc540d63237876b799229ae71a2526fbc52cd"); JObject jObjs = Helper.ReadResources <JObject>("Sha512NistTestData"); int size = 64; byte[] toHash = new byte[3 * size]; byte[] M0 = seed; byte[] M1 = seed; byte[] M2 = seed; using Sha512Fo sha = new Sha512Fo(); foreach (var item in jObjs["MonteCarlo"]) { byte[] expected = Helper.HexToBytes(item.ToString()); for (int i = 0; i < 1000; i++) { Buffer.BlockCopy(M0, 0, toHash, 0, size); Buffer.BlockCopy(M1, 0, toHash, size, size); Buffer.BlockCopy(M2, 0, toHash, size * 2, size); M0 = M1; M1 = M2; M2 = sha.ComputeHash(toHash); } M0 = M2; M1 = M2; Assert.Equal(expected, M2); } }
private unsafe bool Loop21() { using Sha512Fo sha512 = new Sha512Fo(); ulong[] ipad = new ulong[80]; ulong[] opad = new ulong[80]; using Sha256Fo sha256 = new Sha256Fo(); var cartesian = CartesianProduct.Create(Enumerable.Repeat(Enumerable.Range(0, 2048), missCount)); fixed(ulong *iPt = ipad, oPt = opad) fixed(uint *wPt = &sha256.w[0], hPt = &sha256.hashState[0], wrd = &wordIndexes[0]) fixed(int *mi = &missingIndexes[0]) fixed(byte *mnPt = &mnBytes[0]) { wPt[7] = 0b10000000_00000000_00000000_00000000U; wPt[15] = 224; foreach (var item in cartesian) { int j = 0; foreach (var k in item) { wrd[mi[j]] = (uint)k; j++; } wPt[0] = wrd[0] << 21 | wrd[1] << 10 | wrd[2] >> 1; wPt[1] = wrd[2] << 31 | wrd[3] << 20 | wrd[4] << 9 | wrd[5] >> 2; wPt[2] = wrd[5] << 30 | wrd[6] << 19 | wrd[7] << 8 | wrd[8] >> 3; wPt[3] = wrd[8] << 29 | wrd[9] << 18 | wrd[10] << 7 | wrd[11] >> 4; wPt[4] = wrd[11] << 28 | wrd[12] << 17 | wrd[13] << 6 | wrd[14] >> 5; wPt[5] = wrd[14] << 27 | wrd[15] << 16 | wrd[16] << 5 | wrd[17] >> 6; wPt[6] = wrd[17] << 26 | wrd[18] << 15 | wrd[19] << 4 | wrd[20] >> 7; sha256.Init(hPt); sha256.Compress28(hPt, wPt); if ((wrd[20] & 0b111_1111) == hPt[0] >> 25) { int mnLen = 0; for (int i = 0; i < 21; i++) { foreach (byte b in wordBytes[wrd[i]]) { mnPt[mnLen++] = b; } mnPt[mnLen++] = SpaceByte; } if (SetBip32(sha512, mnPt, --mnLen, iPt, oPt)) { return(SetResult(mnLen)); } } } } return(false); }
public unsafe void Compress192SecondBlockTest() { int dataLen = 192; byte[] data = GetRandomBytes(dataLen); byte[] expected = ComputeSingleSha(data); ulong *hPt = stackalloc ulong[Sha512Fo.UBufferSize]; ulong *wPt = hPt + Sha512Fo.HashStateSize; Sha512Fo.Init(hPt); int dIndex = 0; for (int i = 0; i < 16; i++, dIndex += 8) { wPt[i] = ((ulong)data[dIndex] << 56) | ((ulong)data[dIndex + 1] << 48) | ((ulong)data[dIndex + 2] << 40) | ((ulong)data[dIndex + 3] << 32) | ((ulong)data[dIndex + 4] << 24) | ((ulong)data[dIndex + 5] << 16) | ((ulong)data[dIndex + 6] << 8) | data[dIndex + 7]; } Sha512Fo.SetW(wPt); Sha512Fo.CompressBlockWithWSet(hPt, wPt); for (int i = 0; i < 8; i++, dIndex += 8) { wPt[i] = ((ulong)data[dIndex] << 56) | ((ulong)data[dIndex + 1] << 48) | ((ulong)data[dIndex + 2] << 40) | ((ulong)data[dIndex + 3] << 32) | ((ulong)data[dIndex + 4] << 24) | ((ulong)data[dIndex + 5] << 16) | ((ulong)data[dIndex + 6] << 8) | data[dIndex + 7]; } 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] = (ulong)dataLen * 8; Sha512Fo.Compress192SecondBlock(hPt, wPt); byte[] actual = Sha512Fo.GetBytes(hPt); Assert.Equal(expected, actual); }
public unsafe void Compress192SecondBlockTest() { int dataLen = 192; byte[] data = GetRandomBytes(dataLen); byte[] expected = ComputeSingleSha(data); using Sha512Fo sha = new Sha512Fo(); fixed(ulong *hPt = &sha.hashState[0], wPt = &sha.w[0]) { sha.Init(hPt); int dIndex = 0; for (int i = 0; i < 16; i++, dIndex += 8) { wPt[i] = ((ulong)data[dIndex] << 56) | ((ulong)data[dIndex + 1] << 48) | ((ulong)data[dIndex + 2] << 40) | ((ulong)data[dIndex + 3] << 32) | ((ulong)data[dIndex + 4] << 24) | ((ulong)data[dIndex + 5] << 16) | ((ulong)data[dIndex + 6] << 8) | data[dIndex + 7]; } sha.CompressBlock(hPt, wPt); for (int i = 0; i < 8; i++, dIndex += 8) { wPt[i] = ((ulong)data[dIndex] << 56) | ((ulong)data[dIndex + 1] << 48) | ((ulong)data[dIndex + 2] << 40) | ((ulong)data[dIndex + 3] << 32) | ((ulong)data[dIndex + 4] << 24) | ((ulong)data[dIndex + 5] << 16) | ((ulong)data[dIndex + 6] << 8) | data[dIndex + 7]; } 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] = (ulong)dataLen * 8; sha.Compress192SecondBlock(hPt, wPt); byte[] actual = sha.GetBytes(hPt); Assert.Equal(expected, actual); } }
public void ComputeHash_ReuseTest() { byte[] msg1 = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog"); byte[] msg2 = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy cog"); byte[] exp1 = Helper.HexToBytes("07e547d9586f6a73f73fbac0435ed76951218fb7d0c8d788a309d785436bbb642e93a252a954f23912547d1e8a3b5ed6e1bfd7097821233fa0538f3db854fee6"); byte[] exp2 = Helper.HexToBytes("3eeee1d0e11733ef152a6c29503b3ae20c4f1f3cda4cb26f1bc1a41f91c7fe4ab3bd86494049e201c4bd5155f31ecb7a3c8606843c4cc8dfcab7da11c8ae5045"); byte[] act1 = Sha512Fo.ComputeHash(msg1); byte[] act2 = Sha512Fo.ComputeHash(msg2); Assert.Equal(exp1, act1); Assert.Equal(exp2, act2); }
public unsafe void Constructor_FromSha512Test() { using Sha512Fo sha = new(); byte[] data = new byte[] { 1, 2, 3 }; // Get hashstate ready first sha.ComputeHash(data); fixed(ulong *hPt = &sha.hashState[0]) { byte[] hash = Sha512Fo.GetFirst32Bytes(hPt); var val1 = new Scalar(hash, out int of1); var val2 = new Scalar(hPt, out int of2); Assert.True(val1 == val2); Assert.Equal(of1, of2); Assert.Equal(0, of1); } }
public unsafe void Constructor_FromSha512Test() { byte[] data = new byte[] { 1, 2, 3 }; ulong *hPt = stackalloc ulong[Sha512Fo.UBufferSize]; ulong *wPt = hPt + Sha512Fo.HashStateSize; fixed(byte *dPt = data) { // Get hashstate ready first Sha512Fo.CompressData(dPt, data.Length, data.Length, hPt, wPt); byte[] hash = Sha512Fo.GetFirst32Bytes(hPt); Scalar val1 = new(hash, out int of1); Scalar val2 = new(hPt, out int of2); Assert.True(val1 == val2); Assert.Equal(of1, of2); Assert.Equal(0, of1); } }
public void ComputeHashTest(byte[] message, byte[] expectedHash) { using Sha512Fo sha = new Sha512Fo(); byte[] actualHash = sha.ComputeHash(message); Assert.Equal(expectedHash, actualHash); }
public void ComputeHash_NistLongTest(byte[] message, byte[] expected) { byte[] actual = Sha512Fo.ComputeHash(message); Assert.Equal(expected, actual); }
private unsafe void Loop12(int firstItem, int firstIndex, ParallelLoopState loopState) { var cartesian = CartesianProduct.Create(Enumerable.Repeat(Enumerable.Range(0, 2048), missCount - 1)); using Sha512Fo sha512 = new Sha512Fo(); ulong[] ipad = new ulong[80]; ulong[] opad = new ulong[80]; using Sha256Fo sha256 = new Sha256Fo(); byte[] localMnBytes = new byte[mnBytes.Length]; var localCopy = new byte[allWordsBytes.Length][]; Array.Copy(allWordsBytes, localCopy, allWordsBytes.Length); uint[] localWIndex = new uint[wordIndexes.Length]; Array.Copy(wordIndexes, localWIndex, wordIndexes.Length); fixed(ulong *iPt = &ipad[0], oPt = &opad[0]) fixed(uint *wPt = &sha256.w[0], hPt = &sha256.hashState[0], wrd = &localWIndex[0]) fixed(int *mi = &missingIndexes[1]) fixed(byte *mnPt = &localMnBytes[0]) { wPt[4] = 0b10000000_00000000_00000000_00000000U; wPt[15] = 128; wrd[firstIndex] = (uint)firstItem; foreach (var item in cartesian) { if (loopState.IsStopped) { return; } int j = 0; foreach (var k in item) { wrd[mi[j]] = (uint)k; j++; } wPt[0] = wrd[0] << 21 | wrd[1] << 10 | wrd[2] >> 1; wPt[1] = wrd[2] << 31 | wrd[3] << 20 | wrd[4] << 9 | wrd[5] >> 2; wPt[2] = wrd[5] << 30 | wrd[6] << 19 | wrd[7] << 8 | wrd[8] >> 3; wPt[3] = wrd[8] << 29 | wrd[9] << 18 | wrd[10] << 7 | wrd[11] >> 4; sha256.Init(hPt); sha256.Compress16(hPt, wPt); if ((wrd[11] & 0b1111) == hPt[0] >> 28) { int mnLen = 0; for (int i = 0; i < 12; i++) { var temp = localCopy[wrd[i]]; Buffer.BlockCopy(temp, 0, mnBytes, mnLen, temp.Length); mnLen += temp.Length; } if (SetBip32(sha512, mnPt, --mnLen, iPt, oPt)) { SetResultParallel(mnPt, mnLen); loopState.Stop(); break; } } } } report.IncrementProgress(); }
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)); } }
public void ComputeHash_NistLongTest(byte[] message, byte[] expected) { using Sha512Fo sha = new Sha512Fo(); byte[] actual = sha.ComputeHash(message); Assert.Equal(expected, actual); }
private unsafe bool Loop24() { using Sha512Fo sha512 = new Sha512Fo(); ulong[] ipad = new ulong[80]; ulong[] opad = new ulong[80]; using Sha256Fo sha256 = new Sha256Fo(); var cartesian = CartesianProduct.Create(Enumerable.Repeat(Enumerable.Range(0, 2048), missCount)); fixed(ulong *iPt = ipad, oPt = opad) fixed(uint *wPt = &sha256.w[0], hPt = &sha256.hashState[0], wrd = &wordIndexes[0]) fixed(int *mi = &missingIndexes[0]) fixed(byte *mnPt = &mnBytes[0]) { wPt[8] = 0b10000000_00000000_00000000_00000000U; wPt[15] = 256; foreach (var item in cartesian) { int j = 0; foreach (var k in item) { wrd[mi[j]] = (uint)k; j++; } // 0000_0000 0000_0000 0000_0111 1111_1111 -> 1111_1111 1110_0000 0000_0000 0000_0000 // 0000_0000 0000_0000 0000_0222 2222_2222 -> 0000_0000 0002_2222 2222_2200 0000_0000 // 0000_0000 0000_0000 0000_0333 3333_3333 -> 0000_0000 0000_0000 0000_0033 3333_3333 -> 3 // 1111_1111 1112_2222 2222_2233 3333_3333 wPt[0] = wrd[0] << 21 | wrd[1] << 10 | wrd[2] >> 1; // 0000_0000 0000_0000 0000_0000 0000_0003 -> 3000_0000 0000_0000 0000_0000 0000_0000 // 0000_0000 0000_0000 0000_0444 4444_4444 -> 0444_4444 4444_0000 0000_0000 0000_0000 // 0000_0000 0000_0000 0000_0555 5555_5555 -> 0000_0000 0000_5555 5555_5550 0000_0000 // 0000_0000 0000_0000 0000_0666 6666_6666 -> 0000_0000 0000_0000 0000_0006 6666_6666 -> 66 // 3444_4444 4444_5555 5555_5556 6666_6666 wPt[1] = wrd[2] << 31 | wrd[3] << 20 | wrd[4] << 9 | wrd[5] >> 2; // 0000_0000 0000_0000 0000_0000 0000_0066 -> 6600_0000 0000_0000 0000_0000 0000_0000 // 0000_0000 0000_0000 0000_0777 7777_7777 -> 0077_7777 7777_7000 0000_0000 0000_0000 // 0000_0000 0000_0000 0000_0888 8888_8888 -> 0000_0000 0000_0888 8888_8888 0000_0000 // 0000_0000 0000_0000 0000_0999 9999_9999 -> 0000_0000 0000_0000 0000_0000 9999_9999 -> 999 // 6677_7777 7777_7888 8888_8888 9999_9999 wPt[2] = wrd[5] << 30 | wrd[6] << 19 | wrd[7] << 8 | wrd[8] >> 3; // 0000_0000 0000_0000 0000_0000 0000_0999 -> 9990_0000 0000_0000 0000_0000 0000_0000 // 0000_0000 0000_0000 0000_0AAA AAAA_AAAA -> 000A_AAAA AAAA_AA00 0000_0000 0000_0000 // 0000_0000 0000_0000 0000_0BBB BBBB_BBBB -> 0000_0000 0000_00BB BBBB_BBBB B000_0000 // 0000_0000 0000_0000 0000_0CCC CCCC_CCCC -> 0000_0000 0000_0000 0000_0000 0CCC_CCCC -> CCCC // 999A_AAAA AAAA_AABB BBBB_BBBB BCCC_CCCC wPt[3] = wrd[8] << 29 | wrd[9] << 18 | wrd[10] << 7 | wrd[11] >> 4; // 0000_0000 0000_0000 0000_0000 0000_CCCC -> CCCC_0000 0000_0000 0000_0000 0000_0000 // 0000_0000 0000_0000 0000_0DDD DDDD_DDDD -> 0000_DDDD DDDD_DDD0 0000_0000 0000_0000 // 0000_0000 0000_0000 0000_0EEE EEEE_EEEE -> 0000_0000 0000_000E EEEE_EEEE EE00_0000 // 0000_0000 0000_0000 0000_0FFF FFFF_FFFF -> 0000_0000 0000_0000 0000_0000 00FF_FFFF -> FFFF_F // CCCC_DDDD DDDD_DDDE EEEE_EEEE EEFF_FFFF wPt[4] = wrd[11] << 28 | wrd[12] << 17 | wrd[13] << 6 | wrd[14] >> 5; // 0000_0000 0000_0000 0000_0000 000F_FFFF -> FFFF_F000 0000_0000 0000_0000 0000_0000 // 0000_0000 0000_0000 0000_0GGG GGGG_GGGG -> 0000_0GGG GGGG_GGGG 0000_0000 0000_0000 // 0000_0000 0000_0000 0000_0HHH HHHH_HHHH -> 0000_0000 0000_0000 HHHH_HHHH HHH0_0000 // 0000_0000 0000_0000 0000_0III IIII_IIII -> 0000_0000 0000_0000 0000_0000 000I_IIII -> IIII_II // -> FFFF_FGGG GGGG_GGGG HHHH_HHHH HHHI_IIII wPt[5] = wrd[14] << 27 | wrd[15] << 16 | wrd[16] << 5 | wrd[17] >> 6; // 0000_0000 0000_0000 0000_0000 00II_IIII -> IIII_II00 0000_0000 0000_0000 0000_0000 // 0000_0000 0000_0000 0000_0JJJ JJJJ_JJJJ -> 0000_00JJ JJJJ_JJJJ J000_0000 0000_0000 // 0000_0000 0000_0000 0000_0KKK KKKK_KKKK -> 0000_0000 0000_0000 0KKK_KKKK KKKK_0000 // 0000_0000 0000_0000 0000_0LLL LLLL_LLLL -> 0000_0000 0000_0000 0000_0000 0000_LLLL -> LLLL_LLL // -> IIII_IIJJ JJJJ_JJJJ JKKK_KKKK KKKK_LLLL wPt[6] = wrd[17] << 26 | wrd[18] << 15 | wrd[19] << 4 | wrd[20] >> 7; // 0000_0000 0000_0000 0000_0000 0LLL_LLLL -> LLLL_LLL0 0000_0000 0000_0000 0000_0000 // 0000_0000 0000_0000 0000_0MMM MMMM_MMMM -> 0000_000M MMMM_MMMM MM00_0000 0000_0000 // 0000_0000 0000_0000 0000_0NNN NNNN_NNNN -> 0000_0000 0000_0000 00NN_NNNN NNNN_N000 // 0000_0000 0000_0000 0000_0OOO OOOO_OOOO -> 0000_0000 0000_0000 0000_0000 0000_0OOO -> OOOO_OOOO // -> LLLL_LLLM MMMM_MMMM MMNN_NNNN NNNN_NOOO wPt[7] = wrd[20] << 25 | wrd[21] << 14 | wrd[22] << 3 | wrd[23] >> 8; sha256.Init(hPt); sha256.Compress32(hPt, wPt); if ((byte)wrd[23] == hPt[0] >> 24) { int mnLen = 0; for (int i = 0; i < 24; i++) { foreach (byte b in wordBytes[wrd[i]]) { mnPt[mnLen++] = b; } mnPt[mnLen++] = SpaceByte; } if (SetBip32(sha512, mnPt, --mnLen, iPt, oPt)) { return(SetResult(mnLen)); } } } } return(false); }
public unsafe bool SetBip32(ulong *bigBuffer, ICompareService comparer) { ulong *hPt = bigBuffer; ulong *wPt = hPt + Sha512Fo.HashStateSize; ulong *seedPt = wPt + Sha512Fo.WorkingVectorSize; ulong *iPt = seedPt + Sha512Fo.HashStateSize; ulong *oPt = iPt + Sha512Fo.WorkingVectorSize; // *** BIP32 *** // Set from entropy/seed by computing HMAC(data=seed, key="Bitcoin seed") // Final result is SHA512(outer_pad | SHA512(inner_pad | data)) where data is 64-byte seed // 1. Compute SHA512(inner_pad | data) Sha512Fo.Init_InnerPad_Bitcoinseed(hPt); *(Block64 *)wPt = *(Block64 *)seedPt; // from wPt[8] to wPt[15] didn't change Sha512Fo.Compress192SecondBlock(hPt, wPt); // 2. Compute SHA512(outer_pad | hash) *(Block64 *)wPt = *(Block64 *)hPt; // ** Copy hashState before changing it ** // from wPt[8] to wPt[15] didn't change Sha512Fo.Init_OuterPad_Bitcoinseed(hPt); Sha512Fo.Compress192SecondBlock(hPt, wPt); // Master key is set. PrivateKey= first 32-bytes of hPt and ChainCode is second 32-bytes // Each child is derived by computing HMAC(data=(hardened? 0|prvKey : pubkey) | index, key=ChainCode) // ChainCode is the second 32-byte half of the hash. Set pad items that never change here: // TODO: this part can be set by the caller outside its loop iPt[4] = 0x3636363636363636U; iPt[5] = 0x3636363636363636U; iPt[6] = 0x3636363636363636U; iPt[7] = 0x3636363636363636U; iPt[8] = 0x3636363636363636U; iPt[9] = 0x3636363636363636U; iPt[10] = 0x3636363636363636U; iPt[11] = 0x3636363636363636U; iPt[12] = 0x3636363636363636U; iPt[13] = 0x3636363636363636U; iPt[14] = 0x3636363636363636U; iPt[15] = 0x3636363636363636U; oPt[4] = 0x5c5c5c5c5c5c5c5cU; oPt[5] = 0x5c5c5c5c5c5c5c5cU; oPt[6] = 0x5c5c5c5c5c5c5c5cU; oPt[7] = 0x5c5c5c5c5c5c5c5cU; oPt[8] = 0x5c5c5c5c5c5c5c5cU; oPt[9] = 0x5c5c5c5c5c5c5c5cU; oPt[10] = 0x5c5c5c5c5c5c5c5cU; oPt[11] = 0x5c5c5c5c5c5c5c5cU; oPt[12] = 0x5c5c5c5c5c5c5c5cU; oPt[13] = 0x5c5c5c5c5c5c5c5cU; oPt[14] = 0x5c5c5c5c5c5c5c5cU; oPt[15] = 0x5c5c5c5c5c5c5c5cU; Scalar sclrParent = new(hPt, out int overflow); if (overflow != 0) { return(false); } foreach (uint index in path.Indexes) { if ((index & 0x80000000) != 0) // IsHardened { // First _byte_ is zero // private-key is the first 32 bytes (4 items) of hPt (total 33 bytes) // 4 bytes index + SHA padding are also added wPt[0] = (ulong)sclrParent.b7 << 24 | (ulong)sclrParent.b6 >> 8; wPt[1] = (ulong)sclrParent.b6 << 56 | (ulong)sclrParent.b5 << 24 | (ulong)sclrParent.b4 >> 8; wPt[2] = (ulong)sclrParent.b4 << 56 | (ulong)sclrParent.b3 << 24 | (ulong)sclrParent.b2 >> 8; wPt[3] = (ulong)sclrParent.b2 << 56 | (ulong)sclrParent.b1 << 24 | (ulong)sclrParent.b0 >> 8; wPt[4] = (ulong)sclrParent.b0 << 56 | (ulong)index << 24 | 0b00000000_00000000_00000000_00000000_00000000_10000000_00000000_00000000UL; } else { Span <byte> pubkeyBytes = comparer.Calc.GetPubkey(sclrParent, true); fixed(byte *pubXPt = &pubkeyBytes[0]) { wPt[0] = (ulong)pubXPt[0] << 56 | (ulong)pubXPt[1] << 48 | (ulong)pubXPt[2] << 40 | (ulong)pubXPt[3] << 32 | (ulong)pubXPt[4] << 24 | (ulong)pubXPt[5] << 16 | (ulong)pubXPt[6] << 8 | pubXPt[7]; wPt[1] = (ulong)pubXPt[8] << 56 | (ulong)pubXPt[9] << 48 | (ulong)pubXPt[10] << 40 | (ulong)pubXPt[11] << 32 | (ulong)pubXPt[12] << 24 | (ulong)pubXPt[13] << 16 | (ulong)pubXPt[14] << 8 | pubXPt[15]; wPt[2] = (ulong)pubXPt[16] << 56 | (ulong)pubXPt[17] << 48 | (ulong)pubXPt[18] << 40 | (ulong)pubXPt[19] << 32 | (ulong)pubXPt[20] << 24 | (ulong)pubXPt[21] << 16 | (ulong)pubXPt[22] << 8 | pubXPt[23]; wPt[3] = (ulong)pubXPt[24] << 56 | (ulong)pubXPt[25] << 48 | (ulong)pubXPt[26] << 40 | (ulong)pubXPt[27] << 32 | (ulong)pubXPt[28] << 24 | (ulong)pubXPt[29] << 16 | (ulong)pubXPt[30] << 8 | pubXPt[31]; wPt[4] = (ulong)pubXPt[32] << 56 | (ulong)index << 24 | 0b00000000_00000000_00000000_00000000_00000000_10000000_00000000_00000000UL; } } 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] = 1320; // (1+32+4 + 128)*8 // Final result is SHA512(outer_pad | SHA512(inner_pad | 37_byte_data)) // 1. Compute SHA512(inner_pad | 37_byte_data) // Set pads to be used as working vectors (key is ChainCode that is the second 32 bytes of SHA512 iPt[0] = 0x3636363636363636U ^ hPt[4]; iPt[1] = 0x3636363636363636U ^ hPt[5]; iPt[2] = 0x3636363636363636U ^ hPt[6]; iPt[3] = 0x3636363636363636U ^ hPt[7]; oPt[0] = 0x5c5c5c5c5c5c5c5cU ^ hPt[4]; oPt[1] = 0x5c5c5c5c5c5c5c5cU ^ hPt[5]; oPt[2] = 0x5c5c5c5c5c5c5c5cU ^ hPt[6]; oPt[3] = 0x5c5c5c5c5c5c5c5cU ^ hPt[7]; Sha512Fo.Init(hPt); Sha512Fo.SetW(iPt); Sha512Fo.CompressBlockWithWSet(hPt, iPt); Sha512Fo.Compress165SecondBlock(hPt, wPt); // 2. Compute SHA512(outer_pad | hash) *(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; // (128+64)*8 Sha512Fo.Init(hPt); Sha512Fo.SetW(oPt); Sha512Fo.CompressBlockWithWSet(hPt, oPt); Sha512Fo.Compress192SecondBlock(hPt, wPt); // New private key is (parentPrvKey + int(hPt)) % order sclrParent = sclrParent.Add(new Scalar(hPt, out _), out _); } // Child extended key (private key + chianCode) should be set by adding the index to the end of the Path // and have been computed already hPt[0] = (ulong)sclrParent.b7 << 32 | sclrParent.b6; hPt[1] = (ulong)sclrParent.b5 << 32 | sclrParent.b4; hPt[2] = (ulong)sclrParent.b3 << 32 | sclrParent.b2; hPt[3] = (ulong)sclrParent.b1 << 32 | sclrParent.b0; return(comparer.Compare(hPt)); }
public unsafe bool SetBip32(Sha512Fo sha, byte *mnPt, int mnLen, ulong *iPt, ulong *oPt) { // The process is: PBKDF2(password=UTF8(mnemonic), salt=UTF8("mnemonic+passphrase") -> BIP32 seed // BIP32 -> HMACSHA(data=seed, key=MasterKeyHashKey) -> HMACSHA(data=key|index, key=ChainCode) // All HMACSHAs are using 512 variant // *** PBKDF2 *** // dkLen/HmacLen=1 => only 1 block => no loop needed // Salt is the "mnemonic+passPhrase" + blockNumber(=1) => fixed and set during precomputing ulong[] resultOfF = new ulong[8]; ulong[] uTemp = new ulong[80]; ulong[] iPadHashStateTemp = new ulong[8]; ulong[] oPadHashStateTemp = new ulong[8]; ulong parkey0, parkey1, parkey2, parkey3, carry; fixed(byte *dPt = &pbkdf2Salt[0]) fixed(ulong *hPt = &sha.hashState[0], wPt = &sha.w[0], seedPt = &resultOfF[0], uPt = &uTemp[0], ihPt = &iPadHashStateTemp[0], ohPt = &oPadHashStateTemp[0]) { // Setting values in uTemp that never change uPt[8] = 0b10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000UL; uPt[9] = 0; uPt[10] = 0; uPt[11] = 0; uPt[12] = 0; uPt[13] = 0; uPt[14] = 0; uPt[15] = 1536; // Set HMAC key ie. set pads (used as working vectors) if (mnLen > Sha512Fo.BlockByteSize) { // Key bytes must be hashed first sha.Init(hPt); sha.CompressData(mnPt, mnLen, mnLen, hPt, wPt); // Set pads to be used as working vectors iPt[0] = 0x3636363636363636U ^ hPt[0]; iPt[1] = 0x3636363636363636U ^ hPt[1]; iPt[2] = 0x3636363636363636U ^ hPt[2]; iPt[3] = 0x3636363636363636U ^ hPt[3]; iPt[4] = 0x3636363636363636U ^ hPt[4]; iPt[5] = 0x3636363636363636U ^ hPt[5]; iPt[6] = 0x3636363636363636U ^ hPt[6]; iPt[7] = 0x3636363636363636U ^ hPt[7]; iPt[8] = 0x3636363636363636U; iPt[9] = 0x3636363636363636U; iPt[10] = 0x3636363636363636U; iPt[11] = 0x3636363636363636U; iPt[12] = 0x3636363636363636U; iPt[13] = 0x3636363636363636U; iPt[14] = 0x3636363636363636U; iPt[15] = 0x3636363636363636U; oPt[0] = 0x5c5c5c5c5c5c5c5cU ^ hPt[0]; oPt[1] = 0x5c5c5c5c5c5c5c5cU ^ hPt[1]; oPt[2] = 0x5c5c5c5c5c5c5c5cU ^ hPt[2]; oPt[3] = 0x5c5c5c5c5c5c5c5cU ^ hPt[3]; oPt[4] = 0x5c5c5c5c5c5c5c5cU ^ hPt[4]; oPt[5] = 0x5c5c5c5c5c5c5c5cU ^ hPt[5]; oPt[6] = 0x5c5c5c5c5c5c5c5cU ^ hPt[6]; oPt[7] = 0x5c5c5c5c5c5c5c5cU ^ hPt[7]; oPt[8] = 0x5c5c5c5c5c5c5c5cU; oPt[9] = 0x5c5c5c5c5c5c5c5cU; oPt[10] = 0x5c5c5c5c5c5c5c5cU; oPt[11] = 0x5c5c5c5c5c5c5c5cU; oPt[12] = 0x5c5c5c5c5c5c5c5cU; oPt[13] = 0x5c5c5c5c5c5c5c5cU; oPt[14] = 0x5c5c5c5c5c5c5c5cU; oPt[15] = 0x5c5c5c5c5c5c5c5cU; } else { byte[] temp = new byte[Sha512Fo.BlockByteSize]; fixed(byte *tPt = &temp[0]) { Buffer.MemoryCopy(mnPt, tPt, Sha512Fo.BlockByteSize, mnLen); for (int i = 0, j = 0; i < 16; i++, j += 8) { ulong val = ((ulong)tPt[j] << 56) | ((ulong)tPt[j + 1] << 48) | ((ulong)tPt[j + 2] << 40) | ((ulong)tPt[j + 3] << 32) | ((ulong)tPt[j + 4] << 24) | ((ulong)tPt[j + 5] << 16) | ((ulong)tPt[j + 6] << 8) | tPt[j + 7]; iPt[i] = 0x3636363636363636U ^ val; oPt[i] = 0x5c5c5c5c5c5c5c5cU ^ val; } } } // F() // compute u1 = hmac.ComputeHash(data=pbkdf2Salt); // Final result is SHA512(outer_pad | SHA512(inner_pad | data)) where data is pbkdf2Salt // 1. Compute SHA512(inner_pad | data) sha.Init(hPt); sha.CompressBlock(hPt, iPt); // Make a copy of hashState of inner-pad to be used in the loop below (explaination in the loop) *(Block64 *)ihPt = *(Block64 *)hPt; // Data length is unknown and an initial block of 128 bytes was already compressed sha.CompressData(dPt, pbkdf2Salt.Length, pbkdf2Salt.Length + 128, hPt, wPt); // 2. Compute SHA512(outer_pad | hash) *(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 sha.Init(hPt); sha.CompressBlock(hPt, oPt); // Make a copy of hashState of outer-pad to be used in the loop below (explaination in the loop) *(Block64 *)ohPt = *(Block64 *)hPt; sha.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 *)uPt = *(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 *)ihPt; sha.Compress192SecondBlock(hPt, uPt); // 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 *)ohPt; sha.Compress192SecondBlock(hPt, wPt); // result of F() is XOR sum of all u arrays if (Avx2.IsSupported) // AVX512 :( { Vector256 <ulong> part1 = Avx2.Xor(Avx2.LoadVector256(seedPt), Avx2.LoadVector256(hPt)); Vector256 <ulong> part2 = Avx2.Xor(Avx2.LoadVector256(seedPt + 4), Avx2.LoadVector256(hPt + 4)); Avx2.Store(seedPt, part1); Avx2.Store(seedPt + 4, part2); } else { 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]; } } // *** BIP32 *** // Set from entropy/seed by computing HMAC(data=seed, key="Bitcoin seed") // Final result is SHA512(outer_pad | SHA512(inner_pad | data)) where data is 64-byte seed // 1. Compute SHA512(inner_pad | data) sha.Init_InnerPad_Bitcoinseed(hPt); *(Block64 *)wPt = *(Block64 *)seedPt; // from wPt[8] to wPt[15] didn't change sha.Compress192SecondBlock(hPt, wPt); // 2. Compute SHA512(outer_pad | hash) *(Block64 *)wPt = *(Block64 *)hPt; // ** Copy hashState before changing it ** // from wPt[8] to wPt[15] didn't change sha.Init_OuterPad_Bitcoinseed(hPt); sha.Compress192SecondBlock(hPt, wPt); // Master key is set. PrivateKey= first 32-bytes of hPt and ChainCode is second 32-bytes // Each child is derived by computing HMAC(data=(hardened? 0|prvKey : pubkey) | index, key=ChainCode) // ChainCode is the second 32-byte half of the hash. Set pad items that never change here: iPt[4] = 0x3636363636363636U; iPt[5] = 0x3636363636363636U; iPt[6] = 0x3636363636363636U; iPt[7] = 0x3636363636363636U; iPt[8] = 0x3636363636363636U; iPt[9] = 0x3636363636363636U; iPt[10] = 0x3636363636363636U; iPt[11] = 0x3636363636363636U; iPt[12] = 0x3636363636363636U; iPt[13] = 0x3636363636363636U; iPt[14] = 0x3636363636363636U; iPt[15] = 0x3636363636363636U; oPt[4] = 0x5c5c5c5c5c5c5c5cU; oPt[5] = 0x5c5c5c5c5c5c5c5cU; oPt[6] = 0x5c5c5c5c5c5c5c5cU; oPt[7] = 0x5c5c5c5c5c5c5c5cU; oPt[8] = 0x5c5c5c5c5c5c5c5cU; oPt[9] = 0x5c5c5c5c5c5c5c5cU; oPt[10] = 0x5c5c5c5c5c5c5c5cU; oPt[11] = 0x5c5c5c5c5c5c5c5cU; oPt[12] = 0x5c5c5c5c5c5c5c5cU; oPt[13] = 0x5c5c5c5c5c5c5c5cU; oPt[14] = 0x5c5c5c5c5c5c5c5cU; oPt[15] = 0x5c5c5c5c5c5c5c5cU; uPt[5] = 0; uPt[6] = 0; uPt[7] = 0; uPt[8] = 0; uPt[9] = 0; uPt[10] = 0; uPt[11] = 0; uPt[12] = 0; uPt[13] = 0; uPt[14] = 0; uPt[15] = 1320; // (1+32+4 + 128)*8 BigInteger kParent = new BigInteger(sha.GetFirst32Bytes(hPt), true, true); if (kParent == 0 || kParent >= order) { return(false); } parkey0 = hPt[3]; parkey1 = hPt[2]; parkey2 = hPt[1]; parkey3 = hPt[0]; foreach (var index in path.Indexes) { if ((index & 0x80000000) != 0) // IsHardened { // First _byte_ is zero // private-key is the first 32 bytes (4 items) of hPt (total 33 bytes) // 4 bytes index + SHA padding are also added uPt[0] = parkey3 >> 8; uPt[1] = parkey3 << 56 | parkey2 >> 8; uPt[2] = parkey2 << 56 | parkey1 >> 8; uPt[3] = parkey1 << 56 | parkey0 >> 8; uPt[4] = parkey0 << 56 | (ulong)index << 24 | 0b00000000_00000000_00000000_00000000_00000000_10000000_00000000_00000000UL; } else { var point = calc.MultiplyByG(kParent); byte[] xBytes = point.X.ToByteArray(true, true).PadLeft(32); fixed(byte *pubXPt = &xBytes[0]) { uPt[0] = (point.Y.IsEven ? 0x0200000000000000UL : 0x0300000000000000UL) | (ulong)pubXPt[0] << 48 | (ulong)pubXPt[1] << 40 | (ulong)pubXPt[2] << 32 | (ulong)pubXPt[3] << 24 | (ulong)pubXPt[4] << 16 | (ulong)pubXPt[5] << 8 | pubXPt[6]; uPt[1] = (ulong)pubXPt[7] << 56 | (ulong)pubXPt[8] << 48 | (ulong)pubXPt[9] << 40 | (ulong)pubXPt[10] << 32 | (ulong)pubXPt[11] << 24 | (ulong)pubXPt[12] << 16 | (ulong)pubXPt[13] << 8 | pubXPt[14]; uPt[2] = (ulong)pubXPt[15] << 56 | (ulong)pubXPt[16] << 48 | (ulong)pubXPt[17] << 40 | (ulong)pubXPt[18] << 32 | (ulong)pubXPt[19] << 24 | (ulong)pubXPt[20] << 16 | (ulong)pubXPt[21] << 8 | pubXPt[22]; uPt[3] = (ulong)pubXPt[23] << 56 | (ulong)pubXPt[24] << 48 | (ulong)pubXPt[25] << 40 | (ulong)pubXPt[26] << 32 | (ulong)pubXPt[27] << 24 | (ulong)pubXPt[28] << 16 | (ulong)pubXPt[29] << 8 | pubXPt[30]; uPt[4] = (ulong)pubXPt[31] << 56 | (ulong)index << 24 | 0b00000000_00000000_00000000_00000000_00000000_10000000_00000000_00000000UL; } } // Final result is SHA512(outer_pad | SHA512(inner_pad | 37_byte_data)) // 1. Compute SHA512(inner_pad | 37_byte_data) // Set pads to be used as working vectors (key is ChainCode that is the second 32 bytes of SHA512 iPt[0] = 0x3636363636363636U ^ hPt[4]; iPt[1] = 0x3636363636363636U ^ hPt[5]; iPt[2] = 0x3636363636363636U ^ hPt[6]; iPt[3] = 0x3636363636363636U ^ hPt[7]; oPt[0] = 0x5c5c5c5c5c5c5c5cU ^ hPt[4]; oPt[1] = 0x5c5c5c5c5c5c5c5cU ^ hPt[5]; oPt[2] = 0x5c5c5c5c5c5c5c5cU ^ hPt[6]; oPt[3] = 0x5c5c5c5c5c5c5c5cU ^ hPt[7]; sha.Init(hPt); sha.CompressBlock(hPt, iPt); sha.Compress165SecondBlock(hPt, uPt); // 2. Compute SHA512(outer_pad | hash) *(Block64 *)wPt = *(Block64 *)hPt; // from wPt[8] to wPt[15] didn't change sha.Init(hPt); sha.CompressBlock(hPt, oPt); sha.Compress192SecondBlock(hPt, wPt); // New private key is (parentPrvKey + int(hPt)) % order // TODO: this is a bottleneck and needs to be replaced by a ModularUInt256 instance kParent = (kParent + new BigInteger(sha.GetFirst32Bytes(hPt), true, true)) % order; ulong toAdd = hPt[3]; parkey0 += toAdd; if (parkey0 < toAdd) { parkey1++; } toAdd = hPt[2]; parkey1 += toAdd; if (parkey1 < toAdd) { parkey2++; } toAdd = hPt[1]; parkey2 += toAdd; if (parkey2 < toAdd) { parkey3++; } toAdd = hPt[0]; parkey3 += toAdd; if (parkey3 < toAdd) { carry = 1; } else { carry = 0; } bool bigger = false; if (carry == 1) { bigger = true; } else if (parkey3 == N3) { if (parkey2 > N2) { bigger = true; } else if (parkey2 == N2) { if (parkey1 > N1) { bigger = true; } else if (parkey1 == N1) { if (parkey0 >= N0) { bigger = true; } } } } if (bigger) { if (parkey0 < N0) { parkey1--; } parkey0 -= N0; if (parkey1 < N1) { parkey2--; } parkey1 -= N1; if (parkey2 < N2) { parkey3--; } parkey2 -= N2; parkey3 -= N3; } } // Child extended key (private key + chianCode) should be set by adding the index to the end of the Path // and have been computed already hPt[0] = parkey3; hPt[1] = parkey2; hPt[2] = parkey1; hPt[3] = parkey0; return(comparer.Compare(sha.GetFirst32Bytes(hPt))); } }
private static unsafe void SetHmacPads(byte[] mnBytes, ulong[] pads) { Debug.Assert(mnBytes != null && mnBytes.Length > 0); Debug.Assert(pads != null && pads.Length == 2 * Sha512Fo.HashStateSize); // PBKDF2: // compute u1 = hmac.ComputeHash(data=pbkdf2Salt/pass, key=mnemonic); // u1 = SHA512(outer_pad | SHA512(inner_pad | pass | 0x00000001 )) // First block of each SHA512 is the pad with key already set to mnemonic so they can be pre-computed ulong *hPt = stackalloc ulong[Sha512Fo.UBufferSize + Sha512Fo.WorkingVectorSize]; ulong *iPt = hPt + Sha512Fo.HashStateSize; ulong *oPt = hPt + Sha512Fo.UBufferSize; fixed(byte *mnPt = &mnBytes[0]) fixed(ulong *rPt = &pads[0]) { if (mnBytes.Length > Sha512Fo.BlockByteSize) { // Key bytes must be hashed first Sha512Fo.Init(hPt); Sha512Fo.CompressData(mnPt, mnBytes.Length, mnBytes.Length, hPt, iPt); iPt[0] = 0x3636363636363636U ^ hPt[0]; iPt[1] = 0x3636363636363636U ^ hPt[1]; iPt[2] = 0x3636363636363636U ^ hPt[2]; iPt[3] = 0x3636363636363636U ^ hPt[3]; iPt[4] = 0x3636363636363636U ^ hPt[4]; iPt[5] = 0x3636363636363636U ^ hPt[5]; iPt[6] = 0x3636363636363636U ^ hPt[6]; iPt[7] = 0x3636363636363636U ^ hPt[7]; iPt[8] = 0x3636363636363636U; iPt[9] = 0x3636363636363636U; iPt[10] = 0x3636363636363636U; iPt[11] = 0x3636363636363636U; iPt[12] = 0x3636363636363636U; iPt[13] = 0x3636363636363636U; iPt[14] = 0x3636363636363636U; iPt[15] = 0x3636363636363636U; oPt[0] = 0x5c5c5c5c5c5c5c5cU ^ hPt[0]; oPt[1] = 0x5c5c5c5c5c5c5c5cU ^ hPt[1]; oPt[2] = 0x5c5c5c5c5c5c5c5cU ^ hPt[2]; oPt[3] = 0x5c5c5c5c5c5c5c5cU ^ hPt[3]; oPt[4] = 0x5c5c5c5c5c5c5c5cU ^ hPt[4]; oPt[5] = 0x5c5c5c5c5c5c5c5cU ^ hPt[5]; oPt[6] = 0x5c5c5c5c5c5c5c5cU ^ hPt[6]; oPt[7] = 0x5c5c5c5c5c5c5c5cU ^ hPt[7]; oPt[8] = 0x5c5c5c5c5c5c5c5cU; oPt[9] = 0x5c5c5c5c5c5c5c5cU; oPt[10] = 0x5c5c5c5c5c5c5c5cU; oPt[11] = 0x5c5c5c5c5c5c5c5cU; oPt[12] = 0x5c5c5c5c5c5c5c5cU; oPt[13] = 0x5c5c5c5c5c5c5c5cU; oPt[14] = 0x5c5c5c5c5c5c5c5cU; oPt[15] = 0x5c5c5c5c5c5c5c5cU; } else { byte[] temp = new byte[Sha512Fo.BlockByteSize]; fixed(byte *tPt = &temp[0]) { Buffer.MemoryCopy(mnPt, tPt, Sha512Fo.BlockByteSize, mnBytes.Length); for (int i = 0, j = 0; i < 16; i++, j += 8) { ulong val = ((ulong)tPt[j] << 56) | ((ulong)tPt[j + 1] << 48) | ((ulong)tPt[j + 2] << 40) | ((ulong)tPt[j + 3] << 32) | ((ulong)tPt[j + 4] << 24) | ((ulong)tPt[j + 5] << 16) | ((ulong)tPt[j + 6] << 8) | tPt[j + 7]; iPt[i] = 0x3636363636363636U ^ val; oPt[i] = 0x5c5c5c5c5c5c5c5cU ^ val; } } } Sha512Fo.Init(hPt); Sha512Fo.SetW(iPt); Sha512Fo.CompressBlockWithWSet(hPt, iPt); *(Block64 *)rPt = *(Block64 *)hPt; Sha512Fo.Init(hPt); Sha512Fo.SetW(oPt); Sha512Fo.CompressBlockWithWSet(hPt, oPt); *(Block64 *)(rPt + Sha512Fo.HashStateSize) = *(Block64 *)hPt; } }
public void ComputeHashTest(byte[] message, byte[] expectedHash) { byte[] actualHash = Sha512Fo.ComputeHash(message); Assert.Equal(expectedHash, actualHash); }