Imported Upstream version 5.16.0.100

Former-commit-id: 38faa55fb9669e35e7d8448b15c25dc447f25767
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2018-08-07 15:19:03 +00:00
parent 0a9828183b
commit 7d7f676260
4419 changed files with 170950 additions and 90273 deletions

View File

@@ -7,6 +7,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Security.Cryptograph
{DF73E985-E143-4BF5-9FA4-E199E7D36235} = {DF73E985-E143-4BF5-9FA4-E199E7D36235}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Security.Cryptography.Primitives.Performance.Tests", "tests\Performance\System.Security.Cryptography.Primitives.Performance.Tests.csproj", "{FB3EA273-567D-414F-B36D-3698BE8D198B}"
ProjectSection(ProjectDependencies) = postProject
{DF73E985-E143-4BF5-9FA4-E199E7D36235} = {DF73E985-E143-4BF5-9FA4-E199E7D36235}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Security.Cryptography.Primitives", "src\System.Security.Cryptography.Primitives.csproj", "{DF73E985-E143-4BF5-9FA4-E199E7D36235}"
ProjectSection(ProjectDependencies) = postProject
{F050C895-297F-41C6-98C3-406D791AD515} = {F050C895-297F-41C6-98C3-406D791AD515}
@@ -30,6 +35,10 @@ Global
{101EB757-55A4-4F48-841C-C088640B8F57}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU
{101EB757-55A4-4F48-841C-C088640B8F57}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU
{101EB757-55A4-4F48-841C-C088640B8F57}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU
{FB3EA273-567D-414F-B36D-3698BE8D198B}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU
{FB3EA273-567D-414F-B36D-3698BE8D198B}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU
{FB3EA273-567D-414F-B36D-3698BE8D198B}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU
{FB3EA273-567D-414F-B36D-3698BE8D198B}.Release|Any CPU.Build.0 = netcoreapp-Release|Any CPU
{DF73E985-E143-4BF5-9FA4-E199E7D36235}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU
{DF73E985-E143-4BF5-9FA4-E199E7D36235}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU
{DF73E985-E143-4BF5-9FA4-E199E7D36235}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU
@@ -44,6 +53,7 @@ Global
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{101EB757-55A4-4F48-841C-C088640B8F57} = {1A2F9F4A-A032-433E-B914-ADD5992BB178}
{FB3EA273-567D-414F-B36D-3698BE8D198B} = {1A2F9F4A-A032-433E-B914-ADD5992BB178}
{DF73E985-E143-4BF5-9FA4-E199E7D36235} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD}
{F050C895-297F-41C6-98C3-406D791AD515} = {2E666815-2EDB-464B-9DF6-380BF4789AD4}
EndGlobalSection

View File

@@ -36,6 +36,11 @@ namespace System.Security.Cryptography
[System.ComponentModel.EditorBrowsableAttribute((System.ComponentModel.EditorBrowsableState)(1))]
OFB = 3,
}
public static partial class CryptographicOperations
{
public static bool FixedTimeEquals(System.ReadOnlySpan<byte> left, System.ReadOnlySpan<byte> right) => throw null;
public static void ZeroMemory(System.Span<byte> buffer) => throw null;
}
public partial class CryptographicUnexpectedOperationException : System.Security.Cryptography.CryptographicException
{
public CryptographicUnexpectedOperationException() { }

View File

@@ -17,4 +17,4 @@
<ProjectReference Include="..\..\System.Threading.Tasks\ref\System.Threading.Tasks.csproj" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
</Project>

View File

@@ -79,6 +79,9 @@
<data name="Cryptography_CryptoStream_FlushFinalBlockTwice" xml:space="preserve">
<value>FlushFinalBlock() method was called twice on a CryptoStream. It can only be called once.</value>
</data>
<data name="Cryptography_DefaultAlgorithm_NotSupported" xml:space="preserve">
<value>This platform does not allow the automatic selection of an algorithm.</value>
</data>
<data name="Cryptography_HashNotYetFinalized" xml:space="preserve">
<value>Hash must be finalized before the hash value is retrieved.</value>
</data>

View File

@@ -13,6 +13,8 @@
<ItemGroup>
<Compile Include="System\Security\Cryptography\AsymmetricAlgorithm.cs" />
<Compile Include="System\Security\Cryptography\CipherMode.cs" />
<Compile Include="System\Security\Cryptography\CryptoConfigForwarder.cs" />
<Compile Include="System\Security\Cryptography\CryptographicOperations.cs" />
<Compile Include="System\Security\Cryptography\CryptographicUnexpectedOperationException.cs" />
<Compile Include="System\Security\Cryptography\CryptoStream.cs" />
<Compile Include="System\Security\Cryptography\CryptoStreamMode.cs" />

View File

@@ -11,15 +11,11 @@ namespace System.Security.Cryptography
protected AsymmetricAlgorithm() { }
public static AsymmetricAlgorithm Create()
{
return Create("System.Security.Cryptography.AsymmetricAlgorithm");
}
public static AsymmetricAlgorithm Create() =>
throw new PlatformNotSupportedException(SR.Cryptography_DefaultAlgorithm_NotSupported);
public static AsymmetricAlgorithm Create(string algName)
{
throw new PlatformNotSupportedException();
}
public static AsymmetricAlgorithm Create(string algName) =>
(AsymmetricAlgorithm)CryptoConfigForwarder.CreateFromName(algName);
public virtual int KeySize
{

View File

@@ -0,0 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Reflection;
namespace System.Security.Cryptography
{
internal static class CryptoConfigForwarder
{
private static readonly Func<string, object> s_createFromName = BindCreateFromName();
private static Func<string, object> BindCreateFromName()
{
const string CryptoConfigTypeName =
"System.Security.Cryptography.CryptoConfig, System.Security.Cryptography.Algorithms";
const string CreateFromNameMethodName = "CreateFromName";
Type t = Type.GetType(CryptoConfigTypeName, throwOnError: true);
MethodInfo createFromName = t.GetMethod(CreateFromNameMethodName, new[] { typeof(string) });
if (createFromName == null)
{
throw new MissingMethodException(t.FullName, CreateFromNameMethodName);
}
return (Func<string, object>)createFromName.CreateDelegate(typeof(Func<string, object>));
}
internal static object CreateFromName(string name) => s_createFromName(name);
}
}

View File

@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Buffers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -238,7 +239,7 @@ namespace System.Security.Cryptography
public override int Read(byte[] buffer, int offset, int count)
{
CheckReadArguments(buffer, offset, count);
return ReadAsyncCore(buffer, offset, count, default(CancellationToken), useAsync: false).ConfigureAwait(false).GetAwaiter().GetResult();
return ReadAsyncCore(buffer, offset, count, default(CancellationToken), useAsync: false).GetAwaiter().GetResult();
}
private void CheckReadArguments(byte[] buffer, int offset, int count)
@@ -270,6 +271,8 @@ namespace System.Security.Cryptography
Buffer.BlockCopy(_outputBuffer, 0, buffer, offset, _outputBufferIndex);
bytesToDeliver -= _outputBufferIndex;
currentOutputIndex += _outputBufferIndex;
int toClear = _outputBuffer.Length - _outputBufferIndex;
CryptographicOperations.ZeroMemory(new Span<byte>(_outputBuffer, _outputBufferIndex, toClear));
_outputBufferIndex = 0;
}
else
@@ -277,6 +280,10 @@ namespace System.Security.Cryptography
Buffer.BlockCopy(_outputBuffer, 0, buffer, offset, count);
Buffer.BlockCopy(_outputBuffer, count, _outputBuffer, 0, _outputBufferIndex - count);
_outputBufferIndex -= count;
int toClear = _outputBuffer.Length - _outputBufferIndex;
CryptographicOperations.ZeroMemory(new Span<byte>(_outputBuffer, _outputBufferIndex, toClear));
return (count);
}
}
@@ -294,65 +301,98 @@ namespace System.Security.Cryptography
int numOutputBytes;
// OK, see first if it's a multi-block transform and we can speed up things
if (bytesToDeliver > _outputBlockSize)
int blocksToProcess = bytesToDeliver / _outputBlockSize;
if (blocksToProcess > 1 && _transform.CanTransformMultipleBlocks)
{
if (_transform.CanTransformMultipleBlocks)
int numWholeBlocksInBytes = blocksToProcess * _inputBlockSize;
byte[] tempInputBuffer = ArrayPool<byte>.Shared.Rent(numWholeBlocksInBytes);
byte[] tempOutputBuffer = null;
try
{
int BlocksToProcess = bytesToDeliver / _outputBlockSize;
int numWholeBlocksInBytes = BlocksToProcess * _inputBlockSize;
byte[] tempInputBuffer = new byte[numWholeBlocksInBytes];
// get first the block already read
Buffer.BlockCopy(_inputBuffer, 0, tempInputBuffer, 0, _inputBufferIndex);
amountRead = _inputBufferIndex;
amountRead += useAsync ?
await _stream.ReadAsync(tempInputBuffer, _inputBufferIndex, numWholeBlocksInBytes - _inputBufferIndex, cancellationToken) :
amountRead = useAsync ?
await _stream.ReadAsync(new Memory<byte>(tempInputBuffer, _inputBufferIndex, numWholeBlocksInBytes - _inputBufferIndex), cancellationToken) :
_stream.Read(tempInputBuffer, _inputBufferIndex, numWholeBlocksInBytes - _inputBufferIndex);
_inputBufferIndex = 0;
if (amountRead <= _inputBlockSize)
int totalInput = _inputBufferIndex + amountRead;
// If there's still less than a block, copy the new data into the hold buffer and move to the slow read.
if (totalInput < _inputBlockSize)
{
_inputBuffer = tempInputBuffer;
_inputBufferIndex = amountRead;
goto slow;
Buffer.BlockCopy(tempInputBuffer, _inputBufferIndex, _inputBuffer, _inputBufferIndex, amountRead);
_inputBufferIndex = totalInput;
}
// Make amountRead an integral multiple of _InputBlockSize
int numWholeReadBlocksInBytes = (amountRead / _inputBlockSize) * _inputBlockSize;
int numIgnoredBytes = amountRead - numWholeReadBlocksInBytes;
if (numIgnoredBytes != 0)
else
{
_inputBufferIndex = numIgnoredBytes;
Buffer.BlockCopy(tempInputBuffer, numWholeReadBlocksInBytes, _inputBuffer, 0, numIgnoredBytes);
// Copy any held data into tempInputBuffer now that we know we're proceeding
Buffer.BlockCopy(_inputBuffer, 0, tempInputBuffer, 0, _inputBufferIndex);
CryptographicOperations.ZeroMemory(new Span<byte>(_inputBuffer, 0, _inputBufferIndex));
amountRead += _inputBufferIndex;
_inputBufferIndex = 0;
// Make amountRead an integral multiple of _InputBlockSize
int numWholeReadBlocks = amountRead / _inputBlockSize;
int numWholeReadBlocksInBytes = numWholeReadBlocks * _inputBlockSize;
int numIgnoredBytes = amountRead - numWholeReadBlocksInBytes;
if (numIgnoredBytes != 0)
{
_inputBufferIndex = numIgnoredBytes;
Buffer.BlockCopy(tempInputBuffer, numWholeReadBlocksInBytes, _inputBuffer, 0, numIgnoredBytes);
}
tempOutputBuffer = ArrayPool<byte>.Shared.Rent(numWholeReadBlocks * _outputBlockSize);
numOutputBytes = _transform.TransformBlock(tempInputBuffer, 0, numWholeReadBlocksInBytes, tempOutputBuffer, 0);
Buffer.BlockCopy(tempOutputBuffer, 0, buffer, currentOutputIndex, numOutputBytes);
// Clear what was written while we know how much that was
CryptographicOperations.ZeroMemory(new Span<byte>(tempOutputBuffer, 0, numOutputBytes));
ArrayPool<byte>.Shared.Return(tempOutputBuffer);
tempOutputBuffer = null;
bytesToDeliver -= numOutputBytes;
currentOutputIndex += numOutputBytes;
}
byte[] tempOutputBuffer = new byte[(numWholeReadBlocksInBytes / _inputBlockSize) * _outputBlockSize];
numOutputBytes = _transform.TransformBlock(tempInputBuffer, 0, numWholeReadBlocksInBytes, tempOutputBuffer, 0);
Buffer.BlockCopy(tempOutputBuffer, 0, buffer, currentOutputIndex, numOutputBytes);
// Now, tempInputBuffer and tempOutputBuffer are no more needed, so zeroize them to protect plain text
Array.Clear(tempInputBuffer, 0, tempInputBuffer.Length);
Array.Clear(tempOutputBuffer, 0, tempOutputBuffer.Length);
bytesToDeliver -= numOutputBytes;
currentOutputIndex += numOutputBytes;
}
finally
{
// If we rented and then an exception happened we don't know how much was written to,
// clear the whole thing and return it.
if (tempOutputBuffer != null)
{
CryptographicOperations.ZeroMemory(tempOutputBuffer);
ArrayPool<byte>.Shared.Return(tempOutputBuffer);
tempOutputBuffer = null;
}
CryptographicOperations.ZeroMemory(new Span<byte>(tempInputBuffer, 0, numWholeBlocksInBytes));
ArrayPool<byte>.Shared.Return(tempInputBuffer);
tempInputBuffer = null;
}
}
slow:
// try to fill _InputBuffer so we have something to transform
while (bytesToDeliver > 0)
{
while (_inputBufferIndex < _inputBlockSize)
{
amountRead = useAsync ?
await _stream.ReadAsync(_inputBuffer, _inputBufferIndex, _inputBlockSize - _inputBufferIndex, cancellationToken) :
await _stream.ReadAsync(new Memory<byte>(_inputBuffer, _inputBufferIndex, _inputBlockSize - _inputBufferIndex), cancellationToken) :
_stream.Read(_inputBuffer, _inputBufferIndex, _inputBlockSize - _inputBufferIndex);
// first, check to see if we're at the end of the input stream
if (amountRead == 0) goto ProcessFinalBlock;
_inputBufferIndex += amountRead;
}
numOutputBytes = _transform.TransformBlock(_inputBuffer, 0, _inputBlockSize, _outputBuffer, 0);
_inputBufferIndex = 0;
if (bytesToDeliver >= numOutputBytes)
{
Buffer.BlockCopy(_outputBuffer, 0, buffer, currentOutputIndex, numOutputBytes);
CryptographicOperations.ZeroMemory(new Span<byte>(_outputBuffer, 0, numOutputBytes));
currentOutputIndex += numOutputBytes;
bytesToDeliver -= numOutputBytes;
}
@@ -361,6 +401,8 @@ namespace System.Security.Cryptography
Buffer.BlockCopy(_outputBuffer, 0, buffer, currentOutputIndex, bytesToDeliver);
_outputBufferIndex = numOutputBytes - bytesToDeliver;
Buffer.BlockCopy(_outputBuffer, bytesToDeliver, _outputBuffer, 0, _outputBufferIndex);
int toClear = _outputBuffer.Length - _outputBufferIndex;
CryptographicOperations.ZeroMemory(new Span<byte>(_outputBuffer, _outputBufferIndex, toClear));
return count;
}
}
@@ -381,6 +423,8 @@ namespace System.Security.Cryptography
Buffer.BlockCopy(_outputBuffer, 0, buffer, currentOutputIndex, bytesToDeliver);
_outputBufferIndex -= bytesToDeliver;
Buffer.BlockCopy(_outputBuffer, bytesToDeliver, _outputBuffer, 0, _outputBufferIndex);
int toClear = _outputBuffer.Length - _outputBufferIndex;
CryptographicOperations.ZeroMemory(new Span<byte>(_outputBuffer, _outputBufferIndex, toClear));
return (count);
}
else
@@ -388,6 +432,7 @@ namespace System.Security.Cryptography
Buffer.BlockCopy(_outputBuffer, 0, buffer, currentOutputIndex, _outputBufferIndex);
bytesToDeliver -= _outputBufferIndex;
_outputBufferIndex = 0;
CryptographicOperations.ZeroMemory(_outputBuffer);
return (count - bytesToDeliver);
}
}
@@ -475,7 +520,7 @@ namespace System.Security.Cryptography
if (_outputBufferIndex > 0)
{
if (useAsync)
await _stream.WriteAsync(_outputBuffer, 0, _outputBufferIndex, cancellationToken);
await _stream.WriteAsync(new ReadOnlyMemory<byte>(_outputBuffer, 0, _outputBufferIndex), cancellationToken);
else
_stream.Write(_outputBuffer, 0, _outputBufferIndex);
_outputBufferIndex = 0;
@@ -488,7 +533,7 @@ namespace System.Security.Cryptography
numOutputBytes = _transform.TransformBlock(_inputBuffer, 0, _inputBlockSize, _outputBuffer, 0);
// write out the bytes we just got
if (useAsync)
await _stream.WriteAsync(_outputBuffer, 0, numOutputBytes, cancellationToken);
await _stream.WriteAsync(new ReadOnlyMemory<byte>(_outputBuffer, 0, numOutputBytes), cancellationToken);
else
_stream.Write(_outputBuffer, 0, numOutputBytes);
@@ -500,21 +545,38 @@ namespace System.Security.Cryptography
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)
if (_transform.CanTransformMultipleBlocks && numWholeBlocks > 1)
{
int numWholeBlocks = bytesToWrite / _inputBlockSize;
int numWholeBlocksInBytes = numWholeBlocks * _inputBlockSize;
byte[] _tempOutputBuffer = new byte[numWholeBlocks * _outputBlockSize];
numOutputBytes = _transform.TransformBlock(buffer, currentInputIndex, numWholeBlocksInBytes, _tempOutputBuffer, 0);
byte[] tempOutputBuffer = ArrayPool<byte>.Shared.Rent(numWholeBlocks * _outputBlockSize);
numOutputBytes = 0;
if (useAsync)
await _stream.WriteAsync(_tempOutputBuffer, 0, numOutputBytes, cancellationToken);
else
_stream.Write(_tempOutputBuffer, 0, numOutputBytes);
try
{
numOutputBytes =
_transform.TransformBlock(buffer, currentInputIndex, numWholeBlocksInBytes, tempOutputBuffer, 0);
currentInputIndex += numWholeBlocksInBytes;
bytesToWrite -= numWholeBlocksInBytes;
if (useAsync)
{
await _stream.WriteAsync(new ReadOnlyMemory<byte>(tempOutputBuffer, 0, numOutputBytes), cancellationToken);
}
else
{
_stream.Write(tempOutputBuffer, 0, numOutputBytes);
}
currentInputIndex += numWholeBlocksInBytes;
bytesToWrite -= numWholeBlocksInBytes;
}
finally
{
CryptographicOperations.ZeroMemory(new Span<byte>(tempOutputBuffer, 0, numOutputBytes));
ArrayPool<byte>.Shared.Return(tempOutputBuffer);
tempOutputBuffer = null;
}
}
else
{
@@ -522,7 +584,7 @@ namespace System.Security.Cryptography
numOutputBytes = _transform.TransformBlock(buffer, currentInputIndex, _inputBlockSize, _outputBuffer, 0);
if (useAsync)
await _stream.WriteAsync(_outputBuffer, 0, numOutputBytes, cancellationToken);
await _stream.WriteAsync(new ReadOnlyMemory<byte>(_outputBuffer, 0, numOutputBytes), cancellationToken);
else
_stream.Write(_outputBuffer, 0, numOutputBytes);

View File

@@ -0,0 +1,63 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Runtime.CompilerServices;
namespace System.Security.Cryptography
{
public static class CryptographicOperations
{
/// <summary>
/// Determine the equality of two byte sequences in an amount of time which depends on
/// the length of the sequences, but not the values.
/// </summary>
/// <param name="left">The first buffer to compare.</param>
/// <param name="right">The second buffer to compare.</param>
/// <returns>
/// <c>true</c> if <paramref name="left"/> and <paramref name="right"/> have the same
/// values for <see cref="ReadOnlySpan{T}.Length"/> and the same contents, <c>false</c>
/// otherwise.
/// </returns>
/// <remarks>
/// This method compares two buffers' contents for equality in a manner which does not
/// leak timing information, making it ideal for use within cryptographic routines.
/// This method will short-circuit and return <c>false</c> only if <paramref name="left"/>
/// and <paramref name="right"/> have different lengths.
///
/// Fixed-time behavior is guaranteed in all other cases, including if <paramref name="left"/>
/// and <paramref name="right"/> reference the same address.
/// </remarks>
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static bool FixedTimeEquals(ReadOnlySpan<byte> left, ReadOnlySpan<byte> right)
{
// NoOptimization because we want this method to be exactly as non-short-circuiting
// as written.
//
// NoInlining because the NoOptimization would get lost if the method got inlined.
if (left.Length != right.Length)
{
return false;
}
int length = left.Length;
int accum = 0;
for (int i = 0; i < length; i++)
{
accum |= left[i] - right[i];
}
return accum == 0;
}
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static void ZeroMemory(Span<byte> buffer)
{
// NoOptimize to prevent the optimizer from deciding this call is unnecessary
// NoInlining to prevent the inliner from forgetting that the method was no-optimize
buffer.Clear();
}
}
}

View File

@@ -17,9 +17,11 @@ namespace System.Security.Cryptography
protected HMAC() { }
public static new HMAC Create() => Create("System.Security.Cryptography.HMAC");
public static new HMAC Create() =>
throw new PlatformNotSupportedException(SR.Cryptography_DefaultAlgorithm_NotSupported);
public static new HMAC Create(string algorithmName) => throw new PlatformNotSupportedException();
public static new HMAC Create(string algorithmName) =>
(HMAC)CryptoConfigForwarder.CreateFromName(algorithmName);
public string HashName
{

View File

@@ -16,9 +16,11 @@ namespace System.Security.Cryptography
protected HashAlgorithm() { }
public static HashAlgorithm Create() => Create("System.Security.Cryptography.HashAlgorithm");
public static HashAlgorithm Create() =>
throw new PlatformNotSupportedException(SR.Cryptography_DefaultAlgorithm_NotSupported);
public static HashAlgorithm Create(string hashName) => throw new PlatformNotSupportedException();
public static HashAlgorithm Create(string hashName) =>
(HashAlgorithm)CryptoConfigForwarder.CreateFromName(hashName);
public virtual int HashSize => HashSizeValue;
@@ -96,13 +98,23 @@ namespace System.Security.Cryptography
throw new ObjectDisposedException(null);
// Default the buffer size to 4K.
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
byte[] buffer = ArrayPool<byte>.Shared.Rent(4096);
try
{
HashCore(buffer, 0, bytesRead);
int bytesRead;
while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) > 0)
{
HashCore(buffer, 0, bytesRead);
}
return CaptureHashCodeAndReinitialize();
}
finally
{
CryptographicOperations.ZeroMemory(buffer);
ArrayPool<byte>.Shared.Return(buffer);
}
return CaptureHashCodeAndReinitialize();
}
private byte[] CaptureHashCodeAndReinitialize()

View File

@@ -8,15 +8,11 @@ namespace System.Security.Cryptography
{
protected KeyedHashAlgorithm() { }
public static new KeyedHashAlgorithm Create()
{
return Create("System.Security.Cryptography.KeyedHashAlgorithm");
}
public static new KeyedHashAlgorithm Create() =>
throw new PlatformNotSupportedException(SR.Cryptography_DefaultAlgorithm_NotSupported);
public static new KeyedHashAlgorithm Create(string algName)
{
throw new PlatformNotSupportedException();
}
public static new KeyedHashAlgorithm Create(string algName) =>
(KeyedHashAlgorithm)CryptoConfigForwarder.CreateFromName(algName);
public virtual byte[] Key
{

View File

@@ -12,15 +12,11 @@ namespace System.Security.Cryptography
PaddingValue = PaddingMode.PKCS7;
}
public static SymmetricAlgorithm Create()
{
return Create("System.Security.Cryptography.SymmetricAlgorithm");
}
public static SymmetricAlgorithm Create() =>
throw new PlatformNotSupportedException(SR.Cryptography_DefaultAlgorithm_NotSupported);
public static SymmetricAlgorithm Create(string algName)
{
throw new PlatformNotSupportedException();
}
public static SymmetricAlgorithm Create(string algName) =>
(SymmetricAlgorithm)CryptoConfigForwarder.CreateFromName(algName);
public virtual int FeedbackSize
{

View File

@@ -9,20 +9,151 @@ namespace System.Security.Cryptography.CryptoConfigTests
public static class CryptoConfigTests
{
[Fact]
public static void StaticCreateMethods()
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
public static void DefaultStaticCreateMethods()
{
// These are not supported because CryptoConfig exists in Algorithms assembly.
// CryptoConfig exists in Algorithms partly because it requires the Oid class in Encoding assembly.
// .NET Core does not allow the base classes to pick an algorithm.
Assert.Throws<PlatformNotSupportedException>(() => AsymmetricAlgorithm.Create());
Assert.Throws<PlatformNotSupportedException>(() => AsymmetricAlgorithm.Create(null));
Assert.Throws<PlatformNotSupportedException>(() => HashAlgorithm.Create());
Assert.Throws<PlatformNotSupportedException>(() => HashAlgorithm.Create(null));
Assert.Throws<PlatformNotSupportedException>(() => KeyedHashAlgorithm.Create());
Assert.Throws<PlatformNotSupportedException>(() => KeyedHashAlgorithm.Create(null));
Assert.Throws<PlatformNotSupportedException>(() => HMAC.Create());
Assert.Throws<PlatformNotSupportedException>(() => HMAC.Create(null));
Assert.Throws<PlatformNotSupportedException>(() => SymmetricAlgorithm.Create());
Assert.Throws<PlatformNotSupportedException>(() => SymmetricAlgorithm.Create(null));
}
[Fact]
public static void NamedCreateMethods_NullInput()
{
AssertExtensions.Throws<ArgumentNullException>("name", () => AsymmetricAlgorithm.Create(null));
AssertExtensions.Throws<ArgumentNullException>("name", () => HashAlgorithm.Create(null));
AssertExtensions.Throws<ArgumentNullException>("name", () => KeyedHashAlgorithm.Create(null));
AssertExtensions.Throws<ArgumentNullException>("name", () => HMAC.Create(null));
AssertExtensions.Throws<ArgumentNullException>("name", () => SymmetricAlgorithm.Create(null));
}
// The returned types on .NET Framework can differ when the machine is in FIPS mode.
// So check hash algorithms via a more complicated manner.
[Theory]
[InlineData("MD5", typeof(MD5))]
[InlineData("http://www.w3.org/2001/04/xmldsig-more#md5", typeof(MD5))]
[InlineData("System.Security.Cryptography.HashAlgorithm", typeof(SHA1))]
[InlineData("SHA1", typeof(SHA1))]
[InlineData("http://www.w3.org/2000/09/xmldsig#sha1", typeof(SHA1))]
[InlineData("SHA256", typeof(SHA256))]
[InlineData("SHA-256", typeof(SHA256))]
[InlineData("http://www.w3.org/2001/04/xmlenc#sha256", typeof(SHA256))]
[InlineData("SHA384", typeof(SHA384))]
[InlineData("SHA-384", typeof(SHA384))]
[InlineData("http://www.w3.org/2001/04/xmldsig-more#sha384", typeof(SHA384))]
[InlineData("SHA512", typeof(SHA512))]
[InlineData("SHA-512", typeof(SHA512))]
[InlineData("http://www.w3.org/2001/04/xmlenc#sha512", typeof(SHA512))]
public static void NamedHashAlgorithmCreate(string identifier, Type baseType)
{
using (HashAlgorithm created = HashAlgorithm.Create(identifier))
{
Assert.NotNull(created);
Assert.IsAssignableFrom(baseType, created);
using (HashAlgorithm equivalent =
(HashAlgorithm)baseType.GetMethod("Create", Array.Empty<Type>()).Invoke(null, null))
{
byte[] input = { 1, 2, 3, 4, 5 };
byte[] equivHash = equivalent.ComputeHash(input);
byte[] createdHash = created.ComputeHash(input);
Assert.Equal(equivHash, createdHash);
}
}
}
[Theory]
[InlineData("System.Security.Cryptography.HMAC", typeof(HMACSHA1))]
[InlineData("System.Security.Cryptography.KeyedHashAlgorithm", typeof(HMACSHA1))]
[InlineData("System.Security.Cryptography.HMACSHA1", typeof(HMACSHA1))]
[InlineData("HMACSHA1", typeof(HMACSHA1))]
[InlineData("http://www.w3.org/2000/09/xmldsig#hmac-sha1", typeof(HMACSHA1))]
[InlineData("System.Security.Cryptography.HMACSHA256", typeof(HMACSHA256))]
[InlineData("HMACSHA256", typeof(HMACSHA256))]
[InlineData("http://www.w3.org/2001/04/xmldsig-more#hmac-sha256", typeof(HMACSHA256))]
[InlineData("System.Security.Cryptography.HMACSHA384", typeof(HMACSHA384))]
[InlineData("HMACSHA384", typeof(HMACSHA384))]
[InlineData("http://www.w3.org/2001/04/xmldsig-more#hmac-sha384", typeof(HMACSHA384))]
[InlineData("System.Security.Cryptography.HMACSHA512", typeof(HMACSHA512))]
[InlineData("HMACSHA512", typeof(HMACSHA512))]
[InlineData("http://www.w3.org/2001/04/xmldsig-more#hmac-sha512", typeof(HMACSHA512))]
[InlineData("System.Security.Cryptography.HMACMD5", typeof(HMACMD5))]
[InlineData("HMACMD5", typeof(HMACMD5))]
[InlineData("http://www.w3.org/2001/04/xmldsig-more#hmac-md5", typeof(HMACMD5))]
public static void NamedKeyedHashAlgorithmCreate(string identifier, Type actualType)
{
using (KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create(identifier))
{
Assert.IsType(actualType, kha);
// .NET Core only has HMAC keyed hash algorithms, so combine the two tests
using (HMAC hmac = HMAC.Create(identifier))
{
Assert.IsType(actualType, hmac);
}
}
}
[Theory]
[InlineData("AES", typeof(Aes))]
[InlineData("Rijndael", typeof(Rijndael))]
[InlineData("System.Security.Cryptography.Rijndael", typeof(Rijndael))]
[InlineData("http://www.w3.org/2001/04/xmlenc#aes128-cbc", typeof(Rijndael))]
[InlineData("http://www.w3.org/2001/04/xmlenc#aes192-cbc", typeof(Rijndael))]
[InlineData("http://www.w3.org/2001/04/xmlenc#aes256-cbc", typeof(Rijndael))]
[InlineData("3DES", typeof(TripleDES))]
[InlineData("TripleDES", typeof(TripleDES))]
[InlineData("System.Security.Cryptography.TripleDES", typeof(TripleDES))]
[InlineData("http://www.w3.org/2001/04/xmlenc#tripledes-cbc", typeof(TripleDES))]
[InlineData("DES", typeof(DES))]
[InlineData("System.Security.Cryptography.DES", typeof(DES))]
[InlineData("http://www.w3.org/2001/04/xmlenc#des-cbc", typeof(DES))]
public static void NamedSymmetricAlgorithmCreate(string identifier, Type baseType)
{
using (SymmetricAlgorithm created = SymmetricAlgorithm.Create(identifier))
{
Assert.NotNull(created);
Assert.IsAssignableFrom(baseType, created);
}
}
[Theory]
[InlineData("RSA", typeof(RSA))]
[InlineData("System.Security.Cryptography.RSA", typeof(RSA))]
[InlineData("ECDsa", typeof(ECDsa))]
[InlineData("DSA", typeof(DSA))]
[InlineData("System.Security.Cryptography.DSA", typeof(DSA))]
public static void NamedAsymmetricAlgorithmCreate(string identifier, Type baseType)
{
using (AsymmetricAlgorithm created = AsymmetricAlgorithm.Create(identifier))
{
Assert.NotNull(created);
Assert.IsAssignableFrom(baseType, created);
}
}
[Fact]
public static void NamedCreate_Mismatch()
{
Assert.Throws<InvalidCastException>(() => AsymmetricAlgorithm.Create("SHA1"));
Assert.Throws<InvalidCastException>(() => KeyedHashAlgorithm.Create("SHA1"));
Assert.Throws<InvalidCastException>(() => HMAC.Create("SHA1"));
Assert.Throws<InvalidCastException>(() => SymmetricAlgorithm.Create("SHA1"));
Assert.Throws<InvalidCastException>(() => HashAlgorithm.Create("RSA"));
}
[Fact]
public static void NamedCreate_Unknown()
{
const string UnknownAlgorithmName = "XYZZY";
Assert.Null(AsymmetricAlgorithm.Create(UnknownAlgorithmName));
Assert.Null(HashAlgorithm.Create(UnknownAlgorithmName));
Assert.Null(KeyedHashAlgorithm.Create(UnknownAlgorithmName));
Assert.Null(HMAC.Create(UnknownAlgorithmName));
Assert.Null(SymmetricAlgorithm.Create(UnknownAlgorithmName));
}
}
}

View File

@@ -0,0 +1,109 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Buffers;
using System.Reflection;
using System.Runtime.CompilerServices;
using Xunit;
namespace System.Security.Cryptography.Primitives.Tests
{
public static class FixedTimeEqualsTests
{
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(128 / 8)]
[InlineData(256 / 8)]
[InlineData(512 / 8)]
[InlineData(96)]
[InlineData(1024)]
public static void EqualReturnsTrue(int byteLength)
{
byte[] rented = ArrayPool<byte>.Shared.Rent(byteLength);
Span<byte> testSpan = new Span<byte>(rented, 0, byteLength);
RandomNumberGenerator.Fill(testSpan);
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);
}
[Theory]
[InlineData(1)]
[InlineData(128 / 8)]
[InlineData(256 / 8)]
[InlineData(512 / 8)]
[InlineData(96)]
[InlineData(1024)]
public static void UnequalReturnsFalse(int byteLength)
{
byte[] rented = ArrayPool<byte>.Shared.Rent(byteLength);
Span<byte> testSpan = new Span<byte>(rented, 0, byteLength);
RandomNumberGenerator.Fill(testSpan);
byte[] rented2 = ArrayPool<byte>.Shared.Rent(byteLength);
Span<byte> testSpan2 = new Span<byte>(rented2, 0, byteLength);
testSpan.CopyTo(testSpan2);
testSpan[testSpan[0] % testSpan.Length] ^= 0xFF;
bool isEqual = CryptographicOperations.FixedTimeEquals(testSpan, testSpan2);
ArrayPool<byte>.Shared.Return(rented);
ArrayPool<byte>.Shared.Return(rented2);
Assert.False(isEqual);
}
[Theory]
[InlineData(1)]
[InlineData(128 / 8)]
[InlineData(256 / 8)]
[InlineData(512 / 8)]
[InlineData(96)]
[InlineData(1024)]
public static void DifferentLengthsReturnFalse(int byteLength)
{
byte[] rented = ArrayPool<byte>.Shared.Rent(byteLength);
Span<byte> testSpan = new Span<byte>(rented, 0, byteLength);
RandomNumberGenerator.Fill(testSpan);
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");
}
[Fact]
public static void HasCorrectMethodImpl()
{
Type t = typeof(CryptographicOperations);
MethodInfo mi = t.GetMethod(nameof(CryptographicOperations.FixedTimeEquals));
// This method cannot be optimized, or it loses its fixed time guarantees.
// It cannot be inlined, or it loses its no-optimization guarantee.
Assert.Equal(
MethodImplAttributes.NoInlining | MethodImplAttributes.NoOptimization,
mi.MethodImplementationFlags);
}
}
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildConfigurations>
netcoreapp;
</BuildConfigurations>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,115 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.Xunit.Performance;
using Test.Cryptography;
using Xunit;
namespace System.Security.Cryptography.Primitives.Tests.Performance
{
public class Perf_FixedTimeEquals
{
private const int IterationCountFor256Bit = 300000;
[Benchmark(InnerIterationCount = IterationCountFor256Bit)]
public static void FixedTimeEquals_256Bit_Equal()
{
MeasureFixedTimeEquals(
"741202531e19d673ad7fff334594549e7c81a285dd02865ddd12530612a96336",
"0000000000000000000000000000000000000000000000000000000000000000");
}
[Benchmark(InnerIterationCount = IterationCountFor256Bit)]
public static void FixedTimeEquals_256Bit_LastBitDifferent()
{
MeasureFixedTimeEquals(
"741202531e19d673ad7fff334594549e7c81a285dd02865ddd12530612a96336",
"0000000000000000000000000000000000000000000000000000000000000001");
}
[Benchmark(InnerIterationCount = IterationCountFor256Bit)]
public static void FixedTimeEquals_256Bit_FirstBitDifferent()
{
MeasureFixedTimeEquals(
"741202531e19d673ad7fff334594549e7c81a285dd02865ddd12530612a96336",
"8000000000000000000000000000000000000000000000000000000000000000");
}
[Benchmark(InnerIterationCount = IterationCountFor256Bit)]
public static void FixedTimeEquals_256Bit_CascadingErrors()
{
MeasureFixedTimeEquals(
"741202531e19d673ad7fff334594549e7c81a285dd02865ddd12530612a96336",
"0102040810204080112244880000000000000000000000000000000000000000");
}
[Benchmark(InnerIterationCount = IterationCountFor256Bit)]
public static void FixedTimeEquals_256Bit_AllBitsDifferent()
{
MeasureFixedTimeEquals(
"741202531e19d673ad7fff334594549e7c81a285dd02865ddd12530612a96336",
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
}
[Benchmark(InnerIterationCount = IterationCountFor256Bit)]
public static void FixedTimeEquals_256Bit_VersusZero()
{
MeasureFixedTimeEquals(
"741202531e19d673ad7fff334594549e7c81a285dd02865ddd12530612a96336",
"741202531e19d673ad7fff334594549e7c81a285dd02865ddd12530612a96336");
}
[Benchmark(InnerIterationCount = IterationCountFor256Bit)]
public static void FixedTimeEquals_256Bit_SameReference()
{
byte[] test = "741202531e19d673ad7fff334594549e7c81a285dd02865ddd12530612a96336".HexToByteArray();
Span<byte> left = test;
Span<byte> right = test;
foreach (BenchmarkIteration iteration in Benchmark.Iterations)
{
using (iteration.StartMeasurement())
{
for (int i = 0; i < Benchmark.InnerIterationCount; i++)
{
CryptographicOperations.FixedTimeEquals(left, right);
}
}
}
}
// The important statistics from these perf runs aren't the mean, but the t-test for
// every set of the same length being the same as when it was equal.
private static void MeasureFixedTimeEquals(string baseValueHex, string errorVectorHex)
{
if (errorVectorHex.Length != baseValueHex.Length)
{
throw new InvalidOperationException();
}
byte[] a = baseValueHex.HexToByteArray();
byte[] b = errorVectorHex.HexToByteArray();
for (int i = 0; i < a.Length; i++)
{
b[i] ^= a[i];
}
Span<byte> left = a;
Span<byte> right = b;
foreach (BenchmarkIteration iteration in Benchmark.Iterations)
{
using (iteration.StartMeasurement())
{
for (int i = 0; i < Benchmark.InnerIterationCount; i++)
{
CryptographicOperations.FixedTimeEquals(left, right);
}
}
}
}
}
}

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<Import Project="$(CommonTestPath)\Tests.props" />
<PropertyGroup>
<IncludePerformanceTests>true</IncludePerformanceTests>
<ProjectGuid>{FB3EA273-567D-414F-B36D-3698BE8D198B}</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="$(CommonTestPath)\System\PerfUtils.cs">
<Link>Common\System\PerfUtils.cs</Link>
</Compile>
<Compile Include="$(CommonTestPath)\System\Security\Cryptography\ByteUtils.cs">
<Link>CommonTest\System\Security\Cryptography\ByteUtils.cs</Link>
</Compile>
<Compile Include="Perf.FixedTimeEquals.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(CommonPath)\..\perf\PerfRunner\PerfRunner.csproj">
<Project>{69e46a6f-9966-45a5-8945-2559fe337827}</Project>
<Name>PerfRunner</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>

View File

@@ -11,8 +11,13 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="$(CommonTestPath)\System\IO\PositionValueStream.cs">
<Link>CommonTest\System\IO\PositionValueStream.cs</Link>
</Compile>
<Compile Include="AsymmetricAlgorithm\Trivial.cs" />
<Compile Include="CryptoConfigTests.cs" />
<Compile Include="CryptoStream.cs" />
<Compile Include="CryptographicException.cs" />
<Compile Include="HmacAlgorithmTest.cs" />
<Compile Include="KeyedHashAlgorithmTests.cs" />
<Compile Include="Length32Hash.cs" />
@@ -20,14 +25,11 @@
<Compile Include="Sum32Hash.cs" />
<Compile Include="SymmetricAlgorithm\Minimal.cs" />
<Compile Include="SymmetricAlgorithm\Trivial.cs" />
<Compile Include="CryptographicException.cs" />
<Compile Include="$(CommonTestPath)\System\IO\PositionValueStream.cs">
<Link>CommonTest\System\IO\PositionValueStream.cs</Link>
</Compile>
</ItemGroup>
<ItemGroup Condition="'$(TargetGroup)'=='netcoreapp'">
<Compile Include="CryptoConfigTests.cs" />
<Compile Include="FixedTimeEqualsTests.cs" />
<Compile Include="HashAlgorithmTest.netcoreapp.cs" />
<Compile Include="ZeroMemoryTests.cs" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
</Project>

View File

@@ -0,0 +1,77 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Buffers;
using System.Reflection;
using Xunit;
namespace System.Security.Cryptography.Primitives.Tests
{
public static class ZeroMemoryTests
{
[Theory]
[InlineData(1)]
[InlineData(128 / 8)]
[InlineData(256 / 8)]
[InlineData(512 / 8)]
[InlineData(96)]
[InlineData(1024)]
public static void MemoryGetsCleared(int byteLength)
{
byte[] rented = ArrayPool<byte>.Shared.Rent(byteLength);
Span<byte> testSpan = new Span<byte>(rented, 0, byteLength);
bool hasData = false;
// i should really only iterate when byteLength is 1, and then
// only 1/256 executions.
//
// The chances of this failing are 1 in 1.2e24, unless the RNG is broken.
for (int i = 0; i < 10 && !hasData; i++)
{
RandomNumberGenerator.Fill(testSpan);
for (int j = 0; j < testSpan.Length; j++)
{
if (testSpan[j] != 0)
{
hasData = true;
break;
}
}
}
if (!hasData)
{
throw new InvalidOperationException("RNG provided all zero-values");
}
// This test cannot guarantee the effect of the memory being cleared
// on an otherwise abandoned reference; since the act of measuring it
// changes what the optimizer could have done.
//
// But it can check for it calling clear.
CryptographicOperations.ZeroMemory(testSpan);
for (int i = 0; i < testSpan.Length; i++)
{
Assert.Equal(0, testSpan[i]);
}
}
[Fact]
public static void HasCorrectMethodImpl()
{
Type t = typeof(CryptographicOperations);
MethodInfo mi = t.GetMethod(nameof(CryptographicOperations.ZeroMemory));
// This method cannot be optimized, or the optimizer can decide that a call to Clear
// is unnecessary.
// It cannot be inlined, or it loses its no-optimization guarantee.
Assert.Equal(
MethodImplAttributes.NoInlining | MethodImplAttributes.NoOptimization,
mi.MethodImplementationFlags);
}
}
}