private static unsafe void Pbkdf2HmacSha256(ProtectedMemory password, byte *salt, int saltLength, long c, int dkLen, byte *result) { if (c < 1) { throw new ArgumentException("The count " + nameof(c) + " cannot be less than 1!"); } const int digestLength = 0x20; int blockCount = (int)Math.Ceiling((double)dkLen / digestLength); int saltBufferLength = saltLength + sizeof(int); IntPtr hSaltBuffer = Marshal.AllocHGlobal(saltBufferLength); byte * saltBuffer = (byte *)hSaltBuffer; Unsafe.CopyBlock(saltBuffer, salt, (uint)saltLength); for (int i = 1; i <= blockCount; i++) { MarshalExtensions.WriteInt32BigEndian(saltBuffer + saltLength, i); Sha256ProtectedCryptoProvider sha256 = new Sha256ProtectedCryptoProvider(); (IntPtr hU, _) = sha256.ComputeHmacUnsafe(password, saltBuffer, saltBufferLength); Unsafe.CopyBlock(result + ((i - 1) * digestLength), (void *)hU, digestLength); MarshalExtensions.ZeroFree(hU, digestLength); for (long j = 1; j < c; j++) { (IntPtr hUi, _) = sha256.ComputeHmacUnsafe(password, saltBuffer, digestLength); byte *ui = (byte *)hUi; for (int k = 0; k < digestLength; k++) { (result + ((i - 1) * digestLength))[k] ^= ui[k]; } MarshalExtensions.ZeroFree(hUi, digestLength); } } MarshalExtensions.ZeroFree(hSaltBuffer, saltBufferLength); }
public ProtectedMemory ComputeHashProtected(ProtectedMemory protectedMemory, int n, int r, int p, int desiredKeyLength, byte[] salt) { IntPtr hash = Digest(protectedMemory, salt, n, r, p, desiredKeyLength); ProtectedMemory protectedResult = ProtectedMemory.Allocate(desiredKeyLength); using (ProtectedMemoryAccess access = new ProtectedMemoryAccess(protectedResult)) { MarshalExtensions.Copy(hash, 0, access.Handle, 0, desiredKeyLength); } MarshalExtensions.ZeroFree(hash, desiredKeyLength); return(protectedResult); }
public unsafe (IntPtr, int) ComputeHmacUnsafe(byte *key, int keyLength, byte *message, int messageLength, bool freeKey = false) { if (keyLength > blockSize) { (IntPtr reducedKey, _) = ComputeHashUnsafe(key, keyLength); return(ComputeHmacUnsafe((byte *)reducedKey, digestLength, message, messageLength, freeKey = true)); } IntPtr hPaddedKey = Marshal.AllocHGlobal(blockSize); MarshalExtensions.ZeroMemory(hPaddedKey, blockSize); Unsafe.CopyBlock((byte *)hPaddedKey, key, (uint)keyLength); if (freeKey) { MarshalExtensions.ZeroFree((IntPtr)key, keyLength); } IntPtr hOuterKeyPadded = Marshal.AllocHGlobal(blockSize); IntPtr hInnerKeyPadded = Marshal.AllocHGlobal(blockSize); byte * outerKeyPadded = (byte *)hOuterKeyPadded; byte * innerKeyPadded = (byte *)hInnerKeyPadded; Unsafe.CopyBlock(outerKeyPadded, (void *)hPaddedKey, blockSize); Unsafe.CopyBlock(innerKeyPadded, (void *)hPaddedKey, blockSize); MarshalExtensions.ZeroFree(hPaddedKey, blockSize); for (int i = 0; i < blockSize; i++) { outerKeyPadded[i] ^= 0x5c; } for (int i = 0; i < blockSize; i++) { innerKeyPadded[i] ^= 0x36; } int innerInputLength = blockSize + messageLength; IntPtr hInnerInput = Marshal.AllocHGlobal(innerInputLength); byte * innerInput = (byte *)hInnerInput; Unsafe.CopyBlock(innerInput, innerKeyPadded, blockSize); MarshalExtensions.ZeroFree(hInnerKeyPadded, blockSize); Unsafe.CopyBlock(innerInput + blockSize, message, (uint)messageLength); (IntPtr hInnerHash, _) = ComputeHashUnsafe(innerInput, innerInputLength); MarshalExtensions.ZeroFree(hInnerInput, innerInputLength); const int inputLength = blockSize + digestLength; IntPtr hInput = Marshal.AllocHGlobal(inputLength); byte * input = (byte *)hInput; Unsafe.CopyBlock(input, outerKeyPadded, blockSize); Unsafe.CopyBlock(input + blockSize, (void *)hInnerHash, digestLength); (IntPtr hResult, int resultLength) = ComputeHashUnsafe(input, inputLength); MarshalExtensions.ZeroFree(hOuterKeyPadded, blockSize); MarshalExtensions.ZeroFree(hInnerHash, digestLength); MarshalExtensions.ZeroFree(hInput, inputLength); return(hResult, resultLength); }
public string ComputeHash(ProtectedMemory protectedMemory, byte[] salt, int n, int r, int p, int desiredKeyLength) { byte[] resultBytes = new byte[desiredKeyLength]; IntPtr hash = Digest(protectedMemory, salt, n, r, p, desiredKeyLength); Marshal.Copy(hash, resultBytes, 0, desiredKeyLength); MarshalExtensions.ZeroFree(hash, desiredKeyLength); StringBuilder stringBuilder = new StringBuilder(desiredKeyLength + salt.Length + 20); stringBuilder.Append("$s2$"); stringBuilder.Append(n.ToString()).Append('$'); stringBuilder.Append(r.ToString()).Append('$'); stringBuilder.Append(p.ToString()).Append('$'); stringBuilder.Append(Convert.ToBase64String(salt)).Append('$'); stringBuilder.Append(Convert.ToBase64String(resultBytes)); return(stringBuilder.ToString()); }
internal unsafe IntPtr Digest(ProtectedMemory password, byte[] salt) { if (outLength == 0) { throw new InvalidOperationException("Hash not initialized."); } fixed(byte *pSalt = salt) { Pbkdf2HmacSha256(password, pSalt, salt.Length, 1, (int)blockSize * p, B[0]); } int iBlockSize = (int)blockSize; int vLength = n * iBlockSize; IntPtr hV = Marshal.AllocHGlobal(vLength); IntPtr hBuffer = Marshal.AllocHGlobal(iBlockSize); IntPtr hTempBuffer = Marshal.AllocHGlobal(64); byte * v = (byte *)hV; byte * buffer = (byte *)hBuffer; byte * tempBuffer = (byte *)hTempBuffer; for (int i = 0; i < p; i++) { /* https://en.wikipedia.org/wiki/Scrypt * Function ROMix(Block, Iterations) * * Create Iterations copies of X * X ← Block * for i ← 0 to Iterations−1 do * Vi ← X * X ← BlockMix(X) * * for i ← 0 to Iterations−1 do * j ← Integerify(X) mod Iterations * X ← BlockMix(X xor Vj) * * return X */ byte *source = B[i] + (((2 * r) - 1) * 64); for (int k = 0; k < n; k++) { Unsafe.CopyBlock(v + (k * blockSize), B[i], blockSize); Unsafe.CopyBlock(tempBuffer, source, 64u); BlockMix(B[i], v + (k * blockSize), tempBuffer); } long j; int len; for (int k = 0; k < n; k++) { uint *temp = (uint *)(B[i] + (((blockSize >> 6) - 1) * 64)); // C# uses little-endian integers by default! j = (((long)temp[1] << 32) + temp[0]) & (n - 1); len = iBlockSize; byte *d = B[i]; byte *s = v + (j * blockSize); while (len >= 8) { *(ulong *)d ^= *(ulong *)s; d += 8; s += 8; len -= 8; } if (len >= 4) { *(uint *)d ^= *(uint *)s; d += 4; s += 4; len -= 4; } if (len >= 2) { *(ushort *)d ^= *(ushort *)s; d += 2; s += 2; len -= 2; } if (len >= 1) { *d ^= *s; } Unsafe.CopyBlock(buffer, B[i], blockSize); Unsafe.CopyBlock(tempBuffer, source, 64u); BlockMix(B[i], buffer, tempBuffer); } } Marshal.Copy(blockSizeZeros, 0, hBuffer, iBlockSize); Marshal.FreeHGlobal(hBuffer); Marshal.Copy(zeros64, 0, hTempBuffer, 64); Marshal.FreeHGlobal(hTempBuffer); MarshalExtensions.ZeroFree(hV, vLength); IntPtr hOutput = Marshal.AllocHGlobal(outLength); byte * output = (byte *)hOutput; Pbkdf2HmacSha256(password, B[0], (int)blockSize * p, 1, outLength, output); return(hOutput); }
public override void Free() { MarshalExtensions.ZeroFree(directHandle, Size); }