Beispiel #1
0
        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);
                }
            }
        }