private bool VerifyAuthenticationTag(ReadOnlySpan <byte> key, ReadOnlySpan <byte> iv, ReadOnlySpan <byte> associatedData, ReadOnlySpan <byte> ciphertext, ReadOnlySpan <byte> authenticationTag)
        {
            int tagSize = GetTagSize();

            if (authenticationTag.Length != tagSize)
            {
                return(false);
            }

            byte[]? byteArrayToReturnToPool = null;
            int         macLength = associatedData.Length + iv.Length + ciphertext.Length + sizeof(long);
            Span <byte> macBytes  = macLength > Constants.MaxStackallocBytes
                                    ? (byteArrayToReturnToPool = ArrayPool <byte> .Shared.Rent(macLength))
                                    : stackalloc byte[Constants.MaxStackallocBytes];

            try
            {
                macBytes = macBytes.Slice(0, macLength);
                associatedData.CopyTo(macBytes);
                var bytes = macBytes.Slice(associatedData.Length);
                iv.CopyTo(bytes);
                bytes = bytes.Slice(iv.Length);
                ciphertext.CopyTo(bytes);
                bytes = bytes.Slice(ciphertext.Length);
                BinaryPrimitives.WriteInt64BigEndian(bytes, associatedData.Length << 3);

                Sha2        hashAlgorithm = _encryptionAlgorithm.SignatureAlgorithm.Sha;
                Span <byte> hmacKey       = stackalloc byte[Sha2.BlockSizeStackallocThreshold * 2]
                                            .Slice(0, hashAlgorithm.BlockSize * 2);
                Hmac        hmac = new Hmac(hashAlgorithm, key, hmacKey);
                Span <byte> hash = stackalloc byte[AuthenticatedEncryptor.TagSizeStackallocThreshold]
                                   .Slice(0, authenticationTag.Length * 2);
                hmac.ComputeHash(macBytes, hash);
                CryptographicOperations.ZeroMemory(hmacKey);

                return(CryptographicOperations.FixedTimeEquals(authenticationTag, hash.Slice(0, tagSize)));
            }
            finally
            {
                if (byteArrayToReturnToPool != null)
                {
                    ArrayPool <byte> .Shared.Return(byteArrayToReturnToPool);
                }
            }
        }
        public static void EmptyReturnTrue()
        {
            int byteLength = 0;

            byte[] rented = ArrayPool <byte> .Shared.Rent(byteLength);

            Span <byte> testSpan = new Span <byte>(rented, 0, byteLength);

            Fill(rented, 0, byteLength);

            ReadOnlySpan <byte> emptySpan = ReadOnlySpan <byte> .Empty;

            bool isEqualA = CryptographicOperations.FixedTimeEquals(testSpan, emptySpan);
            bool isEqualB = CryptographicOperations.FixedTimeEquals(emptySpan, testSpan);

            ArrayPool <byte> .Shared.Return(rented);

            Assert.True(isEqualA, "FixedTimeEquals(testSpan, emptySpan)");
            Assert.True(isEqualB, "FixedTimeEquals(emptySpan, testSpan)");
        }
        public static void EqualReturnsTrue(int byteLength)
        {
            byte[] rented = ArrayPool <byte> .Shared.Rent(byteLength);

            Span <byte> testSpan = new Span <byte>(rented, 0, byteLength);

            Fill(rented, 0, byteLength);

            byte[] rented2 = ArrayPool <byte> .Shared.Rent(byteLength);

            Span <byte> testSpan2 = new Span <byte>(rented2, 0, byteLength);

            testSpan.CopyTo(testSpan2);

            bool isEqual = CryptographicOperations.FixedTimeEquals(testSpan, testSpan2);

            ArrayPool <byte> .Shared.Return(rented);

            ArrayPool <byte> .Shared.Return(rented2);

            Assert.True(isEqual);
        }
        public static void DifferentLengthsReturnFalse(int byteLength)
        {
            byte[] rented = ArrayPool <byte> .Shared.Rent(byteLength);

            Span <byte> testSpan = new Span <byte>(rented, 0, byteLength);

            Fill(rented, 0, byteLength);

            byte[] rented2 = ArrayPool <byte> .Shared.Rent(byteLength);

            Span <byte> testSpan2 = new Span <byte>(rented2, 0, byteLength);

            testSpan.CopyTo(testSpan2);

            bool isEqualA = CryptographicOperations.FixedTimeEquals(testSpan, testSpan2.Slice(0, byteLength - 1));
            bool isEqualB = CryptographicOperations.FixedTimeEquals(testSpan.Slice(0, byteLength - 1), testSpan2);

            ArrayPool <byte> .Shared.Return(rented);

            ArrayPool <byte> .Shared.Return(rented2);

            Assert.False(isEqualA, "value, value missing last byte");
            Assert.False(isEqualB, "value missing last byte, value");
        }
Exemple #5
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 = _rsa.KeySize / 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 (_rsa.KeySize <= Constants.MaxStackallocBytes * 8 * 2)
                            {
                                tmp = stackalloc byte[Constants.MaxStackallocBytes * 2];
                            }
                            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
        }
Exemple #6
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);
            }
        }