public static void DeriveKey(byte[] password, ReadOnlySpan <byte> salt, Sha2 prf, uint iterationCount, Span <byte> destination) { Debug.Assert(password != null); Debug.Assert(salt != null); Debug.Assert(destination.Length > 0); int numBytesWritten = 0; int numBytesRemaining = destination.Length; int saltWithBlockLength = salt.Length + sizeof(uint); byte[]? saltArray = null; byte[]? arrayToReturn = null; try { Span <byte> saltWithBlockIndex = saltWithBlockLength > SaltSizeThreshold ? (saltArray = ArrayPool <byte> .Shared.Rent(saltWithBlockLength)) : stackalloc byte[SaltSizeThreshold]; saltWithBlockIndex = saltWithBlockIndex.Slice(0, saltWithBlockLength); salt.CopyTo(saltWithBlockIndex); Span <byte> hmacKey = stackalloc byte[Sha2.BlockSizeStackallocThreshold * 2]; var hashAlgorithm = new Hmac(prf, password, hmacKey.Slice(0, prf.BlockSize * 2)); int wSize = prf.GetWorkingSetSize(int.MaxValue); int blockSize = hashAlgorithm.BlockSize; Span <byte> W = wSize > Constants.MaxStackallocBytes ? (arrayToReturn = ArrayPool <byte> .Shared.Rent(wSize)) : stackalloc byte[Constants.MaxStackallocBytes]; W = W.Slice(0, wSize); Span <byte> currentBlock = stackalloc byte[Sha2.HashSizeStackallocThreshold].Slice(0, hashAlgorithm.HashSize); Span <byte> iterationBlock = stackalloc byte[Sha2.HashSizeStackallocThreshold].Slice(0, hashAlgorithm.HashSize); Span <byte> blockIndexDestination = saltWithBlockIndex.Slice(saltWithBlockIndex.Length - sizeof(uint)); for (uint blockIndex = 1; numBytesRemaining > 0; blockIndex++) { BinaryPrimitives.WriteUInt32BigEndian(blockIndexDestination, blockIndex); hashAlgorithm.ComputeHash(saltWithBlockIndex, currentBlock, W); // U_1 currentBlock.CopyTo(iterationBlock); for (int iter = 1; iter < iterationCount; iter++) { hashAlgorithm.ComputeHash(currentBlock, currentBlock, W); Xor(src: currentBlock, dest: iterationBlock); } int numBytesToCopy = Math.Min(numBytesRemaining, iterationBlock.Length); iterationBlock.Slice(0, numBytesToCopy).CopyTo(destination.Slice(numBytesWritten)); numBytesWritten += numBytesToCopy; numBytesRemaining -= numBytesToCopy; } } finally { if (saltArray != null) { ArrayPool <byte> .Shared.Return(saltArray); } if (arrayToReturn != null) { ArrayPool <byte> .Shared.Return(arrayToReturn); } } }