Beispiel #1
0
        // Taken from https://github.com/dotnet/runtime/blob/master/src/libraries/System.Security.Cryptography.Primitives/src/System/Security/Cryptography/CryptoStream.cs#L516
        public static int Transform(ICryptoTransform transform, ReadOnlySpan <byte> input, int inputOffset, int inputLength, Span <byte> output)
        {
            byte[] buffer          = input.ToArray();
            int    offset          = inputOffset;
            int    count           = inputLength;
            var    inputBlockSize  = transform.InputBlockSize;
            var    outputBlockSize = transform.OutputBlockSize;

            var inputBuffer = ArrayPool <byte> .Shared.Rent(inputBlockSize);

            var outputBuffer = ArrayPool <byte> .Shared.Rent(outputBlockSize);

            try
            {
                int inputBufferIndex = 0;

                // write <= count bytes to the output stream, transforming as we go.
                // Basic idea: using bytes in the _InputBuffer first, make whole blocks,
                // transform them, and write them out.  Cache any remaining bytes in the _InputBuffer.
                int bytesToWrite      = count;
                int currentInputIndex = offset;

                // if we have some bytes in the _InputBuffer, we have to deal with those first,
                // so let's try to make an entire block out of it
                int numOutputBytes;
                int outputLength = 0;
                while (bytesToWrite > 0)
                {
                    if (bytesToWrite >= inputBlockSize)
                    {
                        // We have at least an entire block's worth to transform
                        int numWholeBlocks = bytesToWrite / inputBlockSize;

                        // If the transform will handle multiple blocks at once, do that
                        if (transform.CanTransformMultipleBlocks && numWholeBlocks > 1)
                        {
                            int numWholeBlocksInBytes = numWholeBlocks * inputBlockSize;

                            byte[] tempOutputBuffer = ArrayPool <byte> .Shared.Rent(numWholeBlocksInBytes);

                            Span <byte> outputSpan;
                            try
                            {
                                numOutputBytes = transform.TransformBlock(buffer, currentInputIndex, numWholeBlocksInBytes, tempOutputBuffer, 0);

                                outputSpan = tempOutputBuffer.AsSpan(0, numOutputBytes);
                                outputSpan.CopyTo(output.Slice(outputLength));
                                outputLength += numOutputBytes;

                                currentInputIndex += numWholeBlocksInBytes;
                                bytesToWrite      -= numWholeBlocksInBytes;
                                CryptographicOperations.ZeroMemory(outputSpan);
                            }
                            finally
                            {
                                ArrayPool <byte> .Shared.Return(tempOutputBuffer);
                            }
                        }
                        else
                        {
                            // do it the slow way
                            numOutputBytes = transform.TransformBlock(buffer, currentInputIndex, inputBlockSize, outputBuffer, 0);

                            outputBuffer.AsSpan(0, numOutputBytes).CopyTo(output.Slice(outputLength));
                            outputLength += numOutputBytes;

                            currentInputIndex += inputBlockSize;
                            bytesToWrite      -= inputBlockSize;
                        }
                    }
                    else
                    {
                        // In this case, we don't have an entire block's worth left, so store it up in the
                        // input buffer, which by now must be empty.
                        Buffer.BlockCopy(buffer, currentInputIndex, inputBuffer, 0, bytesToWrite);
                        inputBufferIndex += bytesToWrite;
                        break;
                    }
                }

                byte[] finalBytes = transform.TransformFinalBlock(inputBuffer, 0, inputBufferIndex);
                finalBytes.AsSpan().CopyTo(output.Slice(outputLength));
                return(outputLength + finalBytes.Length);
            }
            finally
            {
                ArrayPool <byte> .Shared.Return(inputBuffer);

                ArrayPool <byte> .Shared.Return(outputBuffer);
            }
        }
Beispiel #2
0
        /// <inheritsdoc />
        public override bool TryUnwrapKey(ReadOnlySpan <byte> key, Span <byte> destination, JwtHeaderDocument header, out int bytesWritten)
        {
            Debug.Assert(header != null);
            if (key.IsEmpty)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
            }

            if (_disposed)
            {
                ThrowHelper.ThrowObjectDisposedException(GetType());
            }

#if SUPPORT_SPAN_CRYPTO
#if !NETCOREAPP
            return(_rsa.TryDecrypt(key, destination, _padding, out bytesWritten));
#else
            try
            {
                // https://github.com/dotnet/corefx/pull/36601
                bool decrypted;
                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    int keySizeBytes = _key.KeySizeInBits / 8;

                    // OpenSSL does not take a length value for the destination, so it can write out of bounds.
                    // To prevent the OOB write, decrypt into a temporary buffer.
                    if (destination.Length < keySizeBytes)
                    {
                        Span <byte> tmp = stackalloc byte[0];
                        byte[]? rent = null;

                        try
                        {
                            // RSA up through 4096 stackalloc
                            if (_key.KeySizeInBits <= 4096)
                            {
                                tmp = stackalloc byte[keySizeBytes];
                            }
                            else
                            {
                                rent = ArrayPool <byte> .Shared.Rent(keySizeBytes);

                                tmp = rent;
                            }

                            decrypted = _rsa.TryDecrypt(key, tmp, _padding, out bytesWritten);
                            if (decrypted)
                            {
                                if (bytesWritten > destination.Length)
                                {
                                    decrypted    = false;
                                    bytesWritten = 0;
                                }
                                else
                                {
                                    tmp = tmp.Slice(0, bytesWritten);
                                    tmp.CopyTo(destination);
                                }

                                CryptographicOperations.ZeroMemory(tmp);
                            }
                        }
                        finally
                        {
                            if (rent != null)
                            {
                                // Already cleared
                                ArrayPool <byte> .Shared.Return(rent);
                            }
                        }
                    }
                    else
                    {
                        decrypted = _rsa.TryDecrypt(key, destination, _padding, out bytesWritten);
                    }
                }
                else
                {
                    decrypted = _rsa.TryDecrypt(key, destination, _padding, out bytesWritten);
                }

                return(decrypted);
            }
            catch (CryptographicException)
            {
                bytesWritten = 0;
                return(false);
            }
#endif
#else
            try
            {
                var result = _rsa.Decrypt(key.ToArray(), _padding);
                bytesWritten = result.Length;
                result.CopyTo(destination);

                return(true);
            }
            catch (CryptographicException)
            {
                bytesWritten = 0;
                return(false);
            }
#endif
        }