Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corefx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig118
-rw-r--r--BuildToolsVersion.txt2
-rw-r--r--README.md10
-rw-r--r--buildpipeline/DotNet-CoreFx-Trusted-OSX.json12
-rw-r--r--buildpipeline/pipeline.json28
-rw-r--r--dependencies.props16
-rw-r--r--external/ilasm/project.json.template6
-rw-r--r--external/netstandard/netstandard1.x/project.json.template2
-rw-r--r--external/netstandard/project.json.template2
-rw-r--r--external/runtime/NETNative/project.json.template2
-rw-r--r--external/runtime/project.json.template6
-rw-r--r--netci.groovy16
-rw-r--r--pkg/Microsoft.Private.PackageBaseline/packageIndex.json2
-rw-r--r--src/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.cs (renamed from src/Common/src/Internal/Cryptography/OpenSslAsymmetricAlgorithmCore.cs)2
-rw-r--r--src/Common/src/Interop/OSX/Interop.CoreFoundation.CFArray.cs55
-rw-r--r--src/Common/src/Interop/OSX/Interop.CoreFoundation.CFData.cs75
-rw-r--r--src/Common/src/Interop/OSX/Interop.CoreFoundation.CFDate.cs66
-rw-r--r--src/Common/src/Interop/OSX/Interop.CoreFoundation.CFError.cs69
-rw-r--r--src/Common/src/Interop/OSX/Interop.CoreFoundation.CFString.cs103
-rw-r--r--src/Common/src/Interop/OSX/Interop.CoreFoundation.cs27
-rw-r--r--src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ecc.cs64
-rw-r--r--src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Err.cs23
-rw-r--r--src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.cs339
-rw-r--r--src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.RSA.cs175
-rw-r--r--src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecErr.cs23
-rw-r--r--src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecErrMessage.cs28
-rw-r--r--src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecKeyRef.Export.cs218
-rw-r--r--src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecKeyRef.cs255
-rw-r--r--src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ssl.cs627
-rw-r--r--src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SslErr.cs35
-rw-r--r--src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Trust.cs90
-rw-r--r--src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs436
-rw-r--r--src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509Chain.cs50
-rw-r--r--src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509Store.cs73
-rw-r--r--src/Common/src/Interop/Unix/System.Net.Http.Native/Interop.Initialization.cs4
-rw-r--r--src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Initialization.cs4
-rw-r--r--src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs40
-rw-r--r--src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs81
-rw-r--r--src/Common/src/System/Diagnostics/Debug.Unix.cs2
-rw-r--r--src/Common/src/System/Diagnostics/Debug.Windows.cs2
-rw-r--r--src/Common/src/System/Diagnostics/Debug.cs10
-rw-r--r--src/Common/src/System/Net/ContextAwareResult.OSX.cs19
-rw-r--r--src/Common/src/System/Net/IntPtrHelper.cs10
-rw-r--r--src/Common/src/System/Net/Security/Unix/SafeFreeContextBufferChannelBinding.cs46
-rw-r--r--src/Common/src/System/Net/Shims/TraceSource.cs76
-rw-r--r--src/Common/src/System/Net/Shims/readme.md3
-rw-r--r--src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs68
-rw-r--r--src/Common/src/System/Security/Cryptography/DSAOpenSsl.cs8
-rw-r--r--src/Common/src/System/Security/Cryptography/DSASecurityTransforms.cs466
-rw-r--r--src/Common/src/System/Security/Cryptography/DerEncoder.cs42
-rw-r--r--src/Common/src/System/Security/Cryptography/DerSequenceReader.cs268
-rw-r--r--src/Common/src/System/Security/Cryptography/ECDsaOpenSsl.cs10
-rw-r--r--src/Common/src/System/Security/Cryptography/ECDsaSecurityTransforms.cs550
-rw-r--r--src/Common/src/System/Security/Cryptography/KeyBlobHelpers.cs52
-rw-r--r--src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs4
-rw-r--r--src/Common/src/System/Security/Cryptography/RSASecurityTransforms.cs488
-rw-r--r--src/Common/src/System/Security/Cryptography/SecKeyPair.cs46
-rw-r--r--src/Common/tests/System/IO/FileCleanupTestBase.cs6
-rw-r--r--src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAFactory.cs11
-rw-r--r--src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAImportExport.cs3
-rw-r--r--src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyGeneration.cs8
-rw-r--r--src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs5
-rw-r--r--src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs3
-rw-r--r--src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaFactory.cs8
-rw-r--r--src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs4
-rw-r--r--src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.cs18
-rw-r--r--src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs18
-rw-r--r--src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAFactory.cs10
-rw-r--r--src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs4
-rw-r--r--src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs4
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt15
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_digest.cpp6
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_digest.h4
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ecc.cpp98
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ecc.h27
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_hmac.cpp8
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_hmac.h6
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain.cpp376
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain.h121
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_random.cpp2
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_random.h2
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_rsa.cpp269
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_rsa.h61
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_sec.cpp (renamed from src/Common/src/System/Net/Shims/DBNull.cs)9
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_sec.h (renamed from src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_secimportexport.h)8
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_seckey.cpp186
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_seckey.h66
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_signverify.cpp287
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_signverify.h57
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.cpp395
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.h219
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_symmetric.cpp42
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_symmetric.h38
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_trust.cpp218
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_trust.h63
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.cpp490
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h158
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.cpp239
-rw-r--r--src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.h129
-rw-r--r--src/Native/pkg/runtime.native.System.Data.SqlClient.sni/win/runtime.native.System.Data.SqlClient.sni.pkgproj24
-rw-r--r--src/System.CodeDom/src/System/CodeDom/CodeNamespaceImportCollection.cs10
-rw-r--r--src/System.CodeDom/tests/CodeCollections/CodeNamespaceImportCollectionTests.cs4
-rw-r--r--src/System.Collections.NonGeneric/tests/SortedListTests.cs4
-rw-r--r--src/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.CopyToTests.cs2
-rw-r--r--src/System.Collections.Specialized/tests/System.Collections.Specialized.Tests.csproj3
-rw-r--r--src/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj1
-rw-r--r--src/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs35
-rw-r--r--src/System.Diagnostics.DiagnosticSource/ref/Configurations.props2
-rw-r--r--src/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.csproj13
-rw-r--r--src/System.Diagnostics.DiagnosticSource/src/AssemblyInfo.Net46.cs (renamed from src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_secimportexport.cpp)4
-rw-r--r--src/System.Diagnostics.DiagnosticSource/src/Configurations.props2
-rw-r--r--src/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj29
-rw-r--r--src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.Current.net45.cs32
-rw-r--r--src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.Current.net46.cs25
-rw-r--r--src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs21
-rw-r--r--src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Linux.cs23
-rw-r--r--src/System.Diagnostics.Tools/src/Configurations.props1
-rw-r--r--src/System.Diagnostics.Tools/src/System.Diagnostics.Tools.csproj2
-rw-r--r--src/System.Diagnostics.TraceSource/src/Resources/Strings.resx3
-rw-r--r--src/System.Diagnostics.TraceSource/src/System.Diagnostics.TraceSource.csproj1
-rw-r--r--src/System.Diagnostics.TraceSource/src/System/Diagnostics/DefaultTraceListener.cs51
-rw-r--r--src/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceInternal.cs2
-rw-r--r--src/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceSource.cs2
-rw-r--r--src/System.Diagnostics.TraceSource/tests/DefaultTraceListenerClassTests.cs51
-rw-r--r--src/System.Diagnostics.TraceSource/tests/System.Diagnostics.TraceSource.Tests.csproj3
-rw-r--r--src/System.Diagnostics.TraceSource/tests/TestTraceListener.cs8
-rw-r--r--src/System.Diagnostics.TraceSource/tests/TraceClassTests.cs17
-rw-r--r--src/System.Diagnostics.TraceSource/tests/TraceInternalClassTests.cs4
-rw-r--r--src/System.Diagnostics.TraceSource/tests/TraceSourceClassTests.cs20
-rw-r--r--src/System.Diagnostics.TraceSource/tests/TraceTestHelper.cs3
-rw-r--r--src/System.IO.Compression/tests/ZipArchive/zip_ManualAndCompatibilityTests.cs8
-rw-r--r--src/System.IO.FileSystem.DriveInfo/System.IO.FileSystem.DriveInfo.sln8
-rw-r--r--src/System.IO.FileSystem.DriveInfo/tests/Configurations.props2
-rw-r--r--src/System.IO.FileSystem.DriveInfo/tests/System.IO.FileSystem.DriveInfo.Tests.csproj4
-rw-r--r--src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/DirectoryExistsTests.cs2
-rw-r--r--src/System.Linq.Expressions/src/Configurations.props1
-rw-r--r--src/System.Linq.Expressions/src/System.Linq.Expressions.csproj55
-rw-r--r--src/System.Linq.Expressions/tests/Configurations.props2
-rw-r--r--src/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj42
-rw-r--r--src/System.Linq.Parallel/tests/QueryOperators/SequenceEqualTests.cs28
-rw-r--r--src/System.Memory/src/System/Span.cs70
-rw-r--r--src/System.Memory/tests/ReadOnlySpan/Overflow.cs2
-rw-r--r--src/System.Memory/tests/Span/Overflow.cs3
-rw-r--r--src/System.Net.Http/src/Configurations.props3
-rw-r--r--src/System.Net.Http/src/Resources/Strings.resx9
-rw-r--r--src/System.Net.Http/src/System.Net.Http.csproj29
-rw-r--r--src/System.Net.Http/src/System/Net/Http/OSX/CurlHandler.SslProvider.cs133
-rw-r--r--src/System.Net.Http/tests/FunctionalTests/CancellationTest.cs3
-rw-r--r--src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ClientCertificates.cs6
-rw-r--r--src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ServerCertificates.cs65
-rw-r--r--src/System.Net.Http/tests/FunctionalTests/SchSendAuxRecordHttpTest.cs1
-rw-r--r--src/System.Net.HttpListener/ref/System.Net.HttpListener.csproj2
-rw-r--r--src/System.Net.HttpListener/src/System.Net.HttpListener.csproj12
-rw-r--r--src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj3
-rw-r--r--src/System.Net.NetworkInformation/tests/UnitTests/System.Net.NetworkInformation.WinRT.Unit.Tests.csproj3
-rw-r--r--src/System.Net.Ping/src/System.Net.Ping.csproj3
-rw-r--r--src/System.Net.Primitives/src/System.Net.Primitives.csproj3
-rw-r--r--src/System.Net.Primitives/tests/UnitTests/System.Net.Primitives.UnitTests.Tests.csproj3
-rw-r--r--src/System.Net.Security/src/Configurations.props3
-rw-r--r--src/System.Net.Security/src/Resources/Strings.resx21
-rw-r--r--src/System.Net.Security/src/System.Net.Security.csproj159
-rw-r--r--src/System.Net.Security/src/System/Net/CertificateValidationPal.OSX.cs167
-rw-r--r--src/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs96
-rw-r--r--src/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs71
-rw-r--r--src/System.Net.Security/src/System/Net/CertificateValidationPal.cs85
-rw-r--r--src/System.Net.Security/src/System/Net/FixedSizeReader.cs164
-rw-r--r--src/System.Net.Security/src/System/Net/Security/InternalNegotiateStream.cs11
-rw-r--r--src/System.Net.Security/src/System/Net/Security/Pal.Managed/EndpointChannelBindingToken.cs (renamed from src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509ChannelBindingHash.cs)32
-rw-r--r--src/System.Net.Security/src/System/Net/Security/Pal.Managed/SafeChannelBindingHandle.cs87
-rw-r--r--src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs348
-rw-r--r--src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeFreeSslCredentials.cs56
-rw-r--r--src/System.Net.Security/src/System/Net/Security/SecureChannel.cs3
-rw-r--r--src/System.Net.Security/src/System/Net/Security/SslConnectionInfo.OSX.cs857
-rw-r--r--src/System.Net.Security/src/System/Net/Security/SslState.cs11
-rw-r--r--src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs11
-rw-r--r--src/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs332
-rw-r--r--src/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs24
-rw-r--r--src/System.Net.Security/src/System/Net/Security/StreamSizes.OSX.cs29
-rw-r--r--src/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs1
-rw-r--r--src/System.Net.Security/tests/FunctionalTests/LoggingTest.cs1
-rw-r--r--src/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs2
-rw-r--r--src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs1
-rw-r--r--src/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs6
-rw-r--r--src/System.Net.Security/tests/FunctionalTests/TransportContextTest.cs11
-rw-r--r--src/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj3
-rw-r--r--src/System.Net.WebHeaderCollection/System.Net.WebHeaderCollection.sln8
-rw-r--r--src/System.Net.WebHeaderCollection/tests/Configurations.props2
-rw-r--r--src/System.Net.WebHeaderCollection/tests/System.Net.WebHeaderCollection.Tests.csproj4
-rw-r--r--src/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj5
-rw-r--r--src/System.Net.WebSockets.Client/src/System/Net/Sockets/AsyncEventArgsNetworkStream.cs140
-rw-r--r--src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs2
-rw-r--r--src/System.Net.WebSockets.Client/tests/SendReceiveTest.cs5
-rw-r--r--src/System.Private.Uri/System.Private.Uri.sln24
-rw-r--r--src/System.Private.Uri/tests/ExtendedFunctionalTests/Configurations.props8
-rw-r--r--src/System.Private.Uri/tests/ExtendedFunctionalTests/System.Private.Uri.ExtendedFunctional.Tests.csproj4
-rw-r--r--src/System.Private.Uri/tests/FunctionalTests/Configurations.props8
-rw-r--r--src/System.Private.Uri/tests/FunctionalTests/System.Private.Uri.Functional.Tests.csproj4
-rw-r--r--src/System.Private.Uri/tests/UnitTests/Configurations.props8
-rw-r--r--src/System.Private.Uri/tests/UnitTests/System.Private.Uri.Unit.Tests.csproj4
-rw-r--r--src/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs6
-rw-r--r--src/System.Private.Xml/tests/XmlConvert/ToTypeTests.cs2
-rw-r--r--src/System.Reflection.Emit/ref/System.Reflection.Emit.cs3
-rw-r--r--src/System.Reflection.Emit/tests/EnumBuilder/EnumBuilder.Properties.Tests.cs24
-rw-r--r--src/System.Reflection.Emit/tests/GenericTypeParameterBuilder/GenericTypeParameterBuilderMakeArrayType.cs24
-rw-r--r--src/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderMakeArrayType.cs24
-rw-r--r--src/System.Runtime.Extensions/src/ApiCompatBaseline.uapaot.txt29
-rw-r--r--src/System.Runtime.Extensions/src/System.Runtime.Extensions.csproj8
-rw-r--r--src/System.Runtime.Extensions/src/System/AppDomain.cs26
-rw-r--r--src/System.Runtime.Numerics/tests/ComplexTests.cs30
-rw-r--r--src/System.Runtime/ref/System.Runtime.cs2
-rw-r--r--src/System.Runtime/tests/System.Runtime.Tests.csproj3
-rw-r--r--src/System.Runtime/tests/System/Reflection/TypeDelegatorTests.netcoreapp.cs40
-rw-r--r--src/System.Runtime/tests/System/Reflection/TypeInfoTests.netcoreapp.cs40
-rw-r--r--src/System.Runtime/tests/System/TypeTests.netcoreapp.cs40
-rw-r--r--src/System.Security.Cryptography.Algorithms/src/Resources/Strings.resx9
-rw-r--r--src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj198
-rw-r--r--src/System.Security.Cryptography.Algorithms/tests/DefaultDSAProvider.cs4
-rw-r--r--src/System.Security.Cryptography.Algorithms/tests/DefaultECDsaProvider.cs10
-rw-r--r--src/System.Security.Cryptography.Cng/tests/DSACngProvider.cs9
-rw-r--r--src/System.Security.Cryptography.Csp/tests/DSACryptoServiceProviderProvider.cs9
-rw-r--r--src/System.Security.Cryptography.Encoding/src/Configurations.props3
-rw-r--r--src/System.Security.Cryptography.Encoding/src/Internal/Cryptography/AsnFormatter.OSX.cs217
-rw-r--r--src/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.OSX.cs67
-rw-r--r--src/System.Security.Cryptography.Encoding/src/Resources/Strings.resx5
-rw-r--r--src/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj15
-rw-r--r--src/System.Security.Cryptography.Encoding/tests/AsnEncodedData.cs61
-rw-r--r--src/System.Security.Cryptography.Encoding/tests/Oid.cs47
-rw-r--r--src/System.Security.Cryptography.Encoding/tests/Resources/Strings.resx3
-rw-r--r--src/System.Security.Cryptography.OpenSsl/src/Resources/Strings.resx3
-rw-r--r--src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj7
-rw-r--r--src/System.Security.Cryptography.OpenSsl/tests/DsaOpenSslProvider.cs9
-rw-r--r--src/System.Security.Cryptography.Primitives/System.Security.Cryptography.Primitives.sln8
-rw-r--r--src/System.Security.Cryptography.Primitives/tests/Configurations.props4
-rw-r--r--src/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.csproj8
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Configurations.props3
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Oids.cs6
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/CertificatePal.cs930
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ChainPal.cs522
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/FindPal.cs64
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.AppleKeychainStore.cs98
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.AppleTrustStore.cs85
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.ExportPal.cs119
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.LoaderPal.cs60
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.cs172
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs131
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ChainVerifier.cs132
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedCertificateFinder.cs363
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedX509ExtensionProcessor.cs297
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslCertificateFinder.cs347
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs110
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509Encoder.cs187
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.ManagedDecode.cs159
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.OpenSslDecode.cs122
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.cs115
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Microsoft/Win32/SafeHandles/SafePasswordHandle.cs19
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx80
-rw-r--r--src/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj115
-rw-r--r--src/System.Security.Cryptography.X509Certificates/tests/Cert.cs12
-rw-r--r--src/System.Security.Cryptography.X509Certificates/tests/CertTests.cs31
-rw-r--r--src/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs10
-rw-r--r--src/System.Security.Cryptography.X509Certificates/tests/CollectionImportTests.cs27
-rw-r--r--src/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs23
-rw-r--r--src/System.Security.Cryptography.X509Certificates/tests/Configurations.props4
-rw-r--r--src/System.Security.Cryptography.X509Certificates/tests/ContentTypeTests.cs26
-rw-r--r--src/System.Security.Cryptography.X509Certificates/tests/CtorTests.cs7
-rw-r--r--src/System.Security.Cryptography.X509Certificates/tests/ExportTests.cs68
-rw-r--r--src/System.Security.Cryptography.X509Certificates/tests/ExtensionsTests.cs4
-rw-r--r--src/System.Security.Cryptography.X509Certificates/tests/PfxTests.cs49
-rw-r--r--src/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs6
-rw-r--r--src/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj5
-rw-r--r--src/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.OSX.cs249
-rw-r--r--src/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs31
-rw-r--r--src/System.Security.Cryptography.Xml/tests/DSAKeyValueTest.cs4
-rw-r--r--src/System.Text.Encoding.CodePages/System.Text.Encoding.CodePages.sln8
-rw-r--r--src/System.Text.Encoding.CodePages/tests/Configurations.props2
-rw-r--r--src/System.Text.Encoding.CodePages/tests/System.Text.Encoding.CodePages.Tests.csproj4
-rw-r--r--src/System.Threading.Tasks/src/ApiCompatBaseline.uapaot.txt9
-rw-r--r--src/System.Threading.Thread/src/ApiCompatBaseline.uap.txt6
-rw-r--r--src/System.Threading.Thread/src/ApiCompatBaseline.uapaot.txt6
-rw-r--r--src/System.Threading.Thread/src/System.Threading.Thread.csproj3
-rw-r--r--src/System.Threading.Thread/src/System/Threading/Thread.cs14
-rw-r--r--src/System.Threading/src/ApiCompatBaseline.uapaot.txt17
-rw-r--r--src/System.Xml.XmlSerializer/src/System.Xml.XmlSerializer.csproj2
-rw-r--r--src/packages.builds8
-rw-r--r--src/shims/ApiCompatBaseline.uapaot.netstandard20.txt141
-rw-r--r--src/upload-tests.proj4
286 files changed, 16408 insertions, 2385 deletions
diff --git a/.editorconfig b/.editorconfig
index b1d0cb2b48..c050ae10ea 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -14,6 +14,124 @@ indent_size = 4
[project.json]
indent_size = 2
+# C# files
+[*.cs]
+# New line preferences
+csharp_new_line_before_open_brace = all
+csharp_new_line_before_else = true
+csharp_new_line_before_catch = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_within_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_switch_labels = true
+csharp_indent_labels = flush_left
+
+# avoid this. unless absolutely necessary
+dotnet_style_qualification_for_field = false:suggestion
+dotnet_style_qualification_for_property = false:suggestion
+dotnet_style_qualification_for_method = false:suggestion
+dotnet_style_qualification_for_event = false:suggestion
+
+# only use var when it's obvious what the variable type is
+csharp_style_var_for_built_in_types = false:none
+csharp_style_var_when_type_is_apparent = false:none
+csharp_style_var_elsewhere = false:suggestion
+
+# use language keywords instead of BCL types
+dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
+dotnet_style_predefined_type_for_member_access = true:suggestion
+
+# name all constant fields using PascalCase
+dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
+dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
+
+dotnet_naming_symbols.constant_fields.applicable_kinds = field
+dotnet_naming_symbols.constant_fields.required_modifiers = const
+
+dotnet_naming_style.pascal_case_style.capitalization = pascal_case
+
+# static fields should have s_ prefix
+dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion
+dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields
+dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style
+
+dotnet_naming_symbols.static_fields.applicable_kinds = field
+dotnet_naming_symbols.static_fields.required_modifiers = static
+
+dotnet_naming_style.static_prefix_style.required_prefix = s_
+dotnet_naming_style.static_prefix_style.capitalization = camel_case
+
+# internal and private fields should be _camelCase
+dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
+dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
+dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
+
+dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
+dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
+
+dotnet_naming_style.camel_case_underscore_style.required_prefix = _
+dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
+
+# Code style defaults
+dotnet_sort_system_directives_first = true
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = false
+
+# Expression-level preferences
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+
+# Expression-bodied members
+csharp_style_expression_bodied_methods = false:none
+csharp_style_expression_bodied_constructors = false:none
+csharp_style_expression_bodied_operators = false:none
+csharp_style_expression_bodied_properties = true:none
+csharp_style_expression_bodied_indexers = true:none
+csharp_style_expression_bodied_accessors = true:none
+
+# Pattern matching
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+
+# Null checking preferences
+csharp_style_throw_expression = true:suggestion
+csharp_style_conditional_delegate_call = true:suggestion
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = do_not_ignore
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
# C++ Files
[*.{cpp,h,in}]
curly_bracket_next_line = true
diff --git a/BuildToolsVersion.txt b/BuildToolsVersion.txt
index 487225feed..2357eb9a84 100644
--- a/BuildToolsVersion.txt
+++ b/BuildToolsVersion.txt
@@ -1 +1 @@
-1.0.27-prerelease-01407-02
+1.0.27-prerelease-01413-01
diff --git a/README.md b/README.md
index cd64090c24..27689f4696 100644
--- a/README.md
+++ b/README.md
@@ -15,17 +15,17 @@ The corefx repo contains the library implementation (called "CoreFX") for [.NET
|**Fedora 23**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/fedora23_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/fedora23_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/fedora23_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/fedora23_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_fedora23_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_fedora23_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_fedora23_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_fedora23_release/lastCompletedBuild/testReport)|
|**Fedora 24**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/fedora24_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/fedora24_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/fedora24_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/fedora24_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_fedora24_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_fedora24_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_fedora24_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_fedora24_release/lastCompletedBuild/testReport)|
|**openSUSE 42.1**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/opensuse42.1_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/opensuse42.1_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/opensuse42.1_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/opensuse42.1_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_opensuse42.1_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_opensuse42.1_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_opensuse42.1_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_opensuse42.1_release/lastCompletedBuild/testReport)|
-|**OS X 10.11**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/osx_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/osx_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/osx_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/osx_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_osx_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_osx_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_osx_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_osx_release/lastCompletedBuild/testReport)|
+|**OS X 10.12**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/osx10.12_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/osx10.12_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/osx10.12_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/osx10.12_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_osx_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_osx_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_osx_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_osx_release/lastCompletedBuild/testReport)|
|**Red Hat 7.2**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/rhel7.2_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/rhel7.2_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/rhel7.2_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/rhel7.2_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_rhel7.2_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_rhel7.2_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_rhel7.2_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_rhel7.2_release/lastCompletedBuild/testReport)|
|**Ubuntu 14.04**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu14.04_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu14.04_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu14.04_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu14.04_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_ubuntu14.04_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_ubuntu14.04_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_ubuntu14.04_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_ubuntu14.04_release/lastCompletedBuild/testReport)|
|**Ubuntu 16.04**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.04_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.04_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.04_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.04_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_ubuntu16.04_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_ubuntu16.04_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_ubuntu16.04_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_ubuntu16.04_release/lastCompletedBuild/testReport)|
|**Ubuntu 16.10**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.10_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.10_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.10_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/ubuntu16.10_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_ubuntu16.10_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_ubuntu16.10_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_ubuntu16.10_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_ubuntu16.10_release/lastCompletedBuild/testReport)|
|**PortableLinux**|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/portablelinux_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/portablelinux_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/portablelinux_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/portablelinux_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_portablelinux_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_portablelinux_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_portablelinux_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_portablelinux_release/lastCompletedBuild/testReport)|
-|**Windows 7**|||[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win7_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win7_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win7_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win7_release/lastCompletedBuild/testReport)|
+|**Windows 7**| | |[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win7_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win7_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win7_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win7_release/lastCompletedBuild/testReport)|
|**Windows 8.1**|(x86) [![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/windows_nt_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/windows_nt_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/windows_nt_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/windows_nt_release/lastCompletedBuild/testReport)|[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_windows_nt_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_windows_nt_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_windows_nt_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_windows_nt_release/lastCompletedBuild/testReport)|
-|**Windows 10**|||[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win10_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win10_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win10_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win10_release/lastCompletedBuild/testReport)|
-|**Windows Nano Server 2016**|||[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_winnano16_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_winnano16_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_winnano16_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_winnano16_release/lastCompletedBuild/testReport)|
-|**Code Coverage (Windows)**|||[![code coverage](https://ci.dot.net/job/dotnet_corefx/job/master/job/code_coverage_windows/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/code_coverage_windows/Code_Coverage_Report)||
+|**Windows 10**| | |[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win10_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win10_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win10_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_win10_release/lastCompletedBuild/testReport)|
+|**Windows Nano Server 2016**| | |[![x64-debug](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_winnano16_debug/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_winnano16_debug/lastCompletedBuild/testReport)|[![x64-release](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_winnano16_release/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/outerloop_winnano16_release/lastCompletedBuild/testReport)|
+|**Code Coverage (Windows)**| | |[![code coverage](https://ci.dot.net/job/dotnet_corefx/job/master/job/code_coverage_windows/badge/icon)](https://ci.dot.net/job/dotnet_corefx/job/master/job/code_coverage_windows/Code_Coverage_Report)||
## How to Engage, Contribute and Provide Feedback
diff --git a/buildpipeline/DotNet-CoreFx-Trusted-OSX.json b/buildpipeline/DotNet-CoreFx-Trusted-OSX.json
index 6d5fe0cfaf..1c5f0c0d78 100644
--- a/buildpipeline/DotNet-CoreFx-Trusted-OSX.json
+++ b/buildpipeline/DotNet-CoreFx-Trusted-OSX.json
@@ -291,7 +291,7 @@
}
},
"demands": [
- "Agent.OS -equals darwin"
+ "Configuration -equals VSTSAgent_Mac_v3.0.0"
],
"retentionRules": [
{
@@ -331,11 +331,11 @@
"quality": "definition",
"queue": {
"pool": {
- "id": 39,
- "name": "DotNet-Build"
+ "id": 97,
+ "name": "DotNetCore-Build"
},
- "id": 36,
- "name": "DotNet-Build"
+ "id": 330,
+ "name": "DotNetCore-Build"
},
"path": "\\",
"type": "build",
@@ -349,4 +349,4 @@
"state": "wellFormed",
"revision": 418097459
}
-} \ No newline at end of file
+}
diff --git a/buildpipeline/pipeline.json b/buildpipeline/pipeline.json
index 516ee0f0f2..4152eb1c55 100644
--- a/buildpipeline/pipeline.json
+++ b/buildpipeline/pipeline.json
@@ -15,7 +15,7 @@
"PB_ConfigurationGroup": "Release",
"PB_BuildArguments": "-buildArch=x64 -Release -stripSymbols",
"PB_BuildTestsArguments": "-buildArch=x64 -Release -SkipTests -- /p:ArchiveTests=true /p:EnableDumpling=true",
- "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Release /p:\"EnableCloudTest=true\" /p:\"TestProduct=corefx /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=Linux\"",
+ "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Release /p:\"TestProduct=corefx /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=Linux\"",
"PB_SyncArguments": "-p -- /p:ArchGroup=x64"
},
"Definitions": [
@@ -83,9 +83,9 @@
"Name": "DotNet-CoreFx-Trusted-Linux",
"Parameters": {
"PB_DockerTag": "rhel7_prereqs_2",
- "PB_TargetQueue": "Redhat.72.Amd64",
"PB_BuildArguments": "-buildArch=x64 -Release -portableLinux",
- "PB_SyncArguments": "-p -portableLinux -- /p:ArchGroup=x64"
+ "PB_SyncArguments": "-p -portableLinux -- /p:ArchGroup=x64",
+ "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Release /p:\"TestProduct=corefx /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=Linux\" /p:\"TargetQueues=Redhat.72.Amd64\,Debian.82.Amd64\"",
},
"ReportingParameters": {
"OperatingSystem": "RedHat 7",
@@ -191,13 +191,13 @@
"PB_BuildArguments": "-buildArch=x64 -Release -stripSymbols",
"PB_BuildTestsArguments": "-buildArch=x64 -Release -SkipTests -- /p:ArchiveTests=true",
"PB_SyncArguments": "-p -- /p:ArchGroup=x64",
- "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Release /p:\"EnableCloudTest=true\" /p:\"TestProduct=corefx /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=OSX\""
+ "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Release /p:\"TestProduct=corefx /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=OSX\""
},
"Definitions": [
{
"Name": "DotNet-CoreFx-Trusted-OSX",
"Parameters": {
- "PB_TargetQueue": "OSX.1011.Amd64"
+ "PB_TargetQueue": "OSX.1012.Amd64"
},
"ReportingParameters": {
"OperatingSystem": "OSX",
@@ -255,7 +255,7 @@
"PB_BuildArguments": "-buildArch=x64 -Release -- /p:SignType=real /p:RuntimeOS=win10",
"PB_BuildTestsArguments": "-buildArch=x64 -Release -SkipTests -- /p:RuntimeOS=win10 /p:ArchiveTests=true",
"PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10",
- "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Release /p:\"EnableCloudTest=true\" /p:\"TargetQueues=Windows.10.Amd64;Windows.7.Amd64;Windows.81.Amd64\" /p:\"TestProduct=corefx\" /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=Windows_NT\""
+ "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Release /p:\"TargetQueues=Windows.10.Amd64,Windows.7.Amd64,Windows.81.Amd64\" /p:\"TestProduct=corefx\" /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=Windows_NT\""
},
"ReportingParameters": {
"OperatingSystem": "Windows",
@@ -272,7 +272,7 @@
"PB_BuildArguments": "-buildArch=x86 -Release -- /p:SignType=real /p:RuntimeOS=win10",
"PB_BuildTestsArguments": "-buildArch=x86 -Release -SkipTests -- /p:RuntimeOS=win10 /p:ArchiveTests=true",
"PB_SyncArguments": "-p -- /p:ArchGroup=x86 /p:RuntimeOS=win10",
- "PB_CreateHelixArguments": "/p:ArchGroup=x86 /p:ConfigurationGroup=Release /p:\"EnableCloudTest=true\" /p:\"TargetQueues=Windows.10.Amd64;Windows.7.Amd64;Windows.81.Amd64\" /p:\"TestProduct=corefx\" /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=Windows_NT\""
+ "PB_CreateHelixArguments": "/p:ArchGroup=x86 /p:ConfigurationGroup=Release /p:\"TargetQueues=Windows.10.Amd64,Windows.7.Amd64,Windows.81.Amd64\" /p:\"TestProduct=corefx\" /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=Windows_NT\""
},
"ReportingParameters": {
"OperatingSystem": "Windows",
@@ -398,7 +398,7 @@
"PB_ConfigurationGroup": "Debug",
"PB_BuildArguments": "-buildArch=x64 -Debug",
"PB_BuildTestsArguments": "-buildArch=x64 -Debug -SkipTests -- /p:ArchiveTests=true /p:EnableDumpling=true",
- "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Debug /p:\"EnableCloudTest=true\" /p:\"TestProduct=corefx /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=Linux\"",
+ "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Debug /p:\"TestProduct=corefx /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=Linux\"",
"PB_SyncArguments": "-p -- /p:ArchGroup=x64"
},
"Definitions": [
@@ -466,9 +466,9 @@
"Name": "DotNet-CoreFx-Trusted-Linux",
"Parameters": {
"PB_DockerTag": "rhel7_prereqs_2",
- "PB_TargetQueue": "Redhat.72.Amd64",
"PB_BuildArguments": "-buildArch=x64 -Debug -portableLinux",
- "PB_SyncArguments": "-p -portableLinux -- /p:ArchGroup=x64"
+ "PB_SyncArguments": "-p -portableLinux -- /p:ArchGroup=x64",
+ "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Debug /p:\"TestProduct=corefx /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=Linux\" /p:\"TargetQueues=Redhat.72.Amd64\,Debian.82.Amd64\"",
},
"ReportingParameters": {
"OperatingSystem": "RedHat 7",
@@ -573,13 +573,13 @@
"PB_BuildArguments": "-buildArch=x64 -Debug",
"PB_BuildTestsArguments": "-buildArch=x64 -Debug -SkipTests -- /p:ArchiveTests=true",
"PB_SyncArguments": "-p -- /p:ArchGroup=x64",
- "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Debug /p:\"EnableCloudTest=true\" /p:\"TestProduct=corefx /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=OSX\""
+ "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Debug /p:\"TestProduct=corefx /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=OSX\""
},
"Definitions": [
{
"Name": "DotNet-CoreFx-Trusted-OSX",
"Parameters": {
- "PB_TargetQueue": "OSX.1011.Amd64"
+ "PB_TargetQueue": "OSX.1012.Amd64"
},
"ReportingParameters": {
"OperatingSystem": "OSX",
@@ -637,7 +637,7 @@
"PB_BuildArguments": "-buildArch=x64 -Debug -- /p:SignType=real /p:RuntimeOS=win10",
"PB_BuildTestsArguments": "-buildArch=x64 -Debug -SkipTests -- /p:RuntimeOS=win10 /p:ArchiveTests=true",
"PB_SyncArguments": "-p -- /p:ArchGroup=x64 /p:RuntimeOS=win10",
- "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Debug /p:\"EnableCloudTest=true\" /p:\"TargetQueues=Windows.10.Amd64;Windows.7.Amd64;Windows.81.Amd64\" /p:\"TestProduct=corefx\" /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=Windows_NT\""
+ "PB_CreateHelixArguments": "/p:ArchGroup=x64 /p:ConfigurationGroup=Debug /p:\"TargetQueues=Windows.10.Amd64,Windows.7.Amd64,Windows.81.Amd64\" /p:\"TestProduct=corefx\" /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=Windows_NT\""
},
"ReportingParameters": {
"OperatingSystem": "Windows",
@@ -654,7 +654,7 @@
"PB_BuildArguments": "-buildArch=x86 -Debug -- /p:SignType=real /p:RuntimeOS=win10",
"PB_BuildTestsArguments": "-buildArch=x86 -Debug -SkipTests -- /p:RuntimeOS=win10 /p:ArchiveTests=true",
"PB_SyncArguments": "-p -- /p:ArchGroup=x86 /p:RuntimeOS=win10",
- "PB_CreateHelixArguments": "/p:ArchGroup=x86 /p:ConfigurationGroup=Debug /p:\"EnableCloudTest=true\" /p:\"TargetQueues=Windows.10.Amd64;Windows.7.Amd64;Windows.81.Amd64\" /p:\"TestProduct=corefx\" /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=Windows_NT\""
+ "PB_CreateHelixArguments": "/p:ArchGroup=x86 /p:ConfigurationGroup=Debug /p:\"TargetQueues=Windows.10.Amd64,Windows.7.Amd64,Windows.81.Amd64\" /p:\"TestProduct=corefx\" /p:\"TimeoutInSeconds=1200\" /p:\"TargetOS=Windows_NT\""
},
"ReportingParameters": {
"OperatingSystem": "Windows",
diff --git a/dependencies.props b/dependencies.props
index 97e0d77aa7..d2d670bd31 100644
--- a/dependencies.props
+++ b/dependencies.props
@@ -17,21 +17,21 @@
These ref versions are pulled from https://github.com/dotnet/versions.
-->
<PropertyGroup>
- <CoreFxCurrentRef>c71b7f12c4a5ff1b24d1e784bb74c3853f0dee10</CoreFxCurrentRef>
- <CoreClrCurrentRef>2781d4f716841661106d057cd05fb0970ad7890c</CoreClrCurrentRef>
+ <CoreFxCurrentRef>8928ad83e2e5c33ce116c3b63eb94f611d72c2e2</CoreFxCurrentRef>
+ <CoreClrCurrentRef>b48a83561e6763d50668717a46130aaa94a4687e</CoreClrCurrentRef>
<ExternalCurrentRef>3b8a99621d89ad9877f053ba8af25e0f25dcd9d8</ExternalCurrentRef>
- <ProjectNTfsCurrentRef>d2fd71b2fbc3be52229fe62345d1a56caee61e7a</ProjectNTfsCurrentRef>
+ <ProjectNTfsCurrentRef>11ca41f4d74f949571948e16546f64ceb43e33c4</ProjectNTfsCurrentRef>
<SniCurrentRef>05650e53f2aa4497f74cd6e9b053d3f69f64b0bd</SniCurrentRef>
- <StandardCurrentRef>6b6e77941a94b0b41e9791cfe2fdabe260c6736c</StandardCurrentRef>
+ <StandardCurrentRef>08ff016839d62c9455d16245524f46b16d7a1074</StandardCurrentRef>
</PropertyGroup>
<!-- Auto-upgraded properties for each build info dependency. -->
<PropertyGroup>
- <CoreFxExpectedPrerelease>beta-25110-02</CoreFxExpectedPrerelease>
- <CoreClrExpectedPrerelease>beta-25113-02</CoreClrExpectedPrerelease>
+ <CoreFxExpectedPrerelease>beta-25114-01</CoreFxExpectedPrerelease>
+ <CoreClrExpectedPrerelease>beta-25115-02</CoreClrExpectedPrerelease>
<ExternalExpectedPrerelease>beta-25016-01</ExternalExpectedPrerelease>
- <ProjectNTfsExpectedPrerelease>beta-25113-00</ProjectNTfsExpectedPrerelease>
- <NETStandardPackageVersion>2.0.0-beta-25113-01</NETStandardPackageVersion>
+ <ProjectNTfsExpectedPrerelease>beta-25115-00</ProjectNTfsExpectedPrerelease>
+ <NETStandardPackageVersion>2.0.0-beta-25115-01</NETStandardPackageVersion>
<NETStandardPackageId>NETStandard.Library</NETStandardPackageId>
<!-- Use the SNI runtime package -->
<SniPackageVersion>4.4.0-beta-25007-02</SniPackageVersion>
diff --git a/external/ilasm/project.json.template b/external/ilasm/project.json.template
index ef9f235dc3..e2bd340a0b 100644
--- a/external/ilasm/project.json.template
+++ b/external/ilasm/project.json.template
@@ -2,9 +2,9 @@
"frameworks": {
"{TFM}": {
"dependencies": {
- "Microsoft.NETCore.Platforms": "2.0.0-beta-25110-02",
- "Microsoft.NETCore.Runtime.CoreCLR": "2.0.0-beta-25113-02",
- "Microsoft.NETCore.ILAsm": "2.0.0-beta-25113-02"
+ "Microsoft.NETCore.Platforms": "2.0.0-beta-25114-01",
+ "Microsoft.NETCore.Runtime.CoreCLR": "2.0.0-beta-25115-02",
+ "Microsoft.NETCore.ILAsm": "2.0.0-beta-25115-02"
}
}
},
diff --git a/external/netstandard/netstandard1.x/project.json.template b/external/netstandard/netstandard1.x/project.json.template
index 356b3e498b..1e54ebd425 100644
--- a/external/netstandard/netstandard1.x/project.json.template
+++ b/external/netstandard/netstandard1.x/project.json.template
@@ -2,7 +2,7 @@
"frameworks": {
"{TFM}": {
"dependencies": {
- "NETStandard.Library": "2.0.0-beta-25113-01",
+ "NETStandard.Library": "2.0.0-beta-25115-01",
"System.Diagnostics.Contracts": "4.3.0",
"System.Diagnostics.Debug": "4.3.0",
"System.Dynamic.Runtime": "4.3.0",
diff --git a/external/netstandard/project.json.template b/external/netstandard/project.json.template
index 284fe04a8b..fa9b8298db 100644
--- a/external/netstandard/project.json.template
+++ b/external/netstandard/project.json.template
@@ -2,7 +2,7 @@
"frameworks": {
"{TFM}": {
"dependencies": {
- "NETStandard.Library": "2.0.0-beta-25113-01"
+ "NETStandard.Library": "2.0.0-beta-25115-01"
}
}
}
diff --git a/external/runtime/NETNative/project.json.template b/external/runtime/NETNative/project.json.template
index d82f2972b5..ac37bd415d 100644
--- a/external/runtime/NETNative/project.json.template
+++ b/external/runtime/NETNative/project.json.template
@@ -2,7 +2,7 @@
"frameworks": {
"{TFM}": {
"dependencies": {
- "Microsoft.TargetingPack.Private.NETNative": "1.1.0-beta-25113-00"
+ "Microsoft.TargetingPack.Private.NETNative": "1.1.0-beta-25115-00"
}
}
},
diff --git a/external/runtime/project.json.template b/external/runtime/project.json.template
index edb3036736..165e3021a2 100644
--- a/external/runtime/project.json.template
+++ b/external/runtime/project.json.template
@@ -2,9 +2,9 @@
"frameworks": {
"{TFM}": {
"dependencies": {
- "Microsoft.NETCore.Platforms": "2.0.0-beta-25110-02",
- "Microsoft.NETCore.Runtime.CoreCLR": "2.0.0-beta-25113-02",
- "Microsoft.NETCore.TestHost": "2.0.0-beta-25113-02",
+ "Microsoft.NETCore.Platforms": "2.0.0-beta-25114-01",
+ "Microsoft.NETCore.Runtime.CoreCLR": "2.0.0-beta-25115-02",
+ "Microsoft.NETCore.TestHost": "2.0.0-beta-25115-02",
"runtime.native.System.Data.SqlClient.sni": "4.4.0-beta-24913-02",
"Microsoft.NETCore.DotNetHost": "1.2.0-beta-001259-00",
"Microsoft.NETCore.DotNetHostPolicy": "1.2.0-beta-001259-00"
diff --git a/netci.groovy b/netci.groovy
index 48e3e7162f..8fb76de6b3 100644
--- a/netci.groovy
+++ b/netci.groovy
@@ -19,7 +19,7 @@ def osGroupMap = ['Ubuntu14.04':'Linux',
'Debian8.4':'Linux',
'Fedora23':'Linux',
'Fedora24':'Linux',
- 'OSX':'OSX',
+ 'OSX10.12':'OSX',
'Windows_NT':'Windows_NT',
'CentOS7.1': 'Linux',
'OpenSUSE13.2': 'Linux',
@@ -33,7 +33,7 @@ def osShortName = ['Windows 10': 'win10',
'Windows 7' : 'win7',
'Windows_NT' : 'windows_nt',
'Ubuntu14.04' : 'ubuntu14.04',
- 'OSX' : 'osx',
+ 'OSX10.12' : 'osx',
'Windows Nano 2016' : 'winnano16',
'Ubuntu16.04' : 'ubuntu16.04',
'Ubuntu16.10' : 'ubuntu16.10',
@@ -209,7 +209,7 @@ def buildArchConfiguration = ['Debug': 'x86',
// Define outerloop testing for OSes that can build and run. Run locally on each machine.
// **************************
[true, false].each { isPR ->
- ['Windows 10', 'Windows 7', 'Windows_NT', 'Ubuntu14.04', 'Ubuntu16.04', 'Ubuntu16.10', 'CentOS7.1', 'OpenSUSE13.2', 'OpenSUSE42.1', 'RHEL7.2', 'Fedora23', 'Fedora24', 'Debian8.4', 'OSX', 'PortableLinux'].each { osName ->
+ ['Windows 10', 'Windows 7', 'Windows_NT', 'Ubuntu14.04', 'Ubuntu16.04', 'Ubuntu16.10', 'CentOS7.1', 'OpenSUSE13.2', 'OpenSUSE42.1', 'RHEL7.2', 'Fedora23', 'Fedora24', 'Debian8.4', 'OSX10.12', 'PortableLinux'].each { osName ->
['Debug', 'Release'].each { configurationGroup ->
def osForMachineAffinity = osName
@@ -227,7 +227,7 @@ def buildArchConfiguration = ['Debug': 'x86',
batchFile("call \"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat\" x86 && build.cmd -${configurationGroup}")
batchFile("call \"C:\\Program Files (x86)\\Microsoft Visual Studio 14.0\\VC\\vcvarsall.bat\" x86 && build-tests.cmd -${configurationGroup} -outerloop -- /p:IsCIBuild=true")
}
- else if (osName == 'OSX') {
+ else if (osName == 'OSX10.12') {
shell("HOME=\$WORKSPACE/tempHome ./build.sh -${configurationGroup.toLowerCase()}")
shell("HOME=\$WORKSPACE/tempHome ./build-tests.sh -${configurationGroup.toLowerCase()} -outerloop -- /p:IsCIBuild=true")
}
@@ -246,7 +246,7 @@ def buildArchConfiguration = ['Debug': 'x86',
}
// Set the affinity. OS name matches the machine affinity.
- if (osName == 'Windows_NT' || osName == 'OSX') {
+ if (osName == 'Windows_NT' || osName == 'OSX10.12') {
Utilities.setMachineAffinity(newJob, osForMachineAffinity, "latest-or-auto-elevated")
}
else if (osGroupMap[osName] == 'Linux') {
@@ -294,6 +294,8 @@ def buildArchConfiguration = ['Debug': 'x86',
Utilities.setMachineAffinity(newJob, 'Windows_NT', 'latest-or-auto')
+ Utilities.setMachineAffinity(newJob, 'Windows_NT', 'latest-or-auto')
+
// Set up standard options.
Utilities.standardJobSetup(newJob, project, /* isPR */ false, "*/${branch}")
@@ -380,7 +382,7 @@ def buildArchConfiguration = ['Debug': 'x86',
[true, false].each { isPR ->
['netcoreapp'].each { targetGroup ->
['Debug', 'Release'].each { configurationGroup ->
- ['Windows_NT', 'Ubuntu14.04', 'Ubuntu16.04', 'Ubuntu16.10', 'Debian8.4', 'CentOS7.1', 'OpenSUSE13.2', 'OpenSUSE42.1', 'Fedora23', 'Fedora24', 'RHEL7.2', 'OSX', 'PortableLinux'].each { osName ->
+ ['Windows_NT', 'Ubuntu14.04', 'Ubuntu16.04', 'Ubuntu16.10', 'Debian8.4', 'CentOS7.1', 'OpenSUSE13.2', 'OpenSUSE42.1', 'Fedora23', 'Fedora24', 'RHEL7.2', 'OSX10.12', 'PortableLinux'].each { osName ->
def osGroup = osGroupMap[osName]
def osForMachineAffinity = osName
@@ -434,7 +436,7 @@ def buildArchConfiguration = ['Debug': 'x86',
// Set up triggers
if (isPR) {
// Set PR trigger, we run Windows_NT, Ubuntu 14.04, CentOS 7.1, PortableLinux and OSX on every PR.
- if ( osName == 'Windows_NT' || osName == 'Ubuntu14.04' || osName == 'CentOS7.1' || osName == 'OSX' || osName== 'PortableLinux') {
+ if ( osName == 'Windows_NT' || osName == 'Ubuntu14.04' || osName == 'CentOS7.1' || osName == 'OSX10.12' || osName== 'PortableLinux') {
Utilities.addGithubPRTriggerForBranch(newJob, branch, "Innerloop ${osName} ${configurationGroup} ${archGroup} Build and Test")
}
else {
diff --git a/pkg/Microsoft.Private.PackageBaseline/packageIndex.json b/pkg/Microsoft.Private.PackageBaseline/packageIndex.json
index b9083cda8c..6649daabce 100644
--- a/pkg/Microsoft.Private.PackageBaseline/packageIndex.json
+++ b/pkg/Microsoft.Private.PackageBaseline/packageIndex.json
@@ -402,7 +402,7 @@
"4.0.0",
"4.3.0"
],
- "BaselineVersion": "4.3.0",
+ "BaselineVersion": "4.4.0",
"InboxOn": {}
},
"runtime.win7-x64.runtime.native.System.Data.SqlClient.sni": {
diff --git a/src/Common/src/Internal/Cryptography/OpenSslAsymmetricAlgorithmCore.cs b/src/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.cs
index 6c78ddc57c..ea045eb524 100644
--- a/src/Common/src/Internal/Cryptography/OpenSslAsymmetricAlgorithmCore.cs
+++ b/src/Common/src/Internal/Cryptography/AsymmetricAlgorithmHelpers.cs
@@ -12,7 +12,7 @@ namespace Internal.Cryptography
//
// Common infrastructure for AsymmetricAlgorithm-derived classes that layer on OpenSSL.
//
- internal static class OpenSslAsymmetricAlgorithmCore
+ internal static class AsymmetricAlgorithmHelpers
{
public static byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
{
diff --git a/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFArray.cs b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFArray.cs
new file mode 100644
index 0000000000..d0c13510c0
--- /dev/null
+++ b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFArray.cs
@@ -0,0 +1,55 @@
+// 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;
+using System.Runtime.InteropServices;
+
+using Microsoft.Win32.SafeHandles;
+
+// Declared as signed long, which has sizeof(void*) on OSX.
+using CFIndex=System.IntPtr;
+
+internal static partial class Interop
+{
+ internal static partial class CoreFoundation
+ {
+ [DllImport(Libraries.CoreFoundationLibrary, EntryPoint = "CFArrayGetCount")]
+ private static extern CFIndex _CFArrayGetCount(SafeCFArrayHandle cfArray);
+
+ // Follows the "Get" version of the "Create" rule, so needs to return an IntPtr to
+ // prevent CFRelease from being called on the SafeHandle close.
+ [DllImport(Libraries.CoreFoundationLibrary, EntryPoint = "CFArrayGetValueAtIndex")]
+ private static extern IntPtr CFArrayGetValueAtIndex(SafeCFArrayHandle cfArray, CFIndex index);
+
+ internal static long CFArrayGetCount(SafeCFArrayHandle cfArray)
+ {
+ return _CFArrayGetCount(cfArray).ToInt64();
+ }
+
+ internal static IntPtr CFArrayGetValueAtIndex(SafeCFArrayHandle cfArray, int index)
+ {
+ return CFArrayGetValueAtIndex(cfArray, new CFIndex(index));
+ }
+ }
+}
+
+namespace Microsoft.Win32.SafeHandles
+{
+ internal sealed class SafeCFArrayHandle : SafeHandle
+ {
+ internal SafeCFArrayHandle()
+ : base(IntPtr.Zero, ownsHandle: true)
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.CoreFoundation.CFRelease(handle);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+ }
+}
diff --git a/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFData.cs b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFData.cs
new file mode 100644
index 0000000000..9e8e05fa2b
--- /dev/null
+++ b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFData.cs
@@ -0,0 +1,75 @@
+// 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;
+using System.Runtime.InteropServices;
+
+using Microsoft.Win32.SafeHandles;
+
+// Declared as signed long, which has sizeof(void*) on OSX.
+using CFIndex=System.IntPtr;
+
+internal static partial class Interop
+{
+ internal static partial class CoreFoundation
+ {
+ [DllImport(Libraries.CoreFoundationLibrary)]
+ private static extern unsafe byte* CFDataGetBytePtr(SafeCFDataHandle cfData);
+
+ [DllImport(Libraries.CoreFoundationLibrary)]
+ private static extern CFIndex CFDataGetLength(SafeCFDataHandle cfData);
+
+ internal static byte[] CFGetData(SafeCFDataHandle cfData)
+ {
+ bool addedRef = false;
+
+ try
+ {
+ cfData.DangerousAddRef(ref addedRef);
+ byte[] bytes = new byte[CFDataGetLength(cfData).ToInt64()];
+
+ unsafe
+ {
+ byte* dataBytes = CFDataGetBytePtr(cfData);
+ Marshal.Copy((IntPtr)dataBytes, bytes, 0, bytes.Length);
+ }
+
+ return bytes;
+
+ }
+ finally
+ {
+ if (addedRef)
+ {
+ cfData.DangerousRelease();
+ }
+ }
+ }
+ }
+}
+
+namespace Microsoft.Win32.SafeHandles
+{
+ internal sealed class SafeCFDataHandle : SafeHandle
+ {
+ internal SafeCFDataHandle()
+ : base(IntPtr.Zero, ownsHandle: true)
+ {
+ }
+
+ internal SafeCFDataHandle(IntPtr handle, bool ownsHandle)
+ : base(handle, ownsHandle)
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.CoreFoundation.CFRelease(handle);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+ }
+}
diff --git a/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFDate.cs b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFDate.cs
new file mode 100644
index 0000000000..c3c6784160
--- /dev/null
+++ b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFDate.cs
@@ -0,0 +1,66 @@
+// 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;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+using Microsoft.Win32.SafeHandles;
+
+using CFAbsoluteTime=System.Double;
+
+internal static partial class Interop
+{
+ internal static partial class CoreFoundation
+ {
+ // https://developer.apple.com/reference/corefoundation/cfabsolutetime
+ private static readonly DateTime s_cfDateEpoch = new DateTime(2001, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+ [DllImport(Libraries.CoreFoundationLibrary)]
+ private static extern SafeCFDateHandle CFDateCreate(IntPtr zero, CFAbsoluteTime at);
+
+ internal static SafeCFDateHandle CFDateCreate(DateTime date)
+ {
+ Debug.Assert(
+ date.Kind != DateTimeKind.Unspecified,
+ "DateTimeKind.Unspecified should be specified to Local or UTC by the caller");
+
+ // UTC stays unchanged, Local is changed.
+ // Unspecified gets treated as Local (which may or may not be desired).
+ DateTime utcDate = date.ToUniversalTime();
+
+ double epochDeltaSeconds = (utcDate - s_cfDateEpoch).TotalSeconds;
+
+ SafeCFDateHandle cfDate = CFDateCreate(IntPtr.Zero, epochDeltaSeconds);
+
+ if (cfDate.IsInvalid)
+ {
+ cfDate.Dispose();
+ throw new OutOfMemoryException();
+ }
+
+ return cfDate;
+ }
+ }
+}
+
+namespace Microsoft.Win32.SafeHandles
+{
+ internal sealed class SafeCFDateHandle : SafeHandle
+ {
+ internal SafeCFDateHandle()
+ : base(IntPtr.Zero, ownsHandle: true)
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.CoreFoundation.CFRelease(handle);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+ }
+}
diff --git a/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFError.cs b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFError.cs
new file mode 100644
index 0000000000..af3b5cd4c3
--- /dev/null
+++ b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFError.cs
@@ -0,0 +1,69 @@
+// 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;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+using Microsoft.Win32.SafeHandles;
+
+// Declared as signed long, which has sizeof(void*) on OSX.
+using CFIndex=System.IntPtr;
+
+internal static partial class Interop
+{
+ internal static partial class CoreFoundation
+ {
+ [DllImport(Libraries.CoreFoundationLibrary)]
+ private static extern CFIndex CFErrorGetCode(SafeCFErrorHandle cfError);
+
+ [DllImport(Libraries.CoreFoundationLibrary)]
+ private static extern SafeCFStringHandle CFErrorCopyDescription(SafeCFErrorHandle cfError);
+
+ internal static int GetErrorCode(SafeCFErrorHandle cfError)
+ {
+ unchecked
+ {
+ return (int)(CFErrorGetCode(cfError).ToInt64());
+ }
+ }
+
+ internal static string GetErrorDescription(SafeCFErrorHandle cfError)
+ {
+ Debug.Assert(cfError != null);
+
+ if (cfError.IsInvalid)
+ {
+ return null;
+ }
+
+ Debug.Assert(!cfError.IsClosed);
+
+ using (SafeCFStringHandle cfString = CFErrorCopyDescription(cfError))
+ {
+ return CFStringToString(cfString);
+ }
+ }
+ }
+}
+
+namespace Microsoft.Win32.SafeHandles
+{
+ internal sealed class SafeCFErrorHandle : SafeHandle
+ {
+ internal SafeCFErrorHandle()
+ : base(IntPtr.Zero, ownsHandle: true)
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.CoreFoundation.CFRelease(handle);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+ }
+}
diff --git a/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFString.cs b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFString.cs
new file mode 100644
index 0000000000..aa7a15054c
--- /dev/null
+++ b/src/Common/src/Interop/OSX/Interop.CoreFoundation.CFString.cs
@@ -0,0 +1,103 @@
+// 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;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class CoreFoundation
+ {
+ /// <summary>
+ /// Returns the interior pointer of the cfString if it has the specified encoding.
+ /// If it has the wrong encoding, or if the interior pointer isn't being shared for some reason, returns NULL
+ /// </summary>
+ [DllImport(Libraries.CoreFoundationLibrary)]
+ private static extern IntPtr CFStringGetCStringPtr(
+ SafeCFStringHandle cfString,
+ CFStringBuiltInEncodings encoding);
+
+ [DllImport(Libraries.CoreFoundationLibrary)]
+ private static extern SafeCFDataHandle CFStringCreateExternalRepresentation(
+ IntPtr alloc,
+ SafeCFStringHandle theString,
+ CFStringBuiltInEncodings encoding,
+ byte lossByte);
+
+ internal static string CFStringToString(SafeCFStringHandle cfString)
+ {
+ Debug.Assert(cfString != null);
+ Debug.Assert(!cfString.IsInvalid);
+ Debug.Assert(!cfString.IsClosed);
+
+ // If the string is already stored internally as UTF-8 we can (usually)
+ // get the raw pointer to the data blob, then we can Marshal in the string
+ // via pointer semantics, avoiding a copy.
+ IntPtr interiorPointer = CFStringGetCStringPtr(
+ cfString,
+ CFStringBuiltInEncodings.kCFStringEncodingUTF8);
+
+ if (interiorPointer != IntPtr.Zero)
+ {
+ return Marshal.PtrToStringUTF8(interiorPointer);
+ }
+
+ SafeCFDataHandle cfData = CFStringCreateExternalRepresentation(
+ IntPtr.Zero,
+ cfString,
+ CFStringBuiltInEncodings.kCFStringEncodingUTF8,
+ 0);
+
+ using (cfData)
+ {
+ bool addedRef = false;
+
+ try
+ {
+ cfData.DangerousAddRef(ref addedRef);
+
+ unsafe
+ {
+ // Note that CFDataGetLength(cfData).ToInt32() will throw on
+ // too large of an input. Since a >2GB string is pretty unlikely,
+ // that's considered a good thing here.
+ return Encoding.UTF8.GetString(
+ CFDataGetBytePtr(cfData),
+ CFDataGetLength(cfData).ToInt32());
+ }
+ }
+ finally
+ {
+ if (addedRef)
+ {
+ cfData.DangerousRelease();
+ }
+ }
+ }
+ }
+ }
+}
+
+namespace Microsoft.Win32.SafeHandles
+{
+ internal sealed class SafeCFStringHandle : SafeHandle
+ {
+ internal SafeCFStringHandle()
+ : base(IntPtr.Zero, ownsHandle: true)
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.CoreFoundation.CFRelease(handle);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+ }
+}
diff --git a/src/Common/src/Interop/OSX/Interop.CoreFoundation.cs b/src/Common/src/Interop/OSX/Interop.CoreFoundation.cs
index eaa9f03b95..ff36ebc91f 100644
--- a/src/Common/src/Interop/OSX/Interop.CoreFoundation.cs
+++ b/src/Common/src/Interop/OSX/Interop.CoreFoundation.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
@@ -50,7 +51,21 @@ internal static partial class Interop
IntPtr allocator,
string str,
CFStringBuiltInEncodings encoding);
-
+
+ /// <summary>
+ /// Creates a CFStringRef from a 8-bit String object. Follows the "Create Rule" where if you create it, you delete it.
+ /// </summary>
+ /// <param name="allocator">Should be IntPtr.Zero</param>
+ /// <param name="str">The string to get a CFStringRef for</param>
+ /// <param name="encoding">The encoding of the str variable. This should be UTF 8 for OS X</param>
+ /// <returns>Returns a pointer to a CFString on success; otherwise, returns IntPtr.Zero</returns>
+ /// <remarks>For *nix systems, the CLR maps ANSI to UTF-8, so be explicit about that</remarks>
+ [DllImport(Interop.Libraries.CoreFoundationLibrary, CharSet = CharSet.Ansi)]
+ private static extern SafeCreateHandle CFStringCreateWithCString(
+ IntPtr allocator,
+ IntPtr str,
+ CFStringBuiltInEncodings encoding);
+
/// <summary>
/// Creates a CFStringRef from a 8-bit String object. Follows the "Create Rule" where if you create it, you delete it.
/// </summary>
@@ -62,6 +77,16 @@ internal static partial class Interop
}
/// <summary>
+ /// Creates a CFStringRef from a 8-bit String object. Follows the "Create Rule" where if you create it, you delete it.
+ /// </summary>
+ /// <param name="utf8str">The string to get a CFStringRef for</param>
+ /// <returns>Returns a valid SafeCreateHandle to a CFString on success; otherwise, returns an invalid SafeCreateHandle</returns>
+ internal static SafeCreateHandle CFStringCreateWithCString(IntPtr utf8str)
+ {
+ return CFStringCreateWithCString(IntPtr.Zero, utf8str, CFStringBuiltInEncodings.kCFStringEncodingUTF8);
+ }
+
+ /// <summary>
/// Creates a pointer to an unmanaged CFArray containing the input values. Follows the "Create Rule" where if you create it, you delete it.
/// </summary>
/// <param name="allocator">Should be IntPtr.Zero</param>
diff --git a/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ecc.cs b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ecc.cs
new file mode 100644
index 0000000000..313a1a4deb
--- /dev/null
+++ b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ecc.cs
@@ -0,0 +1,64 @@
+// 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.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Apple;
+
+internal static partial class Interop
+{
+ internal static partial class AppleCrypto
+ {
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_EccGenerateKey(
+ int keySizeInBits,
+ SafeKeychainHandle tempKeychain,
+ out SafeSecKeyRefHandle pPublicKey,
+ out SafeSecKeyRefHandle pPrivateKey,
+ out int pOSStatus);
+
+ [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_EccGetKeySizeInBits")]
+ internal static extern long EccGetKeySizeInBits(SafeSecKeyRefHandle publicKey);
+
+ internal static void EccGenerateKey(
+ int keySizeInBits,
+ out SafeSecKeyRefHandle pPublicKey,
+ out SafeSecKeyRefHandle pPrivateKey)
+ {
+ using (SafeTemporaryKeychainHandle tempKeychain = CreateTemporaryKeychain())
+ {
+ SafeSecKeyRefHandle keychainPublic;
+ SafeSecKeyRefHandle keychainPrivate;
+ int osStatus;
+
+ int result = AppleCryptoNative_EccGenerateKey(
+ keySizeInBits,
+ tempKeychain,
+ out keychainPublic,
+ out keychainPrivate,
+ out osStatus);
+
+ if (result == 1)
+ {
+ pPublicKey = keychainPublic;
+ pPrivateKey = keychainPrivate;
+ return;
+ }
+
+ using (keychainPrivate)
+ using (keychainPublic)
+ {
+ if (result == 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ Debug.Fail($"Unexpected result from AppleCryptoNative_EccGenerateKey: {result}");
+ throw new CryptographicException();
+ }
+ }
+ }
+ }
+}
diff --git a/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Err.cs b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Err.cs
index a6ffb436c5..b33c9f41bf 100644
--- a/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Err.cs
+++ b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Err.cs
@@ -3,7 +3,9 @@
// See the LICENSE file in the project root for more information.
using System;
+using System.Diagnostics;
using System.Security.Cryptography;
+using Microsoft.Win32.SafeHandles;
internal static partial class Interop
{
@@ -22,6 +24,27 @@ internal static partial class Interop
errorType));
}
+ internal static Exception CreateExceptionForCFError(SafeCFErrorHandle cfError)
+ {
+ Debug.Assert(cfError != null);
+
+ if (cfError.IsInvalid)
+ {
+ return new CryptographicException();
+ }
+
+ return new AppleCFErrorCryptographicException(cfError);
+ }
+
+ private sealed class AppleCFErrorCryptographicException : CryptographicException
+ {
+ internal AppleCFErrorCryptographicException(SafeCFErrorHandle cfError)
+ : base(Interop.CoreFoundation.GetErrorDescription(cfError))
+ {
+ HResult = Interop.CoreFoundation.GetErrorCode(cfError);
+ }
+ }
+
private sealed class AppleCommonCryptoCryptographicException : CryptographicException
{
internal AppleCommonCryptoCryptographicException(int errorCode, string message)
diff --git a/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.cs b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.cs
new file mode 100644
index 0000000000..f090360582
--- /dev/null
+++ b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Keychain.cs
@@ -0,0 +1,339 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Apple;
+
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class AppleCrypto
+ {
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SecKeychainItemCopyKeychain(
+ IntPtr item,
+ out SafeKeychainHandle keychain);
+
+ [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SecKeychainCreate")]
+ private static extern int AppleCryptoNative_SecKeychainCreateTemporary(
+ string path,
+ int utf8PassphraseLength,
+ byte[] utf8Passphrase,
+ out SafeTemporaryKeychainHandle keychain);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SecKeychainDelete(IntPtr keychain);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SecKeychainCopyDefault(out SafeKeychainHandle keychain);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SecKeychainOpen(
+ string keychainPath,
+ out SafeKeychainHandle keychain);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SecKeychainEnumerateCerts(
+ SafeKeychainHandle keychain,
+ out SafeCFArrayHandle matches,
+ out int pOSStatus);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SecKeychainEnumerateIdentities(
+ SafeKeychainHandle keychain,
+ out SafeCFArrayHandle matches,
+ out int pOSStatus);
+
+ internal static SafeKeychainHandle SecKeychainItemCopyKeychain(SafeKeychainItemHandle item)
+ {
+ bool addedRef = false;
+
+ try
+ {
+ item.DangerousAddRef(ref addedRef);
+ var handle = SecKeychainItemCopyKeychain(item.DangerousGetHandle());
+ return handle;
+ }
+ finally
+ {
+ if (addedRef)
+ {
+ item.DangerousRelease();
+ }
+ }
+ }
+
+ internal static SafeKeychainHandle SecKeychainItemCopyKeychain(IntPtr item)
+ {
+ SafeKeychainHandle keychain;
+ int osStatus = AppleCryptoNative_SecKeychainItemCopyKeychain(item, out keychain);
+
+ // A whole lot of NULL is expected from this.
+ // Any key or cert which isn't keychain-backed, and this is the primary way we'd find that out.
+ if (keychain.IsInvalid)
+ {
+ GC.SuppressFinalize(keychain);
+ }
+
+ if (osStatus == 0)
+ {
+ return keychain;
+ }
+
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ internal static SafeKeychainHandle SecKeychainCopyDefault()
+ {
+ SafeKeychainHandle keychain;
+ int osStatus = AppleCryptoNative_SecKeychainCopyDefault(out keychain);
+
+ if (osStatus == 0)
+ {
+ return keychain;
+ }
+
+ keychain.Dispose();
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ internal static SafeKeychainHandle SecKeychainOpen(string keychainPath)
+ {
+ SafeKeychainHandle keychain;
+ int osStatus = AppleCryptoNative_SecKeychainOpen(keychainPath, out keychain);
+
+ if (osStatus == 0)
+ {
+ return keychain;
+ }
+
+ keychain.Dispose();
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ internal static SafeCFArrayHandle KeychainEnumerateCerts(SafeKeychainHandle keychainHandle)
+ {
+ SafeCFArrayHandle matches;
+ int osStatus;
+ int result = AppleCryptoNative_SecKeychainEnumerateCerts(keychainHandle, out matches, out osStatus);
+
+ if (result == 1)
+ {
+ return matches;
+ }
+
+ matches.Dispose();
+
+ if (result == 0)
+ throw CreateExceptionForOSStatus(osStatus);
+
+ Debug.Fail($"Unexpected result from AppleCryptoNative_SecKeychainEnumerateCerts: {result}");
+ throw new CryptographicException();
+ }
+
+ internal static SafeCFArrayHandle KeychainEnumerateIdentities(SafeKeychainHandle keychainHandle)
+ {
+ SafeCFArrayHandle matches;
+ int osStatus;
+ int result = AppleCryptoNative_SecKeychainEnumerateIdentities(keychainHandle, out matches, out osStatus);
+
+ if (result == 1)
+ {
+ return matches;
+ }
+
+ matches.Dispose();
+
+ if (result == 0)
+ throw CreateExceptionForOSStatus(osStatus);
+
+ Debug.Fail($"Unexpected result from AppleCryptoNative_SecKeychainEnumerateCerts: {result}");
+ throw new CryptographicException();
+ }
+
+ internal static SafeTemporaryKeychainHandle CreateTemporaryKeychain()
+ {
+ string tmpKeychainPath = Path.Combine(
+ Path.GetTempPath(),
+ Guid.NewGuid().ToString("N") + ".keychain");
+
+ // Use a distinct GUID so that if a keychain is abandoned it isn't recoverable.
+ string tmpKeychainPassphrase = Guid.NewGuid().ToString("N");
+
+ byte[] utf8Passphrase = System.Text.Encoding.UTF8.GetBytes(tmpKeychainPassphrase);
+
+ SafeTemporaryKeychainHandle keychain;
+
+ int osStatus = AppleCryptoNative_SecKeychainCreateTemporary(
+ tmpKeychainPath,
+ utf8Passphrase.Length,
+ utf8Passphrase,
+ out keychain);
+
+ SafeTemporaryKeychainHandle.TrackKeychain(keychain);
+
+ if (osStatus != 0)
+ {
+ keychain.Dispose();
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ return keychain;
+ }
+
+ internal static void SecKeychainDelete(IntPtr handle, bool throwOnError=true)
+ {
+ int osStatus = AppleCryptoNative_SecKeychainDelete(handle);
+
+ if (throwOnError && osStatus != 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+ }
+ }
+}
+
+namespace System.Security.Cryptography.Apple
+{
+ internal class SafeKeychainItemHandle : SafeHandle
+ {
+ internal SafeKeychainItemHandle()
+ : base(IntPtr.Zero, ownsHandle: true)
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ SafeTemporaryKeychainHandle.UntrackItem(handle);
+ Interop.CoreFoundation.CFRelease(handle);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+ }
+
+ internal class SafeKeychainHandle : SafeHandle
+ {
+ internal SafeKeychainHandle()
+ : base(IntPtr.Zero, ownsHandle: true)
+ {
+ }
+
+ internal SafeKeychainHandle(IntPtr handle)
+ : base(handle, ownsHandle: true)
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.CoreFoundation.CFRelease(handle);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+ }
+
+ internal sealed class SafeTemporaryKeychainHandle : SafeKeychainHandle
+ {
+ private static readonly Dictionary<IntPtr, SafeTemporaryKeychainHandle> s_lookup =
+ new Dictionary<IntPtr, SafeTemporaryKeychainHandle>();
+
+ internal SafeTemporaryKeychainHandle()
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ lock (s_lookup)
+ {
+ s_lookup.Remove(handle);
+ }
+
+ Interop.AppleCrypto.SecKeychainDelete(handle, throwOnError: false);
+ return base.ReleaseHandle();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && SafeHandleCache<SafeTemporaryKeychainHandle>.IsCachedInvalidHandle(this))
+ {
+ return;
+ }
+
+ base.Dispose(disposing);
+ }
+
+ public static SafeTemporaryKeychainHandle InvalidHandle =>
+ SafeHandleCache<SafeTemporaryKeychainHandle>.GetInvalidHandle(() => new SafeTemporaryKeychainHandle());
+
+ internal static void TrackKeychain(SafeTemporaryKeychainHandle toTrack)
+ {
+ if (toTrack.IsInvalid)
+ {
+ return;
+ }
+
+ lock (s_lookup)
+ {
+ Debug.Assert(!s_lookup.ContainsKey(toTrack.handle));
+
+ s_lookup[toTrack.handle] = toTrack;
+ }
+ }
+
+ internal static void TrackItem(SafeKeychainItemHandle keychainItem)
+ {
+ if (keychainItem.IsInvalid)
+ return;
+
+ using (SafeKeychainHandle keychain = Interop.AppleCrypto.SecKeychainItemCopyKeychain(keychainItem))
+ {
+ if (keychain.IsInvalid)
+ {
+ return;
+ }
+
+ lock (s_lookup)
+ {
+ SafeTemporaryKeychainHandle temporaryHandle;
+
+ if (s_lookup.TryGetValue(keychain.DangerousGetHandle(), out temporaryHandle))
+ {
+ bool ignored = false;
+ temporaryHandle.DangerousAddRef(ref ignored);
+ }
+ }
+ }
+ }
+
+ internal static void UntrackItem(IntPtr keychainItem)
+ {
+ using (SafeKeychainHandle keychain = Interop.AppleCrypto.SecKeychainItemCopyKeychain(keychainItem))
+ {
+ if (keychain.IsInvalid)
+ {
+ return;
+ }
+
+ lock (s_lookup)
+ {
+ SafeTemporaryKeychainHandle temporaryHandle;
+
+ if (s_lookup.TryGetValue(keychain.DangerousGetHandle(), out temporaryHandle))
+ {
+ temporaryHandle.DangerousRelease();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.RSA.cs b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.RSA.cs
new file mode 100644
index 0000000000..06ce4bdc59
--- /dev/null
+++ b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.RSA.cs
@@ -0,0 +1,175 @@
+// 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;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Apple;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class AppleCrypto
+ {
+ [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_RsaGenerateKey")]
+ private static extern int AppleCryptoNative_RsaGenerateKey(
+ int keySizeInBits,
+ SafeKeychainHandle keychain,
+ out SafeSecKeyRefHandle pPublicKey,
+ out SafeSecKeyRefHandle pPrivateKey,
+ out int pOSStatus);
+
+ [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_RsaEncryptOaep")]
+ private static extern int RsaEncryptOaep(
+ SafeSecKeyRefHandle publicKey,
+ byte[] pbData,
+ int cbData,
+ PAL_HashAlgorithm mgfAlgorithm,
+ out SafeCFDataHandle pEncryptedOut,
+ out SafeCFErrorHandle pErrorOut);
+
+ [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_RsaEncryptPkcs")]
+ private static extern int RsaEncryptPkcs(
+ SafeSecKeyRefHandle publicKey,
+ byte[] pbData,
+ int cbData,
+ out SafeCFDataHandle pEncryptedOut,
+ out SafeCFErrorHandle pErrorOut);
+
+ [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_RsaDecryptOaep")]
+ private static extern int RsaDecryptOaep(
+ SafeSecKeyRefHandle publicKey,
+ byte[] pbData,
+ int cbData,
+ PAL_HashAlgorithm mgfAlgorithm,
+ out SafeCFDataHandle pEncryptedOut,
+ out SafeCFErrorHandle pErrorOut);
+
+ [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_RsaDecryptPkcs")]
+ private static extern int RsaDecryptPkcs(
+ SafeSecKeyRefHandle publicKey,
+ byte[] pbData,
+ int cbData,
+ out SafeCFDataHandle pEncryptedOut,
+ out SafeCFErrorHandle pErrorOut);
+
+ internal static void RsaGenerateKey(
+ int keySizeInBits,
+ out SafeSecKeyRefHandle pPublicKey,
+ out SafeSecKeyRefHandle pPrivateKey)
+ {
+ using (SafeTemporaryKeychainHandle tempKeychain = CreateTemporaryKeychain())
+ {
+ SafeSecKeyRefHandle keychainPublic;
+ SafeSecKeyRefHandle keychainPrivate;
+ int osStatus;
+
+ int result = AppleCryptoNative_RsaGenerateKey(
+ keySizeInBits,
+ tempKeychain,
+ out keychainPublic,
+ out keychainPrivate,
+ out osStatus);
+
+ if (result == 1)
+ {
+ pPublicKey = keychainPublic;
+ pPrivateKey = keychainPrivate;
+ return;
+ }
+
+ using (keychainPrivate)
+ using (keychainPublic)
+ {
+ if (result == 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ Debug.Fail($"Unexpected result from AppleCryptoNative_RsaGenerateKey: {result}");
+ throw new CryptographicException();
+ }
+ }
+ }
+
+ internal static byte[] RsaEncrypt(
+ SafeSecKeyRefHandle publicKey,
+ byte[] data,
+ RSAEncryptionPadding padding)
+ {
+ return ExecuteTransform(
+ (out SafeCFDataHandle encrypted, out SafeCFErrorHandle error) =>
+ {
+ if (padding == RSAEncryptionPadding.Pkcs1)
+ {
+ return RsaEncryptPkcs(publicKey, data, data.Length, out encrypted, out error);
+ }
+
+ Debug.Assert(padding.Mode == RSAEncryptionPaddingMode.Oaep);
+
+ return RsaEncryptOaep(
+ publicKey,
+ data,
+ data.Length,
+ PalAlgorithmFromAlgorithmName(padding.OaepHashAlgorithm),
+ out encrypted,
+ out error);
+ });
+
+ }
+
+ internal static byte[] RsaDecrypt(
+ SafeSecKeyRefHandle privateKey,
+ byte[] data,
+ RSAEncryptionPadding padding)
+ {
+ return ExecuteTransform(
+ (out SafeCFDataHandle decrypted, out SafeCFErrorHandle error) =>
+ {
+ if (padding == RSAEncryptionPadding.Pkcs1)
+ {
+ return RsaDecryptPkcs(privateKey, data, data.Length, out decrypted, out error);
+ }
+
+ Debug.Assert(padding.Mode == RSAEncryptionPaddingMode.Oaep);
+
+ return RsaDecryptOaep(
+ privateKey,
+ data,
+ data.Length,
+ PalAlgorithmFromAlgorithmName(padding.OaepHashAlgorithm),
+ out decrypted,
+ out error);
+ });
+ }
+
+ private static Interop.AppleCrypto.PAL_HashAlgorithm PalAlgorithmFromAlgorithmName(
+ HashAlgorithmName hashAlgorithmName)
+ {
+ if (hashAlgorithmName == HashAlgorithmName.MD5)
+ {
+ return Interop.AppleCrypto.PAL_HashAlgorithm.Md5;
+ }
+ else if (hashAlgorithmName == HashAlgorithmName.SHA1)
+ {
+ return Interop.AppleCrypto.PAL_HashAlgorithm.Sha1;
+ }
+ else if (hashAlgorithmName == HashAlgorithmName.SHA256)
+ {
+ return Interop.AppleCrypto.PAL_HashAlgorithm.Sha256;
+ }
+ else if (hashAlgorithmName == HashAlgorithmName.SHA384)
+ {
+ return Interop.AppleCrypto.PAL_HashAlgorithm.Sha384;
+ }
+ else if (hashAlgorithmName == HashAlgorithmName.SHA512)
+ {
+ return Interop.AppleCrypto.PAL_HashAlgorithm.Sha512;
+ }
+
+ throw new CryptographicException(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmName.Name);
+ }
+ }
+}
diff --git a/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecErr.cs b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecErr.cs
new file mode 100644
index 0000000000..48154a2288
--- /dev/null
+++ b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecErr.cs
@@ -0,0 +1,23 @@
+// 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;
+
+internal static partial class Interop
+{
+ internal static partial class AppleCrypto
+ {
+ internal static Exception CreateExceptionForOSStatus(int osStatus)
+ {
+ string msg = GetSecErrorString(osStatus);
+
+ if (msg == null)
+ {
+ return CreateExceptionForCCError(osStatus, "OSStatus");
+ }
+
+ return new AppleCommonCryptoCryptographicException(osStatus, msg);
+ }
+ }
+}
diff --git a/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecErrMessage.cs b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecErrMessage.cs
new file mode 100644
index 0000000000..c8192f482d
--- /dev/null
+++ b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecErrMessage.cs
@@ -0,0 +1,28 @@
+// 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.InteropServices;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class AppleCrypto
+ {
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern SafeCFStringHandle AppleCryptoNative_SecCopyErrorMessageString(int osStatus);
+
+ internal static string GetSecErrorString(int osStatus)
+ {
+ using (SafeCFStringHandle cfString = AppleCryptoNative_SecCopyErrorMessageString(osStatus))
+ {
+ if (cfString.IsInvalid)
+ {
+ return null;
+ }
+
+ return CoreFoundation.CFStringToString(cfString);
+ }
+ }
+ }
+}
diff --git a/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecKeyRef.Export.cs b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecKeyRef.Export.cs
new file mode 100644
index 0000000000..91e6d7feaf
--- /dev/null
+++ b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecKeyRef.Export.cs
@@ -0,0 +1,218 @@
+// 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;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Apple;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class AppleCrypto
+ {
+ private const string OidPbes2 = "1.2.840.113549.1.5.13";
+ private const string OidPbkdf2 = "1.2.840.113549.1.5.12";
+ private const string OidSha1 = "1.3.14.3.2.26";
+ private const string OidTripleDesCbc = "1.2.840.113549.3.7";
+
+ private static readonly SafeCreateHandle s_nullExportString = new SafeCreateHandle();
+
+ private static readonly SafeCreateHandle s_emptyExportString =
+ CoreFoundation.CFStringCreateWithCString("");
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SecKeyExport(
+ SafeSecKeyRefHandle key,
+ int exportPrivate,
+ SafeCreateHandle cfExportPassphrase,
+ out SafeCFDataHandle cfDataOut,
+ out int pOSStatus);
+
+ internal static DerSequenceReader SecKeyExport(
+ SafeSecKeyRefHandle key,
+ bool exportPrivate)
+ {
+ // Apple requires all private keys to be exported encrypted, but since we're trying to export
+ // as parsed structures we will need to decrypt it for the user.
+ const string ExportPassword = "DotnetExportPassphrase";
+
+ SafeCreateHandle exportPassword = exportPrivate
+ ? CoreFoundation.CFStringCreateWithCString(ExportPassword)
+ : s_nullExportString;
+
+ int ret;
+ SafeCFDataHandle cfData;
+ int osStatus;
+
+ try
+ {
+ ret = AppleCryptoNative_SecKeyExport(
+ key,
+ exportPrivate ? 1 : 0,
+ exportPassword,
+ out cfData,
+ out osStatus);
+ }
+ finally
+ {
+ if (exportPassword != s_nullExportString)
+ {
+ exportPassword.Dispose();
+ }
+ }
+
+ byte[] exportedData;
+
+ using (cfData)
+ {
+ if (ret == 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ if (ret != 1)
+ {
+ Debug.Fail($"AppleCryptoNative_SecKeyExport returned {ret}");
+ throw new CryptographicException();
+ }
+
+ exportedData = CoreFoundation.CFGetData(cfData);
+ }
+
+ DerSequenceReader reader = new DerSequenceReader(exportedData);
+
+ if (!exportPrivate)
+ {
+ return reader;
+ }
+
+ byte tag = reader.PeekTag();
+
+ // PKCS#8 defines two structures, PrivateKeyInfo, which starts with an integer,
+ // and EncryptedPrivateKey, which starts with an encryption algorithm (DER sequence).
+ if (tag == (byte)DerSequenceReader.DerTag.Integer)
+ {
+ return reader;
+ }
+
+ const byte ConstructedSequence =
+ DerSequenceReader.ConstructedFlag | (byte)DerSequenceReader.DerTag.Sequence;
+
+ if (tag == ConstructedSequence)
+ {
+ return ReadEncryptedPkcs8Blob(ExportPassword, reader);
+ }
+
+ Debug.Fail($"Data was neither PrivateKey or EncryptedPrivateKey: {tag:X2}");
+ throw new CryptographicException();
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "3DES identified from payload by OID")]
+ private static DerSequenceReader ReadEncryptedPkcs8Blob(string passphrase, DerSequenceReader reader)
+ {
+ // EncryptedPrivateKeyInfo::= SEQUENCE {
+ // encryptionAlgorithm EncryptionAlgorithmIdentifier,
+ // encryptedData EncryptedData }
+ //
+ // EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+ //
+ // EncryptedData ::= OCTET STRING
+ DerSequenceReader algorithmIdentifier = reader.ReadSequence();
+ string algorithmOid = algorithmIdentifier.ReadOidAsString();
+
+ // PBES2 (Password-Based Encryption Scheme 2)
+ if (algorithmOid != OidPbes2)
+ {
+ Debug.Fail($"Expected PBES2 ({OidPbes2}), got {algorithmOid}");
+ throw new CryptographicException();
+ }
+
+ // PBES2-params ::= SEQUENCE {
+ // keyDerivationFunc AlgorithmIdentifier { { PBES2 - KDFs} },
+ // encryptionScheme AlgorithmIdentifier { { PBES2 - Encs} }
+ // }
+
+ DerSequenceReader pbes2Params = algorithmIdentifier.ReadSequence();
+ algorithmIdentifier = pbes2Params.ReadSequence();
+
+ string kdfOid = algorithmIdentifier.ReadOidAsString();
+
+ // PBKDF2 (Password-Based Key Derivation Function 2)
+ if (kdfOid != OidPbkdf2)
+ {
+ Debug.Fail($"Expected PBKDF2 ({OidPbkdf2}), got {kdfOid}");
+ throw new CryptographicException();
+ }
+
+ // PBKDF2-params ::= SEQUENCE {
+ // salt CHOICE {
+ // specified OCTET STRING,
+ // otherSource AlgorithmIdentifier { { PBKDF2 - SaltSources} }
+ // },
+ // iterationCount INTEGER (1..MAX),
+ // keyLength INTEGER(1..MAX) OPTIONAL,
+ // prf AlgorithmIdentifier { { PBKDF2 - PRFs} } DEFAULT algid - hmacWithSHA1
+ // }
+ DerSequenceReader pbkdf2Params = algorithmIdentifier.ReadSequence();
+
+ byte[] salt = pbkdf2Params.ReadOctetString();
+ int iterCount = pbkdf2Params.ReadInteger();
+ int keySize = -1;
+
+ if (pbkdf2Params.HasData && pbkdf2Params.PeekTag() == (byte)DerSequenceReader.DerTag.Integer)
+ {
+ keySize = pbkdf2Params.ReadInteger();
+ }
+
+ if (pbkdf2Params.HasData)
+ {
+ string prfOid = pbkdf2Params.ReadOidAsString();
+
+ // SHA-1 is the only hash algorithm our PBKDF2 supports.
+ if (prfOid != OidSha1)
+ {
+ Debug.Fail($"Expected SHA1 ({OidSha1}), got {prfOid}");
+ throw new CryptographicException();
+ }
+ }
+
+ DerSequenceReader encryptionScheme = pbes2Params.ReadSequence();
+ string cipherOid = encryptionScheme.ReadOidAsString();
+
+ // DES-EDE3-CBC (TripleDES in CBC mode)
+ if (cipherOid != OidTripleDesCbc)
+ {
+ Debug.Fail($"Expected DES-EDE3-CBC ({OidTripleDesCbc}), got {cipherOid}");
+ throw new CryptographicException();
+ }
+
+ byte[] decrypted;
+
+ using (TripleDES des3 = TripleDES.Create())
+ {
+ if (keySize == -1)
+ {
+ foreach (KeySizes keySizes in des3.LegalKeySizes)
+ {
+ keySize = Math.Max(keySize, keySizes.MaxSize);
+ }
+ }
+
+ byte[] iv = encryptionScheme.ReadOctetString();
+
+ using (Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(passphrase, salt, iterCount))
+ using (ICryptoTransform decryptor = des3.CreateDecryptor(pbkdf2.GetBytes(keySize / 8), iv))
+ {
+ byte[] encrypted = reader.ReadOctetString();
+ decrypted = decryptor.TransformFinalBlock(encrypted, 0, encrypted.Length);
+ }
+ }
+
+ DerSequenceReader pkcs8Reader = new DerSequenceReader(decrypted);
+ return pkcs8Reader;
+ }
+ }
+}
diff --git a/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecKeyRef.cs b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecKeyRef.cs
new file mode 100644
index 0000000000..e516af3324
--- /dev/null
+++ b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SecKeyRef.cs
@@ -0,0 +1,255 @@
+// 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.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Apple;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class AppleCrypto
+ {
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SecKeyImportEphemeral(
+ byte[] pbKeyBlob,
+ int cbKeyBlob,
+ int isPrivateKey,
+ out SafeSecKeyRefHandle ppKeyOut,
+ out int pOSStatus);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_GenerateSignature(
+ SafeSecKeyRefHandle privateKey,
+ byte[] pbDataHash,
+ int cbDataHash,
+ out SafeCFDataHandle pSignatureOut,
+ out SafeCFErrorHandle pErrorOut);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_GenerateSignatureWithHashAlgorithm(
+ SafeSecKeyRefHandle privateKey,
+ byte[] pbDataHash,
+ int cbDataHash,
+ PAL_HashAlgorithm hashAlgorithm,
+ out SafeCFDataHandle pSignatureOut,
+ out SafeCFErrorHandle pErrorOut);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_VerifySignature(
+ SafeSecKeyRefHandle publicKey,
+ byte[] pbDataHash,
+ int cbDataHash,
+ byte[] pbSignature,
+ int cbSignature,
+ out SafeCFErrorHandle pErrorOut);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_VerifySignatureWithHashAlgorithm(
+ SafeSecKeyRefHandle publicKey,
+ byte[] pbDataHash,
+ int cbDataHash,
+ byte[] pbSignature,
+ int cbSignature,
+ PAL_HashAlgorithm hashAlgorithm,
+ out SafeCFErrorHandle pErrorOut);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern ulong AppleCryptoNative_SecKeyGetSimpleKeySizeInBytes(SafeSecKeyRefHandle publicKey);
+
+ private delegate int SecKeyTransform(out SafeCFDataHandle data, out SafeCFErrorHandle error);
+
+ private static byte[] ExecuteTransform(SecKeyTransform transform)
+ {
+ const int Success = 1;
+ const int kErrorSeeError = -2;
+
+ SafeCFDataHandle data;
+ SafeCFErrorHandle error;
+
+ int ret = transform(out data, out error);
+
+ using (error)
+ using (data)
+ {
+ if (ret == Success)
+ {
+ return CoreFoundation.CFGetData(data);
+ }
+
+ if (ret == kErrorSeeError)
+ {
+ throw CreateExceptionForCFError(error);
+ }
+
+ Debug.Fail($"transform returned {ret}");
+ throw new CryptographicException();
+ }
+ }
+
+ internal static int GetSimpleKeySizeInBits(SafeSecKeyRefHandle publicKey)
+ {
+ ulong keySizeInBytes = AppleCryptoNative_SecKeyGetSimpleKeySizeInBytes(publicKey);
+
+ checked
+ {
+ return (int)(keySizeInBytes * 8);
+ }
+ }
+
+ internal static SafeSecKeyRefHandle ImportEphemeralKey(byte[] keyBlob, bool hasPrivateKey)
+ {
+ Debug.Assert(keyBlob != null);
+
+ SafeSecKeyRefHandle keyHandle;
+ int osStatus;
+
+ int ret = AppleCryptoNative_SecKeyImportEphemeral(
+ keyBlob,
+ keyBlob.Length,
+ hasPrivateKey ? 1 : 0,
+ out keyHandle,
+ out osStatus);
+
+ if (ret == 1 && !keyHandle.IsInvalid)
+ {
+ return keyHandle;
+ }
+
+ if (ret == 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ Debug.Fail($"SecKeyImportEphemeral returned {ret}");
+ throw new CryptographicException();
+ }
+
+ internal static byte[] GenerateSignature(SafeSecKeyRefHandle privateKey, byte[] dataHash)
+ {
+ Debug.Assert(privateKey != null, "privateKey != null");
+ Debug.Assert(dataHash != null, "dataHash != null");
+
+ return ExecuteTransform(
+ (out SafeCFDataHandle signature, out SafeCFErrorHandle error) =>
+ AppleCryptoNative_GenerateSignature(
+ privateKey,
+ dataHash,
+ dataHash.Length,
+ out signature,
+ out error));
+ }
+
+ internal static byte[] GenerateSignature(
+ SafeSecKeyRefHandle privateKey,
+ byte[] dataHash,
+ PAL_HashAlgorithm hashAlgorithm)
+ {
+ Debug.Assert(privateKey != null, "privateKey != null");
+ Debug.Assert(dataHash != null, "dataHash != null");
+ Debug.Assert(hashAlgorithm != PAL_HashAlgorithm.Unknown, "hashAlgorithm != PAL_HashAlgorithm.Unknown");
+
+ return ExecuteTransform(
+ (out SafeCFDataHandle signature, out SafeCFErrorHandle error) =>
+ AppleCryptoNative_GenerateSignatureWithHashAlgorithm(
+ privateKey,
+ dataHash,
+ dataHash.Length,
+ hashAlgorithm,
+ out signature,
+ out error));
+ }
+
+ internal static bool VerifySignature(
+ SafeSecKeyRefHandle publicKey,
+ byte[] dataHash,
+ byte[] signature)
+ {
+ Debug.Assert(publicKey != null, "publicKey != null");
+ Debug.Assert(dataHash != null, "dataHash != null");
+ Debug.Assert(signature != null, "signature != null");
+
+ SafeCFErrorHandle error;
+
+ int ret = AppleCryptoNative_VerifySignature(
+ publicKey,
+ dataHash,
+ dataHash.Length,
+ signature,
+ signature.Length,
+ out error);
+
+ const int True = 1;
+ const int False = 0;
+ const int kErrorSeeError = -2;
+
+ using (error)
+ {
+ switch (ret)
+ {
+ case True:
+ return true;
+ case False:
+ return false;
+ case kErrorSeeError:
+ throw CreateExceptionForCFError(error);
+ default:
+ Debug.Fail($"VerifySignature returned {ret}");
+ throw new CryptographicException();
+ }
+ }
+ }
+
+ internal static bool VerifySignature(
+ SafeSecKeyRefHandle publicKey,
+ byte[] dataHash,
+ byte[] signature,
+ PAL_HashAlgorithm hashAlgorithm)
+ {
+ Debug.Assert(publicKey != null, "publicKey != null");
+ Debug.Assert(dataHash != null, "dataHash != null");
+ Debug.Assert(signature != null, "signature != null");
+ Debug.Assert(hashAlgorithm != PAL_HashAlgorithm.Unknown);
+
+ SafeCFErrorHandle error;
+
+ int ret = AppleCryptoNative_VerifySignatureWithHashAlgorithm(
+ publicKey,
+ dataHash,
+ dataHash.Length,
+ signature,
+ signature.Length,
+ hashAlgorithm,
+ out error);
+
+ const int True = 1;
+ const int False = 0;
+ const int kErrorSeeError = -2;
+
+ using (error)
+ {
+ switch (ret)
+ {
+ case True:
+ return true;
+ case False:
+ return false;
+ case kErrorSeeError:
+ throw CreateExceptionForCFError(error);
+ default:
+ Debug.Fail($"VerifySignature returned {ret}");
+ throw new CryptographicException();
+ }
+ }
+ }
+ }
+}
+
+namespace System.Security.Cryptography.Apple
+{
+ internal sealed class SafeSecKeyRefHandle : SafeKeychainItemHandle
+ {
+ }
+}
diff --git a/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ssl.cs b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ssl.cs
new file mode 100644
index 0000000000..6cc0a8fe89
--- /dev/null
+++ b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Ssl.cs
@@ -0,0 +1,627 @@
+// 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;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Authentication;
+using Microsoft.Win32.SafeHandles;
+using SafeSslHandle = System.Net.SafeSslHandle;
+
+internal static partial class Interop
+{
+ internal static partial class AppleCrypto
+ {
+ // Read data from connection (or an instance delegate captured context) and write it to data
+ // dataLength comes in as the capacity of data, goes out as bytes written.
+ // Note: the true type of dataLength is `size_t*`, but on macOS that's most equal to `void**`
+ internal unsafe delegate int SSLReadFunc(void* connection, byte* data, void** dataLength);
+
+ // (In the C decl for this function data is "const byte*", justifying the second type).
+ // Read *dataLength from data and write it to connection (or an instance delegate captured context),
+ // and set *dataLength to the number of bytes actually transferred.
+ internal unsafe delegate int SSLWriteFunc(void* connection, byte* data, void** dataLength);
+
+ internal enum PAL_TlsHandshakeState
+ {
+ Unknown,
+ Complete,
+ WouldBlock,
+ ServerAuthCompleted,
+ ClientAuthCompleted,
+ }
+
+ internal enum PAL_TlsIo
+ {
+ Unknown,
+ Success,
+ WouldBlock,
+ ClosedGracefully,
+ Renegotiate,
+ }
+
+ // These come from the various SSL/TLS RFCs.
+ internal enum TlsCipherSuite
+ {
+ TLS_NULL_WITH_NULL_NULL = 0x0000,
+ SSL_NULL_WITH_NULL_NULL = 0x0000,
+
+ TLS_RSA_WITH_NULL_MD5 = 0x0001,
+ SSL_RSA_WITH_NULL_MD5 = 0x0001,
+
+ TLS_RSA_WITH_NULL_SHA = 0x0002,
+ SSL_RSA_WITH_NULL_SHA = 0x0002,
+
+ SSL_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003,
+
+ TLS_RSA_WITH_RC4_128_MD5 = 0x0004,
+ SSL_RSA_WITH_RC4_128_MD5 = 0x0004,
+
+ TLS_RSA_WITH_RC4_128_SHA = 0x0005,
+ SSL_RSA_WITH_RC4_128_SHA = 0x0005,
+
+ SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006,
+
+ //Windows has no value for IDEA, so .NET Framework didn't get one.
+ //SSL_RSA_WITH_IDEA_CBC_SHA = 0x0007,
+
+ SSL_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008,
+
+ SSL_RSA_WITH_DES_CBC_SHA = 0x0009,
+
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A,
+ SSL_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A,
+
+ SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B,
+
+ SSL_DH_DSS_WITH_DES_CBC_SHA = 0x000C,
+
+ TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D,
+ SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D,
+
+ SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E,
+
+ SSL_DH_RSA_WITH_DES_CBC_SHA = 0x000F,
+
+ TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010,
+ SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010,
+
+ SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011,
+
+ SSL_DHE_DSS_WITH_DES_CBC_SHA = 0x0012,
+
+ TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013,
+ SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013,
+
+ SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014,
+
+ SSL_DHE_RSA_WITH_DES_CBC_SHA = 0x0015,
+
+ TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016,
+ SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016,
+
+ SSL_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017,
+
+ TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018,
+ SSL_DH_anon_WITH_RC4_128_MD5 = 0x0018,
+
+ SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019,
+
+ SSL_DH_anon_WITH_DES_CBC_SHA = 0x001A,
+
+ TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B,
+ SSL_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B,
+
+ // Windows doesn't support FORTEZZA_DMS, so unclear what value to use.
+ //SSL_FORTEZZA_DMS_WITH_NULL_SHA = 0x001C,
+
+ // Windows doesn't support FORTEZZA_DMS, so unclear what value to use.
+ //SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA = 0x001D,
+
+ TLS_PSK_WITH_NULL_SHA = 0x002C,
+
+ TLS_DHE_PSK_WITH_NULL_SHA = 0x002D,
+
+ TLS_RSA_PSK_WITH_NULL_SHA = 0x002E,
+
+ TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F,
+
+ TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030,
+
+ TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031,
+
+ TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032,
+
+ TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033,
+
+ TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034,
+
+ TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035,
+
+ TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036,
+
+ TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037,
+
+ TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038,
+
+ TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039,
+
+ TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A,
+
+ TLS_RSA_WITH_NULL_SHA256 = 0x003B,
+
+ TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C,
+
+ TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D,
+
+ TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E,
+
+ TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F,
+
+ TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040,
+
+ TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067,
+
+ TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068,
+
+ TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069,
+
+ TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A,
+
+ TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B,
+
+ TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x006C,
+
+ TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x006D,
+
+ TLS_PSK_WITH_RC4_128_SHA = 0x008A,
+
+ TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B,
+
+ TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C,
+
+ TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D,
+
+ TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E,
+
+ TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F,
+
+ TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090,
+
+ TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091,
+
+ TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092,
+
+ TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093,
+
+ TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094,
+
+ TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095,
+
+ TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C,
+
+ TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D,
+
+ TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E,
+
+ TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F,
+
+ TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0,
+
+ TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1,
+
+ TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2,
+
+ TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3,
+
+ TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4,
+
+ TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5,
+
+ TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6,
+
+ TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7,
+
+ TLS_PSK_WITH_AES_128_GCM_SHA256 = 0x00A8,
+
+ TLS_PSK_WITH_AES_256_GCM_SHA384 = 0x00A9,
+
+ TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA,
+
+ TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB,
+
+ TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = 0x00AC,
+
+ TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = 0x00AD,
+
+ TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE,
+
+ TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AF,
+
+ TLS_PSK_WITH_NULL_SHA256 = 0x00B0,
+
+ TLS_PSK_WITH_NULL_SHA384 = 0x00B1,
+
+ TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2,
+
+ TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3,
+
+ TLS_DHE_PSK_WITH_NULL_SHA256 = 0x00B4,
+
+ TLS_DHE_PSK_WITH_NULL_SHA384 = 0x00B5,
+
+ TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = 0x00B6,
+
+ TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = 0x00B7,
+
+ TLS_RSA_PSK_WITH_NULL_SHA256 = 0x00B8,
+
+ TLS_RSA_PSK_WITH_NULL_SHA384 = 0x00B9,
+
+ // Not a real CipherSuite
+ //TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF,
+
+ TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001,
+
+ TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002,
+
+ TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003,
+
+ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004,
+
+ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005,
+
+ TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006,
+
+ TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007,
+
+ TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008,
+
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009,
+
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A,
+
+ TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B,
+
+ TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C,
+
+ TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D,
+
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013,
+
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014,
+
+ TLS_ECDH_anon_WITH_NULL_SHA = 0xC015,
+
+ TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016,
+
+ TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017,
+
+ TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018,
+
+ TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019,
+
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023,
+
+ TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024,
+
+ TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025,
+
+ TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026,
+
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027,
+
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028,
+
+ TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029,
+
+ TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A,
+
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B,
+
+ TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C,
+
+ TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D,
+
+ TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E,
+
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F,
+
+ TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030,
+
+ TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031,
+
+ TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032,
+ }
+
+ [DllImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SslCreateContext")]
+ internal static extern System.Net.SafeSslHandle SslCreateContext(int isServer);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SslSetMinProtocolVersion(
+ SafeSslHandle sslHandle,
+ SslProtocols minProtocolId);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SslSetMaxProtocolVersion(
+ SafeSslHandle sslHandle,
+ SslProtocols maxProtocolId);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SslCopyCertChain(
+ SafeSslHandle sslHandle,
+ out SafeX509ChainHandle pTrustOut,
+ out int pOSStatus);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SslCopyCADistinguishedNames(
+ SafeSslHandle sslHandle,
+ out SafeCFArrayHandle pArrayOut,
+ out int pOSStatus);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SslSetBreakOnServerAuth(
+ SafeSslHandle sslHandle,
+ int setBreak,
+ out int pOSStatus);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SslSetBreakOnClientAuth(
+ SafeSslHandle sslHandle,
+ int setBreak,
+ out int pOSStatus);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SslSetCertificate(
+ SafeSslHandle sslHandle,
+ SafeCreateHandle cfCertRefs);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SslSetTargetName(
+ SafeSslHandle sslHandle,
+ string targetName,
+ int cbTargetName,
+ out int osStatus);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SslHandshake")]
+ internal static extern PAL_TlsHandshakeState SslHandshake(SafeSslHandle sslHandle);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SslSetAcceptClientCert(SafeSslHandle sslHandle);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SslSetIoCallbacks")]
+ internal static extern int SslSetIoCallbacks(
+ SafeSslHandle sslHandle,
+ SSLReadFunc readCallback,
+ SSLWriteFunc writeCallback);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SslWrite")]
+ internal static extern unsafe PAL_TlsIo SslWrite(SafeSslHandle sslHandle, byte* writeFrom, int count, out int bytesWritten);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SslRead")]
+ internal static extern unsafe PAL_TlsIo SslRead(SafeSslHandle sslHandle, byte* writeFrom, int count, out int bytesWritten);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_SslIsHostnameMatch(
+ SafeSslHandle handle,
+ SafeCreateHandle cfHostname,
+ SafeCFDateHandle cfValidTime);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SslShutdown")]
+ internal static extern int SslShutdown(SafeSslHandle sslHandle);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SslGetCipherSuite")]
+ internal static extern int SslGetCipherSuite(SafeSslHandle sslHandle, out TlsCipherSuite cipherSuite);
+
+ [DllImport(Interop.Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_SslGetProtocolVersion")]
+ internal static extern int SslGetProtocolVersion(SafeSslHandle sslHandle, out SslProtocols protocol);
+
+ internal static void SslSetAcceptClientCert(SafeSslHandle sslHandle)
+ {
+ int osStatus = AppleCryptoNative_SslSetAcceptClientCert(sslHandle);
+
+ if (osStatus != 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+ }
+
+ internal static void SslSetMinProtocolVersion(SafeSslHandle sslHandle, SslProtocols minProtocolId)
+ {
+ int osStatus = AppleCryptoNative_SslSetMinProtocolVersion(sslHandle, minProtocolId);
+
+ if (osStatus != 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+ }
+
+ internal static void SslSetMaxProtocolVersion(SafeSslHandle sslHandle, SslProtocols maxProtocolId)
+ {
+ int osStatus = AppleCryptoNative_SslSetMaxProtocolVersion(sslHandle, maxProtocolId);
+
+ if (osStatus != 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+ }
+
+ internal static SafeX509ChainHandle SslCopyCertChain(SafeSslHandle sslHandle)
+ {
+ SafeX509ChainHandle chainHandle;
+ int osStatus;
+ int result = AppleCryptoNative_SslCopyCertChain(sslHandle, out chainHandle, out osStatus);
+
+ if (result == 1)
+ {
+ return chainHandle;
+ }
+
+ chainHandle.Dispose();
+
+ if (result == 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ Debug.Fail($"AppleCryptoNative_SslCopyCertChain returned {result}");
+ throw new SslException();
+ }
+
+ internal static SafeCFArrayHandle SslCopyCADistinguishedNames(SafeSslHandle sslHandle)
+ {
+ SafeCFArrayHandle dnArray;
+ int osStatus;
+ int result = AppleCryptoNative_SslCopyCADistinguishedNames(sslHandle, out dnArray, out osStatus);
+
+ if (result == 1)
+ {
+ return dnArray;
+ }
+
+ dnArray.Dispose();
+
+ if (result == 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ Debug.Fail($"AppleCryptoNative_SslCopyCADistinguishedNames returned {result}");
+ throw new SslException();
+ }
+
+ internal static void SslBreakOnServerAuth(SafeSslHandle sslHandle, bool setBreak)
+ {
+ int osStatus;
+ int result = AppleCryptoNative_SslSetBreakOnServerAuth(sslHandle, setBreak ? 1 : 0, out osStatus);
+
+ if (result == 1)
+ {
+ return;
+ }
+
+ if (result == 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ Debug.Fail($"AppleCryptoNative_SslSetBreakOnServerAuth returned {result}");
+ throw new SslException();
+ }
+
+ internal static void SslBreakOnClientAuth(SafeSslHandle sslHandle, bool setBreak)
+ {
+ int osStatus;
+ int result = AppleCryptoNative_SslSetBreakOnClientAuth(sslHandle, setBreak ? 1 : 0, out osStatus);
+
+ if (result == 1)
+ {
+ return;
+ }
+
+ if (result == 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ Debug.Fail($"AppleCryptoNative_SslSetBreakOnClientAuth returned {result}");
+ throw new SslException();
+ }
+
+ internal static void SslSetCertificate(SafeSslHandle sslHandle, IntPtr[] certChainPtrs)
+ {
+ using (SafeCreateHandle cfCertRefs = CoreFoundation.CFArrayCreate(certChainPtrs, (UIntPtr)certChainPtrs.Length))
+ {
+ int osStatus = AppleCryptoNative_SslSetCertificate(sslHandle, cfCertRefs);
+
+ if (osStatus != 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+ }
+ }
+
+ internal static void SslSetTargetName(SafeSslHandle sslHandle, string targetName)
+ {
+ Debug.Assert(!string.IsNullOrEmpty(targetName));
+
+ int osStatus;
+ int cbTargetName = System.Text.Encoding.UTF8.GetByteCount(targetName);
+
+ int result = AppleCryptoNative_SslSetTargetName(sslHandle, targetName, cbTargetName, out osStatus);
+
+ if (result == 1)
+ {
+ return;
+ }
+
+ if (result == 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ Debug.Fail($"AppleCryptoNative_SslSetTargetName returned {result}");
+ throw new SslException();
+ }
+
+ public static bool SslCheckHostnameMatch(SafeSslHandle handle, string hostName, DateTime notBefore)
+ {
+ int result;
+ // The IdnMapping converts Unicode input into the IDNA punycode sequence.
+ // It also does host case normalization. The bypass logic would be something
+ // like "all characters being within [a-z0-9.-]+"
+ // Since it's not documented as being thread safe, create a new one each time.
+ //
+ // The SSL Policy (SecPolicyCreateSSL) has been verified as not inherently supporting
+ // IDNA as of macOS 10.12.1 (Sierra). If it supports low-level IDNA at a later date,
+ // this code could be removed.
+ //
+ // It was verified as supporting case invariant match as of 10.12.1 (Sierra).
+ string matchName = new System.Globalization.IdnMapping().GetAscii(hostName);
+
+ using (SafeCFDateHandle cfNotBefore = CoreFoundation.CFDateCreate(notBefore))
+ using (SafeCreateHandle cfHostname = CoreFoundation.CFStringCreateWithCString(matchName))
+ {
+ result = AppleCryptoNative_SslIsHostnameMatch(handle, cfHostname, cfNotBefore);
+ }
+
+ switch (result)
+ {
+ case 0:
+ return false;
+ case 1:
+ return true;
+ default:
+ Debug.Fail($"AppleCryptoNative_SslIsHostnameMatch returned {result}");
+ throw new SslException();
+ }
+ }
+ }
+}
+
+namespace System.Net
+{
+ internal sealed class SafeSslHandle : SafeHandle
+ {
+ internal SafeSslHandle()
+ : base(IntPtr.Zero, ownsHandle: true)
+ {
+ }
+
+ internal SafeSslHandle(IntPtr invalidHandleValue, bool ownsHandle)
+ : base(invalidHandleValue, ownsHandle)
+ {
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.CoreFoundation.CFRelease(handle);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+ }
+}
diff --git a/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SslErr.cs b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SslErr.cs
new file mode 100644
index 0000000000..f42438d201
--- /dev/null
+++ b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.SslErr.cs
@@ -0,0 +1,35 @@
+// 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;
+
+internal static partial class Interop
+{
+ internal static partial class AppleCrypto
+ {
+ internal class SslException : Exception
+ {
+ internal SslException()
+ {
+ }
+
+ internal SslException(int errorCode, string message)
+ : base(message)
+ {
+ HResult = errorCode;
+ }
+ }
+ }
+
+ internal static partial class AppleCrypto
+ {
+ internal static Exception CreateExceptionForOSStatus(int osStatus)
+ {
+ string msg = GetSecErrorString(osStatus);
+
+ // msg might be null, but that's OK
+ return new SslException(osStatus, msg);
+ }
+ }
+}
diff --git a/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Trust.cs b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Trust.cs
new file mode 100644
index 0000000000..b3bfa24c41
--- /dev/null
+++ b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.Trust.cs
@@ -0,0 +1,90 @@
+// 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.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class AppleCrypto
+ {
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_StoreEnumerateUserRoot(
+ out SafeCFArrayHandle pCertsOut,
+ out int pOSStatusOut);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_StoreEnumerateMachineRoot(
+ out SafeCFArrayHandle pCertsOut,
+ out int pOSStatusOut);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_StoreEnumerateUserDisallowed(
+ out SafeCFArrayHandle pCertsOut,
+ out int pOSStatusOut);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_StoreEnumerateMachineDisallowed(
+ out SafeCFArrayHandle pCertsOut,
+ out int pOSStatusOut);
+
+ private delegate int StoreEnumerator(out SafeCFArrayHandle pCertsOut, out int pOSStatusOut);
+
+ internal static SafeCFArrayHandle StoreEnumerateDisallowed(StoreLocation location)
+ {
+ return EnumerateStore(
+ location,
+ AppleCryptoNative_StoreEnumerateUserDisallowed,
+ AppleCryptoNative_StoreEnumerateMachineDisallowed);
+ }
+
+ internal static SafeCFArrayHandle StoreEnumerateRoot(StoreLocation location)
+ {
+ return EnumerateStore(
+ location,
+ AppleCryptoNative_StoreEnumerateUserRoot,
+ AppleCryptoNative_StoreEnumerateMachineRoot);
+ }
+
+ private static SafeCFArrayHandle EnumerateStore(
+ StoreLocation location,
+ StoreEnumerator userEnumerator,
+ StoreEnumerator machineEnumerator)
+ {
+ int result;
+ SafeCFArrayHandle matches;
+ int osStatus;
+
+ if (location == StoreLocation.CurrentUser)
+ {
+ result = userEnumerator(out matches, out osStatus);
+ }
+ else if (location == StoreLocation.LocalMachine)
+ {
+ result = machineEnumerator(out matches, out osStatus);
+ }
+ else
+ {
+ Debug.Fail($"Unrecognized StoreLocation value: {location}");
+ throw new CryptographicException();
+ }
+
+ if (result == 1)
+ {
+ return matches;
+ }
+
+ matches.Dispose();
+
+ if (result == 0)
+ throw CreateExceptionForOSStatus(osStatus);
+
+ Debug.Fail($"Unexpected result from {location} trust store enumeration: {result}");
+ throw new CryptographicException();
+ }
+ }
+}
diff --git a/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs
new file mode 100644
index 0000000000..4bf5a41f0d
--- /dev/null
+++ b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509.cs
@@ -0,0 +1,436 @@
+// 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;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Apple;
+using System.Security.Cryptography.X509Certificates;
+
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class AppleCrypto
+ {
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_X509ImportCertificate(
+ byte[] pbKeyBlob,
+ int cbKeyBlob,
+ X509ContentType contentType,
+ SafeCreateHandle cfPfxPassphrase,
+ SafeKeychainHandle tmpKeychain,
+ int exportable,
+ out SafeSecCertificateHandle pCertOut,
+ out SafeSecIdentityHandle pPrivateKeyOut,
+ out int pOSStatus);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_X509ImportCollection(
+ byte[] pbKeyBlob,
+ int cbKeyBlob,
+ X509ContentType contentType,
+ SafeCreateHandle cfPfxPassphrase,
+ SafeKeychainHandle tmpKeychain,
+ int exportable,
+ out SafeCFArrayHandle pCollectionOut,
+ out int pOSStatus);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_X509GetRawData(
+ SafeSecCertificateHandle cert,
+ out SafeCFDataHandle cfDataOut,
+ out int pOSStatus);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_X509GetPublicKey(SafeSecCertificateHandle cert, out SafeSecKeyRefHandle publicKey, out int pOSStatus);
+
+ [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_X509GetContentType")]
+ internal static extern X509ContentType X509GetContentType(byte[] pbData, int cbData);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_X509CopyCertFromIdentity(
+ SafeSecIdentityHandle identity,
+ out SafeSecCertificateHandle cert);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_X509CopyPrivateKeyFromIdentity(
+ SafeSecIdentityHandle identity,
+ out SafeSecKeyRefHandle key);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_X509DemuxAndRetainHandle(
+ IntPtr handle,
+ out SafeSecCertificateHandle certHandle,
+ out SafeSecIdentityHandle identityHandle);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_X509ExportData(
+ SafeCreateHandle data,
+ X509ContentType type,
+ SafeCreateHandle cfExportPassphrase,
+ out SafeCFDataHandle pExportOut,
+ out int pOSStatus);
+
+ internal static byte[] X509GetRawData(SafeSecCertificateHandle cert)
+ {
+ int osStatus;
+ SafeCFDataHandle data;
+
+ int ret = AppleCryptoNative_X509GetRawData(
+ cert,
+ out data,
+ out osStatus);
+
+ if (ret == 1)
+ {
+ return CoreFoundation.CFGetData(data);
+ }
+
+ if (ret == 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ Debug.Fail($"Unexpected return value {ret}");
+ throw new CryptographicException();
+ }
+
+ internal static SafeSecCertificateHandle X509ImportCertificate(
+ byte[] bytes,
+ X509ContentType contentType,
+ SafePasswordHandle importPassword,
+ SafeKeychainHandle keychain,
+ bool exportable,
+ out SafeSecIdentityHandle identityHandle)
+ {
+ SafeSecCertificateHandle certHandle;
+ int osStatus;
+ int ret;
+
+ SafeCreateHandle cfPassphrase = s_nullExportString;
+ bool releasePassword = false;
+
+ try
+ {
+ if (!importPassword.IsInvalid)
+ {
+ importPassword.DangerousAddRef(ref releasePassword);
+ IntPtr passwordHandle = importPassword.DangerousGetHandle();
+
+ if (passwordHandle != IntPtr.Zero)
+ {
+ cfPassphrase = CoreFoundation.CFStringCreateWithCString(passwordHandle);
+ }
+ }
+
+ ret = AppleCryptoNative_X509ImportCertificate(
+ bytes,
+ bytes.Length,
+ contentType,
+ cfPassphrase,
+ keychain,
+ exportable ? 1 : 0,
+ out certHandle,
+ out identityHandle,
+ out osStatus);
+
+ SafeTemporaryKeychainHandle.TrackItem(certHandle);
+ SafeTemporaryKeychainHandle.TrackItem(identityHandle);
+ }
+ finally
+ {
+ if (releasePassword)
+ {
+ importPassword.DangerousRelease();
+ }
+
+ if (cfPassphrase != s_nullExportString)
+ {
+ cfPassphrase.Dispose();
+ }
+ }
+
+ if (ret == 1)
+ {
+ return certHandle;
+ }
+
+ certHandle.Dispose();
+ identityHandle.Dispose();
+
+ const int SeeOSStatus = 0;
+ const int ImportReturnedEmpty = -2;
+ const int ImportReturnedNull = -3;
+
+ switch (ret)
+ {
+ case SeeOSStatus:
+ throw CreateExceptionForOSStatus(osStatus);
+ case ImportReturnedNull:
+ case ImportReturnedEmpty:
+ throw new CryptographicException();
+ default:
+ Debug.Fail($"Unexpected return value {ret}");
+ throw new CryptographicException();
+ }
+ }
+
+ internal static SafeCFArrayHandle X509ImportCollection(
+ byte[] bytes,
+ X509ContentType contentType,
+ SafePasswordHandle importPassword,
+ SafeKeychainHandle keychain,
+ bool exportable)
+ {
+ SafeCreateHandle cfPassphrase = s_nullExportString;
+ bool releasePassword = false;
+
+ int ret;
+ SafeCFArrayHandle collectionHandle;
+ int osStatus;
+
+ try
+ {
+ if (!importPassword.IsInvalid)
+ {
+ importPassword.DangerousAddRef(ref releasePassword);
+ IntPtr passwordHandle = importPassword.DangerousGetHandle();
+
+ if (passwordHandle != IntPtr.Zero)
+ {
+ cfPassphrase = CoreFoundation.CFStringCreateWithCString(passwordHandle);
+ }
+ }
+
+ ret = AppleCryptoNative_X509ImportCollection(
+ bytes,
+ bytes.Length,
+ contentType,
+ cfPassphrase,
+ keychain,
+ exportable ? 1 : 0,
+ out collectionHandle,
+ out osStatus);
+
+ if (ret == 1)
+ {
+ return collectionHandle;
+ }
+ }
+ finally
+ {
+ if (releasePassword)
+ {
+ importPassword.DangerousRelease();
+ }
+
+ if (cfPassphrase != s_nullExportString)
+ {
+ cfPassphrase.Dispose();
+ }
+ }
+
+ collectionHandle.Dispose();
+
+ const int SeeOSStatus = 0;
+ const int ImportReturnedEmpty = -2;
+ const int ImportReturnedNull = -3;
+
+ switch (ret)
+ {
+ case SeeOSStatus:
+ throw CreateExceptionForOSStatus(osStatus);
+ case ImportReturnedNull:
+ case ImportReturnedEmpty:
+ throw new CryptographicException();
+ default:
+ Debug.Fail($"Unexpected return value {ret}");
+ throw new CryptographicException();
+ }
+ }
+
+ internal static SafeSecCertificateHandle X509GetCertFromIdentity(SafeSecIdentityHandle identity)
+ {
+ SafeSecCertificateHandle cert;
+ int osStatus = AppleCryptoNative_X509CopyCertFromIdentity(identity, out cert);
+
+ SafeTemporaryKeychainHandle.TrackItem(cert);
+
+ if (osStatus != 0)
+ {
+ cert.Dispose();
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ if (cert.IsInvalid)
+ {
+ cert.Dispose();
+ throw new CryptographicException(SR.Cryptography_OpenInvalidHandle);
+ }
+
+ return cert;
+ }
+
+ internal static SafeSecKeyRefHandle X509GetPrivateKeyFromIdentity(SafeSecIdentityHandle identity)
+ {
+ SafeSecKeyRefHandle key;
+ int osStatus = AppleCryptoNative_X509CopyPrivateKeyFromIdentity(identity, out key);
+
+ SafeTemporaryKeychainHandle.TrackItem(key);
+
+ if (osStatus != 0)
+ {
+ key.Dispose();
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ if (key.IsInvalid)
+ {
+ key.Dispose();
+ throw new CryptographicException(SR.Cryptography_OpenInvalidHandle);
+ }
+
+ return key;
+ }
+
+ internal static SafeSecKeyRefHandle X509GetPublicKey(SafeSecCertificateHandle cert)
+ {
+ SafeSecKeyRefHandle publicKey;
+ int osStatus;
+ int ret = AppleCryptoNative_X509GetPublicKey(cert, out publicKey, out osStatus);
+
+ SafeTemporaryKeychainHandle.TrackItem(publicKey);
+
+ if (ret == 1)
+ {
+ return publicKey;
+ }
+
+ publicKey.Dispose();
+
+ if (ret == 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ Debug.Fail($"Unexpected return value {ret}");
+ throw new CryptographicException();
+ }
+
+ internal static bool X509DemuxAndRetainHandle(
+ IntPtr handle,
+ out SafeSecCertificateHandle certHandle,
+ out SafeSecIdentityHandle identityHandle)
+ {
+ int result = AppleCryptoNative_X509DemuxAndRetainHandle(handle, out certHandle, out identityHandle);
+
+ SafeTemporaryKeychainHandle.TrackItem(certHandle);
+ SafeTemporaryKeychainHandle.TrackItem(identityHandle);
+
+ switch (result)
+ {
+ case 1:
+ return true;
+ case 0:
+ return false;
+ default:
+ Debug.Fail($"AppleCryptoNative_X509DemuxAndRetainHandle returned {result}");
+ throw new CryptographicException();
+ }
+ }
+
+ private static byte[] X509Export(X509ContentType contentType, SafeCreateHandle cfPassphrase, IntPtr[] certHandles)
+ {
+ Debug.Assert(contentType == X509ContentType.Pkcs7 || contentType == X509ContentType.Pkcs12);
+
+ using (SafeCreateHandle handlesArray = CoreFoundation.CFArrayCreate(certHandles, (UIntPtr)certHandles.Length))
+ {
+ SafeCFDataHandle exportData;
+ int osStatus;
+
+ int result = AppleCryptoNative_X509ExportData(
+ handlesArray,
+ contentType,
+ cfPassphrase,
+ out exportData,
+ out osStatus);
+
+ using (exportData)
+ {
+ if (result != 1)
+ {
+ if (result == 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ Debug.Fail($"Unexpected result from AppleCryptoNative_X509ExportData: {result}");
+ throw new CryptographicException();
+ }
+
+ Debug.Assert(!exportData.IsInvalid, "Successful export yielded no data");
+ return CoreFoundation.CFGetData(exportData);
+ }
+ }
+ }
+
+ internal static byte[] X509ExportPkcs7(IntPtr[] certHandles)
+ {
+ return X509Export(X509ContentType.Pkcs7, s_nullExportString, certHandles);
+ }
+
+ internal static byte[] X509ExportPfx(IntPtr[] certHandles, SafePasswordHandle exportPassword)
+ {
+ SafeCreateHandle cfPassphrase = s_emptyExportString;
+ bool releasePassword = false;
+
+ try
+ {
+ if (!exportPassword.IsInvalid)
+ {
+ exportPassword.DangerousAddRef(ref releasePassword);
+ IntPtr passwordHandle = exportPassword.DangerousGetHandle();
+
+ if (passwordHandle != IntPtr.Zero)
+ {
+ cfPassphrase = CoreFoundation.CFStringCreateWithCString(passwordHandle);
+ }
+ }
+
+ return X509Export(X509ContentType.Pkcs12, cfPassphrase, certHandles);
+ }
+ finally
+ {
+ if (releasePassword)
+ {
+ exportPassword.DangerousRelease();
+ }
+
+ if (cfPassphrase != s_emptyExportString)
+ {
+ cfPassphrase.Dispose();
+ }
+ }
+ }
+ }
+}
+
+namespace System.Security.Cryptography.X509Certificates
+{
+ internal sealed class SafeSecIdentityHandle : SafeKeychainItemHandle
+ {
+ public SafeSecIdentityHandle()
+ {
+ }
+ }
+
+ internal sealed class SafeSecCertificateHandle : SafeKeychainItemHandle
+ {
+ public SafeSecCertificateHandle()
+ {
+ }
+ }
+}
diff --git a/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509Chain.cs b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509Chain.cs
new file mode 100644
index 0000000000..7911c1b568
--- /dev/null
+++ b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509Chain.cs
@@ -0,0 +1,50 @@
+// 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;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography.X509Certificates;
+
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class AppleCrypto
+ {
+ [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_X509ChainCreateDefaultPolicy")]
+ internal static extern SafeCreateHandle X509ChainCreateDefaultPolicy();
+
+ [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_X509ChainCreateRevocationPolicy")]
+ internal static extern SafeCreateHandle X509ChainCreateRevocationPolicy();
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ internal static extern int AppleCryptoNative_X509ChainCreate(
+ SafeCreateHandle certs,
+ SafeCreateHandle policies,
+ out SafeX509ChainHandle pTrustOut,
+ out int pOSStatus);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ internal static extern int AppleCryptoNative_X509ChainEvaluate(
+ SafeX509ChainHandle chain,
+ SafeCFDateHandle cfEvaluationTime,
+ bool allowNetwork,
+ out int pOSStatus);
+
+ [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_X509ChainGetChainSize")]
+ internal static extern long X509ChainGetChainSize(SafeX509ChainHandle chain);
+
+ [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_X509ChainGetCertificateAtIndex")]
+ internal static extern IntPtr X509ChainGetCertificateAtIndex(SafeX509ChainHandle chain, long index);
+
+ [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_X509ChainGetTrustResults")]
+ internal static extern SafeCreateHandle X509ChainGetTrustResults(SafeX509ChainHandle chain);
+
+ [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_X509ChainGetStatusAtIndex")]
+ internal static extern int X509ChainGetStatusAtIndex(SafeCreateHandle trustResults, long index, out int pdwStatus);
+
+ [DllImport(Libraries.AppleCryptoNative, EntryPoint = "AppleCryptoNative_GetOSStatusForChainStatus")]
+ internal static extern int GetOSStatusForChainStatus(X509ChainStatusFlags flag);
+ }
+}
diff --git a/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509Store.cs b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509Store.cs
new file mode 100644
index 0000000000..4dd78e34a1
--- /dev/null
+++ b/src/Common/src/Interop/OSX/System.Security.Cryptography.Native.Apple/Interop.X509Store.cs
@@ -0,0 +1,73 @@
+// 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;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Apple;
+using System.Security.Cryptography.X509Certificates;
+
+internal static partial class Interop
+{
+ internal static partial class AppleCrypto
+ {
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_X509StoreAddCertificate(
+ SafeKeychainItemHandle cert,
+ SafeKeychainHandle keychain,
+ out int pOSStatus);
+
+ [DllImport(Libraries.AppleCryptoNative)]
+ private static extern int AppleCryptoNative_X509StoreRemoveCertificate(
+ SafeSecCertificateHandle cert,
+ SafeKeychainHandle keychain,
+ out int pOSStatus);
+
+ internal static void X509StoreAddCertificate(SafeKeychainItemHandle certOrIdentity, SafeKeychainHandle keychain)
+ {
+ int osStatus;
+ int ret = AppleCryptoNative_X509StoreAddCertificate(certOrIdentity, keychain, out osStatus);
+
+ if (ret == 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ if (ret != 1)
+ {
+ Debug.Fail($"Unexpected result from AppleCryptoNative_X509StoreAddCertificate: {ret}");
+ throw new CryptographicException();
+ }
+ }
+
+ internal static void X509StoreRemoveCertificate(SafeSecCertificateHandle certHandle, SafeKeychainHandle keychain)
+ {
+ int osStatus;
+ int ret = AppleCryptoNative_X509StoreRemoveCertificate(certHandle, keychain, out osStatus);
+
+ if (ret == 0)
+ {
+ throw CreateExceptionForOSStatus(osStatus);
+ }
+
+ const int SuccessOrNoMatch = 1;
+ const int UserTrustExists = 2;
+ const int AdminTrustExists = 3;
+
+ switch (ret)
+ {
+ case SuccessOrNoMatch:
+ break;
+ case UserTrustExists:
+ throw new CryptographicException(SR.Cryptography_X509Store_WouldModifyUserTrust);
+ case AdminTrustExists:
+ throw new CryptographicException(SR.Cryptography_X509Store_WouldModifyAdminTrust);
+ default:
+ Debug.Fail($"Unexpected result from AppleCryptoNative_X509StoreRemoveCertificate: {ret}");
+ throw new CryptographicException();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/Common/src/Interop/Unix/System.Net.Http.Native/Interop.Initialization.cs b/src/Common/src/Interop/Unix/System.Net.Http.Native/Interop.Initialization.cs
index c37bc73a7a..743e55c71e 100644
--- a/src/Common/src/Interop/Unix/System.Net.Http.Native/Interop.Initialization.cs
+++ b/src/Common/src/Interop/Unix/System.Net.Http.Native/Interop.Initialization.cs
@@ -23,9 +23,11 @@ internal static partial class Interop
{
static HttpInitializer()
{
+#if !SYSNETHTTP_NO_OPENSSL
// CURL uses OpenSSL which me must initialize first to guarantee thread-safety
CryptoInitializer.Initialize();
-
+#endif
+
if (EnsureCurlIsInitialized() != 0)
{
throw new InvalidOperationException();
diff --git a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Initialization.cs b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Initialization.cs
index 67d071bbe1..3f71974885 100644
--- a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Initialization.cs
+++ b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Initialization.cs
@@ -2,8 +2,6 @@
// 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.InteropServices;
-
internal static partial class Interop
{
// Initialization of libssl threading support is done in a static constructor.
@@ -20,6 +18,7 @@ internal static partial class Interop
internal static class SslInitializer
{
+#if !SYSNETSECURITY_NO_OPENSSL
static SslInitializer()
{
CryptoInitializer.Initialize();
@@ -27,6 +26,7 @@ internal static partial class Interop
//Call ssl specific initializer
Ssl.EnsureLibSslInitialized();
}
+#endif
internal static void Initialize()
{
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs
index de4aca401f..dfe70c1cb8 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OpenSsl.cs
@@ -25,14 +25,13 @@ internal static partial class Interop
internal static SafeChannelBindingHandle QueryChannelBinding(SafeSslHandle context, ChannelBindingKind bindingType)
{
+ Debug.Assert(
+ bindingType != ChannelBindingKind.Endpoint,
+ "Endpoint binding should be handled by EndpointChannelBindingToken");
+
SafeChannelBindingHandle bindingHandle;
switch (bindingType)
{
- case ChannelBindingKind.Endpoint:
- bindingHandle = new SafeChannelBindingHandle(bindingType);
- QueryEndPointChannelBinding(context, bindingHandle);
- break;
-
case ChannelBindingKind.Unique:
bindingHandle = new SafeChannelBindingHandle(bindingType);
QueryUniqueChannelBinding(context, bindingHandle);
@@ -289,37 +288,6 @@ internal static partial class Interop
#region private methods
- private static void QueryEndPointChannelBinding(SafeSslHandle context, SafeChannelBindingHandle bindingHandle)
- {
- using (SafeX509Handle certSafeHandle = GetPeerCertificate(context))
- {
- if (certSafeHandle == null || certSafeHandle.IsInvalid)
- {
- throw CreateSslException(SR.net_ssl_invalid_certificate);
- }
-
- bool gotReference = false;
-
- try
- {
- certSafeHandle.DangerousAddRef(ref gotReference);
- using (X509Certificate2 cert = new X509Certificate2(certSafeHandle.DangerousGetHandle()))
- using (HashAlgorithm hashAlgo = GetHashForChannelBinding(cert))
- {
- byte[] bindingHash = hashAlgo.ComputeHash(cert.RawData);
- bindingHandle.SetCertHash(bindingHash);
- }
- }
- finally
- {
- if (gotReference)
- {
- certSafeHandle.DangerousRelease();
- }
- }
- }
- }
-
private static void QueryUniqueChannelBinding(SafeSslHandle context, SafeChannelBindingHandle bindingHandle)
{
bool sessionReused = Ssl.SslSessionReused(context);
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs
index 19ab1ddc4a..0fcc966f2e 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Ssl.cs
@@ -4,13 +4,8 @@
using System;
using System.Diagnostics;
-using System.Net.Security;
using System.Runtime.InteropServices;
-using System.Security.Authentication;
-using System.Security.Authentication.ExtendedProtection;
-using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
-using System.Text;
using Microsoft.Win32.SafeHandles;
internal static partial class Interop
@@ -313,80 +308,4 @@ namespace Microsoft.Win32.SafeHandles
handle = validSslPointer;
}
}
-
- internal sealed class SafeChannelBindingHandle : SafeHandle
- {
- [StructLayout(LayoutKind.Sequential)]
- private struct SecChannelBindings
- {
- internal int InitiatorLength;
- internal int InitiatorOffset;
- internal int AcceptorAddrType;
- internal int AcceptorLength;
- internal int AcceptorOffset;
- internal int ApplicationDataLength;
- internal int ApplicationDataOffset;
- }
-
- private const int CertHashMaxSize = 128;
- private static readonly byte[] s_tlsServerEndPointByteArray = Encoding.UTF8.GetBytes("tls-server-end-point:");
- private static readonly byte[] s_tlsUniqueByteArray = Encoding.UTF8.GetBytes("tls-unique:");
- private static readonly int s_secChannelBindingSize = Marshal.SizeOf<SecChannelBindings>();
-
- private readonly int _cbtPrefixByteArraySize;
- internal int Length { get; private set; }
- internal IntPtr CertHashPtr { get; private set; }
-
- internal void SetCertHash(byte[] certHashBytes)
- {
- Debug.Assert(certHashBytes != null, "check certHashBytes is not null");
- Debug.Assert(certHashBytes.Length <= CertHashMaxSize);
-
- int length = certHashBytes.Length;
- Marshal.Copy(certHashBytes, 0, CertHashPtr, length);
- SetCertHashLength(length);
- }
-
- private byte[] GetPrefixBytes(ChannelBindingKind kind)
- {
- Debug.Assert(kind == ChannelBindingKind.Endpoint || kind == ChannelBindingKind.Unique);
- return kind == ChannelBindingKind.Endpoint ?
- s_tlsServerEndPointByteArray :
- s_tlsUniqueByteArray;
- }
-
- internal SafeChannelBindingHandle(ChannelBindingKind kind)
- : base(IntPtr.Zero, true)
- {
- byte[] cbtPrefix = GetPrefixBytes(kind);
- _cbtPrefixByteArraySize = cbtPrefix.Length;
- handle = Marshal.AllocHGlobal(s_secChannelBindingSize + _cbtPrefixByteArraySize + CertHashMaxSize);
- IntPtr cbtPrefixPtr = handle + s_secChannelBindingSize;
- Marshal.Copy(cbtPrefix, 0, cbtPrefixPtr, _cbtPrefixByteArraySize);
- CertHashPtr = cbtPrefixPtr + _cbtPrefixByteArraySize;
- Length = CertHashMaxSize;
- }
-
- internal void SetCertHashLength(int certHashLength)
- {
- int cbtLength = _cbtPrefixByteArraySize + certHashLength;
- Length = s_secChannelBindingSize + cbtLength;
-
- SecChannelBindings channelBindings = new SecChannelBindings()
- {
- ApplicationDataLength = cbtLength,
- ApplicationDataOffset = s_secChannelBindingSize
- };
- Marshal.StructureToPtr(channelBindings, handle, true);
- }
-
- public override bool IsInvalid => handle == IntPtr.Zero;
-
- protected override bool ReleaseHandle()
- {
- Marshal.FreeHGlobal(handle);
- SetHandle(IntPtr.Zero);
- return true;
- }
- }
}
diff --git a/src/Common/src/System/Diagnostics/Debug.Unix.cs b/src/Common/src/System/Diagnostics/Debug.Unix.cs
index f50ecce6c7..cbcd411110 100644
--- a/src/Common/src/System/Diagnostics/Debug.Unix.cs
+++ b/src/Common/src/System/Diagnostics/Debug.Unix.cs
@@ -11,6 +11,8 @@ namespace System.Diagnostics
// another version of this partial class with the public visibility
static partial class Debug
{
+ private static string NewLine => "\n";
+
// internal and not readonly so that the tests can swap this out.
internal static IDebugLogger s_logger = new UnixDebugLogger();
diff --git a/src/Common/src/System/Diagnostics/Debug.Windows.cs b/src/Common/src/System/Diagnostics/Debug.Windows.cs
index 1e99105b89..f0f589ccd6 100644
--- a/src/Common/src/System/Diagnostics/Debug.Windows.cs
+++ b/src/Common/src/System/Diagnostics/Debug.Windows.cs
@@ -11,6 +11,8 @@ namespace System.Diagnostics
// another version of this partial class with the public visibility
static partial class Debug
{
+ private static string NewLine => "\r\n";
+
// internal and not read only so that the tests can swap this out.
internal static IDebugLogger s_logger = new WindowsDebugLogger();
diff --git a/src/Common/src/System/Diagnostics/Debug.cs b/src/Common/src/System/Diagnostics/Debug.cs
index 5cc83f43a6..5f893f8518 100644
--- a/src/Common/src/System/Diagnostics/Debug.cs
+++ b/src/Common/src/System/Diagnostics/Debug.cs
@@ -97,7 +97,7 @@ namespace System.Diagnostics
try
{
- stackTrace = Environment.StackTrace;
+ stackTrace = Internal.Runtime.Augments.EnvironmentAugments.StackTrace;
}
catch
{
@@ -123,7 +123,7 @@ namespace System.Diagnostics
private static string FormatAssert(string stackTrace, string message, string detailMessage)
{
- var newLine = GetIndentString() + Environment.NewLine;
+ string newLine = GetIndentString() + NewLine;
return SR.DebugAssertBanner + newLine
+ SR.DebugAssertShortMessage + newLine
+ message + newLine
@@ -141,7 +141,7 @@ namespace System.Diagnostics
[System.Diagnostics.Conditional("DEBUG")]
public static void WriteLine(string message)
{
- Write(message + Environment.NewLine);
+ Write(message + NewLine);
}
[System.Diagnostics.Conditional("DEBUG")]
@@ -160,7 +160,7 @@ namespace System.Diagnostics
s_needIndent = false;
}
WriteCore(message);
- if (message.EndsWith(Environment.NewLine))
+ if (message.EndsWith(NewLine))
{
s_needIndent = true;
}
@@ -323,7 +323,7 @@ namespace System.Diagnostics
private sealed class DebugAssertException : Exception
{
internal DebugAssertException(string message, string detailMessage, string stackTrace) :
- base(message + Environment.NewLine + detailMessage + Environment.NewLine + stackTrace)
+ base(message + NewLine + detailMessage + NewLine + stackTrace)
{
}
}
diff --git a/src/Common/src/System/Net/ContextAwareResult.OSX.cs b/src/Common/src/System/Net/ContextAwareResult.OSX.cs
new file mode 100644
index 0000000000..7120ed1932
--- /dev/null
+++ b/src/Common/src/System/Net/ContextAwareResult.OSX.cs
@@ -0,0 +1,19 @@
+// 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.
+
+namespace System.Net
+{
+ partial class ContextAwareResult
+ {
+ private void SafeCaptureIdentity()
+ {
+ // WindowsIdentity is not supported on Unix
+ }
+
+ private void CleanupInternal()
+ {
+ // Nothing to cleanup
+ }
+ }
+}
diff --git a/src/Common/src/System/Net/IntPtrHelper.cs b/src/Common/src/System/Net/IntPtrHelper.cs
index 7a5c9b8a01..6bd2d35653 100644
--- a/src/Common/src/System/Net/IntPtrHelper.cs
+++ b/src/Common/src/System/Net/IntPtrHelper.cs
@@ -6,14 +6,6 @@ namespace System.Net
{
internal static class IntPtrHelper
{
- internal static IntPtr Add(IntPtr a, int b)
- {
- return (IntPtr)((long)a + (long)b);
- }
-
- internal static long Subtract(IntPtr a, IntPtr b)
- {
- return ((long)a - (long)b);
- }
+ internal static IntPtr Add(IntPtr a, int b) => (IntPtr)((long)a + (long)b);
}
}
diff --git a/src/Common/src/System/Net/Security/Unix/SafeFreeContextBufferChannelBinding.cs b/src/Common/src/System/Net/Security/Unix/SafeFreeContextBufferChannelBinding.cs
deleted file mode 100644
index 07e46a34f7..0000000000
--- a/src/Common/src/System/Net/Security/Unix/SafeFreeContextBufferChannelBinding.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-// 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.Win32.SafeHandles;
-
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Security.Authentication;
-using System.Security.Authentication.ExtendedProtection;
-using System.Security.Cryptography;
-using System.Security.Cryptography.X509Certificates;
-
-namespace System.Net.Security
-{
- internal sealed class SafeFreeContextBufferChannelBinding : ChannelBinding
- {
- private readonly SafeChannelBindingHandle _channelBinding = null;
-
- public override int Size
- {
- get { return _channelBinding.Length; }
- }
-
- public override bool IsInvalid
- {
- get { return _channelBinding.IsInvalid; }
- }
-
- public SafeFreeContextBufferChannelBinding(SafeChannelBindingHandle binding)
- {
- Debug.Assert(null != binding && !binding.IsInvalid, "input channelBinding is invalid");
- bool gotRef = false;
- binding.DangerousAddRef(ref gotRef);
- handle = binding.DangerousGetHandle();
- _channelBinding = binding;
- }
-
- protected override bool ReleaseHandle()
- {
- _channelBinding.DangerousRelease();
- _channelBinding.Dispose();
- return true;
- }
- }
-}
diff --git a/src/Common/src/System/Net/Shims/TraceSource.cs b/src/Common/src/System/Net/Shims/TraceSource.cs
deleted file mode 100644
index ccb8ab32cd..0000000000
--- a/src/Common/src/System/Net/Shims/TraceSource.cs
+++ /dev/null
@@ -1,76 +0,0 @@
-// 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.
-
-// IMPORTANT: This is temporary code.
-
-namespace System.Net
-{
- internal class TraceSource
- {
- public TraceSource(string name) { }
-
- public TraceSource(string name, SourceLevels defaultLevel) { }
-
- public void Close() { }
-
- public void Flush() { }
-
- public void TraceEvent(TraceEventType eventType, int id) { }
-
- public void TraceEvent(TraceEventType eventType, int id, string message) { }
-
- public void TraceEvent(TraceEventType eventType, int id, string format, params object[] args) { }
-
- public void TraceData(TraceEventType eventType, int id, object data) { }
-
- public void TraceData(TraceEventType eventType, int id, params object[] data) { }
-
- public void TraceInformation(string message) { }
-
- public void TraceInformation(string format, params object[] args) { }
-
- public void TraceTransfer(int id, string message, Guid relatedActivityId) { }
-
- public SourceSwitch Switch { get { return new SourceSwitch(); } }
- }
-
- internal enum TraceEventType
- {
- Critical = 0x01,
- Error = 0x02,
- Warning = 0x04,
- Information = 0x08,
- Verbose = 0x10,
- Start = 0x0100,
- Stop = 0x0200,
- Suspend = 0x0400,
- Resume = 0x0800,
- Transfer = 0x1000,
- }
-
- [Flags]
- internal enum SourceLevels
- {
- Off = 0,
- Critical = 0x01,
- Error = 0x03,
- Warning = 0x07,
- Information = 0x0F,
- Verbose = 0x1F,
- ActivityTracing = 0xFF00,
- All = unchecked((int)0xFFFFFFFF),
- }
-
- internal class SourceSwitch
- {
- public SourceSwitch()
- {
- }
-
- public bool ShouldTrace(TraceEventType eventType)
- {
- return false;
- }
- }
-}
diff --git a/src/Common/src/System/Net/Shims/readme.md b/src/Common/src/System/Net/Shims/readme.md
deleted file mode 100644
index 3c67fef15e..0000000000
--- a/src/Common/src/System/Net/Shims/readme.md
+++ /dev/null
@@ -1,3 +0,0 @@
-Shims contains types that are temporarily used to build System.Net types.
-Issue #2891 : These types will be removed once the port is completed.
-
diff --git a/src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs b/src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs
index fe23349d63..705b69e898 100644
--- a/src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs
+++ b/src/Common/src/System/Net/WebSockets/ManagedWebSocket.cs
@@ -5,6 +5,7 @@
using System.Buffers;
using System.Diagnostics;
using System.IO;
+using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
@@ -1202,21 +1203,68 @@ namespace System.Net.WebSockets
/// <returns>The next index into the mask to be used for future applications of the mask.</returns>
private static unsafe int ApplyMask(byte[] toMask, int toMaskOffset, int mask, int maskIndex, long count)
{
- Debug.Assert(toMaskOffset <= toMask.Length - count, $"Unexpected inputs: {toMaskOffset}, {toMask.Length}, {count}");
- Debug.Assert(maskIndex < sizeof(int), $"Unexpected {nameof(maskIndex)}: {maskIndex}");
+ int maskShift = maskIndex * 8;
+ int shiftedMask = (int)(((uint)mask >> maskShift) | ((uint)mask << (32 - maskShift)));
+
+ // Try to use SIMD. We can if the number of bytes we're trying to mask is at least as much
+ // as the width of a vector and if the width is an even multiple of the mask.
+ if (Vector.IsHardwareAccelerated &&
+ Vector<byte>.Count % sizeof(int) == 0 &&
+ count >= Vector<byte>.Count)
+ {
+ // Mask bytes a vector at a time.
+ Vector<byte> maskVector = Vector.AsVectorByte(new Vector<int>(shiftedMask));
+ while (count >= Vector<byte>.Count)
+ {
+ count -= Vector<byte>.Count;
+ (maskVector ^ new Vector<byte>(toMask, toMaskOffset)).CopyTo(toMask, toMaskOffset);
+ toMaskOffset += Vector<byte>.Count;
+ }
+
+ // Fall through to processing any remaining bytes that were less than a vector width.
+ // Since we processed full masks at a time, we don't need to update maskIndex, and
+ // toMaskOffset has already been updated to point to the correct location.
+ }
- byte* maskPtr = (byte*)&mask;
- fixed (byte* toMaskPtr = toMask)
+ // If there are any bytes remaining (either we couldn't use vectors, or the count wasn't
+ // an even multiple of the vector width), process them without vectors.
+ if (count > 0)
{
- byte* p = toMaskPtr + toMaskOffset;
- byte* end = p + count;
- while (p < end)
+ fixed (byte* toMaskPtr = toMask)
{
- *p++ ^= maskPtr[maskIndex];
- maskIndex = (maskIndex + 1) & 3; // & 3 == faster % MaskLength
+ // Get the location in the target array to continue processing.
+ byte* p = toMaskPtr + toMaskOffset;
+
+ // Try to go an int at a time if the remaining data is 4-byte aligned and there's enough remaining.
+ if (((long)p % sizeof(int)) == 0)
+ {
+ while (count >= sizeof(int))
+ {
+ count -= sizeof(int);
+ *((int*)p) ^= shiftedMask;
+ p += sizeof(int);
+ }
+
+ // We don't need to update the maskIndex, as its mod-4 value won't have changed.
+ // `p` points to the remainder.
+ }
+
+ // Process any remaining data a byte at a time.
+ if (count > 0)
+ {
+ byte* maskPtr = (byte*)&mask;
+ byte* end = p + count;
+ while (p < end)
+ {
+ *p++ ^= maskPtr[maskIndex];
+ maskIndex = (maskIndex + 1) & 3;
+ }
+ }
}
- return maskIndex;
}
+
+ // Return the updated index.
+ return maskIndex;
}
/// <summary>Aborts the websocket and throws an exception if an existing operation is in progress.</summary>
diff --git a/src/Common/src/System/Security/Cryptography/DSAOpenSsl.cs b/src/Common/src/System/Security/Cryptography/DSAOpenSsl.cs
index e64c3d424e..161cc3a7fe 100644
--- a/src/Common/src/System/Security/Cryptography/DSAOpenSsl.cs
+++ b/src/Common/src/System/Security/Cryptography/DSAOpenSsl.cs
@@ -173,12 +173,12 @@ namespace System.Security.Cryptography
Debug.Assert(count >= 0 && count <= data.Length);
Debug.Assert(!string.IsNullOrEmpty(hashAlgorithm.Name));
- return OpenSslAsymmetricAlgorithmCore.HashData(data, offset, count, hashAlgorithm);
+ return AsymmetricAlgorithmHelpers.HashData(data, offset, count, hashAlgorithm);
}
protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
{
- return OpenSslAsymmetricAlgorithmCore.HashData(data, hashAlgorithm);
+ return AsymmetricAlgorithmHelpers.HashData(data, hashAlgorithm);
}
public override byte[] CreateSignature(byte[] rgbHash)
@@ -204,7 +204,7 @@ namespace System.Security.Cryptography
signature.Length);
int signatureFieldSize = Interop.Crypto.DsaSignatureFieldSize(key) * BitsPerByte;
- byte[] converted = OpenSslAsymmetricAlgorithmCore.ConvertDerToIeee1363(signature, 0, signatureSize, signatureFieldSize);
+ byte[] converted = AsymmetricAlgorithmHelpers.ConvertDerToIeee1363(signature, 0, signatureSize, signatureFieldSize);
return converted;
}
@@ -224,7 +224,7 @@ namespace System.Security.Cryptography
return false;
}
- byte[] openSslFormat = OpenSslAsymmetricAlgorithmCore.ConvertIeee1363ToDer(rgbSignature);
+ byte[] openSslFormat = AsymmetricAlgorithmHelpers.ConvertIeee1363ToDer(rgbSignature);
return Interop.Crypto.DsaVerify(key, rgbHash, rgbHash.Length, openSslFormat, openSslFormat.Length);
}
diff --git a/src/Common/src/System/Security/Cryptography/DSASecurityTransforms.cs b/src/Common/src/System/Security/Cryptography/DSASecurityTransforms.cs
new file mode 100644
index 0000000000..5ff0be5f41
--- /dev/null
+++ b/src/Common/src/System/Security/Cryptography/DSASecurityTransforms.cs
@@ -0,0 +1,466 @@
+// 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.Diagnostics;
+using System.IO;
+using System.Security.Cryptography.Apple;
+using Internal.Cryptography;
+
+namespace System.Security.Cryptography
+{
+#if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
+
+ public partial class DSA : AsymmetricAlgorithm
+ {
+ public static new DSA Create()
+ {
+ return new DSAImplementation.DSASecurityTransforms();
+ }
+#endif
+
+ internal static partial class DSAImplementation
+ {
+ public sealed partial class DSASecurityTransforms : DSA
+ {
+ private SecKeyPair _keys;
+
+ public DSASecurityTransforms()
+ : this(1024)
+ {
+ }
+
+ public DSASecurityTransforms(int keySize)
+ {
+ KeySize = keySize;
+ }
+
+ internal DSASecurityTransforms(SafeSecKeyRefHandle publicKey)
+ {
+ SetKey(SecKeyPair.PublicOnly(publicKey));
+ }
+
+ internal DSASecurityTransforms(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle privateKey)
+ {
+ SetKey(SecKeyPair.PublicPrivatePair(publicKey, privateKey));
+ }
+
+ public override KeySizes[] LegalKeySizes
+ {
+ get
+ {
+ return new[] { new KeySizes(minSize: 512, maxSize: 1024, skipSize: 64) };
+ }
+ }
+
+ public override int KeySize
+ {
+ get
+ {
+ return base.KeySize;
+ }
+ set
+ {
+ if (KeySize == value)
+ return;
+
+ // Set the KeySize before freeing the key so that an invalid value doesn't throw away the key
+ base.KeySize = value;
+
+ if (_keys != null)
+ {
+ _keys.Dispose();
+ _keys = null;
+ }
+ }
+ }
+
+ public override DSAParameters ExportParameters(bool includePrivateParameters)
+ {
+ SecKeyPair keys = GetKeys();
+
+ if (keys.PublicKey == null ||
+ (includePrivateParameters && keys.PrivateKey == null))
+ {
+ throw new CryptographicException(SR.Cryptography_OpenInvalidHandle);
+ }
+
+ DSAParameters parameters = new DSAParameters();
+
+ DerSequenceReader publicKeyReader =
+ Interop.AppleCrypto.SecKeyExport(keys.PublicKey, exportPrivate: false);
+
+ publicKeyReader.ReadSubjectPublicKeyInfo(ref parameters);
+
+ if (includePrivateParameters)
+ {
+ DerSequenceReader privateKeyReader =
+ Interop.AppleCrypto.SecKeyExport(keys.PrivateKey, exportPrivate: true);
+
+ privateKeyReader.ReadPkcs8Blob(ref parameters);
+ }
+
+ KeyBlobHelpers.TrimPaddingByte(ref parameters.P);
+ KeyBlobHelpers.TrimPaddingByte(ref parameters.Q);
+
+ KeyBlobHelpers.PadOrTrim(ref parameters.G, parameters.P.Length);
+ KeyBlobHelpers.PadOrTrim(ref parameters.Y, parameters.P.Length);
+
+ if (includePrivateParameters)
+ {
+ KeyBlobHelpers.PadOrTrim(ref parameters.X, parameters.Q.Length);
+ }
+
+ return parameters;
+ }
+
+ public override void ImportParameters(DSAParameters parameters)
+ {
+ if (parameters.P == null || parameters.Q == null || parameters.G == null || parameters.Y == null)
+ throw new ArgumentException(SR.Cryptography_InvalidDsaParameters_MissingFields);
+
+ // J is not required and is not even used on CNG blobs.
+ // It should, however, be less than P (J == (P-1) / Q).
+ // This validation check is just to maintain parity with DSACng and DSACryptoServiceProvider,
+ // which also perform this check.
+ if (parameters.J != null && parameters.J.Length >= parameters.P.Length)
+ throw new ArgumentException(SR.Cryptography_InvalidDsaParameters_MismatchedPJ);
+
+ int keySize = parameters.P.Length;
+ bool hasPrivateKey = parameters.X != null;
+
+ if (parameters.G.Length != keySize || parameters.Y.Length != keySize)
+ throw new ArgumentException(SR.Cryptography_InvalidDsaParameters_MismatchedPGY);
+
+ if (hasPrivateKey && parameters.X.Length != parameters.Q.Length)
+ throw new ArgumentException(SR.Cryptography_InvalidDsaParameters_MismatchedQX);
+
+ if (!(8 * parameters.P.Length).IsLegalSize(LegalKeySizes))
+ throw new CryptographicException(SR.Cryptography_InvalidKeySize);
+
+ if (parameters.Q.Length != 20)
+ throw new CryptographicException(SR.Cryptography_InvalidDsaParameters_QRestriction_ShortKey);
+
+ if (hasPrivateKey)
+ {
+ SafeSecKeyRefHandle privateKey = ImportKey(parameters);
+
+ DSAParameters publicOnly = parameters;
+ publicOnly.X = null;
+
+ SafeSecKeyRefHandle publicKey;
+ try
+ {
+ publicKey = ImportKey(publicOnly);
+ }
+ catch
+ {
+ privateKey.Dispose();
+ throw;
+ }
+
+ SetKey(SecKeyPair.PublicPrivatePair(publicKey, privateKey));
+ }
+ else
+ {
+ SafeSecKeyRefHandle publicKey = ImportKey(parameters);
+ SetKey(SecKeyPair.PublicOnly(publicKey));
+ }
+ }
+
+ private static SafeSecKeyRefHandle ImportKey(DSAParameters parameters)
+ {
+ bool hasPrivateKey = parameters.X != null;
+ byte[] blob = hasPrivateKey ? parameters.ToPrivateKeyBlob() : parameters.ToSubjectPublicKeyInfo();
+
+ return Interop.AppleCrypto.ImportEphemeralKey(blob, hasPrivateKey);
+ }
+
+ public override byte[] CreateSignature(byte[] hash)
+ {
+ if (hash == null)
+ throw new ArgumentNullException(nameof(hash));
+
+ SecKeyPair keys = GetKeys();
+
+ if (keys.PrivateKey == null)
+ {
+ throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey);
+ }
+
+ byte[] derFormatSignature = Interop.AppleCrypto.GenerateSignature(keys.PrivateKey, hash);
+
+ // Since the AppleCrypto implementation is limited to FIPS 186-2, signature field sizes
+ // are always 160 bits / 20 bytes (the size of SHA-1, and the only legal length for Q).
+ byte[] ieeeFormatSignature = AsymmetricAlgorithmHelpers.ConvertDerToIeee1363(
+ derFormatSignature,
+ 0,
+ derFormatSignature.Length,
+ fieldSizeBits: 160);
+
+ return ieeeFormatSignature;
+ }
+
+ public override bool VerifySignature(byte[] hash, byte[] signature)
+ {
+ if (hash == null)
+ throw new ArgumentNullException(nameof(hash));
+ if (signature == null)
+ throw new ArgumentNullException(nameof(signature));
+
+ byte[] derFormatSignature = AsymmetricAlgorithmHelpers.ConvertIeee1363ToDer(signature);
+
+ return Interop.AppleCrypto.VerifySignature(
+ GetKeys().PublicKey,
+ hash,
+ derFormatSignature);
+ }
+
+ protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
+ {
+ if (hashAlgorithm != HashAlgorithmName.SHA1)
+ {
+ // Matching DSACryptoServiceProvider's "I only understand SHA-1/FIPS 186-2" exception
+ throw new CryptographicException(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithm.Name);
+ }
+
+ return AsymmetricAlgorithmHelpers.HashData(data, offset, count, hashAlgorithm);
+ }
+
+ protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
+ {
+ return AsymmetricAlgorithmHelpers.HashData(data, hashAlgorithm);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (_keys != null)
+ {
+ _keys.Dispose();
+ _keys = null;
+ }
+ }
+
+ base.Dispose(disposing);
+ }
+
+ private SecKeyPair GetKeys()
+ {
+ SecKeyPair current = _keys;
+
+ if (current != null)
+ {
+ return current;
+ }
+
+ // macOS 10.11 and macOS 10.12 declare DSA invalid for key generation.
+ // Rather than write code which might or might not work, returning
+ // (OSStatus)-4 (errSecUnimplemented), just make the exception occur here.
+ //
+ // When the native code can be verified, then it can be added.
+ throw new PlatformNotSupportedException(SR.Cryptography_DSA_KeyGenNotSupported);
+ }
+
+ private void SetKey(SecKeyPair newKeyPair)
+ {
+ SecKeyPair current = _keys;
+ _keys = newKeyPair;
+ current?.Dispose();
+
+ if (newKeyPair != null)
+ {
+ int size = Interop.AppleCrypto.GetSimpleKeySizeInBits(newKeyPair.PublicKey);
+ KeySizeValue = size;
+ }
+ }
+ }
+ }
+#if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
+ }
+#else
+ internal static class KeySizeHelpers
+ {
+
+ public static bool IsLegalSize(this int size, KeySizes[] legalSizes)
+ {
+ for (int i = 0; i < legalSizes.Length; i++)
+ {
+ KeySizes currentSizes = legalSizes[i];
+
+ // If a cipher has only one valid key size, MinSize == MaxSize and SkipSize will be 0
+ if (currentSizes.SkipSize == 0)
+ {
+ if (currentSizes.MinSize == size)
+ return true;
+ }
+ else if (size >= currentSizes.MinSize && size <= currentSizes.MaxSize)
+ {
+ // If the number is in range, check to see if it's a legal increment above MinSize
+ int delta = size - currentSizes.MinSize;
+
+ // While it would be unusual to see KeySizes { 10, 20, 5 } and { 11, 14, 1 }, it could happen.
+ // So don't return false just because this one doesn't match.
+ if (delta % currentSizes.SkipSize == 0)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+#endif
+
+ internal static class DsaKeyBlobHelpers
+ {
+ private static readonly Oid s_idDsa = new Oid("1.2.840.10040.4.1");
+
+ internal static void ReadSubjectPublicKeyInfo(this DerSequenceReader keyInfo, ref DSAParameters parameters)
+ {
+ // SubjectPublicKeyInfo::= SEQUENCE {
+ // algorithm AlgorithmIdentifier,
+ // subjectPublicKey BIT STRING }
+ DerSequenceReader algorithm = keyInfo.ReadSequence();
+ string algorithmOid = algorithm.ReadOidAsString();
+
+ // EC Public Key
+ if (algorithmOid != s_idDsa.Value)
+ {
+ throw new CryptographicException();
+ }
+
+ // Dss-Parms ::= SEQUENCE {
+ // p INTEGER,
+ // q INTEGER,
+ // g INTEGER
+ // }
+
+ DerSequenceReader algParameters = algorithm.ReadSequence();
+ byte[] publicKeyBlob = keyInfo.ReadBitString();
+ // We don't care about the rest of the blob here, but it's expected to not exist.
+
+ ReadSubjectPublicKeyInfo(algParameters, publicKeyBlob, ref parameters);
+ }
+
+ internal static void ReadSubjectPublicKeyInfo(
+ this DerSequenceReader algParameters,
+ byte[] publicKeyBlob,
+ ref DSAParameters parameters)
+ {
+ parameters.P = algParameters.ReadIntegerBytes();
+ parameters.Q = algParameters.ReadIntegerBytes();
+ parameters.G = algParameters.ReadIntegerBytes();
+
+ DerSequenceReader privateKeyReader = DerSequenceReader.CreateForPayload(publicKeyBlob);
+ parameters.Y = privateKeyReader.ReadIntegerBytes();
+
+ KeyBlobHelpers.TrimPaddingByte(ref parameters.P);
+ KeyBlobHelpers.TrimPaddingByte(ref parameters.Q);
+
+ KeyBlobHelpers.PadOrTrim(ref parameters.G, parameters.P.Length);
+ KeyBlobHelpers.PadOrTrim(ref parameters.Y, parameters.P.Length);
+ }
+
+ internal static byte[] ToSubjectPublicKeyInfo(this DSAParameters parameters)
+ {
+ // SubjectPublicKeyInfo::= SEQUENCE {
+ // algorithm AlgorithmIdentifier,
+ // subjectPublicKey BIT STRING }
+
+ // Dss-Parms ::= SEQUENCE {
+ // p INTEGER,
+ // q INTEGER,
+ // g INTEGER
+ // }
+
+ return DerEncoder.ConstructSequence(
+ DerEncoder.ConstructSegmentedSequence(
+ DerEncoder.SegmentedEncodeOid(s_idDsa),
+ DerEncoder.ConstructSegmentedSequence(
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.P),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.Q),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.G)
+ )
+ ),
+ DerEncoder.SegmentedEncodeBitString(
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.Y))
+ );
+ }
+
+ internal static void ReadPkcs8Blob(this DerSequenceReader reader, ref DSAParameters parameters)
+ {
+ // Since the PKCS#8 blob for DSS/DSA does not include the public key (Y) this
+ // structure is only read after filling the public half.
+ Debug.Assert(parameters.P != null);
+ Debug.Assert(parameters.Q != null);
+ Debug.Assert(parameters.G != null);
+ Debug.Assert(parameters.Y != null);
+
+ // OneAsymmetricKey ::= SEQUENCE {
+ // version Version,
+ // privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ // privateKey PrivateKey,
+ // attributes [0] Attributes OPTIONAL,
+ // ...,
+ // [[2: publicKey [1] PublicKey OPTIONAL ]],
+ // ...
+ // }
+ //
+ // PrivateKeyInfo ::= OneAsymmetricKey
+ //
+ // PrivateKey ::= OCTET STRING
+
+ int version = reader.ReadInteger();
+
+ // We understand both version 0 and 1 formats,
+ // which are now known as v1 and v2, respectively.
+ if (version > 1)
+ {
+ throw new CryptographicException();
+ }
+
+ {
+ // Ensure we're reading DSA, extract the parameters
+ DerSequenceReader algorithm = reader.ReadSequence();
+
+ string algorithmOid = algorithm.ReadOidAsString();
+
+ if (algorithmOid != s_idDsa.Value)
+ {
+ throw new CryptographicException();
+ }
+
+ // The Dss-Params SEQUENCE is present here, but not needed since
+ // we got it from the public key already.
+ }
+
+ byte[] privateKeyBlob = reader.ReadOctetString();
+ DerSequenceReader privateKeyReader = DerSequenceReader.CreateForPayload(privateKeyBlob);
+ parameters.X = privateKeyReader.ReadIntegerBytes();
+ }
+
+ internal static byte[] ToPrivateKeyBlob(this DSAParameters parameters)
+ {
+ Debug.Assert(parameters.X != null);
+
+ // DSAPrivateKey ::= SEQUENCE(
+ // version INTEGER,
+ // p INTEGER,
+ // q INTEGER,
+ // g INTEGER,
+ // y INTEGER,
+ // x INTEGER,
+ // )
+
+ return DerEncoder.ConstructSequence(
+ DerEncoder.SegmentedEncodeUnsignedInteger(new byte[] { 0 }),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.P),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.Q),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.G),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.Y),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.X));
+ }
+ }
+}
diff --git a/src/Common/src/System/Security/Cryptography/DerEncoder.cs b/src/Common/src/System/Security/Cryptography/DerEncoder.cs
index fb3f89bc76..cb196b5c3a 100644
--- a/src/Common/src/System/Security/Cryptography/DerEncoder.cs
+++ b/src/Common/src/System/Security/Cryptography/DerEncoder.cs
@@ -175,6 +175,20 @@ namespace System.Security.Cryptography
}
/// <summary>
+ /// Encode the segments { tag, length, value } of a BIT STRING which is wrapped over
+ /// other DER-encoded data.
+ /// </summary>
+ /// <param name="childSegments"></param>
+ /// <remarks>
+ /// Despite containing other DER-encoded data this does not get the constructed bit,
+ /// because it doesn't when encoding public keys in SubjectPublicKeyInfo</remarks>
+ /// <returns></returns>
+ internal static byte[][] SegmentedEncodeBitString(params byte[][][] childSegments)
+ {
+ return SegmentedEncodeBitString(ConcatenateArrays(childSegments));
+ }
+
+ /// <summary>
/// Encode the segments { tag, length, value } of a bit string where all bits are significant.
/// </summary>
/// <param name="data">The data to encode</param>
@@ -509,6 +523,34 @@ namespace System.Security.Cryptography
}
/// <summary>
+ /// Make a context-specific tagged value which is constructed of other DER encoded values.
+ /// Logically the same as a SEQUENCE, but providing context as to data interpretation (and usually
+ /// indicates an optional element adjacent to another SEQUENCE).
+ /// </summary>
+ /// <param name="contextId">The value's context ID</param>
+ /// <param name="items">Series of Tag-Length-Value triplets to build into one sequence.</param>
+ /// <returns>The encoded segments { tag, length, value }</returns>
+ internal static byte[][] ConstructSegmentedContextSpecificValue(int contextId, params byte[][][] items)
+ {
+ Debug.Assert(items != null);
+ Debug.Assert(contextId >= 0 && contextId <= 30);
+
+ byte[] data = ConcatenateArrays(items);
+
+ byte tagId = (byte)(
+ DerSequenceReader.ConstructedFlag |
+ DerSequenceReader.ContextSpecificTagFlag |
+ contextId);
+
+ return new byte[][]
+ {
+ new byte[] { tagId },
+ EncodeLength(data.Length),
+ data,
+ };
+ }
+
+ /// <summary>
/// Make a constructed SET of the byte-triplets of the contents, but leave
/// the value in a segmented form (to be included in a larger SEQUENCE).
/// </summary>
diff --git a/src/Common/src/System/Security/Cryptography/DerSequenceReader.cs b/src/Common/src/System/Security/Cryptography/DerSequenceReader.cs
index e358113399..741e6085d5 100644
--- a/src/Common/src/System/Security/Cryptography/DerSequenceReader.cs
+++ b/src/Common/src/System/Security/Cryptography/DerSequenceReader.cs
@@ -3,8 +3,10 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
+using System.Globalization;
using System.Numerics;
using System.Text;
+using System.Threading;
namespace System.Security.Cryptography
{
@@ -15,6 +17,16 @@ namespace System.Security.Cryptography
internal class DerSequenceReader
{
internal const byte ContextSpecificTagFlag = 0x80;
+ internal const byte ConstructedFlag = 0x20;
+ internal const byte ContextSpecificConstructedTag0 = ContextSpecificTagFlag | ConstructedFlag;
+ internal const byte ContextSpecificConstructedTag1 = ContextSpecificConstructedTag0 | 1;
+ internal const byte ContextSpecificConstructedTag2 = ContextSpecificConstructedTag0 | 2;
+ internal const byte ContextSpecificConstructedTag3 = ContextSpecificConstructedTag0 | 3;
+ internal const byte ConstructedSequence = ConstructedFlag | (byte)DerTag.Sequence;
+
+ internal const byte TagNumberMask = 0x1F;
+
+ internal static DateTimeFormatInfo s_validityDateTimeFormatInfo;
private readonly byte[] _data;
private readonly int _end;
@@ -40,17 +52,23 @@ namespace System.Security.Cryptography
}
internal DerSequenceReader(byte[] data, int offset, int length)
+ : this(DerTag.Sequence, data, offset, length)
+ {
+ }
+
+ internal DerSequenceReader(DerTag tagToEat, byte[] data, int offset, int length)
{
_data = data;
_end = offset + length;
Debug.Assert(data != null, "Data is null");
Debug.Assert(offset >= 0, "Offset is negative");
- Debug.Assert(length > 2, "Length is too short");
- Debug.Assert(data.Length >= offset + length, "Array is too short");
+
+ if (length < 2 || length > data.Length - offset)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
_position = offset;
- EatTag(DerTag.Sequence);
+ EatTag(tagToEat);
ContentLength = EatLength();
}
@@ -66,9 +84,22 @@ namespace System.Security.Cryptography
internal byte PeekTag()
{
+ if (!HasData)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
return _data[_position];
}
+ internal bool HasTag(DerTag expectedTag)
+ {
+ return HasTag((byte)expectedTag);
+ }
+
+ internal bool HasTag(byte expectedTag)
+ {
+ return HasData && _data[_position] == expectedTag;
+ }
+
internal void SkipValue()
{
EatTag((DerTag)PeekTag());
@@ -76,6 +107,37 @@ namespace System.Security.Cryptography
_position += contentLength;
}
+ /// <summary>
+ /// Returns the next value encoded (this includes tag and length)
+ /// </summary>
+ internal byte[] ReadNextEncodedValue()
+ {
+ int lengthLength;
+ int contentLength = ScanContentLength(_data, _position + 1, out lengthLength);
+ // Length of tag, encoded length, and the content
+ int totalLength = 1 + lengthLength + contentLength;
+
+ byte[] encodedValue = new byte[totalLength];
+ Buffer.BlockCopy(_data, _position, encodedValue, 0, totalLength);
+
+ _position += totalLength;
+ return encodedValue;
+ }
+
+ internal bool ReadBoolean()
+ {
+ EatTag(DerTag.Boolean);
+
+ int length = EatLength();
+
+ if (length != 1)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
+ bool value = _data[_position] != 0;
+ _position += length;
+ return value;
+ }
+
internal int ReadInteger()
{
byte[] integerBytes = ReadIntegerBytes();
@@ -94,6 +156,22 @@ namespace System.Security.Cryptography
return ReadContentAsBytes();
}
+ internal byte[] ReadBitString()
+ {
+ EatTag(DerTag.BitString);
+
+ int contentLength = EatLength();
+ // skip the "unused bits" byte
+ contentLength--;
+ _position++;
+
+ byte[] octets = new byte[contentLength];
+ Buffer.BlockCopy(_data, _position, octets, 0, contentLength);
+
+ _position += contentLength;
+ return octets;
+ }
+
internal byte[] ReadOctetString()
{
EatTag(DerTag.OctetString);
@@ -161,20 +239,53 @@ namespace System.Security.Cryptography
return new Oid(ReadOidAsString());
}
- internal DerSequenceReader ReadSequence()
+ internal string ReadUtf8String()
+ {
+ EatTag(DerTag.UTF8String);
+ int contentLength = EatLength();
+
+ string str = System.Text.Encoding.UTF8.GetString(_data, _position, contentLength);
+ _position += contentLength;
+
+ return TrimTrailingNulls(str);
+ }
+
+ private DerSequenceReader ReadCollectionWithTag(DerTag expected)
{
// DerSequenceReader wants to read its own tag, so don't EatTag here.
- CheckTag(DerTag.Sequence, _data, _position);
+ CheckTag(expected, _data, _position);
int lengthLength;
int contentLength = ScanContentLength(_data, _position + 1, out lengthLength);
int totalLength = 1 + lengthLength + contentLength;
- DerSequenceReader reader = new DerSequenceReader(_data, _position, totalLength);
+ DerSequenceReader reader = new DerSequenceReader(expected, _data, _position, totalLength);
_position += totalLength;
return reader;
}
+ internal DerSequenceReader ReadSequence()
+ {
+ return ReadCollectionWithTag(DerTag.Sequence);
+ }
+
+ internal DerSequenceReader ReadSet()
+ {
+ return ReadCollectionWithTag(DerTag.Set);
+ }
+
+ internal string ReadPrintableString()
+ {
+ EatTag(DerTag.PrintableString);
+ int contentLength = EatLength();
+
+ // PrintableString is a subset of ASCII, so just return the ASCII interpretation.
+ string str = System.Text.Encoding.ASCII.GetString(_data, _position, contentLength);
+ _position += contentLength;
+
+ return TrimTrailingNulls(str);
+ }
+
internal string ReadIA5String()
{
EatTag(DerTag.IA5String);
@@ -185,7 +296,108 @@ namespace System.Security.Cryptography
string ia5String = System.Text.Encoding.ASCII.GetString(_data, _position, contentLength);
_position += contentLength;
- return ia5String;
+ return TrimTrailingNulls(ia5String);
+ }
+
+ internal DateTime ReadX509Date()
+ {
+ byte tag = PeekTag();
+
+ switch ((DerTag)tag)
+ {
+ case DerTag.UTCTime:
+ return ReadUtcTime();
+ case DerTag.GeneralizedTime:
+ return ReadGeneralizedTime();
+ }
+
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
+
+ internal DateTime ReadUtcTime()
+ {
+ return ReadTime(DerTag.UTCTime, "yyMMddHHmmss'Z'");
+ }
+
+ internal DateTime ReadGeneralizedTime()
+ {
+ // Currently only supports reading times with no fractional seconds or time differentials
+ // as RFC 2630 doesn't allow these. In case this is done, the format string has to be parsed
+ // to follow rules on X.680 and X.690.
+ return ReadTime(DerTag.GeneralizedTime, "yyyyMMddHHmmss'Z'");
+ }
+
+ internal string ReadBMPString()
+ {
+ EatTag(DerTag.BMPString);
+ int contentLength = EatLength();
+
+ // BMPString or Basic Multilingual Plane, is equal to UCS-2.
+ // And since this is cryptography, it's Big Endian.
+ string str = System.Text.Encoding.BigEndianUnicode.GetString(_data, _position, contentLength);
+ _position += contentLength;
+
+ return TrimTrailingNulls(str);
+ }
+
+ private static string TrimTrailingNulls(string value)
+ {
+ // .NET's string comparisons start by checking the length, so a trailing
+ // NULL character which was literally embedded in the DER would cause a
+ // failure in .NET whereas it wouldn't have with strcmp.
+ if (value?.Length > 0)
+ {
+ int newLength = value.Length;
+
+ while (newLength > 0 && value[newLength - 1] == 0)
+ {
+ newLength--;
+ }
+
+ if (newLength != value.Length)
+ {
+ return value.Substring(0, newLength);
+ }
+ }
+
+ return value;
+ }
+
+ private DateTime ReadTime(DerTag timeTag, string formatString)
+ {
+ EatTag(timeTag);
+ int contentLength = EatLength();
+
+ string decodedTime = System.Text.Encoding.ASCII.GetString(_data, _position, contentLength);
+ _position += contentLength;
+
+ Debug.Assert(
+ decodedTime[decodedTime.Length - 1] == 'Z',
+ $"The date doesn't follow the X.690 format, ending with {decodedTime[decodedTime.Length - 1]}");
+
+ DateTime time;
+
+ DateTimeFormatInfo fi = LazyInitializer.EnsureInitialized(
+ ref s_validityDateTimeFormatInfo,
+ () =>
+ {
+ var clone = (DateTimeFormatInfo)CultureInfo.InvariantCulture.DateTimeFormat.Clone();
+ clone.Calendar.TwoDigitYearMax = 2049;
+
+ return clone;
+ });
+
+ if (!DateTime.TryParseExact(
+ decodedTime,
+ formatString,
+ fi,
+ DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
+ out time))
+ {
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
+
+ return time;
}
private byte[] ReadContentAsBytes()
@@ -201,12 +413,18 @@ namespace System.Security.Cryptography
private void EatTag(DerTag expected)
{
+ if (!HasData)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
CheckTag(expected, _data, _position);
_position++;
}
private static void CheckTag(DerTag expected, byte[] data, int position)
{
+ if (position >= data.Length)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
byte actual = data[position];
// Context-specific datatypes cannot be tag-verified
@@ -215,15 +433,21 @@ namespace System.Security.Cryptography
return;
}
- byte relevant = (byte)(actual & 0x1F);
- byte expectedByte = (byte)expected;
+ byte relevant = (byte)(actual & TagNumberMask);
+ byte expectedByte = (byte)((byte)expected & TagNumberMask);
if (expectedByte != relevant)
{
- throw new InvalidOperationException(
- "Expected tag '0x" + expectedByte.ToString("X2") +
- "', got '0x" + actual.ToString("X2") +
- "' at position " + position);
+ throw new CryptographicException(
+ SR.Cryptography_Der_Invalid_Encoding
+#if DEBUG
+ ,
+ new InvalidOperationException(
+ "Expected tag '0x" + expectedByte.ToString("X2") +
+ "', got '0x" + actual.ToString("X2") +
+ "' at position " + position)
+#endif
+ );
}
}
@@ -238,10 +462,16 @@ namespace System.Security.Cryptography
private static int ScanContentLength(byte[] data, int offset, out int bytesConsumed)
{
+ if (offset >= data.Length)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
byte lengthOrLengthLength = data[offset];
if (lengthOrLengthLength < 0x80)
{
+ if (lengthOrLengthLength > data.Length - offset)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
bytesConsumed = 1;
return lengthOrLengthLength;
}
@@ -249,6 +479,13 @@ namespace System.Security.Cryptography
// The one byte which was lengthLength, plus the number of bytes it said to consume.
bytesConsumed = 1 + (lengthOrLengthLength & 0x7F);
+ if (bytesConsumed > data.Length - offset)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
+ // CER indefinite length is not supported.
+ if (bytesConsumed == 1)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
int end = offset + bytesConsumed;
int accum = 0;
@@ -260,6 +497,9 @@ namespace System.Security.Cryptography
accum += data[i];
}
+ if (accum > data.Length - offset - bytesConsumed)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
return accum;
}
@@ -278,6 +518,8 @@ namespace System.Security.Cryptography
T61String = 0x14,
IA5String = 0x16,
UTCTime = 0x17,
+ GeneralizedTime = 0x18,
+ BMPString = 0x1E,
}
}
}
diff --git a/src/Common/src/System/Security/Cryptography/ECDsaOpenSsl.cs b/src/Common/src/System/Security/Cryptography/ECDsaOpenSsl.cs
index fcb94503e7..370bb75c08 100644
--- a/src/Common/src/System/Security/Cryptography/ECDsaOpenSsl.cs
+++ b/src/Common/src/System/Security/Cryptography/ECDsaOpenSsl.cs
@@ -84,7 +84,7 @@ namespace System.Security.Cryptography
if (!Interop.Crypto.EcDsaSign(hash, hash.Length, signature, ref signatureLength, key))
throw Interop.Crypto.CreateOpenSslCryptographicException();
- byte[] converted = OpenSslAsymmetricAlgorithmCore.ConvertDerToIeee1363(signature, 0, signatureLength, KeySize);
+ byte[] converted = AsymmetricAlgorithmHelpers.ConvertDerToIeee1363(signature, 0, signatureLength, KeySize);
return converted;
}
@@ -99,14 +99,14 @@ namespace System.Security.Cryptography
// The signature format for .NET is r.Concat(s). Each of r and s are of length BitsToBytes(KeySize), even
// when they would have leading zeroes. If it's the correct size, then we need to encode it from
// r.Concat(s) to SEQUENCE(INTEGER(r), INTEGER(s)), because that's the format that OpenSSL expects.
- int expectedBytes = 2 * OpenSslAsymmetricAlgorithmCore.BitsToBytes(KeySize);
+ int expectedBytes = 2 * AsymmetricAlgorithmHelpers.BitsToBytes(KeySize);
if (signature.Length != expectedBytes)
{
// The input isn't of the right length, so we can't sensibly re-encode it.
return false;
}
- byte[] openSslFormat = OpenSslAsymmetricAlgorithmCore.ConvertIeee1363ToDer(signature);
+ byte[] openSslFormat = AsymmetricAlgorithmHelpers.ConvertIeee1363ToDer(signature);
SafeEcKeyHandle key = _key.Value;
int verifyResult = Interop.Crypto.EcDsaVerify(hash, hash.Length, openSslFormat, openSslFormat.Length, key);
@@ -115,12 +115,12 @@ namespace System.Security.Cryptography
protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
{
- return OpenSslAsymmetricAlgorithmCore.HashData(data, offset, count, hashAlgorithm);
+ return AsymmetricAlgorithmHelpers.HashData(data, offset, count, hashAlgorithm);
}
protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
{
- return OpenSslAsymmetricAlgorithmCore.HashData(data, hashAlgorithm);
+ return AsymmetricAlgorithmHelpers.HashData(data, hashAlgorithm);
}
protected override void Dispose(bool disposing)
diff --git a/src/Common/src/System/Security/Cryptography/ECDsaSecurityTransforms.cs b/src/Common/src/System/Security/Cryptography/ECDsaSecurityTransforms.cs
new file mode 100644
index 0000000000..fdd4600e47
--- /dev/null
+++ b/src/Common/src/System/Security/Cryptography/ECDsaSecurityTransforms.cs
@@ -0,0 +1,550 @@
+// 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.Diagnostics;
+using System.IO;
+using System.Security.Cryptography.Apple;
+using Internal.Cryptography;
+
+namespace System.Security.Cryptography
+{
+#if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
+ public partial class ECDsa : AsymmetricAlgorithm
+ {
+ /// <summary>
+ /// Creates an instance of the platform specific implementation of the cref="ECDsa" algorithm.
+ /// </summary>
+ public static new ECDsa Create()
+ {
+ return new ECDsaImplementation.ECDsaSecurityTransforms();
+ }
+
+ /// <summary>
+ /// Creates an instance of the platform specific implementation of the cref="ECDsa" algorithm.
+ /// </summary>
+ /// <param name="curve">
+ /// The <see cref="ECCurve"/> representing the elliptic curve.
+ /// </param>
+ public static ECDsa Create(ECCurve curve)
+ {
+ ECDsa ecdsa = Create();
+ ecdsa.GenerateKey(curve);
+ return ecdsa;
+ }
+
+ /// <summary>
+ /// Creates an instance of the platform specific implementation of the cref="ECDsa" algorithm.
+ /// </summary>
+ /// <param name="parameters">
+ /// The <see cref="ECParameters"/> representing the elliptic curve parameters.
+ /// </param>
+ public static ECDsa Create(ECParameters parameters)
+ {
+ ECDsa ecdsa = Create();
+ ecdsa.ImportParameters(parameters);
+ return ecdsa;
+ }
+#endif
+ internal static partial class ECDsaImplementation
+ {
+ public sealed partial class ECDsaSecurityTransforms : ECDsa
+ {
+ private SecKeyPair _keys;
+
+ public ECDsaSecurityTransforms()
+ {
+ KeySize = 521;
+ }
+
+ internal ECDsaSecurityTransforms(SafeSecKeyRefHandle publicKey)
+ {
+ SetKey(SecKeyPair.PublicOnly(publicKey));
+ }
+
+ internal ECDsaSecurityTransforms(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle privateKey)
+ {
+ SetKey(SecKeyPair.PublicPrivatePair(publicKey, privateKey));
+ }
+
+ public override KeySizes[] LegalKeySizes
+ {
+ get
+ {
+ // Return the three sizes that can be explicitly set (for backwards compatibility)
+ return new[] {
+ new KeySizes(minSize: 256, maxSize: 384, skipSize: 128),
+ new KeySizes(minSize: 521, maxSize: 521, skipSize: 0),
+ };
+ }
+ }
+
+ public override int KeySize
+ {
+ get
+ {
+ return base.KeySize;
+ }
+ set
+ {
+ if (KeySize == value)
+ return;
+
+ // Set the KeySize before freeing the key so that an invalid value doesn't throw away the key
+ base.KeySize = value;
+
+ if (_keys != null)
+ {
+ _keys.Dispose();
+ _keys = null;
+ }
+ }
+ }
+
+ public override byte[] SignHash(byte[] hash)
+ {
+ if (hash == null)
+ throw new ArgumentNullException(nameof(hash));
+
+ SecKeyPair keys = GetKeys();
+
+ if (keys.PrivateKey == null)
+ {
+ throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey);
+ }
+
+ byte[] derFormatSignature = Interop.AppleCrypto.GenerateSignature(keys.PrivateKey, hash);
+ byte[] ieeeFormatSignature = AsymmetricAlgorithmHelpers.ConvertDerToIeee1363(
+ derFormatSignature,
+ 0,
+ derFormatSignature.Length,
+ KeySize);
+
+ return ieeeFormatSignature;
+ }
+
+ public override bool VerifyHash(byte[] hash, byte[] signature)
+ {
+ if (hash == null)
+ throw new ArgumentNullException(nameof(hash));
+ if (signature == null)
+ throw new ArgumentNullException(nameof(signature));
+
+ byte[] derFormatSignature = AsymmetricAlgorithmHelpers.ConvertIeee1363ToDer(signature);
+
+ return Interop.AppleCrypto.VerifySignature(
+ GetKeys().PublicKey,
+ hash,
+ derFormatSignature);
+ }
+
+ protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
+ {
+ return AsymmetricAlgorithmHelpers.HashData(data, offset, count, hashAlgorithm);
+ }
+
+ protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
+ {
+ return AsymmetricAlgorithmHelpers.HashData(data, hashAlgorithm);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (_keys != null)
+ {
+ _keys.Dispose();
+ _keys = null;
+ }
+ }
+
+ base.Dispose(disposing);
+ }
+
+ public override ECParameters ExportExplicitParameters(bool includePrivateParameters)
+ {
+ throw new PlatformNotSupportedException(SR.Cryptography_ECC_NamedCurvesOnly);
+ }
+
+ public override ECParameters ExportParameters(bool includePrivateParameters)
+ {
+ SecKeyPair keys = GetKeys();
+
+ SafeSecKeyRefHandle keyHandle = includePrivateParameters ? keys.PrivateKey : keys.PublicKey;
+
+ if (keyHandle == null)
+ {
+ throw new CryptographicException(SR.Cryptography_OpenInvalidHandle);
+ }
+
+ DerSequenceReader keyReader = Interop.AppleCrypto.SecKeyExport(keyHandle, includePrivateParameters);
+ ECParameters parameters = new ECParameters();
+
+ if (includePrivateParameters)
+ {
+ keyReader.ReadPkcs8Blob(ref parameters);
+ }
+ else
+ {
+ keyReader.ReadSubjectPublicKeyInfo(ref parameters);
+ }
+
+ int size = AsymmetricAlgorithmHelpers.BitsToBytes(KeySize);
+
+ KeyBlobHelpers.PadOrTrim(ref parameters.Q.X, size);
+ KeyBlobHelpers.PadOrTrim(ref parameters.Q.Y, size);
+
+ if (includePrivateParameters)
+ {
+ KeyBlobHelpers.PadOrTrim(ref parameters.D, size);
+ }
+
+ return parameters;
+ }
+
+ public override void ImportParameters(ECParameters parameters)
+ {
+ parameters.Validate();
+
+ bool isPrivateKey = parameters.D != null;
+
+ if (isPrivateKey)
+ {
+ // Start with the private key, in case some of the private key fields don't
+ // match the public key fields and the system determines an integrity failure.
+ //
+ // Public import should go off without a hitch.
+ SafeSecKeyRefHandle privateKey = ImportKey(parameters);
+
+ ECParameters publicOnly = parameters;
+ publicOnly.D = null;
+
+ SafeSecKeyRefHandle publicKey;
+ try
+ {
+ publicKey = ImportKey(publicOnly);
+ }
+ catch
+ {
+ privateKey.Dispose();
+ throw;
+ }
+
+ SetKey(SecKeyPair.PublicPrivatePair(publicKey, privateKey));
+ }
+ else
+ {
+ SafeSecKeyRefHandle publicKey = ImportKey(parameters);
+ SetKey(SecKeyPair.PublicOnly(publicKey));
+ }
+ }
+
+ public override void GenerateKey(ECCurve curve)
+ {
+ curve.Validate();
+
+ if (!curve.IsNamed)
+ {
+ throw new PlatformNotSupportedException(SR.Cryptography_ECC_NamedCurvesOnly);
+ }
+
+ int keySize;
+
+ switch (curve.Oid.Value)
+ {
+ // secp256r1 / nistp256
+ case "1.2.840.10045.3.1.7":
+ keySize = 256;
+ break;
+ // secp384r1 / nistp384
+ case "1.3.132.0.34":
+ keySize = 384;
+ break;
+ // secp521r1 / nistp521
+ case "1.3.132.0.35":
+ keySize = 521;
+ break;
+ default:
+ throw new PlatformNotSupportedException(
+ SR.Format(SR.Cryptography_CurveNotSupported, curve.Oid.Value));
+ }
+
+ // Clear the current key, because GenerateKey on the same curve makes a new key,
+ // unlike setting the KeySize property to the current value.
+ SetKey(null);
+ KeySizeValue = keySize;
+
+ // Generate the keys immediately, because that's what the verb of this method is.
+ GetKeys();
+ }
+
+ private static SafeSecKeyRefHandle ImportKey(ECParameters parameters)
+ {
+ bool isPrivateKey = parameters.D != null;
+ byte[] blob = isPrivateKey ? parameters.ToPrivateKeyBlob() : parameters.ToSubjectPublicKeyInfo();
+
+ return Interop.AppleCrypto.ImportEphemeralKey(blob, isPrivateKey);
+ }
+
+ private void SetKey(SecKeyPair newKeyPair)
+ {
+ SecKeyPair current = _keys;
+ _keys = newKeyPair;
+ current?.Dispose();
+
+ if (newKeyPair != null)
+ {
+ long size = Interop.AppleCrypto.EccGetKeySizeInBits(newKeyPair.PublicKey);
+
+ Debug.Assert(size == 256 || size == 384 || size == 521, $"Unknown keysize ({size})");
+ KeySizeValue = (int)size;
+ }
+ }
+
+ private SecKeyPair GetKeys()
+ {
+ SecKeyPair current = _keys;
+
+ if (current != null)
+ {
+ return current;
+ }
+
+ SafeSecKeyRefHandle publicKey;
+ SafeSecKeyRefHandle privateKey;
+
+ Interop.AppleCrypto.EccGenerateKey(KeySizeValue, out publicKey, out privateKey);
+
+ current = SecKeyPair.PublicPrivatePair(publicKey, privateKey);
+ _keys = current;
+ return current;
+ }
+ }
+ }
+#if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
+ }
+#endif
+
+ internal static class EcKeyBlobHelpers
+ {
+ private static readonly byte[] s_version1 = { 1 };
+ private static readonly byte[][] s_encodedVersion1 = DerEncoder.SegmentedEncodeUnsignedInteger(s_version1);
+
+ private static readonly Oid s_idEcPublicKey = new Oid("1.2.840.10045.2.1", null);
+ private static readonly byte[][] s_encodedIdEcPublicKey = DerEncoder.SegmentedEncodeOid(s_idEcPublicKey);
+
+ internal static void ReadPkcs8Blob(this DerSequenceReader reader, ref ECParameters parameters)
+ {
+ // OneAsymmetricKey ::= SEQUENCE {
+ // version Version,
+ // privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ // privateKey PrivateKey,
+ // attributes [0] Attributes OPTIONAL,
+ // ...,
+ // [[2: publicKey [1] PublicKey OPTIONAL ]],
+ // ...
+ // }
+ //
+ // PrivateKeyInfo ::= OneAsymmetricKey
+ //
+ // PrivateKey ::= OCTET STRING
+
+ int version = reader.ReadInteger();
+
+ // We understand both version 0 and 1 formats,
+ // which are now known as v1 and v2, respectively.
+ if (version > 1)
+ {
+ throw new CryptographicException();
+ }
+
+ {
+ // Ensure we're reading EC Public Key (well, Private, but the OID says Public)
+ DerSequenceReader algorithm = reader.ReadSequence();
+
+ string algorithmOid = algorithm.ReadOidAsString();
+
+ if (algorithmOid != s_idEcPublicKey.Value)
+ {
+ throw new CryptographicException();
+ }
+ }
+
+ byte[] privateKeyBlob = reader.ReadOctetString();
+
+ // ECPrivateKey{CURVES:IOSet} ::= SEQUENCE {
+ // version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
+ // privateKey OCTET STRING,
+ // parameters [0] Parameters{{IOSet}} OPTIONAL,
+ // publicKey [1] BIT STRING OPTIONAL
+ // }
+ DerSequenceReader keyReader = new DerSequenceReader(privateKeyBlob);
+ version = keyReader.ReadInteger();
+
+ // We understand the version 1 format
+ if (version > 1)
+ {
+ throw new CryptographicException();
+ }
+
+ parameters.D = keyReader.ReadOctetString();
+
+ // Check for context specific 0
+ const byte ConstructedContextSpecific =
+ DerSequenceReader.ContextSpecificTagFlag | DerSequenceReader.ConstructedFlag;
+
+ const byte ConstructedContextSpecific0 = (ConstructedContextSpecific | 0);
+ const byte ConstructedContextSpecific1 = (ConstructedContextSpecific | 1);
+
+ if (keyReader.PeekTag() != ConstructedContextSpecific0)
+ {
+ throw new CryptographicException();
+ }
+
+ // Parameters ::= CHOICE {
+ // ecParameters ECParameters,
+ // namedCurve CURVES.&id({ CurveNames}),
+ // implicitlyCA NULL
+ // }
+ DerSequenceReader parametersReader = keyReader.ReadSequence();
+
+ if (parametersReader.PeekTag() != (int)DerSequenceReader.DerTag.ObjectIdentifier)
+ {
+ throw new PlatformNotSupportedException(SR.Cryptography_ECC_NamedCurvesOnly);
+ }
+
+ parameters.Curve = ECCurve.CreateFromValue(parametersReader.ReadOidAsString());
+
+ // Check for context specific 1
+ if (keyReader.PeekTag() != ConstructedContextSpecific1)
+ {
+ throw new CryptographicException();
+ }
+
+ keyReader = keyReader.ReadSequence();
+ byte[] encodedPoint = keyReader.ReadBitString();
+ ReadEncodedPoint(encodedPoint, ref parameters);
+
+ // We don't care about the rest of the blob here, but it's expected to not exist.
+ }
+
+ internal static void ReadSubjectPublicKeyInfo(this DerSequenceReader keyInfo, ref ECParameters parameters)
+ {
+ // SubjectPublicKeyInfo::= SEQUENCE {
+ // algorithm AlgorithmIdentifier,
+ // subjectPublicKey BIT STRING }
+ DerSequenceReader algorithm = keyInfo.ReadSequence();
+ string algorithmOid = algorithm.ReadOidAsString();
+
+ // EC Public Key
+ if (algorithmOid != s_idEcPublicKey.Value)
+ {
+ throw new CryptographicException();
+ }
+
+ if (algorithm.PeekTag() != (int)DerSequenceReader.DerTag.ObjectIdentifier)
+ {
+ throw new PlatformNotSupportedException(SR.Cryptography_ECC_NamedCurvesOnly);
+ }
+
+ parameters.Curve = ECCurve.CreateFromValue(algorithm.ReadOidAsString());
+
+ byte[] encodedPoint = keyInfo.ReadBitString();
+ ReadEncodedPoint(encodedPoint, ref parameters);
+
+ // We don't care about the rest of the blob here, but it's expected to not exist.
+ }
+
+ internal static byte[] ToSubjectPublicKeyInfo(this ECParameters parameters)
+ {
+ parameters.Validate();
+
+ if (!parameters.Curve.IsNamed)
+ {
+ throw new PlatformNotSupportedException(SR.Cryptography_ECC_NamedCurvesOnly);
+ }
+
+ byte[] pointBlob = GetPointBlob(ref parameters);
+
+ return DerEncoder.ConstructSequence(
+ DerEncoder.ConstructSegmentedSequence(
+ s_encodedIdEcPublicKey,
+ DerEncoder.SegmentedEncodeOid(parameters.Curve.Oid)),
+ DerEncoder.SegmentedEncodeBitString(pointBlob));
+ }
+
+ private static byte[] GetPointBlob(ref ECParameters parameters)
+ {
+ byte[] pointBlob = new byte[parameters.Q.X.Length + parameters.Q.Y.Length + 1];
+
+ // Uncompressed point
+ pointBlob[0] = 0x04;
+ Buffer.BlockCopy(parameters.Q.X, 0, pointBlob, 1, parameters.Q.X.Length);
+ Buffer.BlockCopy(parameters.Q.Y, 0, pointBlob, 1 + parameters.Q.X.Length, parameters.Q.Y.Length);
+ return pointBlob;
+ }
+
+ internal static byte[] ToPrivateKeyBlob(this ECParameters parameters)
+ {
+ parameters.Validate();
+
+ if (!parameters.Curve.IsNamed)
+ {
+ throw new PlatformNotSupportedException(SR.Cryptography_ECC_NamedCurvesOnly);
+ }
+
+ byte[] pointBlob = GetPointBlob(ref parameters);
+
+ // ECPrivateKey{CURVES:IOSet} ::= SEQUENCE {
+ // version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
+ // privateKey OCTET STRING,
+ // parameters [0] Parameters{{IOSet}} OPTIONAL,
+ // publicKey [1] BIT STRING OPTIONAL
+ // }
+ return DerEncoder.ConstructSequence(
+ s_encodedVersion1,
+ DerEncoder.SegmentedEncodeOctetString(parameters.D),
+ DerEncoder.ConstructSegmentedContextSpecificValue(
+ 0,
+ DerEncoder.SegmentedEncodeOid(parameters.Curve.Oid)),
+ DerEncoder.ConstructSegmentedContextSpecificValue(
+ 1,
+ DerEncoder.SegmentedEncodeBitString(pointBlob)));
+ }
+
+ private static void ReadEncodedPoint(byte[] encodedPoint, ref ECParameters parameters)
+ {
+ if (encodedPoint == null || encodedPoint.Length < 1)
+ {
+ throw new CryptographicException();
+ }
+
+ byte encoding = encodedPoint[0];
+
+ switch (encoding)
+ {
+ // Uncompressed encoding (04 xbytes ybytes)
+ case 0x04:
+ // Hybrid encoding, ~yp == 0 (06 xbytes ybytes)
+ case 0x06:
+ // Hybrid encoding, ~yp == 1 (07 xbytes ybytes)
+ case 0x07:
+ break;
+ default:
+ Debug.Fail($"Don't know how to read point encoding {encoding}");
+ throw new CryptographicException();
+ }
+
+ // For formats 04, 06, and 07 the X and Y points are equal length, and they should
+ // already be left-padded with zeros in cases where they're short.
+ int pointEncodingSize = (encodedPoint.Length - 1) / 2;
+ byte[] encodedX = new byte[pointEncodingSize];
+ byte[] encodedY = new byte[pointEncodingSize];
+ Buffer.BlockCopy(encodedPoint, 1, encodedX, 0, pointEncodingSize);
+ Buffer.BlockCopy(encodedPoint, 1 + pointEncodingSize, encodedY, 0, pointEncodingSize);
+ parameters.Q.X = encodedX;
+ parameters.Q.Y = encodedY;
+ }
+ }
+}
diff --git a/src/Common/src/System/Security/Cryptography/KeyBlobHelpers.cs b/src/Common/src/System/Security/Cryptography/KeyBlobHelpers.cs
new file mode 100644
index 0000000000..10bf17bcd6
--- /dev/null
+++ b/src/Common/src/System/Security/Cryptography/KeyBlobHelpers.cs
@@ -0,0 +1,52 @@
+// 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.
+
+namespace System.Security.Cryptography
+{
+ internal static class KeyBlobHelpers
+ {
+ internal static byte[] TrimPaddingByte(byte[] data)
+ {
+ byte[] dataLocal = data;
+ TrimPaddingByte(ref dataLocal);
+ return dataLocal;
+ }
+
+ internal static void TrimPaddingByte(ref byte[] data)
+ {
+ if (data[0] != 0)
+ return;
+
+ byte[] newData = new byte[data.Length - 1];
+ Buffer.BlockCopy(data, 1, newData, 0, newData.Length);
+ data = newData;
+ }
+
+ internal static byte[] PadOrTrim(byte[] data, int length)
+ {
+ byte[] dataLocal = data;
+ PadOrTrim(ref dataLocal, length);
+ return dataLocal;
+ }
+
+ internal static void PadOrTrim(ref byte[] data, int length)
+ {
+ if (data.Length == length)
+ return;
+
+ // Need to skip the sign-padding byte.
+ if (data.Length == length + 1 && data[0] == 0)
+ {
+ TrimPaddingByte(ref data);
+ return;
+ }
+
+ int offset = length - data.Length;
+
+ byte[] newData = new byte[length];
+ Buffer.BlockCopy(data, 0, newData, offset, data.Length);
+ data = newData;
+ }
+ }
+}
diff --git a/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs b/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
index 7120279ff2..a2a68937d0 100644
--- a/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
+++ b/src/Common/src/System/Security/Cryptography/RSAOpenSsl.cs
@@ -352,12 +352,12 @@ namespace System.Security.Cryptography
protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
{
- return OpenSslAsymmetricAlgorithmCore.HashData(data, offset, count, hashAlgorithm);
+ return AsymmetricAlgorithmHelpers.HashData(data, offset, count, hashAlgorithm);
}
protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
{
- return OpenSslAsymmetricAlgorithmCore.HashData(data, hashAlgorithm);
+ return AsymmetricAlgorithmHelpers.HashData(data, hashAlgorithm);
}
public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
diff --git a/src/Common/src/System/Security/Cryptography/RSASecurityTransforms.cs b/src/Common/src/System/Security/Cryptography/RSASecurityTransforms.cs
new file mode 100644
index 0000000000..e2c6ab0c21
--- /dev/null
+++ b/src/Common/src/System/Security/Cryptography/RSASecurityTransforms.cs
@@ -0,0 +1,488 @@
+// 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.Diagnostics;
+using System.IO;
+using System.Security.Cryptography.Apple;
+using Internal.Cryptography;
+
+namespace System.Security.Cryptography
+{
+#if INTERNAL_ASYMMETRIC_IMPLEMENTATIONS
+ public partial class RSA : AsymmetricAlgorithm
+ {
+ public static new RSA Create()
+ {
+ return new RSAImplementation.RSASecurityTransforms();
+ }
+ }
+#endif
+
+ internal static partial class RSAImplementation
+ {
+ public sealed partial class RSASecurityTransforms : RSA
+ {
+ private SecKeyPair _keys;
+
+ public RSASecurityTransforms()
+ : this(2048)
+ {
+ }
+
+ public RSASecurityTransforms(int keySize)
+ {
+ KeySize = keySize;
+ }
+
+ internal RSASecurityTransforms(SafeSecKeyRefHandle publicKey)
+ {
+ SetKey(SecKeyPair.PublicOnly(publicKey));
+ }
+
+ internal RSASecurityTransforms(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle privateKey)
+ {
+ SetKey(SecKeyPair.PublicPrivatePair(publicKey, privateKey));
+ }
+
+ public override KeySizes[] LegalKeySizes
+ {
+ get
+ {
+ return new KeySizes[]
+ {
+ // All values are in bits.
+ // 1024 was achieved via experimentation.
+ // 1024 and 1024+64 both generated successfully, 1024-64 produced errSecParam.
+ new KeySizes(minSize: 1024, maxSize: 16384, skipSize: 64),
+ };
+ }
+ }
+
+ public override int KeySize
+ {
+ get
+ {
+ return base.KeySize;
+ }
+ set
+ {
+ if (KeySize == value)
+ return;
+
+ // Set the KeySize before freeing the key so that an invalid value doesn't throw away the key
+ base.KeySize = value;
+
+ if (_keys != null)
+ {
+ _keys.Dispose();
+ _keys = null;
+ }
+ }
+ }
+
+ public override RSAParameters ExportParameters(bool includePrivateParameters)
+ {
+ SecKeyPair keys = GetKeys();
+
+ SafeSecKeyRefHandle keyHandle = includePrivateParameters ? keys.PrivateKey : keys.PublicKey;
+
+ if (keyHandle == null)
+ {
+ throw new CryptographicException(SR.Cryptography_OpenInvalidHandle);
+ }
+
+ DerSequenceReader keyReader = Interop.AppleCrypto.SecKeyExport(keyHandle, includePrivateParameters);
+ RSAParameters parameters = new RSAParameters();
+
+ if (includePrivateParameters)
+ {
+ keyReader.ReadPkcs8Blob(ref parameters);
+ }
+ else
+ {
+ // When exporting a key handle opened from a certificate, it seems to
+ // export as a PKCS#1 blob instead of an X509 SubjectPublicKeyInfo blob.
+ // So, check for that.
+ if (keyReader.PeekTag() == (byte)DerSequenceReader.DerTag.Integer)
+ {
+ keyReader.ReadPkcs1PublicBlob(ref parameters);
+ }
+ else
+ {
+ keyReader.ReadSubjectPublicKeyInfo(ref parameters);
+ }
+ }
+
+ return parameters;
+ }
+
+ public override void ImportParameters(RSAParameters parameters)
+ {
+ bool isPrivateKey = parameters.D != null;
+
+ if (isPrivateKey)
+ {
+ // Start with the private key, in case some of the private key fields
+ // don't match the public key fields.
+ //
+ // Public import should go off without a hitch.
+ SafeSecKeyRefHandle privateKey = ImportKey(parameters);
+
+ RSAParameters publicOnly = new RSAParameters
+ {
+ Modulus = parameters.Modulus,
+ Exponent = parameters.Exponent,
+ };
+
+ SafeSecKeyRefHandle publicKey;
+ try
+ {
+ publicKey = ImportKey(publicOnly);
+ }
+ catch
+ {
+ privateKey.Dispose();
+ throw;
+ }
+
+ SetKey(SecKeyPair.PublicPrivatePair(publicKey, privateKey));
+ }
+ else
+ {
+ SafeSecKeyRefHandle publicKey = ImportKey(parameters);
+ SetKey(SecKeyPair.PublicOnly(publicKey));
+ }
+ }
+
+ public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding)
+ {
+ return Interop.AppleCrypto.RsaEncrypt(GetKeys().PublicKey, data, padding);
+ }
+
+ public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
+ {
+ SecKeyPair keys = GetKeys();
+
+ if (keys.PrivateKey == null)
+ {
+ throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey);
+ }
+
+ return Interop.AppleCrypto.RsaDecrypt(keys.PrivateKey, data, padding);
+ }
+
+ public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
+ {
+ if (padding != RSASignaturePadding.Pkcs1)
+ throw new CryptographicException(SR.Cryptography_InvalidPaddingMode);
+
+ SecKeyPair keys = GetKeys();
+
+ if (keys.PrivateKey == null)
+ {
+ throw new CryptographicException(SR.Cryptography_CSP_NoPrivateKey);
+ }
+
+ return Interop.AppleCrypto.GenerateSignature(
+ keys.PrivateKey,
+ hash,
+ PalAlgorithmFromAlgorithmName(hashAlgorithm));
+ }
+
+ public override bool VerifyHash(
+ byte[] hash,
+ byte[] signature,
+ HashAlgorithmName hashAlgorithm,
+ RSASignaturePadding padding)
+ {
+ if (padding != RSASignaturePadding.Pkcs1)
+ throw new CryptographicException(SR.Cryptography_InvalidPaddingMode);
+
+ return Interop.AppleCrypto.VerifySignature(
+ GetKeys().PublicKey,
+ hash,
+ signature,
+ PalAlgorithmFromAlgorithmName(hashAlgorithm));
+ }
+
+ protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
+ {
+ return AsymmetricAlgorithmHelpers.HashData(data, offset, count, hashAlgorithm);
+ }
+
+ protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
+ {
+ return AsymmetricAlgorithmHelpers.HashData(data, hashAlgorithm);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (_keys != null)
+ {
+ _keys.Dispose();
+ _keys = null;
+ }
+ }
+
+ base.Dispose(disposing);
+ }
+
+ private static Interop.AppleCrypto.PAL_HashAlgorithm PalAlgorithmFromAlgorithmName(
+ HashAlgorithmName hashAlgorithmName)
+ {
+ if (hashAlgorithmName == HashAlgorithmName.MD5)
+ {
+ return Interop.AppleCrypto.PAL_HashAlgorithm.Md5;
+ }
+ else if (hashAlgorithmName == HashAlgorithmName.SHA1)
+ {
+ return Interop.AppleCrypto.PAL_HashAlgorithm.Sha1;
+ }
+ else if (hashAlgorithmName == HashAlgorithmName.SHA256)
+ {
+ return Interop.AppleCrypto.PAL_HashAlgorithm.Sha256;
+ }
+ else if (hashAlgorithmName == HashAlgorithmName.SHA384)
+ {
+ return Interop.AppleCrypto.PAL_HashAlgorithm.Sha384;
+ }
+ else if (hashAlgorithmName == HashAlgorithmName.SHA512)
+ {
+ return Interop.AppleCrypto.PAL_HashAlgorithm.Sha512;
+ }
+
+ throw new CryptographicException(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithmName.Name);
+ }
+
+ private SecKeyPair GetKeys()
+ {
+ SecKeyPair current = _keys;
+
+ if (current != null)
+ {
+ return current;
+ }
+
+ SafeSecKeyRefHandle publicKey;
+ SafeSecKeyRefHandle privateKey;
+
+ Interop.AppleCrypto.RsaGenerateKey(KeySizeValue, out publicKey, out privateKey);
+
+ current = SecKeyPair.PublicPrivatePair(publicKey, privateKey);
+ _keys = current;
+ return current;
+ }
+
+ private void SetKey(SecKeyPair newKeyPair)
+ {
+ SecKeyPair current = _keys;
+ _keys = newKeyPair;
+ current?.Dispose();
+
+ if (newKeyPair != null)
+ {
+ KeySizeValue = Interop.AppleCrypto.GetSimpleKeySizeInBits(newKeyPair.PublicKey);
+ }
+ }
+
+ private static SafeSecKeyRefHandle ImportKey(RSAParameters parameters)
+ {
+ bool isPrivateKey = parameters.D != null;
+ byte[] pkcs1Blob = isPrivateKey ? parameters.ToPkcs1Blob() : parameters.ToSubjectPublicKeyInfo();
+
+ return Interop.AppleCrypto.ImportEphemeralKey(pkcs1Blob, isPrivateKey);
+ }
+ }
+ }
+
+ internal static class RsaKeyBlobHelpers
+ {
+ private const string RsaOid = "1.2.840.113549.1.1.1";
+
+ // The PKCS#1 version blob for an RSA key based on 2 primes.
+ private static readonly byte[] s_versionNumberBytes = { 0 };
+
+ // The AlgorithmIdentifier structure for RSA contains an explicit NULL, for legacy/compat reasons.
+ private static readonly byte[][] s_encodedRsaAlgorithmIdentifier =
+ DerEncoder.ConstructSegmentedSequence(
+ DerEncoder.SegmentedEncodeOid(new Oid(RsaOid)),
+ // DER:NULL (0x05 0x00)
+ new byte[][]
+ {
+ new byte[] { (byte)DerSequenceReader.DerTag.Null },
+ new byte[] { 0 },
+ Array.Empty<byte>(),
+ });
+
+ internal static byte[] ToPkcs1Blob(this RSAParameters parameters)
+ {
+ if (parameters.Exponent == null || parameters.Modulus == null)
+ throw new CryptographicException(SR.Cryptography_InvalidRsaParameters);
+
+ if (parameters.D == null)
+ {
+ if (parameters.P != null ||
+ parameters.DP != null ||
+ parameters.Q != null ||
+ parameters.DQ != null ||
+ parameters.InverseQ != null)
+ {
+ throw new CryptographicException(SR.Cryptography_InvalidRsaParameters);
+ }
+
+ return DerEncoder.ConstructSequence(
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.Modulus),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.Exponent));
+ }
+
+ if (parameters.P == null ||
+ parameters.DP == null ||
+ parameters.Q == null ||
+ parameters.DQ == null ||
+ parameters.InverseQ == null)
+ {
+ throw new CryptographicException(SR.Cryptography_InvalidRsaParameters);
+ }
+
+ return DerEncoder.ConstructSequence(
+ DerEncoder.SegmentedEncodeUnsignedInteger(s_versionNumberBytes),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.Modulus),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.Exponent),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.D),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.P),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.Q),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.DP),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.DQ),
+ DerEncoder.SegmentedEncodeUnsignedInteger(parameters.InverseQ));
+ }
+
+ internal static void ReadPkcs8Blob(this DerSequenceReader reader, ref RSAParameters parameters)
+ {
+ // OneAsymmetricKey ::= SEQUENCE {
+ // version Version,
+ // privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ // privateKey PrivateKey,
+ // attributes [0] Attributes OPTIONAL,
+ // ...,
+ // [[2: publicKey [1] PublicKey OPTIONAL ]],
+ // ...
+ // }
+ //
+ // PrivateKeyInfo ::= OneAsymmetricKey
+ //
+ // PrivateKey ::= OCTET STRING
+
+ int version = reader.ReadInteger();
+
+ // We understand both version 0 and 1 formats,
+ // which are now known as v1 and v2, respectively.
+ if (version > 1)
+ {
+ throw new CryptographicException();
+ }
+
+ {
+ // Ensure we're reading RSA
+ DerSequenceReader algorithm = reader.ReadSequence();
+
+ string algorithmOid = algorithm.ReadOidAsString();
+
+ if (algorithmOid != RsaOid)
+ {
+ throw new CryptographicException();
+ }
+ }
+
+ byte[] privateKeyBytes = reader.ReadOctetString();
+ // Because this was an RSA private key, the key format is PKCS#1.
+ ReadPkcs1PrivateBlob(privateKeyBytes, ref parameters);
+
+ // We don't care about the rest of the blob here, but it's expected to not exist.
+ }
+
+ internal static byte[] ToSubjectPublicKeyInfo(this RSAParameters parameters)
+ {
+ Debug.Assert(parameters.D == null);
+
+ // SubjectPublicKeyInfo::= SEQUENCE {
+ // algorithm AlgorithmIdentifier,
+ // subjectPublicKey BIT STRING }
+ return DerEncoder.ConstructSequence(
+ s_encodedRsaAlgorithmIdentifier,
+ DerEncoder.SegmentedEncodeBitString(
+ parameters.ToPkcs1Blob()));
+ }
+
+ internal static void ReadSubjectPublicKeyInfo(this DerSequenceReader keyInfo, ref RSAParameters parameters)
+ {
+ // SubjectPublicKeyInfo::= SEQUENCE {
+ // algorithm AlgorithmIdentifier,
+ // subjectPublicKey BIT STRING }
+ DerSequenceReader algorithm = keyInfo.ReadSequence();
+ string algorithmOid = algorithm.ReadOidAsString();
+
+ if (algorithmOid != RsaOid)
+ {
+ throw new CryptographicException();
+ }
+
+ byte[] subjectPublicKeyBytes = keyInfo.ReadBitString();
+
+ DerSequenceReader subjectPublicKey = new DerSequenceReader(subjectPublicKeyBytes);
+ subjectPublicKey.ReadPkcs1PublicBlob(ref parameters);
+ }
+
+ internal static void ReadPkcs1PublicBlob(this DerSequenceReader subjectPublicKey, ref RSAParameters parameters)
+ {
+ parameters.Modulus = KeyBlobHelpers.TrimPaddingByte(subjectPublicKey.ReadIntegerBytes());
+ parameters.Exponent = KeyBlobHelpers.TrimPaddingByte(subjectPublicKey.ReadIntegerBytes());
+
+ if (subjectPublicKey.HasData)
+ throw new CryptographicException();
+ }
+
+ private static void ReadPkcs1PrivateBlob(byte[] privateKeyBytes, ref RSAParameters parameters)
+ {
+ // RSAPrivateKey::= SEQUENCE {
+ // version Version,
+ // modulus INTEGER, --n
+ // publicExponent INTEGER, --e
+ // privateExponent INTEGER, --d
+ // prime1 INTEGER, --p
+ // prime2 INTEGER, --q
+ // exponent1 INTEGER, --d mod(p - 1)
+ // exponent2 INTEGER, --d mod(q - 1)
+ // coefficient INTEGER, --(inverse of q) mod p
+ // otherPrimeInfos OtherPrimeInfos OPTIONAL
+ // }
+ DerSequenceReader privateKey = new DerSequenceReader(privateKeyBytes);
+ int version = privateKey.ReadInteger();
+
+ if (version != 0)
+ {
+ throw new CryptographicException();
+ }
+
+ parameters.Modulus = KeyBlobHelpers.TrimPaddingByte(privateKey.ReadIntegerBytes());
+ parameters.Exponent = KeyBlobHelpers.TrimPaddingByte(privateKey.ReadIntegerBytes());
+
+ int modulusLen = parameters.Modulus.Length;
+ int halfModulus = modulusLen / 2;
+
+ parameters.D = KeyBlobHelpers.PadOrTrim(privateKey.ReadIntegerBytes(), modulusLen);
+ parameters.P = KeyBlobHelpers.PadOrTrim(privateKey.ReadIntegerBytes(), halfModulus);
+ parameters.Q = KeyBlobHelpers.PadOrTrim(privateKey.ReadIntegerBytes(), halfModulus);
+ parameters.DP = KeyBlobHelpers.PadOrTrim(privateKey.ReadIntegerBytes(), halfModulus);
+ parameters.DQ = KeyBlobHelpers.PadOrTrim(privateKey.ReadIntegerBytes(), halfModulus);
+ parameters.InverseQ = KeyBlobHelpers.PadOrTrim(privateKey.ReadIntegerBytes(), halfModulus);
+
+ if (privateKey.HasData)
+ {
+ throw new CryptographicException();
+ }
+ }
+ }
+}
diff --git a/src/Common/src/System/Security/Cryptography/SecKeyPair.cs b/src/Common/src/System/Security/Cryptography/SecKeyPair.cs
new file mode 100644
index 0000000000..45719a680b
--- /dev/null
+++ b/src/Common/src/System/Security/Cryptography/SecKeyPair.cs
@@ -0,0 +1,46 @@
+// 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.Security.Cryptography.Apple;
+
+namespace System.Security.Cryptography
+{
+ internal sealed class SecKeyPair : IDisposable
+ {
+ internal SafeSecKeyRefHandle PublicKey { get; private set; }
+ internal SafeSecKeyRefHandle PrivateKey { get; private set; }
+
+ private SecKeyPair(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle privateKey)
+ {
+ PublicKey = publicKey;
+ PrivateKey = privateKey;
+ }
+
+ public void Dispose()
+ {
+ PrivateKey?.Dispose();
+ PrivateKey = null;
+ PublicKey?.Dispose();
+ PublicKey = null;
+ }
+
+ internal static SecKeyPair PublicPrivatePair(SafeSecKeyRefHandle publicKey, SafeSecKeyRefHandle privateKey)
+ {
+ if (publicKey == null || publicKey.IsInvalid)
+ throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, nameof(publicKey));
+ if (privateKey == null || privateKey.IsInvalid)
+ throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, nameof(privateKey));
+
+ return new SecKeyPair(publicKey, privateKey);
+ }
+
+ internal static SecKeyPair PublicOnly(SafeSecKeyRefHandle publicKey)
+ {
+ if (publicKey == null || publicKey.IsInvalid)
+ throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, nameof(publicKey));
+
+ return new SecKeyPair(publicKey, null);
+ }
+ }
+}
diff --git a/src/Common/tests/System/IO/FileCleanupTestBase.cs b/src/Common/tests/System/IO/FileCleanupTestBase.cs
index 3c1eb97bd6..97b865eca6 100644
--- a/src/Common/tests/System/IO/FileCleanupTestBase.cs
+++ b/src/Common/tests/System/IO/FileCleanupTestBase.cs
@@ -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 Xunit;
using System.Runtime.CompilerServices;
namespace System.IO
@@ -22,10 +23,9 @@ namespace System.IO
{
Directory.CreateDirectory(TestDirectory);
}
- catch
+ catch (Exception ex)
{
- // Don't throw exceptions during test class construction. Attempts to use paths
- // under this directory will instead appropriately fail later.
+ Assert.True(false, $"FileCleanupTestBase failed to create {TestDirectory} due to {ex.ToString()}");
}
}
diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAFactory.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAFactory.cs
index b64ed48b87..0fc4f613a9 100644
--- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAFactory.cs
+++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAFactory.cs
@@ -9,6 +9,7 @@ namespace System.Security.Cryptography.Dsa.Tests
DSA Create();
DSA Create(int keySize);
bool SupportsFips186_3 { get; }
+ bool SupportsKeyGeneration { get; }
}
public static partial class DSAFactory
@@ -27,12 +28,8 @@ namespace System.Security.Cryptography.Dsa.Tests
/// If false, 186-2 is assumed which implies key size of 1024 or less and only SHA-1
/// If true, 186-3 includes support for keysizes >1024 and SHA-2 algorithms
/// </summary>
- public static bool SupportsFips186_3
- {
- get
- {
- return s_provider.SupportsFips186_3;
- }
- }
+ public static bool SupportsFips186_3 => s_provider.SupportsFips186_3;
+
+ public static bool SupportsKeyGeneration => s_provider.SupportsKeyGeneration;
}
}
diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAImportExport.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAImportExport.cs
index a6339f45a1..3f3d719777 100644
--- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAImportExport.cs
+++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAImportExport.cs
@@ -9,8 +9,9 @@ namespace System.Security.Cryptography.Dsa.Tests
public partial class DSAImportExport
{
public static bool SupportsFips186_3 => DSAFactory.SupportsFips186_3;
+ public static bool SupportsKeyGeneration => DSAFactory.SupportsKeyGeneration;
- [Fact]
+ [ConditionalFact(nameof(SupportsKeyGeneration))]
public static void ExportAutoKey()
{
DSAParameters privateParams;
diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyGeneration.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyGeneration.cs
index ffefa7eafc..f10e1cd5a0 100644
--- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyGeneration.cs
+++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSAKeyGeneration.cs
@@ -8,6 +8,8 @@ namespace System.Security.Cryptography.Dsa.Tests
{
public partial class DSAKeyGeneration
{
+ public static bool SupportsKeyGeneration => DSAFactory.SupportsKeyGeneration;
+
[Fact]
public static void VerifyDefaultKeySize_Fips186_2()
{
@@ -20,19 +22,19 @@ namespace System.Security.Cryptography.Dsa.Tests
}
}
- [Fact]
+ [ConditionalFact(nameof(SupportsKeyGeneration))]
public static void GenerateMinKey()
{
GenerateKey(dsa => GetMin(dsa.LegalKeySizes));
}
- [Fact]
+ [ConditionalFact(nameof(SupportsKeyGeneration))]
public static void GenerateSecondMinKey()
{
GenerateKey(dsa => GetSecondMin(dsa.LegalKeySizes));
}
- [Fact]
+ [ConditionalFact(nameof(SupportsKeyGeneration))]
public static void GenerateKey_1024()
{
GenerateKey(1024);
diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs
index a0eadb5321..34d2e89e00 100644
--- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs
+++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignVerify.cs
@@ -9,7 +9,7 @@ namespace System.Security.Cryptography.Dsa.Tests
{
public partial class DSASignVerify
{
- [Fact]
+ [ConditionalFact(nameof(SupportsKeyGeneration))]
public static void InvalidKeySize_DoesNotInvalidateKey()
{
using (DSA dsa = DSAFactory.Create())
@@ -23,7 +23,7 @@ namespace System.Security.Cryptography.Dsa.Tests
}
}
- [Fact]
+ [ConditionalFact(nameof(SupportsKeyGeneration))]
public static void SignAndVerifyDataNew1024()
{
using (DSA dsa = DSAFactory.Create(1024))
@@ -188,5 +188,6 @@ namespace System.Security.Cryptography.Dsa.Tests
return DSAFactory.SupportsFips186_3;
}
}
+ public static bool SupportsKeyGeneration => DSAFactory.SupportsKeyGeneration;
}
}
diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs
index fb6fbe8d78..e1ce32f1cb 100644
--- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs
+++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/DSA/DSASignatureFormatter.cs
@@ -14,8 +14,11 @@ namespace System.Security.Cryptography.Dsa.Tests
{
using (DSA dsa = DSAFactory.Create())
{
+ dsa.ImportParameters(DSATestData.GetDSA1024Params());
+
var formatter = new DSASignatureFormatter(dsa);
var deformatter = new DSASignatureDeformatter(dsa);
+
using (SHA1 alg = SHA1.Create())
{
VerifySignature(formatter, deformatter, alg, "SHA1");
diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaFactory.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaFactory.cs
index 0aaedf5383..f31d178507 100644
--- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaFactory.cs
+++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaFactory.cs
@@ -35,12 +35,6 @@ namespace System.Security.Cryptography.EcDsa.Tests
return s_provider.IsCurveValid(oid);
}
- public static bool ExplicitCurvesSupported
- {
- get
- {
- return s_provider.ExplicitCurvesSupported;
- }
- }
+ public static bool ExplicitCurvesSupported => s_provider.ExplicitCurvesSupported;
}
}
diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs
index c902abccfb..7507dba76b 100644
--- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs
+++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaImportExport.cs
@@ -3,12 +3,14 @@
// See the LICENSE file in the project root for more information.
using Xunit;
+using Test.Cryptography;
namespace System.Security.Cryptography.EcDsa.Tests
{
public class ECDsaImportExportTests : ECDsaTestsBase
{
- [Theory, MemberData(nameof(TestCurvesFull))]
+ [Theory]
+ [MemberData(nameof(TestCurvesFull))]
public static void TestNamedCurves(CurveDef curveDef)
{
using (ECDsa ec1 = ECDsaFactory.Create(curveDef.Curve))
diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.cs
index 343613c482..24328acbf5 100644
--- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.cs
+++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/ECDsa/ECDsaTests.cs
@@ -134,7 +134,8 @@ namespace System.Security.Cryptography.EcDsa.Tests
}
}
- [Theory, MemberData(nameof(TestCurves))]
+ [Theory]
+ [MemberData(nameof(TestCurves))]
public void TestRegenKeyNamed(CurveDef curveDef)
{
ECParameters param, param2;
@@ -178,7 +179,8 @@ namespace System.Security.Cryptography.EcDsa.Tests
}
}
- [Theory, MemberData(nameof(TestCurves))]
+ [Theory]
+ [MemberData(nameof(TestCurves))]
public void TestChangeFromNamedCurveToKeySize(CurveDef curveDef)
{
using (ECDsa ec = ECDsaFactory.Create(curveDef.Curve))
@@ -476,7 +478,8 @@ namespace System.Security.Cryptography.EcDsa.Tests
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- [Theory, MemberData(nameof(RealImplementations))]
+ [Theory]
+ [MemberData(nameof(RealImplementations))]
public void SignData_MaxOffset_ZeroLength_NoThrow(ECDsa ecdsa)
{
// Explicitly larger than Array.Empty
@@ -486,7 +489,8 @@ namespace System.Security.Cryptography.EcDsa.Tests
Assert.True(ecdsa.VerifyData(Array.Empty<byte>(), signature, HashAlgorithmName.SHA256));
}
- [Theory, MemberData(nameof(RealImplementations))]
+ [Theory]
+ [MemberData(nameof(RealImplementations))]
public void VerifyData_MaxOffset_ZeroLength_NoThrow(ECDsa ecdsa)
{
// Explicitly larger than Array.Empty
@@ -496,7 +500,8 @@ namespace System.Security.Cryptography.EcDsa.Tests
Assert.True(ecdsa.VerifyData(data, data.Length, 0, signature, HashAlgorithmName.SHA256));
}
- [Theory, MemberData(nameof(RealImplementations))]
+ [Theory]
+ [MemberData(nameof(RealImplementations))]
public void Roundtrip_WithOffset(ECDsa ecdsa)
{
byte[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
@@ -543,7 +548,8 @@ namespace System.Security.Cryptography.EcDsa.Tests
}
}
- [Theory, MemberData(nameof(InteroperableSignatureConfigurations))]
+ [Theory]
+ [MemberData(nameof(InteroperableSignatureConfigurations))]
public void SignVerify_InteroperableSameKeys_RoundTripsUnlessTampered(ECDsa ecdsa, HashAlgorithmName hashAlgorithm)
{
byte[] data = Encoding.UTF8.GetBytes("something to repeat and sign");
diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs
index 96c340c0c7..a1cbdb35d8 100644
--- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs
+++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/ImportExport.cs
@@ -151,6 +151,24 @@ namespace System.Security.Cryptography.Rsa.Tests
}
[Fact]
+ public static void ImportPrivateExportPublic()
+ {
+ RSAParameters imported = TestData.RSA1024Params;
+
+ using (RSA rsa = RSAFactory.Create())
+ {
+ rsa.ImportParameters(imported);
+
+ RSAParameters exportedPublic = rsa.ExportParameters(false);
+
+ Assert.Equal(imported.Modulus, exportedPublic.Modulus);
+ Assert.Equal(imported.Exponent, exportedPublic.Exponent);
+ Assert.Null(exportedPublic.D);
+ ValidateParameters(ref exportedPublic);
+ }
+ }
+
+ [Fact]
public static void MultiExport()
{
RSAParameters imported = TestData.RSA1024Params;
diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAFactory.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAFactory.cs
index 3ce0dbff2f..dbaaf0d73e 100644
--- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAFactory.cs
+++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAFactory.cs
@@ -24,14 +24,8 @@ namespace System.Security.Cryptography.Rsa.Tests
return s_provider.Create(keySize);
}
- public static bool Supports384PrivateKey
- {
- get { return s_provider.Supports384PrivateKey; }
- }
+ public static bool Supports384PrivateKey => s_provider.Supports384PrivateKey;
- public static bool SupportsSha2Oaep
- {
- get { return s_provider.SupportsSha2Oaep; }
- }
+ public static bool SupportsSha2Oaep => s_provider.SupportsSha2Oaep;
}
}
diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs
index e17d5b9d4b..88e995b333 100644
--- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs
+++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSAKeyExchangeFormatter.cs
@@ -15,6 +15,8 @@ namespace System.Security.Cryptography.Rsa.Tests
{
using (RSA rsa = RSAFactory.Create())
{
+ rsa.ImportParameters(TestData.RSA2048Params);
+
var formatter = new RSAOAEPKeyExchangeFormatter(rsa);
var deformatter = new RSAOAEPKeyExchangeDeformatter(rsa);
VerifyDecryptKeyExchange(formatter, deformatter);
@@ -26,6 +28,8 @@ namespace System.Security.Cryptography.Rsa.Tests
{
using (RSA rsa = RSAFactory.Create())
{
+ rsa.ImportParameters(TestData.RSA2048Params);
+
var formatter = new RSAPKCS1KeyExchangeFormatter(rsa);
var deformatter = new RSAPKCS1KeyExchangeDeformatter(rsa);
VerifyDecryptKeyExchange(formatter, deformatter);
diff --git a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs
index 9489c0a214..f774bc8e27 100644
--- a/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs
+++ b/src/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/RSASignatureFormatter.cs
@@ -15,6 +15,8 @@ namespace System.Security.Cryptography.Rsa.Tests
{
using (RSA rsa = RSAFactory.Create())
{
+ rsa.ImportParameters(TestData.RSA2048Params);
+
var formatter = new RSAPKCS1SignatureFormatter(rsa);
var deformatter = new RSAPKCS1SignatureDeformatter(rsa);
@@ -31,6 +33,8 @@ namespace System.Security.Cryptography.Rsa.Tests
{
using (RSA rsa = RSAFactory.Create())
{
+ rsa.ImportParameters(TestData.RSA2048Params);
+
var formatter = new RSAPKCS1SignatureFormatter(rsa);
var deformatter = new RSAPKCS1SignatureDeformatter(rsa);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt b/src/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt
index 8bdca85e48..e0bd075d27 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/CMakeLists.txt
@@ -5,11 +5,24 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
add_definitions(-DPIC=1)
+find_library(COREFOUNDATION_LIBRARY CoreFoundation)
+find_library(SECURITY_LIBRARY Security)
+
set(NATIVECRYPTO_SOURCES
pal_digest.cpp
+ pal_ecc.cpp
pal_hmac.cpp
+ pal_keychain.cpp
pal_random.cpp
+ pal_rsa.cpp
+ pal_sec.cpp
+ pal_seckey.cpp
+ pal_signverify.cpp
+ pal_ssl.cpp
pal_symmetric.cpp
+ pal_trust.cpp
+ pal_x509.cpp
+ pal_x509chain.cpp
)
add_library(System.Security.Cryptography.Native.Apple
@@ -22,6 +35,8 @@ add_library(System.Security.Cryptography.Native.Apple
set_target_properties(System.Security.Cryptography.Native.Apple PROPERTIES PREFIX "")
target_link_libraries(System.Security.Cryptography.Native.Apple
+ ${COREFOUNDATION_LIBRARY}
+ ${SECURITY_LIBRARY}
)
install_library_and_symbols (System.Security.Cryptography.Native.Apple)
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_digest.cpp b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_digest.cpp
index 60c1546036..11b86fcee3 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_digest.cpp
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_digest.cpp
@@ -74,7 +74,7 @@ extern "C" DigestCtx* AppleCryptoNative_DigestCreate(PAL_HashAlgorithm algorithm
return digestCtx;
}
-extern "C" int AppleCryptoNative_DigestUpdate(DigestCtx* ctx, uint8_t* pBuf, int32_t cbBuf)
+extern "C" int32_t AppleCryptoNative_DigestUpdate(DigestCtx* ctx, uint8_t* pBuf, int32_t cbBuf)
{
if (cbBuf == 0)
return 1;
@@ -100,12 +100,12 @@ extern "C" int AppleCryptoNative_DigestUpdate(DigestCtx* ctx, uint8_t* pBuf, int
}
}
-extern "C" int AppleCryptoNative_DigestFinal(DigestCtx* ctx, uint8_t* pOutput, int32_t cbOutput)
+extern "C" int32_t AppleCryptoNative_DigestFinal(DigestCtx* ctx, uint8_t* pOutput, int32_t cbOutput)
{
if (ctx == nullptr || pOutput == nullptr || cbOutput < ctx->cbDigest)
return -1;
- int ret = 0;
+ int32_t ret = 0;
switch (ctx->algorithm)
{
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_digest.h b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_digest.h
index 04b45cd19a..b098632215 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_digest.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_digest.h
@@ -41,11 +41,11 @@ Apply cbBuf bytes of data from pBuf to the ongoing digest represented in ctx.
Returns 1 on success, 0 on failure, any other value on invalid inputs/state.
*/
-extern "C" int AppleCryptoNative_DigestUpdate(DigestCtx* ctx, uint8_t* pBuf, int32_t cbBuf);
+extern "C" int32_t AppleCryptoNative_DigestUpdate(DigestCtx* ctx, uint8_t* pBuf, int32_t cbBuf);
/*
Complete the digest in ctx, copying the results to pOutput, and reset ctx for a new digest.
Returns 1 on success, 0 on failure, any other value on invalid inputs/state.
*/
-extern "C" int AppleCryptoNative_DigestFinal(DigestCtx* ctx, uint8_t* pOutput, int32_t cbOutput);
+extern "C" int32_t AppleCryptoNative_DigestFinal(DigestCtx* ctx, uint8_t* pOutput, int32_t cbOutput);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ecc.cpp b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ecc.cpp
new file mode 100644
index 0000000000..6c7a468907
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ecc.cpp
@@ -0,0 +1,98 @@
+// 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.
+
+#include "pal_ecc.h"
+
+extern "C" int32_t AppleCryptoNative_EccGenerateKey(
+ int32_t keySizeBits, SecKeychainRef tempKeychain, SecKeyRef* pPublicKey, SecKeyRef* pPrivateKey, int32_t* pOSStatus)
+{
+ if (pPublicKey != nullptr)
+ *pPublicKey = nullptr;
+ if (pPrivateKey != nullptr)
+ *pPrivateKey = nullptr;
+
+ if (pPublicKey == nullptr || pPrivateKey == nullptr || pOSStatus == nullptr)
+ return kErrorBadInput;
+
+ CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(nullptr, 2, &kCFTypeDictionaryKeyCallBacks, nullptr);
+
+ CFNumberRef cfKeySizeValue = CFNumberCreate(nullptr, kCFNumberIntType, &keySizeBits);
+ OSStatus status;
+
+ if (attributes != nullptr && cfKeySizeValue != nullptr)
+ {
+ CFDictionaryAddValue(attributes, kSecAttrKeyType, kSecAttrKeyTypeEC);
+ CFDictionaryAddValue(attributes, kSecAttrKeySizeInBits, cfKeySizeValue);
+ CFDictionaryAddValue(attributes, kSecUseKeychain, tempKeychain);
+
+ status = SecKeyGeneratePair(attributes, pPublicKey, pPrivateKey);
+
+ if (status == noErr)
+ {
+ status = ExportImportKey(pPublicKey, kSecItemTypePublicKey);
+ }
+
+ if (status == noErr)
+ {
+ status = ExportImportKey(pPrivateKey, kSecItemTypePrivateKey);
+ }
+ }
+ else
+ {
+ status = errSecAllocate;
+ }
+
+ if (attributes != nullptr)
+ CFRelease(attributes);
+ if (cfKeySizeValue != nullptr)
+ CFRelease(cfKeySizeValue);
+
+ *pOSStatus = status;
+ return status == noErr;
+}
+
+extern "C" uint64_t AppleCryptoNative_EccGetKeySizeInBits(SecKeyRef publicKey)
+{
+ if (publicKey == nullptr)
+ {
+ return 0;
+ }
+
+ size_t blockSize = SecKeyGetBlockSize(publicKey);
+
+ // This seems to be the expected size of an ECDSA signature for this key.
+ // But since Apple uses the DER SEQUENCE(r, s) format the signature size isn't
+ // fixed. It might be trying to encode the biggest the DER value could be:
+ //
+ // 256: r is 32 bytes, but maybe one padding byte, so 33.
+ // s is 32 bytes, but maybe one padding byte, so 33.
+ // each of those values gets one tag and one length byte
+ // 35 * 2 is 70 payload bytes for the sequence, so one length byte
+ // and one tag byte, makes 72.
+ //
+ // 384: r,s are 48 bytes, plus padding, length, and tag: 51
+ // 2 * 51 = 102, requires one length byte and one tag byte, 104.
+ //
+ // 521: neither r nor s can have the high bit set, no padding. 66 content bytes
+ // plus tag and length is 68.
+ // 2 * 68 is 136, since it's greater than 127 it takes 2 length bytes
+ // so 136 + 2 + 1 = 139. Looks like they accounted for padding bytes anyways.
+ //
+ // This completely needs to be revisited if Apple adds support for "generic" ECC.
+ //
+ // Word of caution: While seeking meaning in these numbers I ran across a snippet of code
+ // which suggests that on iOS (vs macOS) they use a different set of reasoning and produce
+ // different numbers (they used (8 + 2*thisValue) on iOS for "signature length").
+ switch (blockSize)
+ {
+ case 72:
+ return 256;
+ case 104:
+ return 384;
+ case 141:
+ return 521;
+ }
+
+ return 0;
+}
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ecc.h b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ecc.h
new file mode 100644
index 0000000000..d6253b883d
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ecc.h
@@ -0,0 +1,27 @@
+// 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.
+
+#pragma once
+
+#include "pal_seckey.h"
+
+#include <Security/Security.h>
+
+/*
+Generate an ECC keypair of the specified size.
+
+Returns 1 on success, 0 on failure. On failure, *pOSStatus should carry the OS failure code.
+*/
+extern "C" int32_t AppleCryptoNative_EccGenerateKey(int32_t keySizeBits,
+ SecKeychainRef tempKeychain,
+ SecKeyRef* pPublicKey,
+ SecKeyRef* pPrivateKey,
+ int32_t* pOSStatus);
+
+/*
+Get the keysize, in bits, of an ECC key.
+
+Returns the keysize, in bits, of the ECC key, or 0 on error.
+*/
+extern "C" uint64_t AppleCryptoNative_EccGetKeySizeInBits(SecKeyRef publicKey);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_hmac.cpp b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_hmac.cpp
index a4d80c5150..8e4f559279 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_hmac.cpp
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_hmac.cpp
@@ -38,7 +38,7 @@ static CCHmacAlgorithm PalAlgorithmToAppleAlgorithm(PAL_HashAlgorithm algorithm)
}
}
-static int GetHmacOutputSize(PAL_HashAlgorithm algorithm)
+static int32_t GetHmacOutputSize(PAL_HashAlgorithm algorithm)
{
switch (algorithm)
{
@@ -79,7 +79,7 @@ extern "C" HmacCtx* AppleCryptoNative_HmacCreate(PAL_HashAlgorithm algorithm, in
return hmacCtx;
}
-extern "C" int AppleCryptoNative_HmacInit(HmacCtx* ctx, uint8_t* pbKey, int32_t cbKey)
+extern "C" int32_t AppleCryptoNative_HmacInit(HmacCtx* ctx, uint8_t* pbKey, int32_t cbKey)
{
if (ctx == nullptr || cbKey < 0)
return 0;
@@ -91,7 +91,7 @@ extern "C" int AppleCryptoNative_HmacInit(HmacCtx* ctx, uint8_t* pbKey, int32_t
return 1;
}
-extern "C" int AppleCryptoNative_HmacUpdate(HmacCtx* ctx, uint8_t* pbData, int32_t cbData)
+extern "C" int32_t AppleCryptoNative_HmacUpdate(HmacCtx* ctx, uint8_t* pbData, int32_t cbData)
{
if (cbData == 0)
return 1;
@@ -103,7 +103,7 @@ extern "C" int AppleCryptoNative_HmacUpdate(HmacCtx* ctx, uint8_t* pbData, int32
return 1;
}
-extern "C" int AppleCryptoNative_HmacFinal(HmacCtx* ctx, uint8_t* pbOutput)
+extern "C" int32_t AppleCryptoNative_HmacFinal(HmacCtx* ctx, uint8_t* pbOutput)
{
if (ctx == nullptr || pbOutput == nullptr)
return 0;
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_hmac.h b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_hmac.h
index e3fe201f2d..4bb3f2961f 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_hmac.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_hmac.h
@@ -29,18 +29,18 @@ Initialize an HMAC to the correct key and start state.
Returns 1 on success, 0 on error.
*/
-extern "C" int AppleCryptoNative_HmacInit(HmacCtx* ctx, uint8_t* pbKey, int32_t cbKey);
+extern "C" int32_t AppleCryptoNative_HmacInit(HmacCtx* ctx, uint8_t* pbKey, int32_t cbKey);
/*
Add data into the HMAC
Returns 1 on success, 0 on error.
*/
-extern "C" int AppleCryptoNative_HmacUpdate(HmacCtx* ctx, uint8_t* pbData, int32_t cbData);
+extern "C" int32_t AppleCryptoNative_HmacUpdate(HmacCtx* ctx, uint8_t* pbData, int32_t cbData);
/*
Complete the HMAC and copy the result into pbOutput.
Returns 1 on success, 0 on error.
*/
-extern "C" int AppleCryptoNative_HmacFinal(HmacCtx* ctx, uint8_t* pbOutput);
+extern "C" int32_t AppleCryptoNative_HmacFinal(HmacCtx* ctx, uint8_t* pbOutput);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain.cpp b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain.cpp
new file mode 100644
index 0000000000..4c7fd927ca
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain.cpp
@@ -0,0 +1,376 @@
+// 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.
+
+#include "pal_keychain.h"
+
+extern "C" int32_t AppleCryptoNative_SecKeychainItemCopyKeychain(SecKeychainItemRef item, SecKeychainRef* pKeychainOut)
+{
+ if (pKeychainOut != nullptr)
+ *pKeychainOut = nullptr;
+
+ if (item == nullptr)
+ return errSecNoSuchKeychain;
+
+ auto itemType = CFGetTypeID(item);
+
+ if (itemType == SecKeyGetTypeID() || itemType == SecIdentityGetTypeID() || itemType == SecCertificateGetTypeID())
+ {
+ OSStatus status = SecKeychainItemCopyKeychain(item, pKeychainOut);
+
+ if (status == noErr)
+ {
+ return status;
+ }
+
+ // Acceptable error codes
+ if (status == errSecNoSuchKeychain || status == errSecInvalidItemRef)
+ {
+ return noErr;
+ }
+
+ return status;
+ }
+
+ return errSecParam;
+}
+
+extern "C" int32_t AppleCryptoNative_SecKeychainCreate(const char* pathName,
+ uint32_t passphraseLength,
+ const uint8_t* passphraseUtf8,
+ SecKeychainRef* pKeychainOut)
+{
+ return SecKeychainCreate(pathName, passphraseLength, passphraseUtf8, false, nullptr, pKeychainOut);
+}
+
+extern "C" int32_t AppleCryptoNative_SecKeychainDelete(SecKeychainRef keychain)
+{
+ return SecKeychainDelete(keychain);
+}
+
+extern "C" int32_t AppleCryptoNative_SecKeychainCopyDefault(SecKeychainRef* pKeychainOut)
+{
+ if (pKeychainOut != nullptr)
+ *pKeychainOut = nullptr;
+
+ return SecKeychainCopyDefault(pKeychainOut);
+}
+
+extern "C" int32_t AppleCryptoNative_SecKeychainOpen(const char* pszKeychainPath, SecKeychainRef* pKeychainOut)
+{
+ if (pKeychainOut != nullptr)
+ *pKeychainOut = nullptr;
+
+ if (pszKeychainPath == nullptr)
+ return errSecParam;
+
+ return SecKeychainOpen(pszKeychainPath, pKeychainOut);
+}
+
+static int32_t
+EnumerateKeychain(SecKeychainRef keychain, CFStringRef matchType, CFArrayRef* pCertsOut, int32_t* pOSStatus)
+{
+ if (pCertsOut != nullptr)
+ *pCertsOut = nullptr;
+ if (pOSStatus != nullptr)
+ *pOSStatus = noErr;
+
+ assert(matchType != nullptr);
+
+ if (keychain == nullptr || pCertsOut == nullptr || pOSStatus == nullptr)
+ return -1;
+
+ CFMutableDictionaryRef query = CFDictionaryCreateMutable(
+ kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ if (query == nullptr)
+ return -2;
+
+ int32_t ret = 0;
+ CFTypeRef result = nullptr;
+ CFArrayRef searchList = CFArrayCreate(
+ nullptr, const_cast<const void**>(reinterpret_cast<void**>(&keychain)), 1, &kCFTypeArrayCallBacks);
+
+ if (searchList == nullptr)
+ {
+ ret = -3;
+ }
+ else
+ {
+ CFDictionarySetValue(query, kSecReturnRef, kCFBooleanTrue);
+ CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
+ CFDictionarySetValue(query, kSecClass, matchType);
+ CFDictionarySetValue(query, kSecMatchSearchList, searchList);
+
+ *pOSStatus = SecItemCopyMatching(query, &result);
+
+ if (*pOSStatus == noErr)
+ {
+ if (result == nullptr || CFGetTypeID(result) != CFArrayGetTypeID())
+ {
+ ret = -3;
+ }
+ else
+ {
+ CFRetain(result);
+ *pCertsOut = reinterpret_cast<CFArrayRef>(result);
+ ret = 1;
+ }
+ }
+ else if (*pOSStatus == errSecItemNotFound)
+ {
+ *pOSStatus = noErr;
+ ret = 1;
+ }
+ else
+ {
+ ret = 0;
+ }
+ }
+
+ if (searchList != nullptr)
+ CFRelease(searchList);
+
+ if (result != nullptr)
+ CFRelease(result);
+
+ CFRelease(query);
+ return ret;
+}
+
+extern "C" int32_t
+AppleCryptoNative_SecKeychainEnumerateCerts(SecKeychainRef keychain, CFArrayRef* pCertsOut, int32_t* pOSStatus)
+{
+ return EnumerateKeychain(keychain, kSecClassCertificate, pCertsOut, pOSStatus);
+}
+
+extern "C" int32_t AppleCryptoNative_SecKeychainEnumerateIdentities(SecKeychainRef keychain,
+ CFArrayRef* pIdentitiesOut,
+ int32_t* pOSStatus)
+{
+ return EnumerateKeychain(keychain, kSecClassIdentity, pIdentitiesOut, pOSStatus);
+}
+
+static OSStatus DeleteInKeychain(CFTypeRef needle, SecKeychainRef haystack)
+{
+ CFMutableDictionaryRef query = CFDictionaryCreateMutable(
+ kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+ if (query == nullptr)
+ return errSecAllocate;
+
+ CFArrayRef searchList = CFArrayCreate(
+ nullptr, const_cast<const void**>(reinterpret_cast<void**>(&haystack)), 1, &kCFTypeArrayCallBacks);
+
+ if (searchList == nullptr)
+ {
+ CFRelease(query);
+ return errSecAllocate;
+ }
+
+ CFArrayRef itemMatch = CFArrayCreate(nullptr, reinterpret_cast<const void**>(&needle), 1, &kCFTypeArrayCallBacks);
+
+ if (itemMatch == nullptr)
+ {
+ CFRelease(searchList);
+ CFRelease(query);
+ return errSecAllocate;
+ }
+
+ CFDictionarySetValue(query, kSecReturnRef, kCFBooleanTrue);
+ CFDictionarySetValue(query, kSecMatchSearchList, searchList);
+ CFDictionarySetValue(query, kSecMatchItemList, itemMatch);
+ CFDictionarySetValue(query, kSecClass, kSecClassIdentity);
+
+ OSStatus status = SecItemDelete(query);
+
+ if (status == errSecItemNotFound)
+ {
+ status = noErr;
+ }
+
+ if (status == noErr)
+ {
+ CFDictionarySetValue(query, kSecClass, kSecClassCertificate);
+ status = SecItemDelete(query);
+ }
+
+ if (status == errSecItemNotFound)
+ {
+ status = noErr;
+ }
+
+ CFRelease(itemMatch);
+ CFRelease(searchList);
+ CFRelease(query);
+
+ return status;
+}
+
+extern "C" int32_t
+AppleCryptoNative_X509StoreAddCertificate(CFTypeRef certOrIdentity, SecKeychainRef keychain, int32_t* pOSStatus)
+{
+ if (pOSStatus != nullptr)
+ *pOSStatus = noErr;
+
+ if (certOrIdentity == nullptr || keychain == nullptr || pOSStatus == nullptr)
+ return -1;
+
+ SecCertificateRef cert = nullptr;
+ SecKeyRef privateKey = nullptr;
+
+ auto inputType = CFGetTypeID(certOrIdentity);
+ OSStatus status = noErr;
+
+ if (inputType == SecCertificateGetTypeID())
+ {
+ cert = reinterpret_cast<SecCertificateRef>(const_cast<void*>(certOrIdentity));
+ CFRetain(cert);
+ }
+ else if (inputType == SecIdentityGetTypeID())
+ {
+ SecIdentityRef identity = reinterpret_cast<SecIdentityRef>(const_cast<void*>(certOrIdentity));
+ status = SecIdentityCopyCertificate(identity, &cert);
+
+ if (status == noErr)
+ {
+ status = SecIdentityCopyPrivateKey(identity, &privateKey);
+ }
+ }
+ else
+ {
+ return -1;
+ }
+
+ SecKeychainItemRef itemCopy = nullptr;
+
+ // Copy the private key into the new keychain first, because it can fail due to
+ // non-exportability. Certificates can only fail for things like I/O errors saving the
+ // keychain back to disk.
+ if (status == noErr && privateKey != nullptr)
+ {
+ status =
+ SecKeychainItemCreateCopy(reinterpret_cast<SecKeychainItemRef>(privateKey), keychain, nullptr, &itemCopy);
+ }
+
+ if (status == errSecDuplicateItem)
+ {
+ status = noErr;
+ }
+
+ // Since we don't care about the itemCopy we'd ideally pass nullptr to SecKeychainItemCreateCopy,
+ // but even though the documentation says it can be null, clang gives an error that null isn't
+ // allowed.
+ if (itemCopy != nullptr)
+ {
+ CFRelease(itemCopy);
+ itemCopy = nullptr;
+ }
+
+ if (status == noErr && cert != nullptr)
+ {
+ status = SecKeychainItemCreateCopy(reinterpret_cast<SecKeychainItemRef>(cert), keychain, nullptr, &itemCopy);
+ }
+
+ if (status == errSecDuplicateItem)
+ {
+ status = noErr;
+ }
+
+ if (itemCopy != nullptr)
+ {
+ CFRelease(itemCopy);
+ itemCopy = nullptr;
+ }
+
+ if (privateKey != nullptr)
+ {
+ CFRelease(privateKey);
+ privateKey = nullptr;
+ }
+
+ if (cert != nullptr)
+ {
+ CFRelease(cert);
+ cert = nullptr;
+ }
+
+ *pOSStatus = status;
+ return status == noErr;
+}
+
+extern "C" int32_t
+AppleCryptoNative_X509StoreRemoveCertificate(CFTypeRef certOrIdentity, SecKeychainRef keychain, int32_t* pOSStatus)
+{
+ if (pOSStatus != nullptr)
+ *pOSStatus = noErr;
+
+ if (certOrIdentity == nullptr || keychain == nullptr || pOSStatus == nullptr)
+ return -1;
+
+ SecCertificateRef cert = nullptr;
+ SecIdentityRef identity = nullptr;
+
+ auto inputType = CFGetTypeID(certOrIdentity);
+ OSStatus status = noErr;
+
+ if (inputType == SecCertificateGetTypeID())
+ {
+ cert = reinterpret_cast<SecCertificateRef>(const_cast<void*>(certOrIdentity));
+ CFRetain(cert);
+ }
+ else if (inputType == SecIdentityGetTypeID())
+ {
+ identity = reinterpret_cast<SecIdentityRef>(const_cast<void*>(certOrIdentity));
+ status = SecIdentityCopyCertificate(identity, &cert);
+
+ if (status != noErr)
+ {
+ *pOSStatus = status;
+ return 0;
+ }
+ }
+ else
+ {
+ return -1;
+ }
+
+ const int32_t kErrorUserTrust = 2;
+ const int32_t kErrorAdminTrust = 3;
+
+ CFArrayRef settings = nullptr;
+
+ if (status == noErr)
+ {
+ status = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainUser, &settings);
+ }
+
+ if (settings != nullptr)
+ {
+ CFRelease(settings);
+ settings = nullptr;
+ }
+
+ if (status == noErr)
+ {
+ CFRelease(cert);
+ return kErrorUserTrust;
+ }
+
+ status = SecTrustSettingsCopyTrustSettings(cert, kSecTrustSettingsDomainAdmin, &settings);
+
+ if (settings != nullptr)
+ {
+ CFRelease(settings);
+ settings = nullptr;
+ }
+
+ if (status == noErr)
+ {
+ CFRelease(cert);
+ return kErrorAdminTrust;
+ }
+
+ *pOSStatus = DeleteInKeychain(cert, keychain);
+ return *pOSStatus == noErr;
+}
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain.h b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain.h
new file mode 100644
index 0000000000..ef3fe27e50
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_keychain.h
@@ -0,0 +1,121 @@
+// 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.
+
+#pragma once
+
+#include "pal_types.h"
+
+#include <Security/Security.h>
+
+/*
+Get a CFRetain()ed SecKeychainRef value for the keychain to which the keychain item belongs.
+
+The behavior of this function is undefined if `item` is not a CFTypeRef.
+For types that are not understood by this function to be keychain items an invalid parameter error is returned.
+Errors of the item having no keychain are suppressed, returning success (0) with *pKeychainOut set to NULL.
+
+For all other situations, see SecKeychainItemCopyKeychain documentation.
+*/
+extern "C" int32_t AppleCryptoNative_SecKeychainItemCopyKeychain(SecKeychainItemRef item, SecKeychainRef* pKeychainOut);
+
+/*
+Create a keychain at the specified location with a given (UTF-8 encoded) lock passphrase.
+
+Returns the result of SecKeychainCreate.
+
+Output:
+pKeychainOut: The SecKeychainRef created by this function
+*/
+extern "C" int32_t AppleCryptoNative_SecKeychainCreate(const char* pathName,
+ uint32_t passphraseLength,
+ const uint8_t* passphraseUtf8,
+ SecKeychainRef* pKeychainOut);
+
+/*
+Delete a keychain, including the file on disk.
+
+Returns the result of SecKeychainDelete
+*/
+extern "C" int32_t AppleCryptoNative_SecKeychainDelete(SecKeychainRef keychain);
+
+/*
+Open the default keychain.
+This is usually login.keychain, but can be adjusted by the user.
+
+Returns the result of SecKeychainCopyDefault.
+
+Output:
+pKeyChainOut: Receives the SecKeychainRef for the default keychain.
+*/
+extern "C" int32_t AppleCryptoNative_SecKeychainCopyDefault(SecKeychainRef* pKeychainOut);
+
+/*
+Open the named keychain (full path to the file).
+
+Returns the result of SecKeychainOpen.
+
+Output:
+pKeychainOut: Receives the SecKeychainRef for the named keychain.
+*/
+extern "C" int32_t AppleCryptoNative_SecKeychainOpen(const char* pszKeychainPath, SecKeychainRef* pKeychainOut);
+
+/*
+Enumerate the certificate objects within the given keychain.
+
+Returns 1 on success (including "no certs found"), 0 on failure, any other value for invalid state.
+
+Output:
+pCertsOut: When the return value is not 1, NULL. Otherwise NULL on "no certs found", or a CFArrayRef for the matches
+(including a single match).
+pOSStatus: Receives the last OSStatus value.
+*/
+extern "C" int32_t
+AppleCryptoNative_SecKeychainEnumerateCerts(SecKeychainRef keychain, CFArrayRef* pCertsOut, int32_t* pOSStatus);
+
+/*
+Enumerate the certificate objects within the given keychain.
+
+Returns 1 on success (including "no certs found"), 0 on failure, any other value for invalid state.
+
+Note that any identity will also necessarily be returned as a certificate with no private key by
+SecKeychainEnumerateCerts. De-duplication of values is the responsibility of the caller.
+
+Output:
+pCertsOut: When the return value is not 1, NULL. Otherwise NULL on "no certs found", or a CFArrayRef for the matches
+(including a single match).
+pOSStatus: Receives the last OSStatus value.
+*/
+extern "C" int32_t AppleCryptoNative_SecKeychainEnumerateIdentities(SecKeychainRef keychain,
+ CFArrayRef* pIdentitiesOut,
+ int32_t* pOSStatus);
+
+/*
+Add a certificate from the specified keychain.
+
+Returns
+0 on failure -> see OSStatus
+1 on success
+any other value is invalid
+
+Output:
+pOSStatus: Receives the last OSStatus value..
+*/
+extern "C" int32_t
+AppleCryptoNative_X509StoreAddCertificate(CFTypeRef certOrIdentity, SecKeychainRef keychain, int32_t* pOSStatus);
+
+/*
+Remove a certificate from the specified keychain.
+
+Returns
+0 on failure -> see OSStatus
+1 on success (including no item to delete),
+2 on blocking user trust modification,
+3 on blocking system trust modification,
+any other value is invalid
+
+Output:
+pOSStatus: Receives the last OSStatus value..
+*/
+extern "C" int32_t
+AppleCryptoNative_X509StoreRemoveCertificate(CFTypeRef certOrIdentity, SecKeychainRef keychain, int32_t* pOSStatus);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_random.cpp b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_random.cpp
index 1095d57016..8115a62534 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_random.cpp
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_random.cpp
@@ -7,7 +7,7 @@
#include <CommonCrypto/CommonCrypto.h>
#include <CommonCrypto/CommonRandom.h>
-extern "C" int AppleCryptoNative_GetRandomBytes(uint8_t* pBuf, uint32_t cbBuf, int32_t* pkCCStatus)
+extern "C" int32_t AppleCryptoNative_GetRandomBytes(uint8_t* pBuf, uint32_t cbBuf, int32_t* pkCCStatus)
{
if (pBuf == nullptr || pkCCStatus == nullptr)
return -1;
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_random.h b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_random.h
index 70ef77dc04..db202c5397 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_random.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_random.h
@@ -11,4 +11,4 @@ Shims CCRandomGenerateBytes, putting the resulting CCRNGStatus value in pkCCStat
Returns 1 on success, 0 on system error (see pkCCStatus), -1 on input error.
*/
-extern "C" int AppleCryptoNative_GetRandomBytes(uint8_t* pBuf, uint32_t cbBuf, int32_t* pkCCStatus);
+extern "C" int32_t AppleCryptoNative_GetRandomBytes(uint8_t* pBuf, uint32_t cbBuf, int32_t* pkCCStatus);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_rsa.cpp b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_rsa.cpp
new file mode 100644
index 0000000000..e273175fe8
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_rsa.cpp
@@ -0,0 +1,269 @@
+// 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.
+
+#include "pal_rsa.h"
+
+static int32_t ExecuteCFDataTransform(
+ SecTransformRef xform, uint8_t* pbData, int32_t cbData, CFDataRef* pDataOut, CFErrorRef* pErrorOut);
+
+extern "C" int32_t AppleCryptoNative_RsaGenerateKey(
+ int32_t keySizeBits, SecKeychainRef tempKeychain, SecKeyRef* pPublicKey, SecKeyRef* pPrivateKey, int32_t* pOSStatus)
+{
+ if (pPublicKey != nullptr)
+ *pPublicKey = nullptr;
+ if (pPrivateKey != nullptr)
+ *pPrivateKey = nullptr;
+
+ if (pPublicKey == nullptr || pPrivateKey == nullptr || pOSStatus == nullptr)
+ return kErrorBadInput;
+ if (keySizeBits < 384 || keySizeBits > 16384)
+ return -2;
+
+ CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(nullptr, 2, &kCFTypeDictionaryKeyCallBacks, nullptr);
+
+ CFNumberRef cfKeySizeValue = CFNumberCreate(nullptr, kCFNumberIntType, &keySizeBits);
+ OSStatus status;
+
+ if (attributes != nullptr && cfKeySizeValue != nullptr)
+ {
+ CFDictionaryAddValue(attributes, kSecAttrKeyType, kSecAttrKeyTypeRSA);
+ CFDictionaryAddValue(attributes, kSecAttrKeySizeInBits, cfKeySizeValue);
+ CFDictionaryAddValue(attributes, kSecUseKeychain, tempKeychain);
+
+ status = SecKeyGeneratePair(attributes, pPublicKey, pPrivateKey);
+
+ if (status == noErr)
+ {
+ status = ExportImportKey(pPublicKey, kSecItemTypePublicKey);
+ }
+
+ if (status == noErr)
+ {
+ status = ExportImportKey(pPrivateKey, kSecItemTypePrivateKey);
+ }
+ }
+ else
+ {
+ status = errSecAllocate;
+ }
+
+ if (attributes != nullptr)
+ CFRelease(attributes);
+ if (cfKeySizeValue != nullptr)
+ CFRelease(cfKeySizeValue);
+
+ *pOSStatus = status;
+ return status == noErr;
+}
+
+static int32_t ExecuteOaepTransform(SecTransformRef xform,
+ uint8_t* pbData,
+ int32_t cbData,
+ PAL_HashAlgorithm algorithm,
+ CFDataRef* pDataOut,
+ CFErrorRef* pErrorOut)
+{
+ if (!SecTransformSetAttribute(xform, kSecPaddingKey, kSecPaddingOAEPKey, pErrorOut))
+ {
+ return kErrorSeeError;
+ }
+
+ // Documentation mentions kSecOAEPMGF1DigestAlgorithmAttributeName, but on the Apple platform
+ // "SHA2" is an algorithm and the size is encoded separately. Since there doesn't seem to be
+ // a second attribute to encode SHA2-256 vs SHA2-384, be limited to SHA-1.
+ if (algorithm != PAL_SHA1)
+ {
+ return kErrorUnknownAlgorithm;
+ }
+
+ return ExecuteCFDataTransform(xform, pbData, cbData, pDataOut, pErrorOut);
+}
+
+extern "C" int32_t AppleCryptoNative_RsaDecryptOaep(SecKeyRef privateKey,
+ uint8_t* pbData,
+ int32_t cbData,
+ PAL_HashAlgorithm mfgAlgorithm,
+ CFDataRef* pDecryptedOut,
+ CFErrorRef* pErrorOut)
+{
+ if (pDecryptedOut != nullptr)
+ *pDecryptedOut = nullptr;
+ if (pErrorOut != nullptr)
+ *pErrorOut = nullptr;
+
+ if (privateKey == nullptr || pbData == nullptr || cbData < 0 || pDecryptedOut == nullptr || pErrorOut == nullptr)
+ {
+ return kErrorBadInput;
+ }
+
+ int32_t ret = kErrorSeeError;
+ SecTransformRef decryptor = SecDecryptTransformCreate(privateKey, pErrorOut);
+
+ if (decryptor != nullptr)
+ {
+ if (*pErrorOut == nullptr)
+ {
+ ret = ExecuteOaepTransform(decryptor, pbData, cbData, mfgAlgorithm, pDecryptedOut, pErrorOut);
+ }
+
+ CFRelease(decryptor);
+ }
+
+ return ret;
+}
+
+extern "C" int32_t AppleCryptoNative_RsaDecryptPkcs(
+ SecKeyRef privateKey, uint8_t* pbData, int32_t cbData, CFDataRef* pDecryptedOut, CFErrorRef* pErrorOut)
+{
+ if (pDecryptedOut != nullptr)
+ *pDecryptedOut = nullptr;
+ if (pErrorOut != nullptr)
+ *pErrorOut = nullptr;
+
+ if (privateKey == nullptr || pbData == nullptr || cbData < 0 || pDecryptedOut == nullptr || pErrorOut == nullptr)
+ {
+ return kErrorBadInput;
+ }
+
+ int32_t ret = kErrorSeeError;
+ SecTransformRef decryptor = SecDecryptTransformCreate(privateKey, pErrorOut);
+
+ if (decryptor != nullptr)
+ {
+ if (*pErrorOut == nullptr)
+ {
+ ret = ExecuteCFDataTransform(decryptor, pbData, cbData, pDecryptedOut, pErrorOut);
+ }
+
+ CFRelease(decryptor);
+ }
+
+ return ret;
+}
+
+extern "C" int32_t AppleCryptoNative_RsaEncryptOaep(SecKeyRef publicKey,
+ uint8_t* pbData,
+ int32_t cbData,
+ PAL_HashAlgorithm mgfAlgorithm,
+ CFDataRef* pEncryptedOut,
+ CFErrorRef* pErrorOut)
+{
+ if (pEncryptedOut != nullptr)
+ *pEncryptedOut = nullptr;
+ if (pErrorOut != nullptr)
+ *pErrorOut = nullptr;
+
+ if (publicKey == nullptr || pbData == nullptr || cbData < 0 || pEncryptedOut == nullptr || pErrorOut == nullptr)
+ {
+ return kErrorBadInput;
+ }
+
+ int32_t ret = kErrorSeeError;
+ SecTransformRef encryptor = SecEncryptTransformCreate(publicKey, pErrorOut);
+
+ if (encryptor != nullptr)
+ {
+ if (*pErrorOut == nullptr)
+ {
+ ret = ExecuteOaepTransform(encryptor, pbData, cbData, mgfAlgorithm, pEncryptedOut, pErrorOut);
+ }
+
+ CFRelease(encryptor);
+ }
+
+ return ret;
+}
+
+extern "C" int32_t AppleCryptoNative_RsaEncryptPkcs(
+ SecKeyRef publicKey, uint8_t* pbData, int32_t cbData, CFDataRef* pEncryptedOut, CFErrorRef* pErrorOut)
+{
+ if (pEncryptedOut != nullptr)
+ *pEncryptedOut = nullptr;
+ if (pErrorOut != nullptr)
+ *pErrorOut = nullptr;
+
+ if (publicKey == nullptr || pbData == nullptr || cbData < 0 || pEncryptedOut == nullptr || pErrorOut == nullptr)
+ {
+ return kErrorBadInput;
+ }
+
+ int32_t ret = kErrorSeeError;
+ SecTransformRef encryptor = SecEncryptTransformCreate(publicKey, pErrorOut);
+
+ if (encryptor != nullptr)
+ {
+ if (*pErrorOut == nullptr)
+ {
+ ret = ExecuteCFDataTransform(encryptor, pbData, cbData, pEncryptedOut, pErrorOut);
+ }
+
+ CFRelease(encryptor);
+ }
+
+ return ret;
+}
+
+static int32_t ExecuteCFDataTransform(
+ SecTransformRef xform, uint8_t* pbData, int32_t cbData, CFDataRef* pDataOut, CFErrorRef* pErrorOut)
+{
+ if (xform == nullptr || pbData == nullptr || cbData < 0 || pDataOut == nullptr || pErrorOut == nullptr)
+ {
+ return kErrorBadInput;
+ }
+
+ *pDataOut = nullptr;
+ *pErrorOut = nullptr;
+
+ CFTypeRef xformOutput = nullptr;
+ CFDataRef cfData = nullptr;
+ int32_t ret = INT_MIN;
+
+ cfData = CFDataCreateWithBytesNoCopy(nullptr, pbData, cbData, kCFAllocatorNull);
+
+ if (cfData == nullptr)
+ {
+ // This probably means that there wasn't enough memory available, but no
+ // particular failure cases are described.
+ return kErrorUnknownState;
+ }
+
+ if (!SecTransformSetAttribute(xform, kSecTransformInputAttributeName, cfData, pErrorOut))
+ {
+ ret = kErrorSeeError;
+ goto cleanup;
+ }
+
+ xformOutput = SecTransformExecute(xform, pErrorOut);
+
+ if (xformOutput == nullptr || *pErrorOut != nullptr)
+ {
+ ret = kErrorSeeError;
+ goto cleanup;
+ }
+
+ if (CFGetTypeID(xformOutput) == CFDataGetTypeID())
+ {
+ CFDataRef cfDataOut = reinterpret_cast<CFDataRef>(const_cast<void*>(xformOutput));
+ CFRetain(cfDataOut);
+ *pDataOut = cfDataOut;
+ ret = 1;
+ }
+ else
+ {
+ ret = kErrorUnknownState;
+ }
+
+cleanup:
+ if (xformOutput != nullptr)
+ {
+ CFRelease(xformOutput);
+ }
+
+ if (cfData != nullptr)
+ {
+ CFRelease(cfData);
+ }
+
+ return ret;
+}
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_rsa.h b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_rsa.h
new file mode 100644
index 0000000000..cdc6b5c6e4
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_rsa.h
@@ -0,0 +1,61 @@
+// 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.
+
+#pragma once
+
+#include "pal_digest.h"
+#include "pal_seckey.h"
+
+#include <Security/Security.h>
+
+/*
+Generate a new RSA keypair with the specified key size, in bits.
+
+Returns 1 on success, 0 on failure. On failure, *pOSStatus should contain the OS reported error.
+*/
+extern "C" int32_t AppleCryptoNative_RsaGenerateKey(int32_t keySizeBits,
+ SecKeychainRef tempKeychain,
+ SecKeyRef* pPublicKey,
+ SecKeyRef* pPrivateKey,
+ int32_t* pOSStatus);
+
+/*
+Decrypt the contents of pbData using the provided privateKey under OAEP padding.
+
+Follows pal_seckey return conventions.
+*/
+extern "C" int32_t AppleCryptoNative_RsaDecryptOaep(SecKeyRef privateKey,
+ uint8_t* pbData,
+ int32_t cbData,
+ PAL_HashAlgorithm mfgAlgorithm,
+ CFDataRef* pDecryptedOut,
+ CFErrorRef* pErrorOut);
+
+/*
+Decrypt the contents of pbData using the provided privateKey under PKCS#1 padding.
+
+Follows pal_seckey return conventions.
+*/
+extern "C" int32_t AppleCryptoNative_RsaDecryptPkcs(
+ SecKeyRef privateKey, uint8_t* pbData, int32_t cbData, CFDataRef* pDecryptedOut, CFErrorRef* pErrorOut);
+
+/*
+Encrypt pbData for the provided publicKey using OAEP padding.
+
+Follows pal_seckey return conventions.
+*/
+extern "C" int32_t AppleCryptoNative_RsaEncryptOaep(SecKeyRef publicKey,
+ uint8_t* pbData,
+ int32_t cbData,
+ PAL_HashAlgorithm mgfAlgorithm,
+ CFDataRef* pEncryptedOut,
+ CFErrorRef* pErrorOut);
+
+/*
+Encrypt pbData for the provided publicKey using PKCS#1 padding.
+
+Follows pal_seckey return conventions.
+*/
+extern "C" int32_t AppleCryptoNative_RsaEncryptPkcs(
+ SecKeyRef publicKey, uint8_t* pbData, int32_t cbData, CFDataRef* pEncryptedOut, CFErrorRef* pErrorOut);
diff --git a/src/Common/src/System/Net/Shims/DBNull.cs b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_sec.cpp
index f8a458cf14..72b37d43f5 100644
--- a/src/Common/src/System/Net/Shims/DBNull.cs
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_sec.cpp
@@ -2,12 +2,9 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-// IMPORTANT: This is temporary code.
+#include "pal_sec.h"
-namespace System
+extern "C" CFStringRef AppleCryptoNative_SecCopyErrorMessageString(int32_t osStatus)
{
- internal class DBNull
- {
- public static readonly DBNull Value = new DBNull();
- }
+ return SecCopyErrorMessageString(osStatus, nullptr);
}
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_secimportexport.h b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_sec.h
index b3ff016e7b..c00b3f74a0 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_secimportexport.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_sec.h
@@ -1,3 +1,4 @@
+
// 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.
@@ -7,3 +8,10 @@
#include "pal_types.h"
#include <Security/Security.h>
+
+/*
+Get an error message for an OSStatus error from the security library.
+
+Returns NULL if no message is available for the code.
+*/
+extern "C" CFStringRef AppleCryptoNative_SecCopyErrorMessageString(OSStatus osStatus);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_seckey.cpp b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_seckey.cpp
new file mode 100644
index 0000000000..c88d30cb65
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_seckey.cpp
@@ -0,0 +1,186 @@
+// 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.
+
+#include "pal_seckey.h"
+
+extern "C" int32_t AppleCryptoNative_SecKeyExport(
+ SecKeyRef pKey, int32_t exportPrivate, CFStringRef cfExportPassphrase, CFDataRef* ppDataOut, int32_t* pOSStatus)
+{
+ if (ppDataOut != nullptr)
+ *ppDataOut = nullptr;
+ if (pOSStatus != nullptr)
+ *pOSStatus = noErr;
+
+ if (pKey == nullptr || ppDataOut == nullptr || pOSStatus == nullptr)
+ {
+ return kErrorBadInput;
+ }
+
+ SecExternalFormat dataFormat = kSecFormatOpenSSL;
+ SecItemImportExportKeyParameters keyParams = {};
+ keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+
+ if (exportPrivate)
+ {
+ if (cfExportPassphrase == nullptr)
+ {
+ return kErrorBadInput;
+ }
+
+ keyParams.passphrase = cfExportPassphrase;
+ dataFormat = kSecFormatWrappedPKCS8;
+ }
+
+ *pOSStatus = SecItemExport(pKey, dataFormat, 0, &keyParams, ppDataOut);
+
+ return (*pOSStatus == noErr);
+}
+
+extern "C" int32_t AppleCryptoNative_SecKeyImportEphemeral(
+ uint8_t* pbKeyBlob, int32_t cbKeyBlob, int32_t isPrivateKey, SecKeyRef* ppKeyOut, int32_t* pOSStatus)
+{
+ if (ppKeyOut != nullptr)
+ *ppKeyOut = nullptr;
+ if (pOSStatus != nullptr)
+ *pOSStatus = noErr;
+
+ if (pbKeyBlob == nullptr || cbKeyBlob < 0 || isPrivateKey < 0 || isPrivateKey > 1 || ppKeyOut == nullptr ||
+ pOSStatus == nullptr)
+ {
+ return kErrorBadInput;
+ }
+
+ int32_t ret = 0;
+ CFDataRef cfData = CFDataCreateWithBytesNoCopy(nullptr, pbKeyBlob, cbKeyBlob, kCFAllocatorNull);
+
+ SecExternalFormat dataFormat = kSecFormatOpenSSL;
+ SecExternalFormat actualFormat = dataFormat;
+
+ SecExternalItemType itemType = isPrivateKey ? kSecItemTypePrivateKey : kSecItemTypePublicKey;
+ SecExternalItemType actualType = itemType;
+
+ CFIndex itemCount;
+ CFArrayRef outItems = nullptr;
+ CFTypeRef outItem = nullptr;
+
+ *pOSStatus = SecItemImport(cfData, nullptr, &actualFormat, &actualType, 0, nullptr, nullptr, &outItems);
+
+ if (*pOSStatus != noErr)
+ {
+ ret = 0;
+ goto cleanup;
+ }
+
+ if (actualFormat != dataFormat || actualType != itemType)
+ {
+ ret = -2;
+ goto cleanup;
+ }
+
+ if (outItems == nullptr)
+ {
+ ret = -3;
+ goto cleanup;
+ }
+
+ itemCount = CFArrayGetCount(outItems);
+
+ if (itemCount == 0)
+ {
+ ret = -4;
+ goto cleanup;
+ }
+
+ if (itemCount > 1)
+ {
+ ret = -5;
+ goto cleanup;
+ }
+
+ outItem = CFArrayGetValueAtIndex(outItems, 0);
+
+ if (outItem == nullptr)
+ {
+ ret = -6;
+ goto cleanup;
+ }
+
+ if (CFGetTypeID(outItem) != SecKeyGetTypeID())
+ {
+ ret = -7;
+ goto cleanup;
+ }
+
+ CFRetain(outItem);
+ *ppKeyOut = reinterpret_cast<SecKeyRef>(const_cast<void*>(outItem));
+ ret = 1;
+
+cleanup:
+ if (outItems != nullptr)
+ {
+ CFRelease(outItems);
+ }
+
+ CFRelease(cfData);
+ return ret;
+}
+
+extern "C" uint64_t AppleCryptoNative_SecKeyGetSimpleKeySizeInBytes(SecKeyRef publicKey)
+{
+ if (publicKey == nullptr)
+ {
+ return 0;
+ }
+
+ return SecKeyGetBlockSize(publicKey);
+}
+
+OSStatus ExportImportKey(SecKeyRef* key, SecExternalItemType type)
+{
+ SecExternalFormat dataFormat = kSecFormatOpenSSL;
+ CFDataRef exportData = nullptr;
+
+ SecItemImportExportKeyParameters keyParams = {};
+ keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+ keyParams.passphrase = CFSTR("ExportImportPassphrase");
+
+ OSStatus status = SecItemExport(*key, dataFormat, 0, &keyParams, &exportData);
+ CFRelease(*key);
+ *key = nullptr;
+
+ SecExternalFormat actualFormat = dataFormat;
+ SecExternalItemType actualType = type;
+ CFArrayRef outItems = nullptr;
+
+ if (status == noErr)
+ {
+ status = SecItemImport(exportData, nullptr, &actualFormat, &actualType, 0, nullptr, nullptr, &outItems);
+ }
+
+ CFRelease(exportData);
+ exportData = nullptr;
+
+ CFRelease(keyParams.passphrase);
+ keyParams.passphrase = nullptr;
+
+ if (status == noErr && outItems != nullptr)
+ {
+ CFIndex count = CFArrayGetCount(outItems);
+
+ if (count == 1)
+ {
+ CFTypeRef outItem = CFArrayGetValueAtIndex(outItems, 0);
+
+ if (CFGetTypeID(outItem) == SecKeyGetTypeID())
+ {
+ CFRetain(outItem);
+ *key = reinterpret_cast<SecKeyRef>(const_cast<void*>(outItem));
+
+ return noErr;
+ }
+ }
+ }
+
+ return errSecBadReq;
+}
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_seckey.h b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_seckey.h
new file mode 100644
index 0000000000..1f32f4e705
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_seckey.h
@@ -0,0 +1,66 @@
+// 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.
+
+#pragma once
+
+#include "pal_types.h"
+
+#include <Security/Security.h>
+
+// Unless another interpretation is "obvious", pal_seckey functions return 1 on success.
+// functions which represent a boolean return 0 on "successful false"
+// otherwise functions will return one of the following return values:
+static const int32_t kErrorBadInput = -1;
+static const int32_t kErrorSeeError = -2;
+static const int32_t kErrorUnknownAlgorithm = -3;
+static const int32_t kErrorUnknownState = -4;
+
+/*
+Export a key object.
+
+Public keys are exported using the "OpenSSL" format option, which means, essentially,
+"whatever format the openssl CLI would use for this algorithm by default".
+
+Private keys are exported using the "Wrapped PKCS#8" format. These formats are available via
+`openssl pkcs8 -topk8 ...`. While the PKCS#8 container is the same for all key types, the
+payload is algorithm-dependent (though identified by the PKCS#8 wrapper).
+
+An export passphrase is required for private keys, and ignored for public keys.
+
+Follows pal_seckey return conventions.
+*/
+extern "C" int32_t AppleCryptoNative_SecKeyExport(
+ SecKeyRef pKey, int32_t exportPrivate, CFStringRef cfExportPassphrase, CFDataRef* ppDataOut, int32_t* pOSStatus);
+
+/*
+Import a key from a key blob.
+
+Imports are always done using the "OpenSSL" format option, which means the format used for an
+unencrypted private key via the openssl CLI verb of the algorithm being imported.
+
+For public keys the "OpenSSL" format is NOT the format used by the openssl CLI for that algorithm,
+but is in fact the X.509 SubjectPublicKeyInfo structure.
+
+Returns 1 on success, 0 on failure (*pOSStatus should be set) and negative numbers for various
+state machine errors.
+*/
+extern "C" int32_t AppleCryptoNative_SecKeyImportEphemeral(
+ uint8_t* pbKeyBlob, int32_t cbKeyBlob, int32_t isPrivateKey, SecKeyRef* ppKeyOut, int32_t* pOSStatus);
+
+/*
+For RSA and DSA this function returns the number of bytes in "the key", which corresponds to
+the length of n/Modulus for RSA and for P in DSA.
+
+For ECC the value should not be used.
+
+0 is returned for invalid inputs.
+*/
+extern "C" uint64_t AppleCryptoNative_SecKeyGetSimpleKeySizeInBytes(SecKeyRef publicKey);
+
+/*
+Export a key and re-import it to the NULL keychain.
+
+Only internal callers are expected.
+*/
+OSStatus ExportImportKey(SecKeyRef* key, SecExternalItemType type);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_signverify.cpp b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_signverify.cpp
new file mode 100644
index 0000000000..e47969929a
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_signverify.cpp
@@ -0,0 +1,287 @@
+// 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.
+
+#include "pal_signverify.h"
+
+static int32_t ExecuteSignTransform(SecTransformRef signer, CFDataRef* pSignatureOut, CFErrorRef* pErrorOut);
+static int32_t ExecuteVerifyTransform(SecTransformRef verifier, CFErrorRef* pErrorOut);
+
+static int32_t ConfigureSignVerifyTransform(
+ SecTransformRef xform, CFDataRef cfDataHash, PAL_HashAlgorithm, bool useDigestAlgorithm, CFErrorRef* pErrorOut);
+
+static int32_t GenerateSignature(SecKeyRef privateKey,
+ uint8_t* pbDataHash,
+ int32_t cbDataHash,
+ PAL_HashAlgorithm hashAlgorithm,
+ bool useHashAlgorithm,
+ CFDataRef* pSignatureOut,
+ CFErrorRef* pErrorOut)
+{
+ if (pSignatureOut != nullptr)
+ *pSignatureOut = nullptr;
+ if (pErrorOut != nullptr)
+ *pErrorOut = nullptr;
+
+ if (privateKey == nullptr || pbDataHash == nullptr || cbDataHash < 0 || pSignatureOut == nullptr ||
+ pErrorOut == nullptr)
+ {
+ return kErrorBadInput;
+ }
+
+ CFDataRef dataHash = CFDataCreateWithBytesNoCopy(nullptr, pbDataHash, cbDataHash, kCFAllocatorNull);
+
+ if (dataHash == nullptr)
+ {
+ return kErrorUnknownState;
+ }
+
+ int32_t ret = kErrorSeeError;
+ SecTransformRef signer = SecSignTransformCreate(privateKey, pErrorOut);
+
+ if (signer != nullptr)
+ {
+ if (*pErrorOut == nullptr)
+ {
+ if (ConfigureSignVerifyTransform(signer, dataHash, hashAlgorithm, useHashAlgorithm, pErrorOut))
+ {
+ ret = ExecuteSignTransform(signer, pSignatureOut, pErrorOut);
+ }
+ }
+
+ CFRelease(signer);
+ }
+
+ CFRelease(dataHash);
+ return ret;
+}
+
+extern "C" int32_t AppleCryptoNative_GenerateSignature(
+ SecKeyRef privateKey, uint8_t* pbDataHash, int32_t cbDataHash, CFDataRef* pSignatureOut, CFErrorRef* pErrorOut)
+{
+ return GenerateSignature(privateKey, pbDataHash, cbDataHash, PAL_Unknown, false, pSignatureOut, pErrorOut);
+}
+
+extern "C" int32_t AppleCryptoNative_GenerateSignatureWithHashAlgorithm(SecKeyRef privateKey,
+ uint8_t* pbDataHash,
+ int32_t cbDataHash,
+ PAL_HashAlgorithm hashAlgorithm,
+ CFDataRef* pSignatureOut,
+ CFErrorRef* pErrorOut)
+{
+ return GenerateSignature(privateKey, pbDataHash, cbDataHash, hashAlgorithm, true, pSignatureOut, pErrorOut);
+}
+
+static int32_t VerifySignature(SecKeyRef publicKey,
+ uint8_t* pbDataHash,
+ int32_t cbDataHash,
+ uint8_t* pbSignature,
+ int32_t cbSignature,
+ PAL_HashAlgorithm hashAlgorithm,
+ bool useHashAlgorithm,
+ CFErrorRef* pErrorOut)
+{
+ if (pErrorOut != nullptr)
+ *pErrorOut = nullptr;
+
+ if (publicKey == nullptr || pbDataHash == nullptr || cbDataHash < 0 || pbSignature == nullptr || cbSignature < 0 ||
+ pErrorOut == nullptr)
+ return kErrorBadInput;
+
+ CFDataRef dataHash = CFDataCreateWithBytesNoCopy(nullptr, pbDataHash, cbDataHash, kCFAllocatorNull);
+
+ if (dataHash == nullptr)
+ {
+ return kErrorUnknownState;
+ }
+
+ CFDataRef signature = CFDataCreateWithBytesNoCopy(nullptr, pbSignature, cbSignature, kCFAllocatorNull);
+
+ if (signature == nullptr)
+ {
+ CFRelease(dataHash);
+ return kErrorUnknownState;
+ }
+
+ int32_t ret = kErrorSeeError;
+ SecTransformRef verifier = SecVerifyTransformCreate(publicKey, signature, pErrorOut);
+
+ if (verifier != nullptr)
+ {
+ if (*pErrorOut == nullptr)
+ {
+ if (ConfigureSignVerifyTransform(verifier, dataHash, hashAlgorithm, useHashAlgorithm, pErrorOut))
+ {
+ ret = ExecuteVerifyTransform(verifier, pErrorOut);
+ }
+ }
+
+ CFRelease(verifier);
+ }
+
+ CFRelease(dataHash);
+ CFRelease(signature);
+
+ return ret;
+}
+
+extern "C" int32_t AppleCryptoNative_VerifySignatureWithHashAlgorithm(SecKeyRef publicKey,
+ uint8_t* pbDataHash,
+ int32_t cbDataHash,
+ uint8_t* pbSignature,
+ int32_t cbSignature,
+ PAL_HashAlgorithm hashAlgorithm,
+ CFErrorRef* pErrorOut)
+{
+ return VerifySignature(publicKey, pbDataHash, cbDataHash, pbSignature, cbSignature, hashAlgorithm, true, pErrorOut);
+}
+
+extern "C" int32_t AppleCryptoNative_VerifySignature(SecKeyRef publicKey,
+ uint8_t* pbDataHash,
+ int32_t cbDataHash,
+ uint8_t* pbSignature,
+ int32_t cbSignature,
+ CFErrorRef* pErrorOut)
+{
+ return VerifySignature(publicKey, pbDataHash, cbDataHash, pbSignature, cbSignature, PAL_Unknown, false, pErrorOut);
+}
+
+static int32_t ExecuteSignTransform(SecTransformRef signer, CFDataRef* pSignatureOut, CFErrorRef* pErrorOut)
+{
+ assert(signer != nullptr);
+ assert(pSignatureOut != nullptr);
+ assert(pErrorOut != nullptr);
+
+ int32_t ret = INT_MIN;
+ CFTypeRef signerResponse = SecTransformExecute(signer, pErrorOut);
+ CFDataRef signature = nullptr;
+
+ if (signerResponse == nullptr || *pErrorOut != nullptr)
+ {
+ ret = kErrorSeeError;
+ goto cleanup;
+ }
+
+ if (CFGetTypeID(signerResponse) != CFDataGetTypeID())
+ {
+ ret = kErrorUnknownState;
+ goto cleanup;
+ }
+
+ signature = reinterpret_cast<CFDataRef>(const_cast<void*>(signerResponse));
+
+ if (CFDataGetLength(signature) > 0)
+ {
+ // We're going to call CFRelease in cleanup, so this keeps it alive
+ // to be interpreted by the managed code.
+ CFRetain(signature);
+ *pSignatureOut = signature;
+ ret = 1;
+ }
+ else
+ {
+ ret = kErrorUnknownState;
+ *pSignatureOut = nullptr;
+ }
+
+cleanup:
+ if (signerResponse != nullptr)
+ {
+ CFRelease(signerResponse);
+ }
+
+ return ret;
+}
+
+static int32_t ExecuteVerifyTransform(SecTransformRef verifier, CFErrorRef* pErrorOut)
+{
+ assert(verifier != nullptr);
+ assert(pErrorOut != nullptr);
+
+ int32_t ret = kErrorSeeError;
+ CFTypeRef verifierResponse = SecTransformExecute(verifier, pErrorOut);
+
+ if (verifierResponse != nullptr)
+ {
+ if (*pErrorOut == nullptr)
+ {
+ ret = (verifierResponse == kCFBooleanTrue);
+ }
+
+ CFRelease(verifierResponse);
+ }
+
+ return ret;
+}
+
+static int32_t ConfigureSignVerifyTransform(SecTransformRef xform,
+ CFDataRef cfDataHash,
+ PAL_HashAlgorithm hashAlgorithm,
+ bool includeHashAlgorithm,
+ CFErrorRef* pErrorOut)
+{
+ if (!SecTransformSetAttribute(xform, kSecInputIsAttributeName, kSecInputIsDigest, pErrorOut))
+ {
+ return 0;
+ }
+
+ if (!SecTransformSetAttribute(xform, kSecTransformInputAttributeName, cfDataHash, pErrorOut))
+ {
+ return 0;
+ }
+
+ if (includeHashAlgorithm)
+ {
+ CFStringRef cfHashName = nullptr;
+ int32_t hashSize = 0;
+
+ switch (hashAlgorithm)
+ {
+ case PAL_MD5:
+ cfHashName = kSecDigestMD5;
+ break;
+ case PAL_SHA1:
+ cfHashName = kSecDigestSHA1;
+ break;
+ case PAL_SHA256:
+ cfHashName = kSecDigestSHA2;
+ hashSize = 256;
+ break;
+ case PAL_SHA384:
+ cfHashName = kSecDigestSHA2;
+ hashSize = 384;
+ break;
+ case PAL_SHA512:
+ cfHashName = kSecDigestSHA2;
+ hashSize = 512;
+ break;
+ default:
+ return kErrorUnknownAlgorithm;
+ }
+
+ if (!SecTransformSetAttribute(xform, kSecDigestTypeAttribute, cfHashName, pErrorOut))
+ {
+ return 0;
+ }
+
+ if (hashSize != 0)
+ {
+ CFNumberRef cfHashSize = CFNumberCreate(nullptr, kCFNumberIntType, &hashSize);
+
+ if (cfHashSize == nullptr)
+ {
+ return 0;
+ }
+
+ if (!SecTransformSetAttribute(xform, kSecDigestLengthAttribute, cfHashSize, pErrorOut))
+ {
+ CFRelease(cfHashSize);
+ return 0;
+ }
+
+ CFRelease(cfHashSize);
+ }
+ }
+
+ return 1;
+}
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_signverify.h b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_signverify.h
new file mode 100644
index 0000000000..164bb4d611
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_signverify.h
@@ -0,0 +1,57 @@
+// 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.
+
+#pragma once
+
+#include "pal_digest.h"
+#include "pal_seckey.h"
+
+#include <Security/Security.h>
+
+/*
+Generate a signature for algorithms which require only the data hash blob, like DSA and ECDSA.
+
+Follows pal_seckey return conventions.
+*/
+extern "C" int32_t AppleCryptoNative_GenerateSignature(
+ SecKeyRef privateKey, uint8_t* pbDataHash, int32_t cbDataHash, CFDataRef* pSignatureOut, CFErrorRef* pErrorOut);
+
+/*
+Generate a signature for algorithms which require the pair of (dataHash, algorithmId), like RSA.
+
+Follows pal_seckey return conventions.
+*/
+extern "C" int32_t AppleCryptoNative_GenerateSignatureWithHashAlgorithm(SecKeyRef privateKey,
+ uint8_t* pbDataHash,
+ int32_t cbDataHash,
+ PAL_HashAlgorithm hashAlgorithm,
+ CFDataRef* pSignatureOut,
+ CFErrorRef* pErrorOut);
+
+/*
+Verify a signature for algorithms which only require the data hash blob, like DSA and ECDSA.
+
+Returns 1 when the signature is correct, 0 when it is incorrect, and otherwise
+follows pal_seckey return conventions.
+*/
+extern "C" int32_t AppleCryptoNative_VerifySignatureWithHashAlgorithm(SecKeyRef publicKey,
+ uint8_t* pbDataHash,
+ int32_t cbDataHash,
+ uint8_t* pbSignature,
+ int32_t cbSignature,
+ PAL_HashAlgorithm hashAlgorithm,
+ CFErrorRef* pErrorOut);
+
+/*
+Verify a signature for algorithms which require the pair of (dataHash, algorithmId), like RSA.
+
+Returns 1 when the signature is correct, 0 when it is incorrect, and otherwise
+follows pal_seckey return conventions.
+*/
+extern "C" int32_t AppleCryptoNative_VerifySignature(SecKeyRef publicKey,
+ uint8_t* pbDataHash,
+ int32_t cbDataHash,
+ uint8_t* pbSignature,
+ int32_t cbSignature,
+ CFErrorRef* pErrorOut);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.cpp b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.cpp
new file mode 100644
index 0000000000..dca5c7a013
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.cpp
@@ -0,0 +1,395 @@
+// 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.
+
+#include "pal_ssl.h"
+
+extern "C" SSLContextRef AppleCryptoNative_SslCreateContext(int32_t isServer)
+{
+ if (isServer != 0 && isServer != 1)
+ return nullptr;
+
+ return SSLCreateContext(nullptr, isServer ? kSSLServerSide : kSSLClientSide, kSSLStreamType);
+}
+
+extern "C" int32_t AppleCryptoNative_SslSetAcceptClientCert(SSLContextRef sslContext)
+{
+ // NULL and other illegal values are handled by the underlying API
+ return SSLSetClientSideAuthenticate(sslContext, kTryAuthenticate);
+}
+
+static SSLProtocol PalSslProtocolToSslProtocol(PAL_SslProtocol palProtocolId)
+{
+ switch (palProtocolId)
+ {
+ case PAL_SslProtocol_Tls12:
+ return kTLSProtocol12;
+ case PAL_SslProtocol_Tls11:
+ return kTLSProtocol11;
+ case PAL_SslProtocol_Tls10:
+ return kTLSProtocol1;
+ case PAL_SslProtocol_Ssl3:
+ return kSSLProtocol3;
+ case PAL_SslProtocol_Ssl2:
+ return kSSLProtocol2;
+ case PAL_SslProtocol_None:
+ default:
+ return kSSLProtocolUnknown;
+ }
+}
+
+extern "C" int32_t AppleCryptoNative_SslSetMinProtocolVersion(SSLContextRef sslContext, PAL_SslProtocol sslProtocol)
+{
+ SSLProtocol protocol = PalSslProtocolToSslProtocol(sslProtocol);
+
+ if (protocol == kSSLProtocolUnknown)
+ return errSecParam;
+
+ // NULL and other illegal values are handled by the underlying API
+ return SSLSetProtocolVersionMin(sslContext, protocol);
+}
+
+extern "C" int32_t AppleCryptoNative_SslSetMaxProtocolVersion(SSLContextRef sslContext, PAL_SslProtocol sslProtocol)
+{
+ SSLProtocol protocol = PalSslProtocolToSslProtocol(sslProtocol);
+
+ if (protocol == kSSLProtocolUnknown)
+ return errSecParam;
+
+ // NULL and other illegal values are handled by the underlying API
+ return SSLSetProtocolVersionMax(sslContext, protocol);
+}
+
+extern "C" int32_t
+AppleCryptoNative_SslCopyCertChain(SSLContextRef sslContext, SecTrustRef* pChainOut, int32_t* pOSStatus)
+{
+ if (pChainOut != nullptr)
+ *pChainOut = nullptr;
+ if (pOSStatus != nullptr)
+ *pOSStatus = noErr;
+
+ if (sslContext == nullptr || pChainOut == nullptr || pOSStatus == nullptr)
+ return -1;
+
+ *pOSStatus = SSLCopyPeerTrust(sslContext, pChainOut);
+ return *pOSStatus == noErr;
+}
+
+extern "C" int32_t
+AppleCryptoNative_SslCopyCADistinguishedNames(SSLContextRef sslContext, CFArrayRef* pArrayOut, int32_t* pOSStatus)
+{
+ if (pArrayOut != nullptr)
+ *pArrayOut = nullptr;
+ if (pOSStatus != nullptr)
+ *pOSStatus = noErr;
+
+ if (sslContext == nullptr || pArrayOut == nullptr || pOSStatus == nullptr)
+ return -1;
+
+ *pOSStatus = SSLCopyDistinguishedNames(sslContext, pArrayOut);
+
+ return *pOSStatus == noErr;
+}
+
+static int32_t AppleCryptoNative_SslSetSessionOption(SSLContextRef sslContext,
+ SSLSessionOption option,
+ int32_t value,
+ int32_t* pOSStatus)
+{
+ if (sslContext == nullptr)
+ return -1;
+
+ if (value != 0 && value != 1)
+ return -2;
+
+ *pOSStatus = SSLSetSessionOption(sslContext, option, !!value);
+
+ return *pOSStatus == noErr;
+}
+
+extern "C" int32_t
+AppleCryptoNative_SslSetBreakOnServerAuth(SSLContextRef sslContext, int32_t setBreak, int32_t* pOSStatus)
+{
+ return AppleCryptoNative_SslSetSessionOption(sslContext, kSSLSessionOptionBreakOnServerAuth, setBreak, pOSStatus);
+}
+
+extern "C" int32_t
+AppleCryptoNative_SslSetBreakOnClientAuth(SSLContextRef sslContext, int32_t setBreak, int32_t* pOSStatus)
+{
+ return AppleCryptoNative_SslSetSessionOption(sslContext, kSSLSessionOptionBreakOnClientAuth, setBreak, pOSStatus);
+}
+
+extern "C" int32_t AppleCryptoNative_SslSetCertificate(SSLContextRef sslContext, CFArrayRef certRefs)
+{
+ // The underlying call handles NULL inputs, so just pass it through
+ return SSLSetCertificate(sslContext, certRefs);
+}
+
+extern "C" int32_t AppleCryptoNative_SslSetTargetName(SSLContextRef sslContext,
+ const char* pszTargetName,
+ int32_t cbTargetName,
+ int32_t* pOSStatus)
+{
+ if (pOSStatus != nullptr)
+ *pOSStatus = noErr;
+
+ if (sslContext == nullptr || pszTargetName == nullptr || pOSStatus == nullptr)
+ return -1;
+
+ if (cbTargetName < 0)
+ return -2;
+
+ size_t currentLength;
+ *pOSStatus = SSLGetPeerDomainNameLength(sslContext, &currentLength);
+
+ // We'll end up walking down the path that sets the hostname more than once during
+ // the handshake dance. But once the handshake starts Secure Transport isn't willing to
+ // listen to this. So, if we've already set it, don't set it again.
+ if (*pOSStatus == noErr && currentLength == 0)
+ {
+ *pOSStatus = SSLSetPeerDomainName(sslContext, pszTargetName, static_cast<size_t>(cbTargetName));
+ }
+
+ return *pOSStatus == noErr;
+}
+
+extern "C" int32_t
+AppleCryptoNative_SslSetIoCallbacks(SSLContextRef sslContext, SSLReadFunc readFunc, SSLWriteFunc writeFunc)
+{
+ return SSLSetIOFuncs(sslContext, readFunc, writeFunc);
+}
+
+extern "C" PAL_TlsHandshakeState AppleCryptoNative_SslHandshake(SSLContextRef sslContext)
+{
+ if (sslContext == nullptr)
+ return PAL_TlsHandshakeState_Unknown;
+
+ OSStatus osStatus = SSLHandshake(sslContext);
+
+ switch (osStatus)
+ {
+ case noErr:
+ return PAL_TlsHandshakeState_Complete;
+ case errSSLWouldBlock:
+ return PAL_TlsHandshakeState_WouldBlock;
+ case errSSLServerAuthCompleted:
+ return PAL_TlsHandshakeState_ServerAuthCompleted;
+ default:
+ return osStatus;
+ }
+}
+
+static PAL_TlsIo OSStatusToPAL_TlsIo(OSStatus status)
+{
+ switch (status)
+ {
+ case noErr:
+ return PAL_TlsIo_Success;
+ case errSSLWouldBlock:
+ return PAL_TlsIo_WouldBlock;
+ case errSSLClosedGraceful:
+ return PAL_TlsIo_ClosedGracefully;
+ default:
+ return status;
+ }
+}
+
+extern "C" PAL_TlsIo
+AppleCryptoNative_SslWrite(SSLContextRef sslContext, const uint8_t* buf, uint32_t bufLen, uint32_t* bytesWritten)
+{
+ if (bytesWritten == nullptr)
+ return PAL_TlsIo_Unknown;
+
+ size_t expected = static_cast<size_t>(bufLen);
+ size_t totalWritten;
+
+ OSStatus status = SSLWrite(sslContext, buf, expected, &totalWritten);
+
+ if (status != noErr)
+ {
+ *bytesWritten = static_cast<uint32_t>(totalWritten);
+ return OSStatusToPAL_TlsIo(status);
+ }
+
+ return PAL_TlsIo_Success;
+}
+
+extern "C" PAL_TlsIo
+AppleCryptoNative_SslRead(SSLContextRef sslContext, uint8_t* buf, uint32_t bufLen, uint32_t* written)
+{
+ if (written == nullptr)
+ return PAL_TlsIo_Unknown;
+
+ size_t writtenSize = 0;
+ size_t bufSize = static_cast<size_t>(bufLen);
+
+ OSStatus status = SSLRead(sslContext, buf, bufSize, &writtenSize);
+
+ if (writtenSize > UINT_MAX)
+ {
+ // This shouldn't happen, because we passed a uint32_t as the initial buffer size.
+ // But, just in case it does, report back that we're no longer in a known state.
+ return PAL_TlsIo_Unknown;
+ }
+
+ *written = static_cast<uint32_t>(writtenSize);
+
+ if (writtenSize == 0 && status == errSSLWouldBlock)
+ {
+ SSLSessionState state = {};
+ OSStatus localStatus = SSLGetSessionState(sslContext, &state);
+
+ if (localStatus == noErr && state == kSSLHandshake)
+ {
+ return PAL_TlsIo_Renegotiate;
+ }
+ }
+
+ return OSStatusToPAL_TlsIo(status);
+}
+
+extern "C" int32_t
+AppleCryptoNative_SslIsHostnameMatch(SSLContextRef sslContext, CFStringRef cfHostname, CFDateRef notBefore)
+{
+ if (sslContext == nullptr || notBefore == nullptr)
+ return -1;
+ if (cfHostname == nullptr)
+ return -2;
+
+ SecPolicyRef sslPolicy = SecPolicyCreateSSL(true, cfHostname);
+
+ if (sslPolicy == nullptr)
+ return -3;
+
+ CFMutableArrayRef certs = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+
+ if (certs == nullptr)
+ return -4;
+
+ SecTrustRef existingTrust = nullptr;
+ OSStatus osStatus = SSLCopyPeerTrust(sslContext, &existingTrust);
+
+ if (osStatus != noErr)
+ {
+ CFRelease(certs);
+ return -5;
+ }
+
+ CFMutableArrayRef anchors = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+
+ if (anchors == nullptr)
+ {
+ CFRelease(certs);
+ return -6;
+ }
+
+ CFIndex count = SecTrustGetCertificateCount(existingTrust);
+
+ for (CFIndex i = 0; i < count; i++)
+ {
+ SecCertificateRef item = SecTrustGetCertificateAtIndex(existingTrust, i);
+ CFArrayAppendValue(certs, item);
+
+ // Copy the EE cert into the anchors set, this will make the chain part
+ // always return true.
+ if (i == 0)
+ {
+ CFArrayAppendValue(anchors, item);
+ }
+ }
+
+ SecTrustRef trust = nullptr;
+ osStatus = SecTrustCreateWithCertificates(certs, sslPolicy, &trust);
+ int32_t ret = INT_MIN;
+
+ if (osStatus == noErr)
+ {
+ osStatus = SecTrustSetAnchorCertificates(trust, anchors);
+ }
+
+ if (osStatus == noErr)
+ {
+ osStatus = SecTrustSetVerifyDate(trust, notBefore);
+ }
+
+ if (osStatus == noErr)
+ {
+ SecTrustResultType trustResult = {};
+
+ osStatus = SecTrustEvaluate(trust, &trustResult);
+
+ if (osStatus != noErr)
+ {
+ ret = -7;
+ }
+ else if (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed)
+ {
+ ret = 1;
+ }
+ else if (trustResult == kSecTrustResultDeny || trustResult == kSecTrustResultRecoverableTrustFailure)
+ {
+ ret = 0;
+ }
+ else
+ {
+ ret = -8;
+ }
+ }
+
+ if (trust != nullptr)
+ CFRelease(trust);
+
+ if (certs != nullptr)
+ CFRelease(certs);
+
+ if (anchors != nullptr)
+ CFRelease(anchors);
+
+ CFRelease(sslPolicy);
+ return ret;
+}
+
+extern "C" int32_t AppleCryptoNative_SslShutdown(SSLContextRef sslContext)
+{
+ return SSLClose(sslContext);
+}
+
+extern "C" int32_t AppleCryptoNative_SslGetProtocolVersion(SSLContextRef sslContext, PAL_SslProtocol* pProtocol)
+{
+ if (pProtocol != nullptr)
+ *pProtocol = 0;
+
+ if (sslContext == nullptr || pProtocol == nullptr)
+ return errSecParam;
+
+ SSLProtocol protocol = kSSLProtocolUnknown;
+ OSStatus osStatus = SSLGetNegotiatedProtocolVersion(sslContext, &protocol);
+
+ if (osStatus == noErr)
+ {
+ PAL_SslProtocol matchedProtocol = PAL_SslProtocol_None;
+
+ if (protocol == kTLSProtocol12)
+ matchedProtocol = PAL_SslProtocol_Tls12;
+ else if (protocol == kTLSProtocol11)
+ matchedProtocol = PAL_SslProtocol_Tls11;
+ else if (protocol == kTLSProtocol1)
+ matchedProtocol = PAL_SslProtocol_Tls10;
+ else if (protocol == kSSLProtocol3)
+ matchedProtocol = PAL_SslProtocol_Ssl3;
+ else if (protocol == kSSLProtocol2)
+ matchedProtocol = PAL_SslProtocol_Ssl2;
+
+ *pProtocol = matchedProtocol;
+ }
+
+ return osStatus;
+}
+
+extern "C" int32_t AppleCryptoNative_SslGetCipherSuite(SSLContextRef sslContext, uint32_t* pCipherSuiteOut)
+{
+ if (pCipherSuiteOut == nullptr)
+ *pCipherSuiteOut = 0;
+
+ return SSLGetNegotiatedCipher(sslContext, pCipherSuiteOut);
+}
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.h b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.h
new file mode 100644
index 0000000000..adc4754c41
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_ssl.h
@@ -0,0 +1,219 @@
+// 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.
+
+#pragma once
+
+#include <Security/Security.h>
+
+enum
+{
+ PAL_TlsHandshakeState_Unknown = 0,
+ PAL_TlsHandshakeState_Complete = 1,
+ PAL_TlsHandshakeState_WouldBlock = 2,
+ PAL_TlsHandshakeState_ServerAuthCompleted = 3,
+ PAL_TlsHandshakeState_ClientAuthCompleted = 4,
+};
+typedef int32_t PAL_TlsHandshakeState;
+
+enum
+{
+ PAL_TlsIo_Unknown = 0,
+ PAL_TlsIo_Success = 1,
+ PAL_TlsIo_WouldBlock = 2,
+ PAL_TlsIo_ClosedGracefully = 3,
+ PAL_TlsIo_Renegotiate = 4,
+};
+typedef int32_t PAL_TlsIo;
+
+enum
+{
+ PAL_SslProtocol_None = 0,
+ PAL_SslProtocol_Ssl2 = 12,
+ PAL_SslProtocol_Ssl3 = 48,
+ PAL_SslProtocol_Tls10 = 192,
+ PAL_SslProtocol_Tls11 = 768,
+ PAL_SslProtocol_Tls12 = 3072,
+};
+typedef int32_t PAL_SslProtocol;
+
+/*
+Create an SSL context, for the Server or Client role as determined by isServer.
+
+Returns NULL if an invalid boolean is given for isServer, an SSLContextRef otherwise.
+*/
+extern "C" SSLContextRef AppleCryptoNative_SslCreateContext(int32_t isServer);
+
+/*
+Indicate that an SSL Context (in server mode) should allow a client to present a mutual auth cert.
+
+Returns The result of SSLSetClientSideAuthenticate
+*/
+extern "C" int32_t AppleCryptoNative_SslSetAcceptClientCert(SSLContextRef sslContext);
+
+/*
+Assign a minimum to the TLS protocol version for this connection.
+
+Returns the output of SSLSetProtocolVersionMin
+*/
+extern "C" int32_t AppleCryptoNative_SslSetMinProtocolVersion(SSLContextRef sslContext, PAL_SslProtocol sslProtocol);
+
+/*
+Assign a maximum to the TLS protocol version for this connection.
+
+Returns the output of SSLSetProtocolVersionMax
+*/
+extern "C" int32_t AppleCryptoNative_SslSetMaxProtocolVersion(SSLContextRef sslContext, PAL_SslProtocol sslProtocol);
+
+/*
+Get the SecTrustRef from the SSL context which represents the certificte chain.
+
+Returns 1 on success, 0 on failure, and other values on invalid state.
+
+Output:
+pChainOut: Receives the SecTrustRef representing the populated chain
+pOSStatus: Receives the value returned by SSLCopyPeerTrust
+*/
+extern "C" int32_t
+AppleCryptoNative_SslCopyCertChain(SSLContextRef sslContext, SecTrustRef* pChainOut, int32_t* pOSStatus);
+
+/*
+Get the list of DN values for acceptable issuers for this connection.
+
+Returns 1 on success, 0 on OSStatus-error, other values for invalid state.
+
+Output:
+pChainOut: Receives an array of CFDataRef values representing the encoded X500 DistinguishedName
+values sent by the server.
+
+pOSStatus: Receives the output of SSLCopyDistinguishedNames.
+*/
+extern "C" int32_t
+AppleCryptoNative_SslCopyCADistinguishedNames(SSLContextRef sslContext, CFArrayRef* pArrayOut, int32_t* pOSStatus);
+
+/*
+Sets the policy of whether or not to break when a server identity has been presented.
+
+Returns 1 on success, 0 on failure, other values on invalid state.
+
+Output:
+pOSStatus: Receives the value returned by SSLSetSessionOption
+*/
+extern "C" int32_t
+AppleCryptoNative_SslSetBreakOnServerAuth(SSLContextRef sslContext, int32_t setBreak, int32_t* pOSStatus);
+
+/*
+Sets the policy of whether or not to break when a client identity has been presented.
+
+Returns 1 on success, 0 on failure, other values on invalid state.
+
+Output:
+pOSStatus: Receives the value returned by SSLSetSessionOption
+*/
+extern "C" int32_t
+AppleCryptoNative_SslSetBreakOnClientAuth(SSLContextRef sslContext, int32_t setBreak, int32_t* pOSStatus);
+
+/*
+Set the certificate chain for the ServerHello or ClientHello message.
+
+certRefs should be an array of [ SecIdentityRef, SecCertificateRef* ], the 0 element being the
+public/private pair for this entity, and all subsequent elements being the public element of an
+intermediate (non-root) certificate.
+
+Returns the output of SSLSetCertificate
+*/
+extern "C" int32_t AppleCryptoNative_SslSetCertificate(SSLContextRef sslContext, CFArrayRef certRefs);
+
+/*
+Set the target hostname for SNI. pszTargetName must already be converted for IDNA if required.
+
+Returns 1 on success, 0 on failure, other values for invalid state.
+
+Output:
+pOSStatus: Receives the value for SSLSetPeerDomainName
+*/
+extern "C" int32_t AppleCryptoNative_SslSetTargetName(SSLContextRef sslContext,
+ const char* pszTargetName,
+ int32_t cbTargetName,
+ int32_t* pOSStatus);
+
+/*
+Register the callbacks for reading and writing data to the SSL context.
+
+Returns the output of SSLSetIOFuncs.
+*/
+extern "C" int32_t
+AppleCryptoNative_SslSetIoCallbacks(SSLContextRef sslContext, SSLReadFunc readFunc, SSLWriteFunc writeFunc);
+
+/*
+Pump the TLS handshake.
+
+Returns an indication of what state the error is in. Any negative number means an error occurred.
+*/
+extern "C" PAL_TlsHandshakeState AppleCryptoNative_SslHandshake(SSLContextRef sslContext);
+
+/*
+Take bufLen bytes of cleartext data from buf and encrypt/frame the data.
+Processed data will then be sent into the write callback.
+
+Returns a PAL_TlsIo code indicitating how to proceed.
+
+Output:
+bytesWritten: When any value other than PAL_TlsIo_Success is returned, receives the number of bytes
+which were read from buf. On PAL_TlsIo_Success the parameter is not written through (but must still
+not be NULL)
+*/
+extern "C" PAL_TlsIo
+AppleCryptoNative_SslWrite(SSLContextRef sslContext, const uint8_t* buf, uint32_t bufLen, uint32_t* bytesWritten);
+
+/*
+Read up to bufLen bytes of framed/encrypted data from the connection into buf.
+Unless a holdover from a previous incomplete read is present this will invoke the read callback
+to get data from "the connection".
+
+Returns a PAL_TlsIo code indicating how to proceed.
+
+Output:
+written: Receives the number of bytes written into buf
+*/
+extern "C" PAL_TlsIo
+AppleCryptoNative_SslRead(SSLContextRef sslContext, uint8_t* buf, uint32_t bufLen, uint32_t* written);
+
+/*
+Check to see if the server identity certificate for this connection matches the requested hostname.
+
+notBefore: Specify the EE/leaf certificate's notBefore value to prevent a false negative due to
+the certificate being expired (or not yet valid).
+
+Returns 1 on match, 0 on mismatch, any other value indicates an invalid state.
+*/
+extern "C" int32_t
+AppleCryptoNative_SslIsHostnameMatch(SSLContextRef sslContext, CFStringRef cfHostname, CFDateRef notBefore);
+
+/*
+Generate a TLS Close alert to terminate the session.
+
+Returns the output of SSLClose
+*/
+extern "C" int32_t AppleCryptoNative_SslShutdown(SSLContextRef sslContext);
+
+/*
+Retrieve the TLS Protocol Version (e.g. TLS1.2) for the current session.
+
+Returns the output of SSLGetNegotiatedProtocolVersion.
+
+Output:
+pProtocol: Receives the protocol ID. PAL_SslProtocol_None is issued on error or an unknown mapping.
+*/
+extern "C" int32_t AppleCryptoNative_SslGetProtocolVersion(SSLContextRef sslContext, PAL_SslProtocol* pProtocol);
+
+/*
+Retrieve the TLS Cipher Suite which was negotiated for the current session.
+
+Returns the output of SSLGetNegotiatedCipher.
+
+Output:
+pProtocol: The TLS CipherSuite value (from the RFC), e.g. ((uint32_t)0xC030) for
+TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
+*/
+extern "C" int32_t AppleCryptoNative_SslGetCipherSuite(SSLContextRef sslContext, uint32_t* pCipherSuiteOut);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_symmetric.cpp b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_symmetric.cpp
index 256d3bc4d3..7d30c5afd7 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_symmetric.cpp
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_symmetric.cpp
@@ -30,16 +30,16 @@ extern "C" void AppleCryptoNative_CryptorFree(CCCryptorRef cryptor)
}
}
-extern "C" int AppleCryptoNative_CryptorCreate(PAL_SymmetricOperation operation,
- PAL_SymmetricAlgorithm algorithm,
- PAL_ChainingMode chainingMode,
- PAL_PaddingMode paddingMode,
- const uint8_t* pbKey,
- int32_t cbKey,
- const uint8_t* pbIv,
- PAL_SymmetricOptions options,
- CCCryptorRef* ppCryptorOut,
- int32_t* pccStatus)
+extern "C" int32_t AppleCryptoNative_CryptorCreate(PAL_SymmetricOperation operation,
+ PAL_SymmetricAlgorithm algorithm,
+ PAL_ChainingMode chainingMode,
+ PAL_PaddingMode paddingMode,
+ const uint8_t* pbKey,
+ int32_t cbKey,
+ const uint8_t* pbIv,
+ PAL_SymmetricOptions options,
+ CCCryptorRef* ppCryptorOut,
+ int32_t* pccStatus)
{
if (pccStatus == nullptr)
return -1;
@@ -53,8 +53,8 @@ extern "C" int AppleCryptoNative_CryptorCreate(PAL_SymmetricOperation operation,
// Ensure we aren't passing through things we don't understand
assert(operation == PAL_OperationEncrypt || operation == PAL_OperationDecrypt);
- assert(algorithm == PAL_AlgorithmAES || algorithm == PAL_AlgorithmDES ||
- algorithm == PAL_Algorithm3DES || algorithm == PAL_AlgorithmRC2);
+ assert(algorithm == PAL_AlgorithmAES || algorithm == PAL_AlgorithmDES || algorithm == PAL_Algorithm3DES ||
+ algorithm == PAL_AlgorithmRC2);
assert(chainingMode == PAL_ChainingModeECB || chainingMode == PAL_ChainingModeCBC);
assert(paddingMode == PAL_PaddingModeNone || paddingMode == PAL_PaddingModePkcs7);
assert(options == 0);
@@ -76,13 +76,13 @@ extern "C" int AppleCryptoNative_CryptorCreate(PAL_SymmetricOperation operation,
return status == kCCSuccess;
}
-extern "C" int AppleCryptoNative_CryptorUpdate(CCCryptorRef cryptor,
- const uint8_t* pbData,
- int32_t cbData,
- uint32_t* pbOutput,
- int32_t cbOutput,
- int32_t* pcbWritten,
- int32_t* pccStatus)
+extern "C" int32_t AppleCryptoNative_CryptorUpdate(CCCryptorRef cryptor,
+ const uint8_t* pbData,
+ int32_t cbData,
+ uint32_t* pbOutput,
+ int32_t cbOutput,
+ int32_t* pcbWritten,
+ int32_t* pccStatus)
{
if (pccStatus == nullptr)
return -1;
@@ -103,7 +103,7 @@ extern "C" int AppleCryptoNative_CryptorUpdate(CCCryptorRef cryptor,
return status == kCCSuccess;
}
-extern "C" int AppleCryptoNative_CryptorFinal(
+extern "C" int32_t AppleCryptoNative_CryptorFinal(
CCCryptorRef cryptor, uint8_t* pbOutput, int32_t cbOutput, int32_t* pcbWritten, int32_t* pccStatus)
{
if (pccStatus == nullptr)
@@ -121,7 +121,7 @@ extern "C" int AppleCryptoNative_CryptorFinal(
return status == kCCSuccess;
}
-extern "C" int AppleCryptoNative_CryptorReset(CCCryptorRef cryptor, const uint8_t* pbIv, int32_t* pccStatus)
+extern "C" int32_t AppleCryptoNative_CryptorReset(CCCryptorRef cryptor, const uint8_t* pbIv, int32_t* pccStatus)
{
if (pccStatus == nullptr)
return -1;
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_symmetric.h b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_symmetric.h
index ba21fe72ee..feed47a5ce 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_symmetric.h
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_symmetric.h
@@ -65,36 +65,36 @@ algorithm and assumed valid. pbIv is only allowed to be NULL for PAL_ChainingMod
Returns 1 on success, 0 on system error, -1 on input error.
*/
-extern "C" int AppleCryptoNative_CryptorCreate(PAL_SymmetricOperation operation,
- PAL_SymmetricAlgorithm algorithm,
- PAL_ChainingMode chainingMode,
- PAL_PaddingMode paddingMode,
- const uint8_t* pbKey,
- int32_t cbKey,
- const uint8_t* pbIv,
- PAL_SymmetricOptions options,
- CCCryptorRef* ppCryptorOut,
- int32_t* pkCCStatus);
+extern "C" int32_t AppleCryptoNative_CryptorCreate(PAL_SymmetricOperation operation,
+ PAL_SymmetricAlgorithm algorithm,
+ PAL_ChainingMode chainingMode,
+ PAL_PaddingMode paddingMode,
+ const uint8_t* pbKey,
+ int32_t cbKey,
+ const uint8_t* pbIv,
+ PAL_SymmetricOptions options,
+ CCCryptorRef* ppCryptorOut,
+ int32_t* pkCCStatus);
/*
Shims CCCryptorUpdate, updating *pkCCStatus as its output.
Returns 1 on success, 0 on system error, -1 on input error.
*/
-extern "C" int AppleCryptoNative_CryptorUpdate(CCCryptorRef cryptor,
- const uint8_t* pbData,
- int32_t cbData,
- uint32_t* pbOutput,
- int32_t cbOutput,
- int32_t* pcbWritten,
- int32_t* pkCCStatus);
+extern "C" int32_t AppleCryptoNative_CryptorUpdate(CCCryptorRef cryptor,
+ const uint8_t* pbData,
+ int32_t cbData,
+ uint32_t* pbOutput,
+ int32_t cbOutput,
+ int32_t* pcbWritten,
+ int32_t* pkCCStatus);
/*
Shims CCCryptorFinal, updating *pkCCStatus as its output.
Returns 1 on success, 0 on system error, -1 on input error.
*/
-extern "C" int AppleCryptoNative_CryptorFinal(
+extern "C" int32_t AppleCryptoNative_CryptorFinal(
CCCryptorRef cryptor, uint8_t* pbOutput, int32_t cbOutput, int32_t* pcbWritten, int32_t* pkCCStatus);
/*
@@ -102,4 +102,4 @@ Shims CCCryptorReset, updating *pkCCStatus as its output.
Returns 1 on success, 0 on system error, -1 on input error.
*/
-extern "C" int AppleCryptoNative_CryptorReset(CCCryptorRef cryptor, const uint8_t* pbIv, int32_t* pkCCStatus);
+extern "C" int32_t AppleCryptoNative_CryptorReset(CCCryptorRef cryptor, const uint8_t* pbIv, int32_t* pkCCStatus);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_trust.cpp b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_trust.cpp
new file mode 100644
index 0000000000..e100eac406
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_trust.cpp
@@ -0,0 +1,218 @@
+// 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.
+
+#include "pal_trust.h"
+
+static bool CheckTrustMatch(SecCertificateRef cert,
+ SecTrustSettingsDomain domain,
+ SecTrustSettingsResult result,
+ OSStatus* pOSStatus)
+{
+ CFArrayRef settings = nullptr;
+ *pOSStatus = SecTrustSettingsCopyTrustSettings(cert, domain, &settings);
+ bool isMatch = false;
+
+ if (*pOSStatus == noErr && settings != nullptr)
+ {
+ CFIndex count = CFArrayGetCount(settings);
+
+ if (count == 0)
+ {
+ *pOSStatus = noErr;
+ // An empty array means that it counts as "Trust Root",
+ // so we match if (and only if) we were asking for that.
+ isMatch = (result == kSecTrustSettingsResultTrustRoot);
+ }
+ else
+ {
+ auto dictionaryTypeId = CFDictionaryGetTypeID();
+ auto numberTypeId = CFNumberGetTypeID();
+
+ for (CFIndex i = 0; i < count; i++)
+ {
+ CFTypeRef obj = CFArrayGetValueAtIndex(settings, i);
+
+ if (CFGetTypeID(obj) != dictionaryTypeId)
+ {
+ continue;
+ }
+
+ CFDictionaryRef dict = reinterpret_cast<CFDictionaryRef>(obj);
+
+ if (CFDictionaryGetCount(dict) > 1)
+ {
+ // This dictionary has constraints. A particular SecTrust evaluation
+ // might or might not make it apply. If the only extra key was a policy
+ // restriction, then matching on the basic X.509 policy might be fair,
+ // but it's not an obvious check with little expectation of applying.
+ //
+ // This may result in X509Chain reporting valid when enumerating the
+ // two root stores doesn't agree.
+ continue;
+ }
+
+ CFTypeRef val = CFDictionaryGetValue(dict, kSecTrustSettingsResult);
+
+ if (val != nullptr && CFGetTypeID(val) == numberTypeId)
+ {
+ CFNumberRef cfNum = reinterpret_cast<CFNumberRef>(val);
+ SecTrustSettingsResult trustValue = {};
+
+ if (CFNumberGetValue(cfNum, kCFNumberSInt32Type, &trustValue))
+ {
+ isMatch = (trustValue == result);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (settings != nullptr)
+ CFRelease(settings);
+
+ return isMatch;
+}
+
+static int32_t EnumerateTrust(SecTrustSettingsDomain domain,
+ SecTrustSettingsResult result,
+ CFMutableArrayRef* pCertsRef,
+ int32_t* pOSStatus)
+{
+ if (pOSStatus != nullptr)
+ *pOSStatus = noErr;
+
+ if (pCertsRef == nullptr || pOSStatus == nullptr)
+ return -1;
+
+ CFMutableArrayRef outArray;
+
+ if (*pCertsRef != nullptr)
+ {
+ outArray = *pCertsRef;
+ }
+ else
+ {
+ outArray = CFArrayCreateMutable(nullptr, 0, &kCFTypeArrayCallBacks);
+ *pCertsRef = outArray;
+ }
+
+ if (outArray == nullptr)
+ {
+ *pOSStatus = errSecAllocate;
+ return 0;
+ }
+
+ CFArrayRef certsWithTrusts = nullptr;
+ *pOSStatus = SecTrustSettingsCopyCertificates(domain, &certsWithTrusts);
+
+ if (*pOSStatus == noErr)
+ {
+ CFIndex count = CFArrayGetCount(certsWithTrusts);
+ auto certTypeId = SecCertificateGetTypeID();
+
+ for (CFIndex i = 0; i < count; i++)
+ {
+ CFTypeRef obj = CFArrayGetValueAtIndex(certsWithTrusts, i);
+
+ if (CFGetTypeID(obj) != certTypeId)
+ {
+ continue;
+ }
+
+ SecCertificateRef cert = reinterpret_cast<SecCertificateRef>(const_cast<void*>(obj));
+ bool isMatch = CheckTrustMatch(cert, domain, result, pOSStatus);
+
+ if (*pOSStatus != noErr)
+ {
+ break;
+ }
+
+ if (isMatch)
+ {
+ CFArrayAppendValue(outArray, cert);
+ }
+ }
+ }
+ else if (*pOSStatus == errSecNoTrustSettings)
+ {
+ // If there are no trust settings at the specified domain,
+ // return the empty list as OK.
+ *pOSStatus = noErr;
+ }
+
+ if (certsWithTrusts != nullptr)
+ {
+ CFRelease(certsWithTrusts);
+ }
+
+ int32_t ret = *pOSStatus == noErr;
+
+ // Note that on a second call the array from the first call will get freed
+ // if an error is encountered.
+ if (ret == 0 || CFArrayGetCount(outArray) == 0)
+ {
+ CFRelease(outArray);
+ *pCertsRef = nullptr;
+ }
+
+ return ret;
+}
+
+extern "C" int32_t AppleCryptoNative_StoreEnumerateUserRoot(CFArrayRef* pCertsOut, int32_t* pOSStatusOut)
+{
+ if (pCertsOut != nullptr)
+ *pCertsOut = nullptr;
+
+ return EnumerateTrust(kSecTrustSettingsDomainUser,
+ kSecTrustSettingsResultTrustRoot,
+ const_cast<CFMutableArrayRef*>(pCertsOut),
+ pOSStatusOut);
+}
+
+extern "C" int32_t AppleCryptoNative_StoreEnumerateMachineRoot(CFArrayRef* pCertsOut, int32_t* pOSStatusOut)
+{
+ if (pCertsOut != nullptr)
+ *pCertsOut = nullptr;
+
+ CFMutableArrayRef* pCertsRef = const_cast<CFMutableArrayRef*>(pCertsOut);
+
+ int32_t ret =
+ EnumerateTrust(kSecTrustSettingsDomainAdmin, kSecTrustSettingsResultTrustRoot, pCertsRef, pOSStatusOut);
+
+ if (ret == 1)
+ {
+ ret = EnumerateTrust(kSecTrustSettingsDomainSystem, kSecTrustSettingsResultTrustRoot, pCertsRef, pOSStatusOut);
+ }
+
+ return ret;
+}
+
+extern "C" int32_t AppleCryptoNative_StoreEnumerateUserDisallowed(CFArrayRef* pCertsOut, int32_t* pOSStatusOut)
+{
+ if (pCertsOut != nullptr)
+ *pCertsOut = nullptr;
+
+ return EnumerateTrust(kSecTrustSettingsDomainUser,
+ kSecTrustSettingsResultDeny,
+ const_cast<CFMutableArrayRef*>(pCertsOut),
+ pOSStatusOut);
+}
+
+extern "C" int32_t AppleCryptoNative_StoreEnumerateMachineDisallowed(CFArrayRef* pCertsOut, int32_t* pOSStatusOut)
+{
+ if (pCertsOut != nullptr)
+ *pCertsOut = nullptr;
+
+ CFMutableArrayRef* pCertsRef = const_cast<CFMutableArrayRef*>(pCertsOut);
+
+ int32_t ret = EnumerateTrust(kSecTrustSettingsDomainAdmin, kSecTrustSettingsResultDeny, pCertsRef, pOSStatusOut);
+
+ if (ret == 1)
+ {
+ ret = EnumerateTrust(kSecTrustSettingsDomainSystem, kSecTrustSettingsResultDeny, pCertsRef, pOSStatusOut);
+ }
+
+ return ret;
+}
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_trust.h b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_trust.h
new file mode 100644
index 0000000000..3ffb8f586c
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_trust.h
@@ -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.
+
+#pragma once
+
+#include "pal_types.h"
+
+#include <Security/Security.h>
+
+/*
+Enumerate the certificates which are root trusted by the user.
+
+Returns 1 on success (including "no certs found"), 0 on failure, any other value for invalid state.
+
+Output:
+pCertsOut: When the return value is not 1, NULL. Otherwise NULL on "no certs found", or a CFArrayRef for the matches
+(including a single match).
+pOSStatus: Receives the last OSStatus value.
+*/
+extern "C" int32_t AppleCryptoNative_StoreEnumerateUserRoot(CFArrayRef* pCertsOut, int32_t* pOSStatusOut);
+
+/*
+Enumerate the certificates which are root trusted by the machine ("admin" and "system" domains).
+
+Returns 1 on success (including "no certs found"), 0 on failure, any other value for invalid state.
+
+Duplicate certificates may be reported by this function, if they are trusted at both the admin and
+system levels. De-duplication is the responsibility of the caller.
+
+Output:
+pCertsOut: When the return value is not 1, NULL. Otherwise NULL on "no certs found", or a CFArrayRef for the matches
+(including a single match).
+pOSStatus: Receives the last OSStatus value.
+*/
+extern "C" int32_t AppleCryptoNative_StoreEnumerateMachineRoot(CFArrayRef* pCertsOut, int32_t* pOSStatusOut);
+
+/*
+Enumerate the certificates which are disallowed by the user.
+
+Returns 1 on success (including "no certs found"), 0 on failure, any other value for invalid state.
+
+Output:
+pCertsOut: When the return value is not 1, NULL. Otherwise NULL on "no certs found", or a CFArrayRef for the matches
+(including a single match).
+pOSStatus: Receives the last OSStatus value.
+*/
+extern "C" int32_t AppleCryptoNative_StoreEnumerateUserRoot(CFArrayRef* pCertsOut, int32_t* pOSStatusOut);
+
+/*
+Enumerate the certificates which are disallowed by the machine ("admin" and "system" domains).
+
+Returns 1 on success (including "no certs found"), 0 on failure, any other value for invalid state.
+
+Duplicate certificates may be reported by this function, if they are disallowed at both the admin
+and system levels. De-duplication is the responsibility of the caller.
+
+Output:
+pCertsOut: When the return value is not 1, NULL. Otherwise NULL on "no certs found", or a CFArrayRef for the matches
+(including a single match).
+pOSStatus: Receives the last OSStatus value.
+*/
+extern "C" int32_t AppleCryptoNative_StoreEnumerateMachineRoot(CFArrayRef* pCertsOut, int32_t* pOSStatusOut);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.cpp b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.cpp
new file mode 100644
index 0000000000..31942b193d
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.cpp
@@ -0,0 +1,490 @@
+// 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.
+
+#include "pal_x509.h"
+
+static const int32_t kErrOutItemsNull = -3;
+static const int32_t kErrOutItemsEmpty = -2;
+
+extern "C" int32_t
+AppleCryptoNative_X509DemuxAndRetainHandle(CFTypeRef handle, SecCertificateRef* pCertOut, SecIdentityRef* pIdentityOut)
+{
+ if (pCertOut != nullptr)
+ *pCertOut = nullptr;
+ if (pIdentityOut != nullptr)
+ *pIdentityOut = nullptr;
+
+ if (handle == nullptr || pCertOut == nullptr || pIdentityOut == nullptr)
+ return kErrorBadInput;
+
+ auto objectType = CFGetTypeID(handle);
+ void* nonConstHandle = const_cast<void*>(handle);
+
+ if (objectType == SecIdentityGetTypeID())
+ {
+ *pIdentityOut = reinterpret_cast<SecIdentityRef>(nonConstHandle);
+ }
+ else if (objectType == SecCertificateGetTypeID())
+ {
+ *pCertOut = reinterpret_cast<SecCertificateRef>(nonConstHandle);
+ }
+ else
+ {
+ return 0;
+ }
+
+ CFRetain(handle);
+ return 1;
+}
+
+extern "C" int32_t
+AppleCryptoNative_X509GetPublicKey(SecCertificateRef cert, SecKeyRef* pPublicKeyOut, int32_t* pOSStatusOut)
+{
+ if (pPublicKeyOut != nullptr)
+ *pPublicKeyOut = nullptr;
+ if (pOSStatusOut != nullptr)
+ *pOSStatusOut = noErr;
+
+ if (cert == nullptr || pPublicKeyOut == nullptr || pOSStatusOut == nullptr)
+ return kErrorBadInput;
+
+ *pOSStatusOut = SecCertificateCopyPublicKey(cert, pPublicKeyOut);
+ return (*pOSStatusOut == noErr);
+}
+
+extern "C" PAL_X509ContentType AppleCryptoNative_X509GetContentType(uint8_t* pbData, int32_t cbData)
+{
+ if (pbData == nullptr || cbData < 0)
+ return PAL_X509Unknown;
+
+ CFDataRef cfData = CFDataCreateWithBytesNoCopy(nullptr, pbData, cbData, kCFAllocatorNull);
+
+ if (cfData == nullptr)
+ return PAL_X509Unknown;
+
+ // The sniffing order is:
+ // * X509 DER
+ // * PKCS7 PEM/DER
+ // * PKCS12 DER (or PEM if Apple has non-standard support for that)
+ // * X509 PEM (or DER, but that already matched)
+ //
+ // If the X509 PEM check is done first SecItemImport will erroneously match
+ // some PKCS#7 blobs and say they were certificates.
+ //
+ // Likewise, if the X509 DER check isn't done first, Apple will report it as
+ // being a PKCS#7.
+ SecCertificateRef certref = SecCertificateCreateWithData(nullptr, cfData);
+
+ if (certref != nullptr)
+ {
+ CFRelease(certref);
+ return PAL_Certificate;
+ }
+
+ SecExternalFormat dataFormat = kSecFormatPKCS7;
+ SecExternalFormat actualFormat = dataFormat;
+ SecExternalItemType itemType = kSecItemTypeAggregate;
+ SecExternalItemType actualType = itemType;
+
+ OSStatus osStatus = SecItemImport(cfData, nullptr, &actualFormat, &actualType, 0, nullptr, nullptr, nullptr);
+
+ if (osStatus == noErr)
+ {
+ if (actualType == itemType && actualFormat == dataFormat)
+ {
+ return PAL_Pkcs7;
+ }
+ }
+
+ dataFormat = kSecFormatPKCS12;
+ actualFormat = dataFormat;
+ itemType = kSecItemTypeAggregate;
+ actualType = itemType;
+
+ osStatus = SecItemImport(cfData, nullptr, &actualFormat, &actualType, 0, nullptr, nullptr, nullptr);
+
+ if (osStatus == errSecPassphraseRequired)
+ {
+ dataFormat = kSecFormatPKCS12;
+ actualFormat = dataFormat;
+ itemType = kSecItemTypeAggregate;
+ actualType = itemType;
+
+ SecItemImportExportKeyParameters importParams = {};
+ importParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+ importParams.passphrase = CFSTR("");
+
+ osStatus = SecItemImport(cfData, nullptr, &actualFormat, &actualType, 0, &importParams, nullptr, nullptr);
+
+ CFRelease(importParams.passphrase);
+ importParams.passphrase = nullptr;
+ }
+
+ if (osStatus == noErr || osStatus == errSecPkcs12VerifyFailure)
+ {
+ if (actualType == itemType && actualFormat == dataFormat)
+ {
+ return PAL_Pkcs12;
+ }
+ }
+
+ dataFormat = kSecFormatX509Cert;
+ actualFormat = dataFormat;
+ itemType = kSecItemTypeCertificate;
+ actualType = itemType;
+
+ osStatus = SecItemImport(cfData, nullptr, &actualFormat, &actualType, 0, nullptr, nullptr, nullptr);
+
+ if (osStatus == noErr)
+ {
+ if (actualType == itemType && actualFormat == dataFormat)
+ {
+ return PAL_Certificate;
+ }
+ }
+
+ return PAL_X509Unknown;
+}
+
+static int32_t ProcessCertificateTypeReturn(CFArrayRef items, SecCertificateRef* pCertOut, SecIdentityRef* pIdentityOut)
+{
+ assert(pCertOut != nullptr && *pCertOut == nullptr);
+ assert(pIdentityOut != nullptr && *pIdentityOut == nullptr);
+
+ if (items == nullptr)
+ {
+ return kErrOutItemsNull;
+ }
+
+ CFIndex itemCount = CFArrayGetCount(items);
+
+ if (itemCount == 0)
+ {
+ return kErrOutItemsEmpty;
+ }
+
+ CFTypeRef bestItem = nullptr;
+
+ for (CFIndex i = 0; i < itemCount; i++)
+ {
+ CFTypeRef current = CFArrayGetValueAtIndex(items, i);
+ auto currentItemType = CFGetTypeID(current);
+
+ if (currentItemType == SecIdentityGetTypeID())
+ {
+ bestItem = current;
+ break;
+ }
+ else if (bestItem == nullptr && currentItemType == SecCertificateGetTypeID())
+ {
+ bestItem = current;
+ }
+ }
+
+ if (bestItem == nullptr)
+ {
+ return -13;
+ }
+
+ if (CFGetTypeID(bestItem) == SecCertificateGetTypeID())
+ {
+ CFRetain(bestItem);
+ *pCertOut = reinterpret_cast<SecCertificateRef>(const_cast<void*>(bestItem));
+ return 1;
+ }
+
+ if (CFGetTypeID(bestItem) == SecIdentityGetTypeID())
+ {
+ CFRetain(bestItem);
+ *pIdentityOut = reinterpret_cast<SecIdentityRef>(const_cast<void*>(bestItem));
+
+ return 1;
+ }
+
+ return -19;
+}
+
+extern "C" int32_t AppleCryptoNative_X509CopyCertFromIdentity(SecIdentityRef identity, SecCertificateRef* pCertOut)
+{
+ if (pCertOut != nullptr)
+ *pCertOut = nullptr;
+
+ // This function handles null inputs for both identity and cert.
+ return SecIdentityCopyCertificate(identity, pCertOut);
+}
+
+extern "C" int32_t AppleCryptoNative_X509CopyPrivateKeyFromIdentity(SecIdentityRef identity, SecKeyRef* pPrivateKeyOut)
+{
+ if (pPrivateKeyOut != nullptr)
+ *pPrivateKeyOut = nullptr;
+
+ // This function handles null inputs for both identity and key
+ return SecIdentityCopyPrivateKey(identity, pPrivateKeyOut);
+}
+
+static int32_t ReadX509(uint8_t* pbData,
+ int32_t cbData,
+ PAL_X509ContentType contentType,
+ CFStringRef cfPfxPassphrase,
+ SecKeychainRef keychain,
+ bool exportable,
+ SecCertificateRef* pCertOut,
+ SecIdentityRef* pIdentityOut,
+ CFArrayRef* pCollectionOut,
+ int32_t* pOSStatus)
+{
+ assert(pbData != nullptr);
+ assert(cbData >= 0);
+ assert((pCertOut == nullptr) == (pIdentityOut == nullptr));
+ assert((pCertOut == nullptr) != (pCollectionOut == nullptr));
+
+ SecExternalFormat dataFormat;
+ SecExternalItemType itemType;
+ int32_t ret = 0;
+ CFArrayRef outItems = nullptr;
+ CFMutableArrayRef keyAttributes = nullptr;
+ SecKeychainRef importKeychain = nullptr;
+
+ SecItemImportExportKeyParameters importParams = {};
+ importParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+
+ if (contentType == PAL_Certificate)
+ {
+ dataFormat = kSecFormatX509Cert;
+ itemType = kSecItemTypeCertificate;
+ }
+ else if (contentType == PAL_Pkcs7)
+ {
+ dataFormat = kSecFormatPKCS7;
+ itemType = kSecItemTypeAggregate;
+ }
+ else if (contentType == PAL_Pkcs12)
+ {
+ dataFormat = kSecFormatPKCS12;
+ itemType = kSecItemTypeAggregate;
+
+ importParams.passphrase = cfPfxPassphrase;
+ importKeychain = keychain;
+
+ if (keychain == nullptr)
+ {
+ return kErrorBadInput;
+ }
+
+ // if keyAttributes is nullptr then it uses SENSITIVE | EXTRACTABLE
+ // so if !exportable was requested, assert SENSITIVE.
+ if (!exportable)
+ {
+ keyAttributes = CFArrayCreateMutable(nullptr, 9, &kCFTypeArrayCallBacks);
+
+ if (keyAttributes == nullptr)
+ {
+ *pOSStatus = errSecAllocate;
+ return 0;
+ }
+
+ int32_t sensitiveValue = CSSM_KEYATTR_SENSITIVE;
+ CFNumberRef sensitive = CFNumberCreate(nullptr, kCFNumberSInt32Type, &sensitiveValue);
+
+ if (sensitive == nullptr)
+ {
+ CFRelease(keyAttributes);
+ *pOSStatus = errSecAllocate;
+ return 0;
+ }
+
+ CFArrayAppendValue(keyAttributes, sensitive);
+ CFRelease(sensitive);
+
+ importParams.keyAttributes = keyAttributes;
+ }
+ }
+ else
+ {
+ *pOSStatus = errSecUnknownFormat;
+ return 0;
+ }
+
+ CFDataRef cfData = CFDataCreateWithBytesNoCopy(nullptr, pbData, cbData, kCFAllocatorNull);
+
+ if (cfData == nullptr)
+ {
+ *pOSStatus = errSecAllocate;
+ }
+
+ if (*pOSStatus == noErr)
+ {
+ *pOSStatus = SecItemImport(cfData, nullptr, &dataFormat, &itemType, 0, &importParams, keychain, &outItems);
+ }
+
+ if (contentType == PAL_Pkcs12 && *pOSStatus == errSecPassphraseRequired && cfPfxPassphrase == nullptr)
+ {
+ if (outItems != nullptr)
+ {
+ CFRelease(outItems);
+ outItems = nullptr;
+ }
+
+ // Try again with the empty string passphrase.
+ importParams.passphrase = CFSTR("");
+
+ *pOSStatus = SecItemImport(cfData, nullptr, &dataFormat, &itemType, 0, &importParams, keychain, &outItems);
+
+ CFRelease(importParams.passphrase);
+ importParams.passphrase = nullptr;
+ }
+
+ if (*pOSStatus == noErr)
+ {
+ if (pCollectionOut != nullptr)
+ {
+ CFRetain(outItems);
+ *pCollectionOut = outItems;
+ ret = 1;
+ }
+ else
+ {
+ ret = ProcessCertificateTypeReturn(outItems, pCertOut, pIdentityOut);
+ }
+ }
+
+ if (keyAttributes != nullptr)
+ {
+ CFRelease(keyAttributes);
+ }
+
+ if (outItems != nullptr)
+ {
+ // In the event this is returned via pCollectionOut it was already
+ // CFRetain()ed, so always CFRelease here.
+ CFRelease(outItems);
+ }
+
+ CFRelease(cfData);
+ return ret;
+}
+
+extern "C" int32_t AppleCryptoNative_X509ImportCollection(uint8_t* pbData,
+ int32_t cbData,
+ PAL_X509ContentType contentType,
+ CFStringRef cfPfxPassphrase,
+ SecKeychainRef keychain,
+ int32_t exportable,
+ CFArrayRef* pCollectionOut,
+ int32_t* pOSStatus)
+{
+ if (pCollectionOut != nullptr)
+ *pCollectionOut = nullptr;
+ if (pOSStatus != nullptr)
+ *pOSStatus = noErr;
+
+ if (pbData == nullptr || cbData < 0 || pCollectionOut == nullptr || pOSStatus == nullptr ||
+ exportable != !!exportable)
+ {
+ return kErrorBadInput;
+ }
+
+ return ReadX509(pbData,
+ cbData,
+ contentType,
+ cfPfxPassphrase,
+ keychain,
+ static_cast<bool>(exportable),
+ nullptr,
+ nullptr,
+ pCollectionOut,
+ pOSStatus);
+}
+
+extern "C" int32_t AppleCryptoNative_X509ImportCertificate(uint8_t* pbData,
+ int32_t cbData,
+ PAL_X509ContentType contentType,
+ CFStringRef cfPfxPassphrase,
+ SecKeychainRef keychain,
+ int32_t exportable,
+ SecCertificateRef* pCertOut,
+ SecIdentityRef* pIdentityOut,
+ int32_t* pOSStatus)
+{
+ if (pCertOut != nullptr)
+ *pCertOut = nullptr;
+ if (pIdentityOut != nullptr)
+ *pIdentityOut = nullptr;
+ if (pOSStatus != nullptr)
+ *pOSStatus = noErr;
+
+ if (pbData == nullptr || cbData < 0 || pCertOut == nullptr || pIdentityOut == nullptr || pOSStatus == nullptr ||
+ exportable != !!exportable)
+ {
+ return kErrorBadInput;
+ }
+
+ return ReadX509(pbData,
+ cbData,
+ contentType,
+ cfPfxPassphrase,
+ keychain,
+ static_cast<bool>(exportable),
+ pCertOut,
+ pIdentityOut,
+ nullptr,
+ pOSStatus);
+}
+
+extern "C" int32_t AppleCryptoNative_X509ExportData(CFArrayRef data,
+ PAL_X509ContentType type,
+ CFStringRef cfExportPassphrase,
+ CFDataRef* pExportOut,
+ int32_t* pOSStatus)
+{
+ if (pExportOut != nullptr)
+ *pExportOut = nullptr;
+ if (pOSStatus != nullptr)
+ *pOSStatus = noErr;
+
+ if (data == nullptr || pExportOut == nullptr || pOSStatus == nullptr)
+ {
+ return kErrorBadInput;
+ }
+
+ SecExternalFormat dataFormat = kSecFormatUnknown;
+
+ switch (type)
+ {
+ case PAL_Pkcs7:
+ dataFormat = kSecFormatPKCS7;
+ break;
+ case PAL_Pkcs12:
+ dataFormat = kSecFormatPKCS12;
+ break;
+ default:
+ return kErrorBadInput;
+ }
+
+ SecItemImportExportKeyParameters keyParams = {};
+ keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+ keyParams.passphrase = cfExportPassphrase;
+
+ *pOSStatus = SecItemExport(data, dataFormat, 0, &keyParams, pExportOut);
+
+ return *pOSStatus == noErr;
+}
+
+extern "C" int32_t AppleCryptoNative_X509GetRawData(SecCertificateRef cert, CFDataRef* ppDataOut, int32_t* pOSStatus)
+{
+ if (ppDataOut != nullptr)
+ *ppDataOut = nullptr;
+ if (pOSStatus != nullptr)
+ *pOSStatus = noErr;
+
+ if (cert == nullptr || ppDataOut == nullptr || pOSStatus == nullptr)
+ return kErrorBadInput;
+
+ SecExternalFormat dataFormat = kSecFormatX509Cert;
+ SecItemImportExportKeyParameters keyParams = {};
+ keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+
+ *pOSStatus = SecItemExport(cert, dataFormat, 0, &keyParams, ppDataOut);
+ return (*pOSStatus == noErr);
+}
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h
new file mode 100644
index 0000000000..ecd30c395d
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.h
@@ -0,0 +1,158 @@
+// 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.
+
+#pragma once
+
+#include "pal_digest.h"
+#include "pal_seckey.h"
+
+#include <Security/Security.h>
+
+enum
+{
+ PAL_X509Unknown = 0,
+ PAL_Certificate = 1,
+ PAL_SerializedCert = 2,
+ PAL_Pkcs12 = 3,
+ PAL_SerializedStore = 4,
+ PAL_Pkcs7 = 5,
+ PAL_Authenticode = 6,
+};
+typedef uint32_t PAL_X509ContentType;
+
+/*
+Given a handle, determine if it represents a SecCertificateRef, SecIdentityRef, or other.
+If the handle is a certificate or identity it is CFRetain()ed (and must later be CFRelease()d).
+
+Returns 1 if the handle was a certificate or identity, 0 otherwise (other values on invalid state).
+
+Output:
+pCertOut: If handle is a certificate, receives handle, otherwise NULL
+pIdentityut: If handle is an identity, receives handle, otherwise NULL
+*/
+extern "C" int32_t
+AppleCryptoNative_X509DemuxAndRetainHandle(CFTypeRef handle, SecCertificateRef* pCertOut, SecIdentityRef* pIdentityOut);
+
+/*
+Extract a SecKeyRef for the public key from the certificate handle.
+
+Returns 1 on success, 0 on failure, any other value on invalid state.
+
+Output:
+pPublicKeyOut: Receives a CFRetain()ed SecKeyRef for the public key
+pOSStatusOut: Receives the result of SecCertificateCopyPublicKey
+*/
+extern "C" int32_t
+AppleCryptoNative_X509GetPublicKey(SecCertificateRef cert, SecKeyRef* pPublicKeyOut, int32_t* pOSStatusOut);
+
+/*
+Determines the data type of the provided input.
+
+Returns the data (format) type of the provided input, PAL_X509Unknown if it cannot be determined.
+*/
+extern "C" PAL_X509ContentType AppleCryptoNative_X509GetContentType(uint8_t* pbData, int32_t cbData);
+
+/*
+Extract a SecCertificateRef for the certificate from an identity handle.
+
+Returns the result of SecIdentityCopyCertificate.
+
+Output:
+pCertOut: Receives a SecCertificateRef for the certificate associated with the identity
+*/
+extern "C" int32_t AppleCryptoNative_X509CopyCertFromIdentity(SecIdentityRef identity, SecCertificateRef* pCertOut);
+
+/*
+Extract a SecKeyRef for the private key from an identity handle.
+
+Returns the result of SecIdentityCopyPrivateKey
+
+Output:
+pPrivateKeyOut: Receives a SecKeyRef for the private key associated with the identity
+*/
+extern "C" int32_t AppleCryptoNative_X509CopyPrivateKeyFromIdentity(SecIdentityRef identity, SecKeyRef* pPrivateKeyOut);
+
+/*
+Read cbData bytes of data from pbData and interpret it to a collection of certificates (or identities).
+
+If cfPfxPassphrase represents the NULL (but not empty) passphrase and a PFX import gets a password
+error then the empty passphrase is automatically attempted.
+
+Any private keys will be loaded into the provided keychain. If the keychain is not provided then
+a PFX load will read only the public (certificate) values.
+
+Returns 1 on success (including empty PKCS7 collections), 0 on failure, other values indicate invalid state.
+
+Output:
+pCollectionOut: Receives an array which contains SecCertificateRef, SecIdentityRef, and possibly other values which were
+read out of the provided blob
+pOSStatus: Receives the output of SecItemImport for the last attempted read
+*/
+extern "C" int32_t AppleCryptoNative_X509ImportCollection(uint8_t* pbData,
+ int32_t cbData,
+ PAL_X509ContentType contentType,
+ CFStringRef cfPfxPassphrase,
+ SecKeychainRef keychain,
+ int32_t exportable,
+ CFArrayRef* pCollectionOut,
+ int32_t* pOSStatus);
+
+/*
+Read cbData bytes of data from pbData and interpret it to a single certificate (or identity).
+
+For a single X.509 certificate, that certificate is emitted.
+For a PKCS#7 blob the signing certificate is returned.
+For a PKCS#12 blob (PFX) the first public+private pair found is returned, or the first certificate.
+
+If cfPfxPassphrase represents the NULL (but not empty) passphrase and a PFX import gets a password
+error then the empty passphrase is automatically attempted.
+
+Any private keys will be loaded into the provided keychain. If the keychain is not provided then
+a PFX load will read only the public (certificate) values.
+
+Returns 1 on success, 0 on failure, -2 on a successful read of an empty collection, other values reprepresent invalid
+state.
+
+Output:
+pCertOut: If the best matched value was a certificate, receives the SecCertificateRef, otherwise receives NULL
+pIdentityOut: If the best matched value was an identity, receives the SecIdentityRef, otherwise receives NULL
+pOSStatus: Receives the return of the last call to SecItemImport
+*/
+extern "C" int32_t AppleCryptoNative_X509ImportCertificate(uint8_t* pbData,
+ int32_t cbData,
+ PAL_X509ContentType contentType,
+ CFStringRef cfPfxPassphrase,
+ SecKeychainRef keychain,
+ int32_t exportable,
+ SecCertificateRef* pCertOut,
+ SecIdentityRef* pIdentityOut,
+ int32_t* pOSStatus);
+
+/*
+Export the certificates (or identities) in data to the requested format type.
+
+Only PKCS#7 and PKCS#12 are supported at this time.
+
+Returns 1 on success, 0 on failure, any other value indicates invalid state.
+
+Output:
+pExportOut: Receives a CFDataRef with the exported blob
+pOSStatus: Receives the result of SecItemExport
+*/
+extern "C" int32_t AppleCryptoNative_X509ExportData(CFArrayRef data,
+ PAL_X509ContentType type,
+ CFStringRef cfExportPassphrase,
+ CFDataRef* pExportOut,
+ int32_t* pOSStatus);
+
+/*
+Extract the DER encoded value of a certificate (public portion only).
+
+Returns 1 on success, 0 on failure, any other value indicates invalid state.
+
+Output:
+ppDataOut: Receives a CFDataRef with the exported blob
+pOSStatus: Receives the result of SecItemExport
+*/
+extern "C" int32_t AppleCryptoNative_X509GetRawData(SecCertificateRef cert, CFDataRef* ppDataOut, int32_t* pOSStatus);
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.cpp b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.cpp
new file mode 100644
index 0000000000..68c05aec7e
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.cpp
@@ -0,0 +1,239 @@
+// 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.
+
+#include "pal_x509chain.h"
+
+#ifndef kCFCoreFoundationVersionNumber10_12
+#define kCFCoreFoundationVersionNumber10_12 1348.00
+#endif
+
+extern "C" SecPolicyRef AppleCryptoNative_X509ChainCreateDefaultPolicy()
+{
+ // Disable on macOS 10.11 and lower due to segfaults within Security.framework.
+ if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_12)
+ return nullptr;
+
+ return SecPolicyCreateBasicX509();
+}
+
+extern "C" SecPolicyRef AppleCryptoNative_X509ChainCreateRevocationPolicy()
+{
+ return SecPolicyCreateRevocation(kSecRevocationUseAnyAvailableMethod);
+}
+
+extern "C" int32_t
+AppleCryptoNative_X509ChainCreate(CFTypeRef certs, CFTypeRef policies, SecTrustRef* pTrustOut, int32_t* pOSStatus)
+{
+ if (pTrustOut != nullptr)
+ *pTrustOut = nullptr;
+ if (pOSStatus != nullptr)
+ *pOSStatus = noErr;
+
+ if (certs == nullptr || policies == nullptr || pTrustOut == nullptr || pOSStatus == nullptr)
+ return -1;
+
+ *pOSStatus = SecTrustCreateWithCertificates(certs, policies, pTrustOut);
+ return *pOSStatus == noErr;
+}
+
+extern "C" int32_t AppleCryptoNative_X509ChainEvaluate(SecTrustRef chain,
+ CFDateRef cfEvaluationTime,
+ bool allowNetwork,
+ int32_t* pOSStatus)
+{
+ if (pOSStatus != nullptr)
+ *pOSStatus = noErr;
+
+ if (chain == nullptr || pOSStatus == nullptr)
+ return -1;
+
+ *pOSStatus = SecTrustSetVerifyDate(chain, cfEvaluationTime);
+
+ if (*pOSStatus != noErr)
+ {
+ return -2;
+ }
+
+ *pOSStatus = SecTrustSetNetworkFetchAllowed(chain, allowNetwork);
+
+ if (*pOSStatus != noErr)
+ {
+ return -3;
+ }
+
+ SecTrustResultType trustResult;
+ *pOSStatus = SecTrustEvaluate(chain, &trustResult);
+
+ if (*pOSStatus != noErr)
+ {
+ return 0;
+ }
+
+ // If: The chain was built with no errors (Unspecified)
+ // Or: The chain was built and involved an explicitly trusted cert (Proceed)
+ // Then: Success.
+ if (trustResult == kSecTrustResultUnspecified || trustResult == kSecTrustResultProceed)
+ {
+ return 1;
+ }
+
+ // Should this be a different return code?
+ return 1;
+}
+
+extern "C" int64_t AppleCryptoNative_X509ChainGetChainSize(SecTrustRef chain)
+{
+ if (chain == nullptr)
+ return -1;
+
+ return SecTrustGetCertificateCount(chain);
+}
+
+extern "C" SecCertificateRef AppleCryptoNative_X509ChainGetCertificateAtIndex(SecTrustRef chain, int64_t index)
+{
+ if (chain == nullptr || index < 0)
+ return nullptr;
+
+ return SecTrustGetCertificateAtIndex(chain, index);
+}
+
+extern "C" CFArrayRef AppleCryptoNative_X509ChainGetTrustResults(SecTrustRef chain)
+{
+ if (chain == nullptr)
+ {
+ return nullptr;
+ }
+
+ CFDictionaryRef detailsAndStuff = SecTrustCopyResult(chain);
+ CFArrayRef details = nullptr;
+
+ if (detailsAndStuff != nullptr)
+ {
+ CFTypeRef detailsPtr = CFDictionaryGetValue(detailsAndStuff, CFSTR("TrustResultDetails"));
+
+ if (detailsPtr != nullptr)
+ {
+ details = reinterpret_cast<CFArrayRef>(const_cast<void*>(detailsPtr));
+ CFRetain(details);
+ }
+ }
+
+ CFRelease(detailsAndStuff);
+ return details;
+}
+
+static void MergeStatusCodes(CFTypeRef key, CFTypeRef value, void* context)
+{
+ // Windows (and therefore .NET) certificate status codes are defined on an int32_t.
+ // The top 32 bits will be used to convey error information, the bottom 32 bits
+ // as a data aggregator for the status codes.
+ uint64_t* pStatus = reinterpret_cast<uint64_t*>(context);
+
+ if (key == nullptr)
+ {
+ return;
+ }
+
+ // If any error code was already set.
+ if (*pStatus > INT_MAX)
+ {
+ return;
+ }
+
+ if (CFGetTypeID(key) != CFStringGetTypeID())
+ {
+ *pStatus |= PAL_X509ChainErrorUnknownValueType;
+ return;
+ }
+
+ (void)value;
+ CFStringRef keyString = reinterpret_cast<CFStringRef>(key);
+
+ if (CFEqual(keyString, CFSTR("NotValidBefore")) || CFEqual(keyString, CFSTR("ValidLeaf")) ||
+ CFEqual(keyString, CFSTR("ValidIntermediates")) || CFEqual(keyString, CFSTR("ValidRoot")))
+ *pStatus |= PAL_X509ChainNotTimeValid;
+ else if (CFEqual(keyString, CFSTR("Revocation")))
+ *pStatus |= PAL_X509ChainRevoked;
+ else if (CFEqual(keyString, CFSTR("KeyUsage")))
+ *pStatus |= PAL_X509ChainNotValidForUsage;
+ else if (CFEqual(keyString, CFSTR("AnchorTrusted")))
+ *pStatus |= PAL_X509ChainUntrustedRoot;
+ else if (CFEqual(keyString, CFSTR("BasicConstraints")))
+ *pStatus |= PAL_X509ChainInvalidBasicConstraints;
+ else if (CFEqual(keyString, CFSTR("UsageConstraints")))
+ *pStatus |= PAL_X509ChainExplicitDistrust;
+ else if (CFEqual(keyString, CFSTR("WeakLeaf")) || CFEqual(keyString, CFSTR("WeakIntermediates")) ||
+ CFEqual(keyString, CFSTR("WeakRoot")))
+ {
+ // Because we won't report this out of a chain built by .NET on Windows,
+ // don't report it here.
+ //
+ // (On Windows CERT_CHAIN_PARA.pStrongSignPara is NULL, so "strongness" checks
+ // are not performed).
+ }
+
+ else
+ {
+ *pStatus |= PAL_X509ChainErrorUnknownValue;
+ }
+}
+
+extern "C" int32_t AppleCryptoNative_X509ChainGetStatusAtIndex(CFArrayRef details, int64_t index, int32_t* pdwStatus)
+{
+ if (pdwStatus != nullptr)
+ *pdwStatus = -1;
+
+ if (details == nullptr || index < 0 || pdwStatus == nullptr)
+ {
+ return -1;
+ }
+
+ CFTypeRef element = CFArrayGetValueAtIndex(details, index);
+
+ if (element == nullptr)
+ {
+ return -2;
+ }
+
+ if (CFGetTypeID(element) != CFDictionaryGetTypeID())
+ {
+ return -2;
+ }
+
+ uint64_t status = 0;
+ CFDictionaryRef statusCodes = reinterpret_cast<CFDictionaryRef>(const_cast<void*>(element));
+ CFDictionaryApplyFunction(statusCodes, MergeStatusCodes, &status);
+
+ *pdwStatus = static_cast<int32_t>(status);
+ return static_cast<int32_t>(status >> 32);
+}
+
+extern "C" int32_t AppleCryptoNative_GetOSStatusForChainStatus(PAL_X509ChainStatusFlags chainStatusFlag)
+{
+ switch (chainStatusFlag)
+ {
+ case PAL_X509ChainNoError:
+ return noErr;
+ case PAL_X509ChainNotTimeValid:
+ // This code is ambiguous. Windows uses it for "not valid",
+ // Apple makes a distinction between "not yet" and "not any more"
+ // So this is just a fallback, because we should have determined it
+ // via different means.`
+ return errSecCertificateExpired;
+ case PAL_X509ChainRevoked:
+ return errSecCertificateRevoked;
+ case PAL_X509ChainNotValidForUsage:
+ return errSecKeyUsageIncorrect;
+ case PAL_X509ChainUntrustedRoot:
+ return errSecNotTrusted;
+ case PAL_X509ChainInvalidBasicConstraints:
+ return errSecNoBasicConstraints;
+ case PAL_X509ChainPartialChain:
+ return errSecCreateChainFailed;
+ case PAL_X509ChainExplicitDistrust:
+ return errSecTrustSettingDeny;
+ default:
+ return errSecCoreFoundationUnknown;
+ }
+}
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.h b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.h
new file mode 100644
index 0000000000..0b03fcbded
--- /dev/null
+++ b/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509chain.h
@@ -0,0 +1,129 @@
+// 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.
+
+#pragma once
+
+#include "pal_digest.h"
+#include "pal_seckey.h"
+
+#include <Security/Security.h>
+
+enum
+{
+ PAL_X509ChainNoError = 0,
+ PAL_X509ChainNotTimeValid = 0x00000001,
+ PAL_X509ChainNotTimeNested = 0x00000002,
+ PAL_X509ChainRevoked = 0x00000004,
+ PAL_X509ChainNotSignatureValid = 0x00000008,
+ PAL_X509ChainNotValidForUsage = 0x00000010,
+ PAL_X509ChainUntrustedRoot = 0x00000020,
+ PAL_X509ChainRevocationStatusUnknown = 0x00000040,
+ PAL_X509ChainCyclic = 0x00000080,
+ PAL_X509ChainInvalidExtension = 0x00000100,
+ PAL_X509ChainInvalidPolicyConstraints = 0x00000200,
+ PAL_X509ChainInvalidBasicConstraints = 0x00000400,
+ PAL_X509ChainInvalidNameConstraints = 0x00000800,
+ PAL_X509ChainHasNotSupportedNameConstraint = 0x00001000,
+ PAL_X509ChainHasNotDefinedNameConstraint = 0x00002000,
+ PAL_X509ChainHasNotPermittedNameConstraint = 0x00004000,
+ PAL_X509ChainHasExcludedNameConstraint = 0x00008000,
+ PAL_X509ChainPartialChain = 0x00010000,
+ PAL_X509ChainCtlNotTimeValid = 0x00020000,
+ PAL_X509ChainCtlNotSignatureValid = 0x00040000,
+ PAL_X509ChainCtlNotValidForUsage = 0x00080000,
+ PAL_X509ChainOfflineRevocation = 0x01000000,
+ PAL_X509ChainNoIssuanceChainPolicy = 0x02000000,
+ PAL_X509ChainExplicitDistrust = 0x04000000,
+ PAL_X509ChainHasNotSupportedCriticalExtension = 0x08000000,
+ PAL_X509ChainHasWeakSignature = 0x00100000,
+};
+typedef uint32_t PAL_X509ChainStatusFlags;
+
+enum
+{
+ PAL_X509ChainErrorNone = 0,
+ PAL_X509ChainErrorUnknownValueType = 0x0001L << 32,
+ PAL_X509ChainErrorUnknownValue = 0x0002L << 32,
+};
+typedef uint64_t PAL_X509ChainErrorFlags;
+
+/*
+Create a SecPolicyRef representing the basic X.509 policy
+*/
+extern "C" SecPolicyRef AppleCryptoNative_X509ChainCreateDefaultPolicy();
+
+/*
+Create a SecPolicyRef which checks for revocation (OCSP or CRL)
+*/
+extern "C" SecPolicyRef AppleCryptoNative_X509ChainCreateRevocationPolicy();
+
+/*
+Create a SecTrustRef to build a chain over the specified certificates with the given policies.
+
+certs can be either a single SecCertificateRef or an array of SecCertificateRefs. The first element
+in the array will be the certificate for which the chain is built, all other certs are to help in
+building intermediates.
+
+Returns 1 on success, 0 on failure, any other value for invalid state.
+
+Output:
+pTrustOut: Receives the SecTrustRef to build the chain, in an unbuilt state
+pOSStatus: Receives the result of SecTrustCreateWithCertificates
+*/
+extern "C" int32_t
+AppleCryptoNative_X509ChainCreate(CFTypeRef certs, CFTypeRef policies, SecTrustRef* pTrustOut, int32_t* pOSStatus);
+
+/*
+Evaluate a certificate chain.
+
+allowNetwork set to true enables fetching of CRL and AIA records
+
+Returns 1 if the chain built successfully, 0 if chain building failed, any other value for invalid
+state. Note that an untrusted chain building successfully still returns 1.
+
+Output:
+pOSStatus: Receives the result of SecTrustEvaluate
+*/
+extern "C" int32_t AppleCryptoNative_X509ChainEvaluate(SecTrustRef chain,
+ CFDateRef cfEvaluationTime,
+ bool allowNetwork,
+ int32_t* pOSStatus);
+
+/*
+Gets the number of certificates in the chain.
+*/
+extern "C" int64_t AppleCryptoNative_X509ChainGetChainSize(SecTrustRef chain);
+
+/*
+Fetches the SecCertificateRef at a given position in the chain. Position 0 is the End-Entity
+certificate, postiion 1 is the issuer of position 0, et cetera.
+*/
+extern "C" SecCertificateRef AppleCryptoNative_X509ChainGetCertificateAtIndex(SecTrustRef chain, int64_t index);
+
+/*
+Get a CFRetain()ed array of dictionaries which contain the detailed results for each element in
+the certificate chain.
+*/
+extern "C" CFArrayRef AppleCryptoNative_X509ChainGetTrustResults(SecTrustRef chain);
+
+/*
+Get the PAL_X509ChainStatusFlags values for the certificate at the requested position within the
+chain.
+
+Returns 0 on success, non-zero on error.
+
+Output:
+pdwStatus: Receives a flags value for the various status codes that went awry at the given position
+*/
+extern "C" int32_t AppleCryptoNative_X509ChainGetStatusAtIndex(CFArrayRef details, int64_t index, int32_t* pdwStatus);
+
+/*
+Looks up the equivalent OSStatus code for a given PAL_X509ChainStatusFlags single-bit value.
+
+Returns errSecCoreFoundationUnknown on bad/unmapped input, otherwise the appropriate response.
+
+Note that PAL_X509ChainNotTimeValid is an ambiguous code, it could be errSecCertificateExpired or
+errSecCertificateNotValidYet. A caller should resolve that code via other means.
+*/
+extern "C" int32_t AppleCryptoNative_GetOSStatusForChainStatus(PAL_X509ChainStatusFlags chainStatusFlag);
diff --git a/src/Native/pkg/runtime.native.System.Data.SqlClient.sni/win/runtime.native.System.Data.SqlClient.sni.pkgproj b/src/Native/pkg/runtime.native.System.Data.SqlClient.sni/win/runtime.native.System.Data.SqlClient.sni.pkgproj
deleted file mode 100644
index b2b8fdc642..0000000000
--- a/src/Native/pkg/runtime.native.System.Data.SqlClient.sni/win/runtime.native.System.Data.SqlClient.sni.pkgproj
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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" />
- <PropertyGroup>
- <!-- This package is also being used for the 1.1 release, so it follows 1.1 versioning -->
- <PackageVersion>4.3.0</PackageVersion>
- <WinVersion Condition="'$(PackagePlatform)'=='arm64'">win10</WinVersion>
- <WinVersion Condition="'$(PackagePlatform)'=='x86' OR '$(PackagePlatform)'=='x64'">win7</WinVersion>
- <!-- use the same naming convention as a runtime package, but don't treat as a runtime dependency -->
- <IdPrefix>runtime.$(WinVersion)-$(PackagePlatform).</IdPrefix>
- <PackagePlatforms>x64;x86;arm64;</PackagePlatforms>
- <SkipValidatePackage>true</SkipValidatePackage>
- <!-- don't package this as runtime specific -->
- <PackageTargetRuntime>
- </PackageTargetRuntime>
- </PropertyGroup>
- <!-- These paths are not built in CoreFx, enable when fixing https://github.com/dotnet/corefx/issues/826 -->
- <ItemGroup>
- <NativeFile Include="$(WinNativePath)sni.dll">
- <TargetPath>runtimes/$(WinVersion)-$(PackagePlatform)/native</TargetPath>
- </NativeFile>
- </ItemGroup>
- <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project> \ No newline at end of file
diff --git a/src/System.CodeDom/src/System/CodeDom/CodeNamespaceImportCollection.cs b/src/System.CodeDom/src/System/CodeDom/CodeNamespaceImportCollection.cs
index 35e2d42735..a86ecdde90 100644
--- a/src/System.CodeDom/src/System/CodeDom/CodeNamespaceImportCollection.cs
+++ b/src/System.CodeDom/src/System/CodeDom/CodeNamespaceImportCollection.cs
@@ -10,12 +10,12 @@ namespace System.CodeDom
[Serializable]
public class CodeNamespaceImportCollection : IList
{
- private readonly List<CodeNamespaceImport> _data = new List<CodeNamespaceImport>();
+ private readonly ArrayList _data = new ArrayList(); // not List<CodeNamespaceImport> to provide desktop-consistent semantics for CopyTo
private readonly Dictionary<string, CodeNamespaceImport> _keys = new Dictionary<string, CodeNamespaceImport>(StringComparer.OrdinalIgnoreCase);
public CodeNamespaceImport this[int index]
{
- get { return _data[index]; }
+ get { return (CodeNamespaceImport)_data[index]; }
set
{
_data[index] = value;
@@ -84,15 +84,15 @@ namespace System.CodeDom
object ICollection.SyncRoot => null;
- void ICollection.CopyTo(Array array, int index) => ((IList)_data).CopyTo(array, index);
+ void ICollection.CopyTo(Array array, int index) => _data.CopyTo(array, index);
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
- int IList.Add(object value) => ((IList)_data).Add((CodeNamespaceImport)value);
+ int IList.Add(object value) => _data.Add((CodeNamespaceImport)value);
void IList.Clear() => Clear();
- bool IList.Contains(object value) => ((IList)_data).Contains(value);
+ bool IList.Contains(object value) => _data.Contains(value);
int IList.IndexOf(object value) => _data.IndexOf((CodeNamespaceImport)value);
diff --git a/src/System.CodeDom/tests/CodeCollections/CodeNamespaceImportCollectionTests.cs b/src/System.CodeDom/tests/CodeCollections/CodeNamespaceImportCollectionTests.cs
index f4076f033e..b7cb7c22c2 100644
--- a/src/System.CodeDom/tests/CodeCollections/CodeNamespaceImportCollectionTests.cs
+++ b/src/System.CodeDom/tests/CodeCollections/CodeNamespaceImportCollectionTests.cs
@@ -17,11 +17,11 @@ namespace System.CodeDom.Tests
protected override bool NullAllowed => false;
protected override bool Enumerator_Current_UndefinedOperation_Throws => true;
- protected override bool IList_CurrentAfterAdd_Throws => false;
protected override bool ICollection_NonGeneric_HasNullSyncRoot => true;
protected override Type ICollection_NonGeneric_CopyTo_NonZeroLowerBound_ThrowType => typeof(ArgumentOutOfRangeException);
- protected override Type ICollection_NonGeneric_CopyTo_ArrayOfEnumType_ThrowType => typeof(ArgumentException);
+ protected override Type ICollection_NonGeneric_CopyTo_ArrayOfIncorrectReferenceType_ThrowType => typeof(InvalidCastException);
+ protected override Type ICollection_NonGeneric_CopyTo_ArrayOfIncorrectValueType_ThrowType => typeof(InvalidCastException);
[Fact]
public void Add()
diff --git a/src/System.Collections.NonGeneric/tests/SortedListTests.cs b/src/System.Collections.NonGeneric/tests/SortedListTests.cs
index ea5e34afdb..eafb5e2b75 100644
--- a/src/System.Collections.NonGeneric/tests/SortedListTests.cs
+++ b/src/System.Collections.NonGeneric/tests/SortedListTests.cs
@@ -855,7 +855,7 @@ namespace System.Collections.Tests
{
IList keys = sortList2.GetKeyList();
AssertExtensions.Throws<ArgumentNullException>("destinationArray", "dest", () => keys.CopyTo(null, 0)); // Array is null
- Assert.Throws<ArgumentException>("array", () => keys.CopyTo(new object[10, 10], 0)); // Array is multidimensional
+ AssertExtensions.Throws<ArgumentException>("array", null, () => keys.CopyTo(new object[10, 10], 0)); // Array is multidimensional -- in netfx ParamName is null
// Index < 0
AssertExtensions.Throws<ArgumentOutOfRangeException>("destinationIndex", "dstIndex", () => keys.CopyTo(new object[100], -1));
@@ -1097,7 +1097,7 @@ namespace System.Collections.Tests
{
IList values = sortList2.GetValueList();
AssertExtensions.Throws<ArgumentNullException>("destinationArray", "dest", () => values.CopyTo(null, 0)); // Array is null
- Assert.Throws<ArgumentException>("array", () => values.CopyTo(new object[10, 10], 0)); // Array is multidimensional
+ AssertExtensions.Throws<ArgumentException>("array", null, () => values.CopyTo(new object[10, 10], 0)); // Array is multidimensional -- in netfx ParamName is null
AssertExtensions.Throws<ArgumentOutOfRangeException>("destinationIndex", "dstIndex", () => values.CopyTo(new object[100], -1)); // Index < 0
AssertExtensions.Throws<ArgumentException>("destinationArray", string.Empty, () => values.CopyTo(new object[150], 51)); // Index + list.Count > array.Count
diff --git a/src/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.CopyToTests.cs b/src/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.CopyToTests.cs
index dabe03a7e9..2a12066951 100644
--- a/src/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.CopyToTests.cs
+++ b/src/System.Collections.Specialized/tests/NameValueCollection/NameValueCollection.CopyToTests.cs
@@ -59,7 +59,7 @@ namespace System.Collections.Specialized.Tests
{
NameValueCollection nameValueCollection = Helpers.CreateNameValueCollection(count);
Assert.Throws<ArgumentNullException>("dest", () => nameValueCollection.CopyTo(null, 0));
- Assert.Throws<ArgumentException>("dest", () => nameValueCollection.CopyTo(new string[count, count], 0));
+ AssertExtensions.Throws<ArgumentException>("dest", null, () => nameValueCollection.CopyTo(new string[count, count], 0)); // in netfx when passing multidimensional arrays Exception.ParamName is null.
Assert.Throws<ArgumentOutOfRangeException>("index", () => nameValueCollection.CopyTo(new string[count], -1));
diff --git a/src/System.Collections.Specialized/tests/System.Collections.Specialized.Tests.csproj b/src/System.Collections.Specialized/tests/System.Collections.Specialized.Tests.csproj
index b91c7204f5..9e0f072ac9 100644
--- a/src/System.Collections.Specialized/tests/System.Collections.Specialized.Tests.csproj
+++ b/src/System.Collections.Specialized/tests/System.Collections.Specialized.Tests.csproj
@@ -8,6 +8,9 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" />
<ItemGroup>
+ <Compile Include="$(CommonTestPath)\System\AssertExtensions.cs">
+ <Link>Common\System\AssertExtensions.cs</Link>
+ </Compile>
<!-- Common Collections tests -->
<Compile Include="$(CommonTestPath)\System\Collections\CollectionAsserts.cs">
<Link>Common\System\Collections\CollectionAsserts.cs</Link>
diff --git a/src/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj b/src/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj
index 8e28398274..cf3ae68087 100644
--- a/src/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj
+++ b/src/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj
@@ -260,7 +260,6 @@
<Reference Include="System.Linq" />
<Reference Include="System.Net.Primitives" />
<Reference Include="System.Net.Security" />
- <Reference Include="System.Net.WebClient" />
<Reference Include="System.ObjectModel" />
<Reference Include="System.Resources.ResourceManager" />
<Reference Include="System.Resources.Writer" />
diff --git a/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs b/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs
index 4aa960fcde..52284b6340 100644
--- a/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs
+++ b/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs
@@ -82,9 +82,6 @@ namespace System.ComponentModel.Design
savedLicenseKeys = new Hashtable();
}
- Uri licenseFile = null;
- // the AppDomain.CurrentDomain.SetupInformation.LicenseFile always returns null.
- // This means we have to find the license file using the fallback logic below.
if (resourceAssembly == null)
{
resourceAssembly = Assembly.GetEntryAssembly();
@@ -167,20 +164,6 @@ namespace System.ComponentModel.Design
DesigntimeLicenseContextSerializer.Deserialize(s, fileName.ToUpper(CultureInfo.InvariantCulture), this);
}
}
-
- if (licenseFile != null)
- {
- Debug.WriteLineIf(s_runtimeLicenseContextSwitch.TraceVerbose, $"licenseFile: {licenseFile.ToString()}");
- Debug.WriteLineIf(s_runtimeLicenseContextSwitch.TraceVerbose, $"opening licenses file over URI {licenseFile.ToString()}");
- Stream s = OpenRead(licenseFile);
- if (s != null)
- {
- string[] segments = licenseFile.Segments;
- string licFileName = segments[segments.Length - 1];
- string key = licFileName.Substring(0, licFileName.LastIndexOf("."));
- DesigntimeLicenseContextSerializer.Deserialize(s, key.ToUpper(CultureInfo.InvariantCulture), this);
- }
- }
}
Debug.WriteLineIf(s_runtimeLicenseContextSwitch.TraceVerbose, $"returning : {(string)savedLicenseKeys[type.AssemblyQualifiedName]}");
return (string)savedLicenseKeys[type.AssemblyQualifiedName];
@@ -216,24 +199,6 @@ namespace System.ComponentModel.Design
//
return satellite.GetManifestResourceStream(name);
}
-
- private static Stream OpenRead(Uri resourceUri)
- {
- Stream result = null;
-
- try
- {
- WebClient webClient = new WebClient();
- webClient.Credentials = CredentialCache.DefaultCredentials;
- result = webClient.OpenRead(resourceUri.ToString());
- }
- catch (Exception e)
- {
- Debug.Fail(e.ToString());
- }
-
- return result;
- }
}
}
diff --git a/src/System.Diagnostics.DiagnosticSource/ref/Configurations.props b/src/System.Diagnostics.DiagnosticSource/ref/Configurations.props
index 129adad32f..767d2c77b3 100644
--- a/src/System.Diagnostics.DiagnosticSource/ref/Configurations.props
+++ b/src/System.Diagnostics.DiagnosticSource/ref/Configurations.props
@@ -4,6 +4,8 @@
<BuildConfigurations>
netstandard;
netstandard1.0;
+ netfx;
+ net46;
</BuildConfigurations>
</PropertyGroup>
</Project> \ No newline at end of file
diff --git a/src/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.csproj b/src/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.csproj
index 4fb859381f..f6ea0cbe23 100644
--- a/src/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.csproj
+++ b/src/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSource.csproj
@@ -5,16 +5,25 @@
<UseOpenKey>true</UseOpenKey>
<ProjectGuid>{3DF9A5D5-3D4B-4378-9B55-CFA6AC0114D9}</ProjectGuid>
</PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'net46-Windows_NT-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'net46-Windows_NT-Release|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.0-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.0-Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netfx-Windows_NT-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netfx-Windows_NT-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="System.Diagnostics.DiagnosticSource.cs" />
</ItemGroup>
- <ItemGroup Condition="'$(TargetGroup)' != 'netstandard1.1'">
+ <ItemGroup Condition="'$(TargetGroup)' != 'netstandard1.0'">
<Compile Include="System.Diagnostics.DiagnosticSourceActivity.cs" />
- <Reference Include="System.Runtime" />
</ItemGroup>
+ <ItemGroup Condition="'$(TargetGroup)' == 'net46' OR '$(TargetGroup)' == 'netfx'">
+ <Reference Include="mscorlib" />
+ </ItemGroup>
+ <ItemGroup Condition="'$(TargetGroup)' == 'netstandard1.0'">
+ <Reference Include="System.Runtime" />
+ </ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project> \ No newline at end of file
diff --git a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_secimportexport.cpp b/src/System.Diagnostics.DiagnosticSource/src/AssemblyInfo.Net46.cs
index aab94032ad..bfb744e1bc 100644
--- a/src/Native/Unix/System.Security.Cryptography.Native.Apple/pal_secimportexport.cpp
+++ b/src/System.Diagnostics.DiagnosticSource/src/AssemblyInfo.Net46.cs
@@ -2,4 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-#include "pal_secimportexport.h"
+using System.Security;
+
+[assembly: AllowPartiallyTrustedCallers]
diff --git a/src/System.Diagnostics.DiagnosticSource/src/Configurations.props b/src/System.Diagnostics.DiagnosticSource/src/Configurations.props
index 53d4595775..a33165590b 100644
--- a/src/System.Diagnostics.DiagnosticSource/src/Configurations.props
+++ b/src/System.Diagnostics.DiagnosticSource/src/Configurations.props
@@ -6,6 +6,8 @@
netstandard;
uap-Windows_NT;
netcoreapp;
+ net46-Windows_NT;
+ netfx-Windows_NT;
</BuildConfigurations>
</PropertyGroup>
</Project> \ No newline at end of file
diff --git a/src/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj b/src/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj
index 604ffb7359..924576594a 100644
--- a/src/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj
+++ b/src/System.Diagnostics.DiagnosticSource/src/System.Diagnostics.DiagnosticSource.csproj
@@ -5,25 +5,27 @@
<ProjectGuid>{F24D3391-2928-4E83-AADE-B34423498750}</ProjectGuid>
<DocumentationFile>$(OutputPath)$(MSBuildProjectName).xml</DocumentationFile>
<UseOpenKey>true</UseOpenKey>
- <!-- duplicate in net46 folder so older nuget clients can consume -->
- <PackageTargetFramework Condition="'$(TargetGroup)' == 'netstandard1.3'">netstandard1.3;net46</PackageTargetFramework>
<!-- To allow this library to work on V4.5 runtimes and other old platforms
we also have a separate complilation of this DLL that works for V4.5
(which is netstandard1.1). Again we duplicate in a portable-* folder
to work with older NuGet clients -->
<PackageTargetFramework Condition="'$(TargetGroup)' == 'netstandard1.1'">netstandard1.1;portable-net45+win8+wpa81</PackageTargetFramework>
- <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+ <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
- <PropertyGroup Condition="'$(TargetGroup)' == 'netstandard1.1' Or '$(TargetGroup)' == 'net45'">
+ <PropertyGroup Condition="'$(TargetGroup)' == 'netstandard1.1'">
<DefineConstants>;NO_EVENTSOURCE_COMPLEX_TYPE_SUPPORT</DefineConstants>
</PropertyGroup>
<!-- Default configurations to help VS understand the configurations -->
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'net46-Windows_NT-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'net46-Windows_NT-Release|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Release|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.3-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.3-Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netfx-Windows_NT-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netfx-Windows_NT-Release|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Release|AnyCPU'" />
<ItemGroup>
@@ -37,21 +39,13 @@
<Compile Include="System\Diagnostics\DiagnosticSourceActivity.cs" />
<None Include="ActivityUserGuide.md" />
</ItemGroup>
- <ItemGroup Condition=" '$(TargetGroup)' != 'net45' And '$(TargetGroup)' != 'netstandard1.1'">
- <Compile Include="System\Diagnostics\Activity.Current.net46.cs" />
- </ItemGroup>
- <ItemGroup Condition=" '$(TargetGroup)' == 'net45' ">
- <Compile Include="System\Diagnostics\Activity.Current.net45.cs" />
- <TargetingPackReference Include="System" />
- <TargetingPackReference Include="System.Runtime.Remoting" />
- </ItemGroup>
- <ItemGroup Condition=" '$(TargetGroup)' == 'netstandard1.3' ">
+ <ItemGroup Condition=" '$(TargetGroup)' == 'netstandard1.3' OR '$(TargetGroup)' == 'uap'">
<Compile Include="System\Diagnostics\Activity.Id.Random.cs" />
</ItemGroup>
- <ItemGroup Condition=" '$(TargetGroup)' != 'netstandard1.3' And '$(TargetGroup)' != 'netstandard1.1'">
+ <ItemGroup Condition=" '$(TargetGroup)' == 'netcoreapp' OR '$(TargetGroup)' == 'netstandard' OR '$(TargetGroup)' == 'net46' OR '$(TargetGroup)' == 'netfx'">
<Compile Include="System\Diagnostics\Activity.Id.MachineName.cs" />
</ItemGroup>
- <ItemGroup>
+ <ItemGroup Condition="'$(TargetGroup)' != 'net46' AND '$(TargetGroup)' != 'netfx'">
<Reference Include="System.Collections" />
<Reference Include="System.Diagnostics.Debug" />
<Reference Include="System.Diagnostics.Tracing" />
@@ -60,5 +54,10 @@
<Reference Include="System.Runtime.Extensions" />
<Reference Include="System.Threading" />
</ItemGroup>
+ <ItemGroup Condition="'$(TargetGroup)' == 'net46' OR '$(TargetGroup)' == 'netfx'">
+ <Compile Include="AssemblyInfo.Net46.cs" />
+ <Reference Include="mscorlib" />
+ <Reference Include="System" />
+ </ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project> \ No newline at end of file
diff --git a/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.Current.net45.cs b/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.Current.net45.cs
deleted file mode 100644
index 3a2a64d79e..0000000000
--- a/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.Current.net45.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// 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.Remoting.Messaging;
-using System.Threading;
-
-namespace System.Diagnostics
-{
- public partial class Activity
- {
- /// <summary>
- /// Returns the current operation (Activity) for the current thread. This flows
- /// across async calls.
- /// </summary>
- public static Activity Current
- {
- get
- {
- return (Activity)CallContext.LogicalGetData(FieldKey);
- }
- private set
- {
- CallContext.LogicalSetData(FieldKey, value);
- }
- }
-
- #region private
- private static readonly string FieldKey = $"{typeof(Activity).FullName}.Value.{AppDomain.CurrentDomain.Id}";
- #endregion
- }
-}
diff --git a/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.Current.net46.cs b/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.Current.net46.cs
deleted file mode 100644
index 58aeef813d..0000000000
--- a/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.Current.net46.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-// 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.Threading;
-
-namespace System.Diagnostics
-{
- public partial class Activity
- {
- /// <summary>
- /// Returns the current operation (Activity) for the current thread. This flows
- /// across async calls.
- /// </summary>
- public static Activity Current
- {
- get { return s_current.Value; }
- private set { s_current.Value = value; }
- }
-
-#region private
- private static readonly AsyncLocal<Activity> s_current = new AsyncLocal<Activity>();
-#endregion // private
- }
-}
diff --git a/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs b/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs
index d1d11a9054..f19a017f01 100644
--- a/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs
+++ b/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Activity.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
+using System.Security;
using System.Threading;
namespace System.Diagnostics
@@ -134,6 +135,16 @@ namespace System.Diagnostics
}
/// <summary>
+ /// Returns the current operation (Activity) for the current thread. This flows
+ /// across async calls.
+ /// </summary>
+ public static Activity Current
+ {
+ get { return s_current.Value; }
+ private set { s_current.Value = value; }
+ }
+
+ /// <summary>
/// Returns the value of the key-value pair added to the activity with <see cref="AddBaggage(string, string)"/>.
/// Returns null if that key does not exist.
/// </summary>
@@ -358,7 +369,7 @@ namespace System.Diagnostics
private string AppendSuffix(string parentId, string suffix, char delimiter)
{
#if DEBUG
- suffix = OperationName + "-" + suffix;
+ suffix = OperationName.Replace('.', '-') + "-" + suffix;
#endif
if (parentId.Length + suffix.Length < RequestIdMaxLength)
return parentId + suffix + delimiter;
@@ -392,13 +403,14 @@ namespace System.Diagnostics
Interlocked.CompareExchange(ref s_uniqPrefix, GenerateInstancePrefix(), null);
}
#if DEBUG
- string ret = s_uniqPrefix + "-" + OperationName + "-" + Interlocked.Increment(ref s_currentRootId).ToString("x") + '.';
+ string ret = s_uniqPrefix + "-" + OperationName.Replace('.', '-') + "-" + Interlocked.Increment(ref s_currentRootId).ToString("x") + '.';
#else // To keep things short, we drop the operation name
string ret = s_uniqPrefix + "-" + Interlocked.Increment(ref s_currentRootId).ToString("x") + '.';
#endif
return ret;
}
+ [SecuritySafeCritical]
private static unsafe long GetRandomNumber()
{
Guid g = Guid.NewGuid();
@@ -413,7 +425,7 @@ namespace System.Diagnostics
//A unique number inside the appdomain, randomized between appdomains.
//Int gives enough randomization and keeps hex-encoded s_currentRootId 8 chars long for most applications
- private static long s_currentRootId = (int)GetRandomNumber();
+ private static long s_currentRootId = (uint)GetRandomNumber();
private const int RequestIdMaxLength = 1024;
private const char RootIdPrefix = '|';
@@ -429,6 +441,7 @@ namespace System.Diagnostics
private KeyValueListNode _tags;
private KeyValueListNode _baggage;
private bool isFinished;
-#endregion // private
+ private static readonly AsyncLocal<Activity> s_current = new AsyncLocal<Activity>();
+ #endregion // private
}
}
diff --git a/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Linux.cs b/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Linux.cs
index 8bdc65217e..a64f50d89a 100644
--- a/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Linux.cs
+++ b/src/System.Diagnostics.Process/src/System/Diagnostics/ProcessManager.Linux.cs
@@ -193,18 +193,27 @@ namespace System.Diagnostics
{
switch (c)
{
- case 'R':
+ case 'R': // Running
return ThreadState.Running;
- case 'S':
- case 'D':
- case 'T':
+
+ case 'D': // Waiting on disk
+ case 'P': // Parked
+ case 'S': // Sleeping in a wait
+ case 't': // Tracing/debugging
+ case 'T': // Stopped on a signal
return ThreadState.Wait;
- case 'Z':
+
+ case 'x': // dead
+ case 'X': // Dead
+ case 'Z': // Zombie
return ThreadState.Terminated;
- case 'W':
+
+ case 'W': // Paging or waking
+ case 'K': // Wakekill
return ThreadState.Transition;
+
default:
- Debug.Fail("Unexpected status character");
+ Debug.Fail($"Unexpected status character: {c}");
return ThreadState.Unknown;
}
}
diff --git a/src/System.Diagnostics.Tools/src/Configurations.props b/src/System.Diagnostics.Tools/src/Configurations.props
index bbb5be814e..aed58fb42e 100644
--- a/src/System.Diagnostics.Tools/src/Configurations.props
+++ b/src/System.Diagnostics.Tools/src/Configurations.props
@@ -3,6 +3,7 @@
<PropertyGroup>
<BuildConfigurations>
uap-Windows_NT;
+ uapaot-Windows_NT;
netcoreapp-Windows_NT;
netcoreapp-Unix;
</BuildConfigurations>
diff --git a/src/System.Diagnostics.Tools/src/System.Diagnostics.Tools.csproj b/src/System.Diagnostics.Tools/src/System.Diagnostics.Tools.csproj
index 04f2d50e14..e6a301528a 100644
--- a/src/System.Diagnostics.Tools/src/System.Diagnostics.Tools.csproj
+++ b/src/System.Diagnostics.Tools/src/System.Diagnostics.Tools.csproj
@@ -14,6 +14,8 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Windows_NT-Release|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uapaot-Windows_NT-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uapaot-Windows_NT-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="System\CodeDom\Compiler\GeneratedCodeAttribute.cs" />
<Compile Include="$(CommonPath)\System\Diagnostics\CodeAnalysis\ExcludeFromCodeCoverageAttribute.cs">
diff --git a/src/System.Diagnostics.TraceSource/src/Resources/Strings.resx b/src/System.Diagnostics.TraceSource/src/Resources/Strings.resx
index aecf61d8b2..d726fb97c6 100644
--- a/src/System.Diagnostics.TraceSource/src/Resources/Strings.resx
+++ b/src/System.Diagnostics.TraceSource/src/Resources/Strings.resx
@@ -58,6 +58,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
+ <data name="ExceptionOccurred" xml:space="preserve">
+ <value>An exception occurred while writing to the log file {0}: {1}.</value>
+ </data>
<data name="MustAddListener" xml:space="preserve">
<value>Only TraceListeners can be added to a TraceListenerCollection.</value>
</data>
diff --git a/src/System.Diagnostics.TraceSource/src/System.Diagnostics.TraceSource.csproj b/src/System.Diagnostics.TraceSource/src/System.Diagnostics.TraceSource.csproj
index ea970fd484..ac1ba88e14 100644
--- a/src/System.Diagnostics.TraceSource/src/System.Diagnostics.TraceSource.csproj
+++ b/src/System.Diagnostics.TraceSource/src/System.Diagnostics.TraceSource.csproj
@@ -65,6 +65,7 @@
<Reference Include="System.Collections.Specialized" />
<Reference Include="System.Diagnostics.Debug" />
<Reference Include="System.Diagnostics.Tools" />
+ <Reference Include="System.IO.FileSystem" />
<Reference Include="System.Resources.ResourceManager" />
<Reference Include="System.Runtime" />
<Reference Include="System.Runtime.Extensions" />
diff --git a/src/System.Diagnostics.TraceSource/src/System/Diagnostics/DefaultTraceListener.cs b/src/System.Diagnostics.TraceSource/src/System/Diagnostics/DefaultTraceListener.cs
index 039989fc9f..843a5c61b6 100644
--- a/src/System.Diagnostics.TraceSource/src/System/Diagnostics/DefaultTraceListener.cs
+++ b/src/System.Diagnostics.TraceSource/src/System/Diagnostics/DefaultTraceListener.cs
@@ -115,7 +115,33 @@ namespace System.Diagnostics
/// </devdoc>
public override void Write(string message)
{
- if (NeedIndent) WriteIndent();
+ Write(message, useLogFile: true);
+ }
+
+ /// <devdoc>
+ /// <para>
+ /// Writes the output followed by a line terminator using <see cref="System.Diagnostics.Debug.Write"/>.
+ /// </para>
+ /// </devdoc>
+ public override void WriteLine(string message)
+ {
+ WriteLine(message, useLogFile: true);
+ }
+
+ private void WriteLine(string message, bool useLogFile)
+ {
+ if (NeedIndent)
+ WriteIndent();
+
+ // The concat is done here to enable a single call to Write
+ Write(message + Environment.NewLine, useLogFile);
+ NeedIndent = true;
+ }
+
+ private void Write(string message, bool useLogFile)
+ {
+ if (NeedIndent)
+ WriteIndent();
// really huge messages mess up both VS and dbmon, so we chop it up into
// reasonable chunks if it's too big
@@ -132,20 +158,21 @@ namespace System.Diagnostics
}
Debug.Write(message.Substring(offset));
}
+
+ if (useLogFile && !string.IsNullOrEmpty(LogFileName))
+ WriteToLogFile(message);
}
- /// <devdoc>
- /// <para>
- /// Writes the output followed by a line terminator using <see cref="System.Diagnostics.Debug.Write"/>.
- /// </para>
- /// </devdoc>
- public override void WriteLine(string message)
+ private void WriteToLogFile(string message)
{
- if (NeedIndent) WriteIndent();
- // I do the concat here to make sure it goes as one call to the output.
- // we would save a stringbuilder operation by calling Write twice.
- Write(message + Environment.NewLine);
- NeedIndent = true;
+ try
+ {
+ File.AppendAllText(LogFileName, message);
+ }
+ catch (Exception e)
+ {
+ WriteLine(string.Format(SR.ExceptionOccurred, LogFileName, e.ToString()), useLogFile: false);
+ }
}
}
}
diff --git a/src/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceInternal.cs b/src/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceInternal.cs
index 423c5e3a7c..6638d2a1f6 100644
--- a/src/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceInternal.cs
+++ b/src/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceInternal.cs
@@ -236,7 +236,7 @@ namespace System.Diagnostics
{
foreach (TraceListener listener in Listeners)
{
- listener.Dispose();
+ listener.Close();
}
}
}
diff --git a/src/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceSource.cs b/src/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceSource.cs
index 9061cdcd22..6ad268fe56 100644
--- a/src/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceSource.cs
+++ b/src/System.Diagnostics.TraceSource/src/System/Diagnostics/TraceSource.cs
@@ -104,7 +104,7 @@ namespace System.Diagnostics
{
foreach (TraceListener listener in _listeners)
{
- listener.Dispose();
+ listener.Close();
}
}
}
diff --git a/src/System.Diagnostics.TraceSource/tests/DefaultTraceListenerClassTests.cs b/src/System.Diagnostics.TraceSource/tests/DefaultTraceListenerClassTests.cs
index 4d94b8c9fa..7fa6899d0c 100644
--- a/src/System.Diagnostics.TraceSource/tests/DefaultTraceListenerClassTests.cs
+++ b/src/System.Diagnostics.TraceSource/tests/DefaultTraceListenerClassTests.cs
@@ -3,19 +3,22 @@
// See the LICENSE file in the project root for more information.
using System.IO;
+using System.Reflection;
using Xunit;
namespace System.Diagnostics.TraceSourceTests
{
- public class DefaultTraceListenerClassTests
+ public class DefaultTraceListenerClassTests : FileCleanupTestBase
{
private class TestDefaultTraceListener : DefaultTraceListener
{
private StringWriter _writer;
+ public bool ShouldOverrideWriteLine { get; set; } = true;
public TestDefaultTraceListener()
{
_writer = new StringWriter();
+ AssertUiEnabled = false;
}
public String Output
@@ -27,6 +30,18 @@ namespace System.Diagnostics.TraceSourceTests
{
_writer.Write(message);
}
+
+ public override void WriteLine(string message)
+ {
+ if (ShouldOverrideWriteLine)
+ {
+ _writer.WriteLine(message);
+ }
+ else
+ {
+ base.WriteLine(message);
+ }
+ }
}
[Fact]
@@ -45,6 +60,40 @@ namespace System.Diagnostics.TraceSourceTests
}
[Fact]
+ public void Fail_WithoutWriteLineOverride()
+ {
+ var listener = new TestDefaultTraceListener();
+ listener.ShouldOverrideWriteLine = false;
+ listener.Fail("FAIL");
+ Assert.False(listener.Output.Contains("FAIL"));
+ }
+
+ [Fact]
+ public void Fail_WithLogFile()
+ {
+ var listener = new TestDefaultTraceListener();
+ string pathToLogFile = GetTestFilePath();
+
+ listener.LogFileName = pathToLogFile;
+ listener.ShouldOverrideWriteLine = false;
+ listener.Fail("FAIL");
+
+ Assert.True(File.Exists(pathToLogFile));
+ Assert.Contains("FAIL", File.ReadAllText(pathToLogFile));
+ }
+
+ [Fact]
+ public void Fail_LogFileDirectoryNotFound()
+ {
+ // Exception should be handled by DefaultTraceListener.WriteLine so no need to assert.
+ var listener = new TestDefaultTraceListener();
+ listener.LogFileName = $"{Guid.NewGuid().ToString("N")}\\LogFile.txt";
+
+ listener.ShouldOverrideWriteLine = false;
+ listener.Fail("FAIL");
+ }
+
+ [Fact]
public void TraceEvent()
{
var listener = new TestDefaultTraceListener();
diff --git a/src/System.Diagnostics.TraceSource/tests/System.Diagnostics.TraceSource.Tests.csproj b/src/System.Diagnostics.TraceSource/tests/System.Diagnostics.TraceSource.Tests.csproj
index bcdb892b39..21f56fbd76 100644
--- a/src/System.Diagnostics.TraceSource/tests/System.Diagnostics.TraceSource.Tests.csproj
+++ b/src/System.Diagnostics.TraceSource/tests/System.Diagnostics.TraceSource.Tests.csproj
@@ -31,6 +31,9 @@
<Compile Include="TraceSwitchClassTests.cs" />
<Compile Include="TraceTestHelper.cs" />
<Compile Include="CorrelationManagerTests.cs" />
+ <Compile Include="$(CommonTestPath)\System\IO\FileCleanupTestBase.cs">
+ <Link>Common\System\IO\FileCleanupTestBase.cs</Link>
+ </Compile>
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
diff --git a/src/System.Diagnostics.TraceSource/tests/TestTraceListener.cs b/src/System.Diagnostics.TraceSource/tests/TestTraceListener.cs
index 48b1611aef..bf36fde93e 100644
--- a/src/System.Diagnostics.TraceSource/tests/TestTraceListener.cs
+++ b/src/System.Diagnostics.TraceSource/tests/TestTraceListener.cs
@@ -19,10 +19,11 @@ namespace System.Diagnostics.TraceSourceTests
WriteLine,
Flush,
Fail,
+ Close
//NOTE: update MethodEnumCount if values are added
}
- private const int MethodEnumCount = 8;
+ private const int MethodEnumCount = 9;
public TestTraceListener(bool threadSafe = false)
: this(null, threadSafe)
@@ -119,5 +120,10 @@ namespace System.Diagnostics.TraceSourceTests
{
Call(Method.WriteLine);
}
+
+ public override void Close()
+ {
+ Call(Method.Close);
+ }
}
}
diff --git a/src/System.Diagnostics.TraceSource/tests/TraceClassTests.cs b/src/System.Diagnostics.TraceSource/tests/TraceClassTests.cs
index 85a9895589..660c2758cb 100644
--- a/src/System.Diagnostics.TraceSource/tests/TraceClassTests.cs
+++ b/src/System.Diagnostics.TraceSource/tests/TraceClassTests.cs
@@ -2,6 +2,8 @@
// 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.IO;
+using System.Reflection;
using Xunit;
namespace System.Diagnostics.TraceSourceTests
@@ -10,7 +12,8 @@ namespace System.Diagnostics.TraceSourceTests
public class TraceClassTests : IDisposable
{
- private const string TestRunnerAssemblyName = "xunit.console.netcore";
+ private readonly string TestRunnerAssemblyName = TraceTestHelper.IsFullFramework ?
+ Path.GetFileName(Environment.GetCommandLineArgs()[0]) : Assembly.GetEntryAssembly().GetName().Name;
void IDisposable.Dispose()
{
@@ -96,13 +99,15 @@ namespace System.Diagnostics.TraceSourceTests
var listener = new TestTraceListener();
Trace.Listeners.Add(listener);
Trace.Close();
- Assert.Equal(1, listener.GetCallCount(Method.Dispose));
+ Assert.Equal(1, listener.GetCallCount(Method.Close));
}
[Fact]
public void Assert1Test()
{
var listener = new TestTraceListener();
+ // We have to clear the listeners list on Trace since there is a trace listener by default with AssertUiEnabled = true in Desktop and that will pop up an assert window with Trace.Fail
+ Trace.Listeners.Clear();
Trace.Listeners.Add(listener);
Trace.Assert(true);
Assert.Equal(0, listener.GetCallCount(Method.WriteLine));
@@ -117,6 +122,8 @@ namespace System.Diagnostics.TraceSourceTests
{
var listener = new TestTraceListener();
var text = new TestTextTraceListener();
+ // We have to clear the listeners list on Trace since there is a trace listener by default with AssertUiEnabled = true in Desktop and that will pop up an assert window with Trace.Fail
+ Trace.Listeners.Clear();
Trace.Listeners.Add(listener);
Trace.Listeners.Add(text);
Trace.Assert(true, "Message");
@@ -136,6 +143,8 @@ namespace System.Diagnostics.TraceSourceTests
{
var listener = new TestTraceListener();
var text = new TestTextTraceListener();
+ // We have to clear the listeners list on Trace since there is a trace listener by default with AssertUiEnabled = true in Desktop and that will pop up an assert window with Trace.Fail
+ Trace.Listeners.Clear();
Trace.Listeners.Add(listener);
Trace.Listeners.Add(text);
Trace.Assert(true, "Message", "Detail");
@@ -303,6 +312,8 @@ namespace System.Diagnostics.TraceSourceTests
public void FailTest()
{
var listener = new TestTraceListener();
+ // We have to clear the listeners list on Trace since there is a trace listener by default with AssertUiEnabled = true in Desktop and that will pop up an assert window with Trace.Fail
+ Trace.Listeners.Clear();
Trace.Listeners.Add(listener);
Trace.Fail("Text");
Assert.Equal(1, listener.GetCallCount(Method.Fail));
@@ -344,6 +355,8 @@ namespace System.Diagnostics.TraceSourceTests
public void TraceTest02()
{
var textTL = new TestTextTraceListener();
+ // We have to clear the listeners list on Trace since there is a trace listener by default with AssertUiEnabled = true in Desktop and that will pop up an assert window with Trace.Fail
+ Trace.Listeners.Clear();
Trace.Listeners.Add(textTL);
Trace.IndentLevel = 0;
Trace.IndentSize = 2;
diff --git a/src/System.Diagnostics.TraceSource/tests/TraceInternalClassTests.cs b/src/System.Diagnostics.TraceSource/tests/TraceInternalClassTests.cs
index ff4d1648fc..9d41d84101 100644
--- a/src/System.Diagnostics.TraceSource/tests/TraceInternalClassTests.cs
+++ b/src/System.Diagnostics.TraceSource/tests/TraceInternalClassTests.cs
@@ -233,6 +233,8 @@ namespace System.Diagnostics.TraceSourceTests
public void FailTest()
{
var listener = GetTraceListener();
+ // We have to clear the listeners list on Trace since there is a trace listener by default with AssertUiEnabled = true in Desktop and that will pop up an assert window with Trace.Fail
+ Trace.Listeners.Clear();
Trace.Listeners.Add(listener);
Trace.Fail("Message");
Assert.Equal(1, listener.GetCallCount(Method.Fail));
@@ -244,6 +246,8 @@ namespace System.Diagnostics.TraceSourceTests
public void Fail2Test()
{
var listener = GetTraceListener();
+ // We have to clear the listeners list on Trace since there is a trace listener by default with AssertUiEnabled = true in Desktop and that will pop up an assert window with Trace.Fail
+ Trace.Listeners.Clear();
Trace.Listeners.Add(listener);
Trace.Fail("Message", "Category");
Assert.Equal(1, listener.GetCallCount(Method.Fail));
diff --git a/src/System.Diagnostics.TraceSource/tests/TraceSourceClassTests.cs b/src/System.Diagnostics.TraceSource/tests/TraceSourceClassTests.cs
index 5df9237e55..b535f8e502 100644
--- a/src/System.Diagnostics.TraceSource/tests/TraceSourceClassTests.cs
+++ b/src/System.Diagnostics.TraceSource/tests/TraceSourceClassTests.cs
@@ -57,9 +57,7 @@ namespace System.Diagnostics.TraceSourceTests
var listener = new TestTraceListener();
trace.Listeners.Add(listener);
trace.Close();
- // NOTE: this assertion fails on .net 4.5
- // where TraceSource.Close calls TraceListener.Close, not Dispose.
- Assert.Equal(1, listener.GetCallCount(Method.Dispose));
+ Assert.Equal(1, listener.GetCallCount(Method.Close));
// Assert that writing to a closed TraceSource is not an error.
trace.TraceEvent(TraceEventType.Critical, 0);
}
@@ -133,8 +131,20 @@ namespace System.Diagnostics.TraceSourceTests
[Fact]
public void EmptySourceName()
{
- Assert.Throws<ArgumentException>("name", () => new TraceSource(string.Empty));
- Assert.Throws<ArgumentException>("name", () => new TraceSource(string.Empty, SourceLevels.All));
+ ArgumentException exception1 = Assert.Throws<ArgumentException>(() => new TraceSource(string.Empty));
+ ArgumentException exception2 = Assert.Throws<ArgumentException>(() => new TraceSource(string.Empty, SourceLevels.All));
+
+ // In Desktop in TraceSource.ctor we create the ArgumentException without param name, just with Message = "name", so ParamName is null
+ if (TraceTestHelper.IsFullFramework)
+ {
+ Assert.Null(exception1.ParamName);
+ Assert.Null(exception2.ParamName);
+ }
+ else
+ {
+ Assert.Equal("name", exception1.ParamName);
+ Assert.Equal("name", exception2.ParamName);
+ }
}
}
diff --git a/src/System.Diagnostics.TraceSource/tests/TraceTestHelper.cs b/src/System.Diagnostics.TraceSource/tests/TraceTestHelper.cs
index 5e17d2e7be..4945991689 100644
--- a/src/System.Diagnostics.TraceSource/tests/TraceTestHelper.cs
+++ b/src/System.Diagnostics.TraceSource/tests/TraceTestHelper.cs
@@ -3,11 +3,14 @@
// See the LICENSE file in the project root for more information.
using System.Text;
+using System.Runtime.InteropServices;
namespace System.Diagnostics.TraceSourceTests
{
static class TraceTestHelper
{
+ internal static bool IsFullFramework => RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase);
+
/// <summary>
/// Resets the static state of the trace objects before a unit test.
/// </summary>
diff --git a/src/System.IO.Compression/tests/ZipArchive/zip_ManualAndCompatibilityTests.cs b/src/System.IO.Compression/tests/ZipArchive/zip_ManualAndCompatibilityTests.cs
index 453ea60d0b..b7a5603fa0 100644
--- a/src/System.IO.Compression/tests/ZipArchive/zip_ManualAndCompatibilityTests.cs
+++ b/src/System.IO.Compression/tests/ZipArchive/zip_ManualAndCompatibilityTests.cs
@@ -11,7 +11,6 @@ namespace System.IO.Compression.Tests
{
[Theory]
[InlineData("7zip.zip", "normal", true, true)]
- [InlineData("deflate64.zip", "normal", true, true)]
[InlineData("windows.zip", "normalWithoutEmptyDir", false, true)]
[InlineData("dotnetzipstreaming.zip", "normal", false, false)]
[InlineData("sharpziplib.zip", "normalWithoutEmptyDir", false, false)]
@@ -21,6 +20,13 @@ namespace System.IO.Compression.Tests
IsZipSameAsDir(await StreamHelpers.CreateTempCopyStream(compat(zipFile)), zfolder(zipFolder), ZipArchiveMode.Update, requireExplicit, checkTimes);
}
+ [Fact]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Deflate64 zip support is a netcore feature not available on full framework.")]
+ public static async Task Deflate64Zip()
+ {
+ IsZipSameAsDir(await StreamHelpers.CreateTempCopyStream(compat("deflate64.zip")), zfolder("normal"), ZipArchiveMode.Update, requireExplicit: true, checkTimes: true);
+ }
+
[Theory]
[InlineData("excel.xlsx", "excel", false, false)]
[InlineData("powerpoint.pptx", "powerpoint", false, false)]
diff --git a/src/System.IO.FileSystem.DriveInfo/System.IO.FileSystem.DriveInfo.sln b/src/System.IO.FileSystem.DriveInfo/System.IO.FileSystem.DriveInfo.sln
index 2ecc25e251..31a9614453 100644
--- a/src/System.IO.FileSystem.DriveInfo/System.IO.FileSystem.DriveInfo.sln
+++ b/src/System.IO.FileSystem.DriveInfo/System.IO.FileSystem.DriveInfo.sln
@@ -26,10 +26,10 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {7D9E5F2F-5677-40FC-AD04-FA7D603E4806}.Debug|Any CPU.ActiveCfg = netstandard-Windows_NT-Debug|Any CPU
- {7D9E5F2F-5677-40FC-AD04-FA7D603E4806}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU
- {7D9E5F2F-5677-40FC-AD04-FA7D603E4806}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU
- {7D9E5F2F-5677-40FC-AD04-FA7D603E4806}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU
+ {7D9E5F2F-5677-40FC-AD04-FA7D603E4806}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU
+ {7D9E5F2F-5677-40FC-AD04-FA7D603E4806}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU
+ {7D9E5F2F-5677-40FC-AD04-FA7D603E4806}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU
+ {7D9E5F2F-5677-40FC-AD04-FA7D603E4806}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU
{29C14AD7-DC03-45DC-897D-8DACC762707E}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU
{29C14AD7-DC03-45DC-897D-8DACC762707E}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU
{29C14AD7-DC03-45DC-897D-8DACC762707E}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU
diff --git a/src/System.IO.FileSystem.DriveInfo/tests/Configurations.props b/src/System.IO.FileSystem.DriveInfo/tests/Configurations.props
index 249c8c18b4..c398e42e89 100644
--- a/src/System.IO.FileSystem.DriveInfo/tests/Configurations.props
+++ b/src/System.IO.FileSystem.DriveInfo/tests/Configurations.props
@@ -2,7 +2,7 @@
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildConfigurations>
- netstandard-Windows_NT;
+ netstandard;
</BuildConfigurations>
</PropertyGroup>
</Project> \ No newline at end of file
diff --git a/src/System.IO.FileSystem.DriveInfo/tests/System.IO.FileSystem.DriveInfo.Tests.csproj b/src/System.IO.FileSystem.DriveInfo/tests/System.IO.FileSystem.DriveInfo.Tests.csproj
index d6c0f17c35..71746d6eec 100644
--- a/src/System.IO.FileSystem.DriveInfo/tests/System.IO.FileSystem.DriveInfo.Tests.csproj
+++ b/src/System.IO.FileSystem.DriveInfo/tests/System.IO.FileSystem.DriveInfo.Tests.csproj
@@ -5,8 +5,8 @@
<ProjectGuid>{7D9E5F2F-5677-40FC-AD04-FA7D603E4806}</ProjectGuid>
</PropertyGroup>
<!-- Default configurations to help VS understand the configurations -->
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Windows_NT-Debug|AnyCPU'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Windows_NT-Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="DriveInfo.Unix.Tests.cs" />
<Compile Include="DriveInfo.Windows.Tests.cs" />
diff --git a/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/DirectoryExistsTests.cs b/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/DirectoryExistsTests.cs
index 3b3e912e4f..f4e8aeb0a0 100644
--- a/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/DirectoryExistsTests.cs
+++ b/src/System.IO.IsolatedStorage/tests/System/IO/IsolatedStorage/DirectoryExistsTests.cs
@@ -70,7 +70,7 @@ namespace System.IO.IsolatedStorage
}
}
- [Theory MemberData("ValidStores")]
+ [Theory MemberData(nameof(ValidStores))]
public void DirectoryExists_Existance(PresetScopes scope)
{
using (var isf = GetPresetScope(scope))
diff --git a/src/System.Linq.Expressions/src/Configurations.props b/src/System.Linq.Expressions/src/Configurations.props
index a8910ef718..475368bbbb 100644
--- a/src/System.Linq.Expressions/src/Configurations.props
+++ b/src/System.Linq.Expressions/src/Configurations.props
@@ -3,6 +3,7 @@
<PropertyGroup>
<BuildConfigurations>
uap-Windows_NT;
+ uapaot-Windows_NT;
netcoreapp;
</BuildConfigurations>
</PropertyGroup>
diff --git a/src/System.Linq.Expressions/src/System.Linq.Expressions.csproj b/src/System.Linq.Expressions/src/System.Linq.Expressions.csproj
index fdb809d4e6..0f15073b1d 100644
--- a/src/System.Linq.Expressions/src/System.Linq.Expressions.csproj
+++ b/src/System.Linq.Expressions/src/System.Linq.Expressions.csproj
@@ -10,12 +10,10 @@
<AssemblyName>System.Linq.Expressions</AssemblyName>
<RootNamespace>System.Linq.Expressions</RootNamespace>
<IsInterpreting Condition="'$(TargetGroup)' == 'uap'">true</IsInterpreting>
-
<!-- Temporarily disable IsInterpreting build as it's broken and needs investigation
https://github.com/dotnet/corefx/issues/16910
-->
<IsInterpreting>false</IsInterpreting>
-
<DefineConstants Condition=" '$(IsInterpreting)' != 'true' ">$(DefineConstants);FEATURE_COMPILE</DefineConstants>
<DefineConstants Condition=" '$(FeatureInterpret)' == 'true' ">$(DefineConstants);FEATURE_INTERPRET</DefineConstants>
</PropertyGroup>
@@ -24,12 +22,43 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Release|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uapaot-Windows_NT-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uapaot-Windows_NT-Release|AnyCPU'" />
<PropertyGroup Condition="'$(TargetGroup)' == 'uapaot'">
<DefineConstants>$(DefineConstants);FEATURE_DYNAMIC_DELEGATE</DefineConstants>
</PropertyGroup>
- <ItemGroup Condition="'$(TargetGroup)' == 'uapaot'">
+ <ItemGroup Condition="'$(TargetGroup)' == 'uapaot'">
<EmbeddedResource Include="Resources\$(AssemblyName).rd.xml" />
- <ReferenceFromRuntime Include="System.Private.CoreLib.DynamicDelegate" />
+ <!-- Needed for DynamicDelegate -->
+ <ReferenceFromRuntime Include="System.Private.CoreLib" />
+ <!-- Causes us to use project references to avoid duplicate definitions that the ref assemblies would give us -->
+ <ProjectReference Include="..\..\System.Diagnostics.Tools\src\System.Diagnostics.Tools.csproj" />
+ <ProjectReference Include="..\..\System.Collections\src\System.Collections.csproj" />
+ <ProjectReference Include="..\..\System.Runtime\src\System.Runtime.csproj" />
+ <ProjectReference Include="..\..\System.Diagnostics.Tools\src\System.Diagnostics.Tools.csproj" />
+ <ProjectReference Include="..\..\System.Runtime.Extensions\src\System.Runtime.Extensions.csproj" />
+ <ProjectReference Include="..\..\System.ObjectModel\src\System.ObjectModel.csproj" />
+ <ProjectReference Include="..\..\System.Reflection.Primitives\src\System.Reflection.Primitives.csproj" />
+ <ProjectReference Include="..\..\System.Linq\src\System.Linq.csproj" />
+ <Reference Include="System.Reflection.Emit.ILGeneration" /> <!-- Have to add reference to ref because the contract is not fulfilled yet -->
+ <Reference Include="System.Reflection.Emit" /> <!-- Have to add reference to ref because the contract is not fulfilled yet -->
+ <Reference Include="System.Reflection.Emit.Lightweight" /> <!-- Have to add reference to ref because the contract is not fulfilled yet -->
+ </ItemGroup>
+ <ItemGroup Condition="'$(TargetGroup)' != 'uapaot'">
+ <Reference Include="System.Collections" />
+ <Reference Include="System.Diagnostics.Contracts" />
+ <Reference Include="System.Diagnostics.Debug" />
+ <Reference Include="System.Diagnostics.Tools" />
+ <Reference Include="System.Linq" />
+ <Reference Include="System.ObjectModel" />
+ <Reference Include="System.Reflection.Emit" />
+ <Reference Include="System.Reflection.Emit.ILGeneration" />
+ <Reference Include="System.Reflection.Emit.Lightweight" />
+ <Reference Include="System.Reflection.Primitives" />
+ <Reference Include="System.Resources.ResourceManager" />
+ <Reference Include="System.Runtime" />
+ <Reference Include="System.Runtime.Extensions" />
+ <Reference Include="System.Threading" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(CommonPath)\System\Collections\Generic\ArrayBuilder.cs">
@@ -131,22 +160,8 @@
<Compile Include="System\Runtime\CompilerServices\RuntimeOps.RuntimeVariables.cs" />
<Compile Include="System\Runtime\CompilerServices\RuntimeOps.MergedRuntimeVariables.cs" />
<Compile Include="System\Runtime\CompilerServices\ReadOnlyCollectionBuilder.cs" />
- <Reference Include="System.Collections" />
- <Reference Include="System.Diagnostics.Contracts" />
- <Reference Include="System.Diagnostics.Debug" />
- <Reference Include="System.Diagnostics.Tools" />
- <Reference Include="System.Linq" />
- <Reference Include="System.ObjectModel" />
- <Reference Include="System.Reflection.Emit" />
- <Reference Include="System.Reflection.Emit.ILGeneration" />
- <Reference Include="System.Reflection.Emit.Lightweight" />
- <Reference Include="System.Reflection.Primitives" />
- <Reference Include="System.Resources.ResourceManager" />
- <Reference Include="System.Runtime" />
- <Reference Include="System.Runtime.Extensions" />
- <Reference Include="System.Threading" />
</ItemGroup>
- <ItemGroup Condition="'$(TargetGroup)'=='netcoreapp' or '$(TargetGroup)'=='uap'">
+ <ItemGroup>
<Compile Include="System\Linq\Expressions\DynamicExpressionVisitor.cs" />
<Compile Include="System\Linq\Expressions\DynamicExpression.cs" />
<Compile Include="System\Linq\Expressions\Expression.netstandard.cs" />
@@ -291,4 +306,4 @@
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project>
+</Project> \ No newline at end of file
diff --git a/src/System.Linq.Expressions/tests/Configurations.props b/src/System.Linq.Expressions/tests/Configurations.props
index 2845c11c54..674306c1dd 100644
--- a/src/System.Linq.Expressions/tests/Configurations.props
+++ b/src/System.Linq.Expressions/tests/Configurations.props
@@ -3,6 +3,8 @@
<PropertyGroup>
<BuildConfigurations>
netcoreapp;
+ uap-Windows_NT;
+ uapaot-Windows_NT;
</BuildConfigurations>
</PropertyGroup>
</Project> \ No newline at end of file
diff --git a/src/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj b/src/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj
index 65602e86d8..db0bbc62a2 100644
--- a/src/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj
+++ b/src/System.Linq.Expressions/tests/System.Linq.Expressions.Tests.csproj
@@ -8,22 +8,62 @@
<PropertyGroup>
<ProjectGuid>{4B4AA59B-89F9-4A34-B3C3-C97EF531EE00}</ProjectGuid>
<IsInterpreting Condition="'$(TargetGroup)' == 'uap'">true</IsInterpreting>
+ <!-- Temporarily disable IsInterpreting build as it's broken and needs investigation
+ https://github.com/dotnet/corefx/issues/16910
+ -->
+ <IsInterpreting>false</IsInterpreting>
<DefineConstants Condition=" '$(IsInterpreting)' != 'true' ">$(DefineConstants);FEATURE_COMPILE</DefineConstants>
<DefineConstants Condition=" '$(FeatureInterpret)' == 'true' ">$(DefineConstants);FEATURE_INTERPRET</DefineConstants>
+ <IncludeDefaultReferences Condition="'$(TargetGroup)'=='uapaot'">false</IncludeDefaultReferences>
</PropertyGroup>
<!-- Default configurations to help VS understand the configurations -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uapaot-Windows_NT-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uapaot-Windows_NT-Release|AnyCPU'" />
<ItemGroup>
<TargetingPackExclusions Include="System.Linq.Expressions" />
<ReferenceFromRuntime Include="System.Linq.Expressions" />
</ItemGroup>
+ <ItemGroup Condition="'$(TargetGroup)' == 'uapaot'">
+ <ReferenceFromRuntime Include="System.Private.CoreLib" />
+ <ReferenceFromRuntime Include="xunit.core" />
+ <ReferenceFromRuntime Include="Xunit.NetCore.Extensions" />
+ <ReferenceFromRuntime Include="xunit.assert" />
+ <ReferenceFromRuntime Include="xunit.abstractions" />
+ <ReferenceFromRuntime Include="xunit.performance.core" />
+ <ReferenceFromRuntime Include="xunit.performance.api" />
+ <ReferenceFromRuntime Include="System.Runtime" />
+ <ReferenceFromRuntime Include="System.Collections" />
+ <ReferenceFromRuntime Include="System.Reflection.Primitives" />
+ <ReferenceFromRuntime Include="Microsoft.CSharp" />
+ <ReferenceFromRuntime Include="System.Runtime.Extensions" />
+ <ReferenceFromRuntime Include="System.Private.Uri" />
+ <ReferenceFromRuntime Include="System.Text.RegularExpressions" />
+ <ReferenceFromRuntime Include="System.Diagnostics.Debug" />
+ <ReferenceFromRuntime Include="System.ObjectModel" />
+ <ReferenceFromRuntime Include="System.Threading.Thread" />
+ <ReferenceFromRuntime Include="System.Threading.Tasks" />
+ <ReferenceFromRuntime Include="System.Linq" />
+ <ReferenceFromRuntime Include="System.Linq.Queryable" />
+ <ReferenceFromRuntime Include="System.Reflection" />
+ <ReferenceFromRuntime Include="System.IO.FileSystem" />
+ <ReferenceFromRuntime Include="System.Runtime.InteropServices.RuntimeInformation" />
+ <ReferenceFromRuntime Include="System.Runtime.InteropServices" />
+ <ReferenceFromRuntime Include="System.Private.Interop" />
+ <ReferenceFromRuntime Include="netstandard" />
+ <Reference Include="System.Reflection.Emit.ILGeneration" />
+ <Reference Include="System.Reflection.Emit" />
+ <Reference Include="System.Reflection.Emit.Lightweight" />
+ </ItemGroup>
<ItemGroup>
<!-- Remove this when TypeBuilder extends TypeInfo. See https://github.com/dotnet/corefx/issues/14334 -->
<Compile Include="$(CommonTestPath)\System\TypeBuilderExtensions.cs" />
</ItemGroup>
<ItemGroup>
- <AdditionalCodeCoverageAssemblies Include="Microsoft.CSharp" />
+ <AdditionalCodeCoverageAssemblies Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="CompilerTests.cs" />
diff --git a/src/System.Linq.Parallel/tests/QueryOperators/SequenceEqualTests.cs b/src/System.Linq.Parallel/tests/QueryOperators/SequenceEqualTests.cs
index e0a3e6a70f..5c553ab364 100644
--- a/src/System.Linq.Parallel/tests/QueryOperators/SequenceEqualTests.cs
+++ b/src/System.Linq.Parallel/tests/QueryOperators/SequenceEqualTests.cs
@@ -169,34 +169,6 @@ namespace System.Linq.Parallel.Tests
}
[Theory]
- [ActiveIssue(16751)] //Cancellation token not shared
- [MemberData("SequenceEqualData", new int[] { /* Sources.OuterLoopCount */ })]
- public static void SequenceEqual_SharedLeft_Cancellation(Labeled<ParallelQuery<int>> left, Labeled<ParallelQuery<int>> right, int count)
- {
- IntegerRangeSet seen = new IntegerRangeSet(0, count);
- CancellationTokenSource cs = new CancellationTokenSource();
-
- Assert.Throws<OperationCanceledException>(() => left.Item.WithCancellation(cs.Token)
- .SequenceEqual(right.Item.Except(ParallelEnumerable.Range(0, count).Select(x => { cs.Cancel(); seen.Add(x); return x; }))));
- // Canceled query means some elements should not be seen.
- Assert.False(seen.All(x => x.Value));
- }
-
- [Theory]
- [ActiveIssue(16751)] //Cancellation token not shared
- [MemberData("SequenceEqualData", new int[] { /* Sources.OuterLoopCount */ })]
- public static void SequenceEqual_SharedRight_Cancellation(Labeled<ParallelQuery<int>> left, Labeled<ParallelQuery<int>> right, int count)
- {
- IntegerRangeSet seen = new IntegerRangeSet(0, count);
- CancellationTokenSource cs = new CancellationTokenSource();
-
- Assert.Throws<OperationCanceledException>(() => left.Item.Except(ParallelEnumerable.Range(0, count).Select(x => { cs.Cancel(); seen.Add(x); return x; }))
- .SequenceEqual(right.Item.WithCancellation(cs.Token)));
- // Canceled query means some elements should not be seen.
- Assert.False(seen.All(x => x.Value));
- }
-
- [Theory]
[MemberData(nameof(SequenceEqualData), new[] { 4 })]
public static void SequenceEqual_AggregateException(Labeled<ParallelQuery<int>> left, Labeled<ParallelQuery<int>> right, int count)
{
diff --git a/src/System.Memory/src/System/Span.cs b/src/System.Memory/src/System/Span.cs
index 5a7aa8ba0d..02b5e484aa 100644
--- a/src/System.Memory/src/System/Span.cs
+++ b/src/System.Memory/src/System/Span.cs
@@ -204,12 +204,12 @@ namespace System
var byteLength = (UIntPtr)((uint)length * Unsafe.SizeOf<T>());
- if ((Unsafe.SizeOf<T>() & (sizeof(IntPtr) - 1)) != 0)
+ if ((Unsafe.SizeOf<T>() & (sizeof(IntPtr) - 1)) != 0)
{
if (_pinnable == null)
{
var ptr = (byte*)_byteOffset.ToPointer();
-
+
SpanHelpers.ClearLessThanPointerSized(ptr, byteLength);
}
else
@@ -311,7 +311,6 @@ namespace System
ThrowHelper.ThrowArgumentException_DestinationTooShort();
}
-
/// <summary>
/// Copies the contents of this span into destination span. If the source
/// and destinations overlap, this method behaves as if the original values in
@@ -323,35 +322,78 @@ namespace System
/// <param name="destination">The span to copy items into.</param>
public bool TryCopyTo(Span<T> destination)
{
- if ((uint)_length > (uint)destination._length)
+ int length = _length;
+ int destLength = destination._length;
+
+ if ((uint)length == 0)
+ return true;
+
+ if ((uint)length > (uint)destLength)
return false;
- // TODO: This is a tide-over implementation as we plan to add a overlap-safe cpblk-based api to Unsafe. (https://github.com/dotnet/corefx/issues/13427)
unsafe
{
ref T src = ref DangerousGetPinnableReference();
ref T dst = ref destination.DangerousGetPinnableReference();
- IntPtr srcMinusDst = Unsafe.ByteOffset<T>(ref dst, ref src);
- int length = _length;
+ IntPtr srcMinusDst = Unsafe.ByteOffset<T>(ref dst, ref src);
bool srcGreaterThanDst = (sizeof(IntPtr) == sizeof(int)) ? srcMinusDst.ToInt32() >= 0 : srcMinusDst.ToInt64() >= 0;
+ IntPtr tailDiff;
+
if (srcGreaterThanDst)
{
- // Source address greater than or equal to destination address. Can do normal copy.
- for (int i = 0; i < length; i++)
+ // If the start of source is greater than the start of destination, then we need to calculate
+ // the different between the end of destination relative to the start of source.
+ tailDiff = Unsafe.ByteOffset<T>(ref Unsafe.Add<T>(ref dst, destLength), ref src);
+ }
+ else
+ {
+ // If the start of source is less than the start of destination, then we need to calculate
+ // the different between the end of source relative to the start of destunation.
+ tailDiff = Unsafe.ByteOffset<T>(ref Unsafe.Add<T>(ref src, length), ref dst);
+ }
+
+ // If the source is entirely before or entirely after the destination and the type inside the span is not
+ // itself a reference type or containing reference types, then we can do a simple block copy of the data.
+ bool isOverlapped = (sizeof(IntPtr) == sizeof(int)) ? tailDiff.ToInt32() < 0 : tailDiff.ToInt64() < 0;
+ if (!isOverlapped && !SpanHelpers.IsReferenceOrContainsReferences<T>())
+ {
+ ref byte dstBytes = ref Unsafe.As<T, byte>(ref dst);
+ ref byte srcBytes = ref Unsafe.As<T, byte>(ref src);
+ ulong byteCount = (ulong)length * (ulong)Unsafe.SizeOf<T>();
+ ulong index = 0;
+
+ while (index < byteCount)
{
- Unsafe.Add<T>(ref dst, i) = Unsafe.Add<T>(ref src, i);
+ uint blockSize = byteCount > uint.MaxValue ? uint.MaxValue : (uint)byteCount;
+ Unsafe.CopyBlock(
+ ref Unsafe.Add(ref dstBytes, (IntPtr)index),
+ ref Unsafe.Add(ref srcBytes, (IntPtr)index),
+ blockSize);
+ index += blockSize;
}
}
else
{
- // Source address less than destination address. Must do backward copy.
- int i = length;
- while (i-- != 0)
+ if (srcGreaterThanDst)
+ {
+ // Source address greater than or equal to destination address. Can do normal copy.
+ for (int i = 0; i < length; i++)
+ {
+ Unsafe.Add<T>(ref dst, i) = Unsafe.Add<T>(ref src, i);
+ }
+ }
+ else
{
- Unsafe.Add<T>(ref dst, i) = Unsafe.Add<T>(ref src, i);
+ // Source address less than destination address. Must do backward copy.
+ int i = length;
+ while (i-- != 0)
+ {
+ Unsafe.Add<T>(ref dst, i) = Unsafe.Add<T>(ref src, i);
+ }
}
}
+
return true;
}
}
diff --git a/src/System.Memory/tests/ReadOnlySpan/Overflow.cs b/src/System.Memory/tests/ReadOnlySpan/Overflow.cs
index b017c42075..e67c765131 100644
--- a/src/System.Memory/tests/ReadOnlySpan/Overflow.cs
+++ b/src/System.Memory/tests/ReadOnlySpan/Overflow.cs
@@ -10,7 +10,9 @@ namespace System.SpanTests
{
public static partial class ReadOnlySpanTests
{
+ [ActiveIssue(16593)]
[Fact]
+ [OuterLoop]
public static void IndexOverflow()
{
//
diff --git a/src/System.Memory/tests/Span/Overflow.cs b/src/System.Memory/tests/Span/Overflow.cs
index 7af9e0903c..314ddefc6e 100644
--- a/src/System.Memory/tests/Span/Overflow.cs
+++ b/src/System.Memory/tests/Span/Overflow.cs
@@ -10,6 +10,9 @@ namespace System.SpanTests
{
public static partial class SpanTests
{
+ [ActiveIssue(16593)]
+ [Fact]
+ [OuterLoop]
public static void IndexOverflow()
{
//
diff --git a/src/System.Net.Http/src/Configurations.props b/src/System.Net.Http/src/Configurations.props
index 80663279fd..7306909691 100644
--- a/src/System.Net.Http/src/Configurations.props
+++ b/src/System.Net.Http/src/Configurations.props
@@ -3,10 +3,11 @@
<PropertyGroup>
<BuildConfigurations>
uap-Windows_NT;
+ netcoreapp-OSX;
netcoreapp-Unix;
netcoreapp-Windows_NT;
net46-Windows_NT;
netfx-Windows_NT;
</BuildConfigurations>
</PropertyGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/System.Net.Http/src/Resources/Strings.resx b/src/System.Net.Http/src/Resources/Strings.resx
index 4554fdc2a2..1aaaa8986d 100644
--- a/src/System.Net.Http/src/Resources/Strings.resx
+++ b/src/System.Net.Http/src/Resources/Strings.resx
@@ -292,4 +292,13 @@
<data name="ObjectDisposed_StreamClosed" xml:space="preserve">
<value>Can not access a closed Stream.</value>
</data>
+ <data name="net_http_libcurl_callback_notsupported" xml:space="preserve">
+ <value>The handler does not support custom handling of certificates with this combination of libcurl ({0}) and its SSL backend ("{1}").</value>
+ </data>
+ <data name="net_http_libcurl_clientcerts_notsupported" xml:space="preserve">
+ <value>The handler does not support client authentication certificates with this combination of libcurl ({0}) and its SSL backend ("{1}").</value>
+ </data>
+ <data name="net_http_libcurl_revocation_notsupported" xml:space="preserve">
+ <value>The handler does not support changing revocation settings with this combination of libcurl ({0}) and its SSL backend ("{1}").</value>
+ </data>
</root>
diff --git a/src/System.Net.Http/src/System.Net.Http.csproj b/src/System.Net.Http/src/System.Net.Http.csproj
index 8b546e1d1f..2878309a51 100644
--- a/src/System.Net.Http/src/System.Net.Http.csproj
+++ b/src/System.Net.Http/src/System.Net.Http.csproj
@@ -28,6 +28,9 @@
<DefineConstants>$(DefineConstants);uap</DefineConstants>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
+ <PropertyGroup Condition=" '$(TargetsOSX)' == 'true' ">
+ <DefineConstants>$(DefineConstants);SYSNETHTTP_NO_OPENSSL</DefineConstants>
+ </PropertyGroup>
<!-- Shims -->
<ItemGroup>
<Compile Include="Shims\Uri.cs" />
@@ -184,10 +187,8 @@
<ItemGroup Condition=" '$(TargetsUnix)' == 'true' ">
<Compile Include="System\Net\Http\HttpClientHandler.Unix.cs" />
<Compile Include="System\Net\Http\Unix\CurlHandler.cs" />
- <Compile Include="System\Net\Http\Unix\CurlHandler.ClientCertificateProvider.cs" />
<Compile Include="System\Net\Http\Unix\CurlHandler.EasyRequest.cs" />
<Compile Include="System\Net\Http\Unix\CurlHandler.MultiAgent.cs" />
- <Compile Include="System\Net\Http\Unix\CurlHandler.SslProvider.cs" />
<Compile Include="System\Net\Http\Unix\CurlException.cs" />
<Compile Include="System\Net\Http\Unix\CurlHandler.CurlResponseMessage.cs" />
<Compile Include="System\Net\Http\Unix\CurlResponseHeaderReader.cs" />
@@ -197,12 +198,6 @@
<Compile Include="$(CommonPath)\System\Net\SecurityProtocol.cs">
<Link>Common\System\Net\SecurityProtocol.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\Net\Security\CertificateValidation.Unix.cs">
- <Link>Common\System\Net\Security\CertificateValidation.Unix.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\System\Threading\Tasks\TaskToApm.cs">
- <Link>Common\System\Threading\Tasks\TaskToApm.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\Interop\Unix\Interop.Errors.cs">
<Link>Common\Interop\Unix\Interop.Errors.cs</Link>
</Compile>
@@ -278,6 +273,11 @@
<Compile Include="$(CommonPath)\System\Net\Http\TlsCertificateExtensions.cs">
<Link>Common\System\Net\Http\TlsCertificateExtensions</Link>
</Compile>
+ <Compile Include="$(CommonPath)\System\Threading\Tasks\TaskToApm.cs">
+ <Link>Common\System\Threading\Tasks\TaskToApm.cs</Link>
+ </Compile>
+ </ItemGroup>
+ <ItemGroup Condition=" '$(TargetsUnix)' == 'true' AND '$(TargetsOSX)' != 'true' ">
<Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.ASN1.cs">
<Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.ASN1.cs</Link>
</Compile>
@@ -317,9 +317,6 @@
<Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.X509StoreCtx.cs">
<Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.X509StoreCtx.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.X509ChannelBindingHash.cs">
- <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.X509ChannelBindingHash.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Net.Security.Native\Interop.Initialization.cs">
<Link>Common\Interop\Unix\System.Net.Security.Native\Interop.Initialization.cs</Link>
</Compile>
@@ -341,6 +338,14 @@
<Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\Asn1SafeHandles.Unix.cs">
<Link>Common\Microsoft\Win32\SafeHandles\Asn1SafeHandles.Unix.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\System\Net\Security\CertificateValidation.Unix.cs">
+ <Link>Common\System\Net\Security\CertificateValidation.Unix.cs</Link>
+ </Compile>
+ <Compile Include="System\Net\Http\Unix\CurlHandler.ClientCertificateProvider.cs" />
+ <Compile Include="System\Net\Http\Unix\CurlHandler.SslProvider.cs" />
+ </ItemGroup>
+ <ItemGroup Condition=" '$(TargetsOSX)' == 'true' ">
+ <Compile Include="System\Net\Http\OSX\CurlHandler.SslProvider.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetGroup)' == 'uap'" >
<Reference Include="Windows" />
@@ -395,4 +400,4 @@
<Reference Include="System.Security.Cryptography.Primitives" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/System.Net.Http/src/System/Net/Http/OSX/CurlHandler.SslProvider.cs b/src/System.Net.Http/src/System/Net/Http/OSX/CurlHandler.SslProvider.cs
new file mode 100644
index 0000000000..9bd2148191
--- /dev/null
+++ b/src/System.Net.Http/src/System/Net/Http/OSX/CurlHandler.SslProvider.cs
@@ -0,0 +1,133 @@
+// 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.Diagnostics;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+
+using CURLcode = Interop.Http.CURLcode;
+
+namespace System.Net.Http
+{
+ internal partial class CurlHandler : HttpMessageHandler
+ {
+ private static class SslProvider
+ {
+ internal static void SetSslOptions(EasyRequest easy, ClientCertificateOption clientCertOption)
+ {
+ Debug.Assert(
+ clientCertOption == ClientCertificateOption.Automatic ||
+ clientCertOption == ClientCertificateOption.Manual);
+
+ // Create a client certificate provider if client certs may be used.
+ X509Certificate2Collection clientCertificates = easy._handler._clientCertificates;
+
+ if (clientCertOption != ClientCertificateOption.Manual || clientCertificates?.Count > 0)
+ {
+ // libcurl does not have an option of accepting a SecIdentityRef via an input option,
+ // only via writing it to a file and letting it load the PFX.
+ // This would require that a) we write said file, and b) that it contaminate the default
+ // keychain (because their PFX loader loads to the default keychain).
+ throw new PlatformNotSupportedException(
+ SR.Format(
+ SR.net_http_libcurl_clientcerts_notsupported,
+ CurlVersionDescription,
+ CurlSslVersionDescription));
+ }
+
+ if (easy._handler.ServerCertificateValidationCallback != null)
+ {
+ // libcurl (as of 7.49.1) does not have any callback which can be registered which fires
+ // between the time that a TLS/SSL handshake has offered up the server certificate and the
+ // time that the HTTP request headers are written. Were there any callback, the option
+ // CURLINFO_TLS_SSL_PTR could be queried (and the backend identifier validated to be
+ // CURLSSLBACKEND_DARWINSSL). Then the SecTrustRef could be extracted to build the chain,
+ // a la SslStream.
+ //
+ // Without the callback the matrix looks like:
+ // * If default-trusted and callback-would-trust: No difference (except side effects, like logging).
+ // * If default-trusted and callback-would-block: Data would have been sent in violation of user trust.
+ // * If not-default-trusted and callback-would-not-trust: No difference (except side effects).
+ // * If not-default-trusted and callback-would-trust: No data sent, which doesn't match user desires.
+ //
+ // Of the two "different" cases, sending when we shouldn't is worse, so that's the direction we
+ // have to cater to. So we'll use default trust, and throw on any custom callback.
+ //
+ // The situation where system trust fails can be remedied by including the certificate into the
+ // user's keychain and setting the SSL policy trust for it to "Always Trust".
+ // Similarly, the "block this" could be attained by setting the SSL policy for a cert in the
+ // keychain to "Never Trust".
+ throw new PlatformNotSupportedException(
+ SR.Format(
+ SR.net_http_libcurl_callback_notsupported,
+ CurlVersionDescription,
+ CurlSslVersionDescription));
+ }
+
+ // Revocation checking is always on for darwinssl (SecureTransport).
+ // If any other backend is used and revocation is requested, we can't guarantee
+ // that assertion.
+ if (easy._handler.CheckCertificateRevocationList &&
+ !CurlSslVersionDescription.Equals("SecureTransport"))
+ {
+ throw new PlatformNotSupportedException(
+ SR.Format(
+ SR.net_http_libcurl_revocation_notsupported,
+ CurlVersionDescription,
+ CurlSslVersionDescription));
+ }
+
+ SetSslVersion(easy);
+ }
+
+ private static void SetSslVersion(EasyRequest easy)
+ {
+ // Get the requested protocols.
+ SslProtocols protocols = easy._handler.SslProtocols;
+ if (protocols == SslProtocols.None)
+ {
+ // Let libcurl use its defaults if None is set.
+ return;
+ }
+
+ // We explicitly disallow choosing SSL2/3. Make sure they were filtered out.
+ Debug.Assert((protocols & ~SecurityProtocol.AllowedSecurityProtocols) == 0,
+ "Disallowed protocols should have been filtered out.");
+
+ // libcurl supports options for either enabling all of the TLS1.* protocols or enabling
+ // just one of them; it doesn't currently support enabling two of the three, e.g. you can't
+ // pick TLS1.1 and TLS1.2 but not TLS1.0, but you can select just TLS1.2.
+ Interop.Http.CurlSslVersion curlSslVersion;
+ switch (protocols)
+ {
+ case SslProtocols.Tls:
+ curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1_0;
+ break;
+ case SslProtocols.Tls11:
+ curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1_1;
+ break;
+ case SslProtocols.Tls12:
+ curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1_2;
+ break;
+
+ case SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12:
+ curlSslVersion = Interop.Http.CurlSslVersion.CURL_SSLVERSION_TLSv1;
+ break;
+
+ default:
+ throw new NotSupportedException(SR.net_securityprotocolnotsupported);
+ }
+
+ try
+ {
+ easy.SetCurlOption(Interop.Http.CURLoption.CURLOPT_SSLVERSION, (long)curlSslVersion);
+ }
+ catch (CurlException e) when (e.HResult == (int)CURLcode.CURLE_UNKNOWN_OPTION)
+ {
+ throw new NotSupportedException(SR.net_securityprotocolnotsupported, e);
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.Net.Http/tests/FunctionalTests/CancellationTest.cs b/src/System.Net.Http/tests/FunctionalTests/CancellationTest.cs
index c7ed620560..eece242c2b 100644
--- a/src/System.Net.Http/tests/FunctionalTests/CancellationTest.cs
+++ b/src/System.Net.Http/tests/FunctionalTests/CancellationTest.cs
@@ -22,7 +22,6 @@ namespace System.Net.Http.Functional.Tests
_output = output;
}
- [ActiveIssue(10504)]
[OuterLoop] // includes seconds of delay
[Theory]
[InlineData(false, false)]
@@ -72,7 +71,7 @@ namespace System.Net.Http.Functional.Tests
_output.WriteLine("GetAsync() completed at: {0}", stopwatch.Elapsed.ToString());
triggerResponseWrite.SetResult(true);
- Assert.True(stopwatch.Elapsed < new TimeSpan(0, 0, 10), "Elapsed time should be short");
+ Assert.True(stopwatch.Elapsed < new TimeSpan(0, 0, 10), $"Elapsed time {stopwatch.Elapsed} should be short");
});
}
}
diff --git a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ClientCertificates.cs b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ClientCertificates.cs
index 200806c502..6fd03e908d 100644
--- a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ClientCertificates.cs
+++ b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ClientCertificates.cs
@@ -139,12 +139,8 @@ namespace System.Net.Http.Functional.Tests
}
private static bool BackendSupportsCustomCertificateHandling =>
- RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ||
- (CurlSslVersionDescription()?.StartsWith("OpenSSL") ?? false);
+ HttpClientHandler_ServerCertificates_Test.BackendSupportsCustomCertificateHandling;
private static bool BackendDoesNotSupportCustomCertificateHandling => !BackendSupportsCustomCertificateHandling;
-
- [DllImport("System.Net.Http.Native", EntryPoint = "HttpNative_GetSslVersionDescription")]
- private static extern string CurlSslVersionDescription();
}
}
diff --git a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ServerCertificates.cs b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ServerCertificates.cs
index c7488f1cd9..560571024f 100644
--- a/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ServerCertificates.cs
+++ b/src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.ServerCertificates.cs
@@ -174,10 +174,45 @@ namespace System.Net.Http.Functional.Tests
[Fact]
public async Task NoCallback_RevokedCertificate_NoRevocationChecking_Succeeds()
{
- using (var client = new HttpClient())
- using (HttpResponseMessage response = await client.GetAsync(Configuration.Http.RevokedCertRemoteServer))
+ // On macOS (libcurl+darwinssl) we cannot turn revocation off.
+ // But we also can't realistically say that the default value for
+ // CheckCertificateRevocationList throws in the general case.
+ try
{
- Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ using (var client = new HttpClient())
+ using (HttpResponseMessage response = await client.GetAsync(Configuration.Http.RevokedCertRemoteServer))
+ {
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ }
+ }
+ catch (HttpRequestException)
+ {
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ throw;
+
+ // If a run on a clean macOS ever fails we need to consider that "false"
+ // for CheckCertificateRevocationList is actually "use a system default" now,
+ // and may require changing how this option is exposed. Considering the variety of
+ // systems this should probably be complex like
+ // enum RevocationCheckingOption {
+ // // Use it if able
+ // BestPlatformSecurity = 0,
+ // // Don't use it, if that's an option.
+ // BestPlatformPerformance,
+ // // Required
+ // MustCheck,
+ // // Prohibited
+ // MustNotCheck,
+ // }
+
+ switch (CurlSslVersionDescription())
+ {
+ case "SecureTransport":
+ // Suppress the exception, making the test pass.
+ break;
+ default:
+ throw;
+ }
}
}
@@ -241,6 +276,8 @@ namespace System.Net.Http.Functional.Tests
[OuterLoop] // TODO: Issue #11345
[ConditionalFact(nameof(BackendDoesNotSupportCustomCertificateHandling))]
+ // For macOS the "custom handling" means that revocation can't be *disabled*. So this test does not apply.
+ [PlatformSpecific(~TestPlatforms.OSX)]
public async Task SSLBackendNotSupported_Revocation_ThrowsPlatformNotSupportedException()
{
using (var client = new HttpClient(new HttpClientHandler() { CheckCertificateRevocationList = true }))
@@ -299,9 +336,25 @@ namespace System.Net.Http.Functional.Tests
}
}
- private static bool BackendSupportsCustomCertificateHandling =>
- RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ||
- (CurlSslVersionDescription()?.StartsWith("OpenSSL") ?? false);
+ internal static bool BackendSupportsCustomCertificateHandling
+ {
+ get
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return true;
+ }
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ return false;
+ }
+
+ // For other Unix-based systems it's true if (and only if) the openssl backend
+ // is used with libcurl.
+ return (CurlSslVersionDescription()?.StartsWith("OpenSSL") ?? false);
+ }
+ }
private static bool BackendDoesNotSupportCustomCertificateHandling => !BackendSupportsCustomCertificateHandling;
diff --git a/src/System.Net.Http/tests/FunctionalTests/SchSendAuxRecordHttpTest.cs b/src/System.Net.Http/tests/FunctionalTests/SchSendAuxRecordHttpTest.cs
index 807d0203eb..709aaa8180 100644
--- a/src/System.Net.Http/tests/FunctionalTests/SchSendAuxRecordHttpTest.cs
+++ b/src/System.Net.Http/tests/FunctionalTests/SchSendAuxRecordHttpTest.cs
@@ -24,7 +24,6 @@ namespace System.Net.Http.Functional.Tests
_output = output;
}
- [ActiveIssue(11623)]
[OuterLoop] // TODO: Issue #11345
[Fact]
[PlatformSpecific(TestPlatforms.Windows)]
diff --git a/src/System.Net.HttpListener/ref/System.Net.HttpListener.csproj b/src/System.Net.HttpListener/ref/System.Net.HttpListener.csproj
index 10139d6c15..92df1a17e8 100644
--- a/src/System.Net.HttpListener/ref/System.Net.HttpListener.csproj
+++ b/src/System.Net.HttpListener/ref/System.Net.HttpListener.csproj
@@ -6,6 +6,8 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="System.Net.HttpListener.cs" />
</ItemGroup>
diff --git a/src/System.Net.HttpListener/src/System.Net.HttpListener.csproj b/src/System.Net.HttpListener/src/System.Net.HttpListener.csproj
index 8973ed6dd9..f9f239f7c3 100644
--- a/src/System.Net.HttpListener/src/System.Net.HttpListener.csproj
+++ b/src/System.Net.HttpListener/src/System.Net.HttpListener.csproj
@@ -6,10 +6,12 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<!-- Default configurations to help VS understand the options -->
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='netcoreapp-Windows_NT-Debug|AnyCPU'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='netcoreapp-Windows_NT-Release|AnyCPU'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='netcoreapp-Unix-Debug|AnyCPU'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='netcoreapp-Unix-Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Unix-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Unix-Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Windows_NT-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Windows_NT-Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Release|AnyCPU'" />
<ItemGroup>
<Reference Include="Microsoft.Win32.Primitives" />
<Reference Include="System.Collections" />
@@ -25,8 +27,10 @@
<Reference Include="System.Net.WebHeaderCollection" />
<Reference Include="System.Net.WebSockets" />
<Reference Include="System.Net.WebSockets.Client" />
+ <Reference Include="System.Numerics.Vectors" />
<Reference Include="System.Resources.ResourceManager" />
<Reference Include="System.Runtime" />
+ <Reference Include="System.Runtime.CompilerServices.Unsafe" />
<Reference Include="System.Runtime.Extensions" />
<Reference Include="System.Runtime.InteropServices" />
<Reference Include="System.Security.Claims" />
diff --git a/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj b/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj
index 40beab68c2..c06340ba5c 100644
--- a/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj
+++ b/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj
@@ -67,9 +67,6 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetsWindows)' == 'true'">
<!-- Logging -->
- <Compile Include="$(CommonPath)\System\Net\Shims\TraceSource.cs">
- <Link>Common\System\Net\Shims\TraceSource.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\System\Net\Logging\DebugThreadTracking.cs">
<Link>Common\System\Net\Logging\DebugThreadTracking.cs</Link>
</Compile>
diff --git a/src/System.Net.NetworkInformation/tests/UnitTests/System.Net.NetworkInformation.WinRT.Unit.Tests.csproj b/src/System.Net.NetworkInformation/tests/UnitTests/System.Net.NetworkInformation.WinRT.Unit.Tests.csproj
index 7c8eebc09f..1bc2c7e6fc 100644
--- a/src/System.Net.NetworkInformation/tests/UnitTests/System.Net.NetworkInformation.WinRT.Unit.Tests.csproj
+++ b/src/System.Net.NetworkInformation/tests/UnitTests/System.Net.NetworkInformation.WinRT.Unit.Tests.csproj
@@ -54,9 +54,6 @@
<Link>ProductionCode\Common\System\NotImplemented.cs</Link>
</Compile>
<!-- Logging -->
- <Compile Include="$(CommonPath)\System\Net\Shims\TraceSource.cs">
- <Link>ProductionCode\Common\System\Net\Shims\TraceSource.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\System\Net\Logging\NetEventSource.Common.cs">
<Link>ProductionCode\Common\System\Net\Logging\NetEventSource.Common.cs</Link>
</Compile>
diff --git a/src/System.Net.Ping/src/System.Net.Ping.csproj b/src/System.Net.Ping/src/System.Net.Ping.csproj
index e5ff8179ea..4dc8b2c01f 100644
--- a/src/System.Net.Ping/src/System.Net.Ping.csproj
+++ b/src/System.Net.Ping/src/System.Net.Ping.csproj
@@ -39,9 +39,6 @@
</ItemGroup>
<!-- Logging -->
<ItemGroup>
- <Compile Include="$(CommonPath)\System\Net\Shims\TraceSource.cs">
- <Link>Common\System\Net\Shims\TraceSource.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\System\Net\Logging\NetEventSource.Common.cs">
<Link>Common\System\Net\Logging\NetEventSource.Common.cs</Link>
</Compile>
diff --git a/src/System.Net.Primitives/src/System.Net.Primitives.csproj b/src/System.Net.Primitives/src/System.Net.Primitives.csproj
index 5a52e4f258..92d4ab951f 100644
--- a/src/System.Net.Primitives/src/System.Net.Primitives.csproj
+++ b/src/System.Net.Primitives/src/System.Net.Primitives.csproj
@@ -83,9 +83,6 @@
<Link>Common\System\IO\StringBuilderCache.cs</Link>
</Compile>
<!-- Logging -->
- <Compile Include="$(CommonPath)\System\Net\Shims\TraceSource.cs">
- <Link>Common\System\Net\Shims\TraceSource.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\System\Net\Logging\NetEventSource.Common.cs">
<Link>Common\System\Net\Logging\NetEventSource.Common.cs</Link>
</Compile>
diff --git a/src/System.Net.Primitives/tests/UnitTests/System.Net.Primitives.UnitTests.Tests.csproj b/src/System.Net.Primitives/tests/UnitTests/System.Net.Primitives.UnitTests.Tests.csproj
index 8182064d83..b5f3db68d9 100644
--- a/src/System.Net.Primitives/tests/UnitTests/System.Net.Primitives.UnitTests.Tests.csproj
+++ b/src/System.Net.Primitives/tests/UnitTests/System.Net.Primitives.UnitTests.Tests.csproj
@@ -92,9 +92,6 @@
<Link>Common\System\IO\StringBuilderCache.cs</Link>
</Compile>
<!-- Logging -->
- <Compile Include="$(CommonPath)\System\Net\Shims\TraceSource.cs">
- <Link>ProductionCode\Common\System\Net\Shims\TraceSource.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\System\Net\Logging\NetEventSource.Common.cs">
<Link>ProductionCode\Common\System\Net\Logging\NetEventSource.Common.cs</Link>
</Compile>
diff --git a/src/System.Net.Security/src/Configurations.props b/src/System.Net.Security/src/Configurations.props
index 62eb1c4776..05d3ab66c3 100644
--- a/src/System.Net.Security/src/Configurations.props
+++ b/src/System.Net.Security/src/Configurations.props
@@ -2,9 +2,10 @@
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildConfigurations>
+ netcoreapp-OSX;
netcoreapp-Unix;
netcoreapp-Windows_NT;
uap-Windows_NT;
</BuildConfigurations>
</PropertyGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/System.Net.Security/src/Resources/Strings.resx b/src/System.Net.Security/src/Resources/Strings.resx
index 939e1c91e7..fb3a593245 100644
--- a/src/System.Net.Security/src/Resources/Strings.resx
+++ b/src/System.Net.Security/src/Resources/Strings.resx
@@ -58,9 +58,6 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
- <data name="ArgumentOutOfRange_NeedNonNegNum" xml:space="preserve">
- <value>Index is less than zero.</value>
- </data>
<data name="net_noseek" xml:space="preserve">
<value>This stream does not support seek operations.</value>
</data>
@@ -187,9 +184,6 @@
<data name="net_invalid_enum" xml:space="preserve">
<value>The specified value is not valid in the '{0}' enumeration.</value>
</data>
- <data name="net_log_exception" xml:space="preserve">
- <value>Exception in {0}::{1} - {2}.</value>
- </data>
<data name="event_EnumerateSecurityPackages" xml:space="preserve">
<value>Enumerating security packages:</value>
</data>
@@ -289,15 +283,6 @@
<data name="security_ServiceNameCollection_EmptyServiceName" xml:space="preserve">
<value>A service name must not be null or empty.</value>
</data>
- <data name="ObjectDisposed_StreamIsClosed" xml:space="preserve">
- <value>Cannot access a closed Stream.</value>
- </data>
- <data name="NotSupported_UnreadableStream" xml:space="preserve">
- <value>Stream does not support reading.</value>
- </data>
- <data name="NotSupported_UnwritableStream" xml:space="preserve">
- <value>Stream does not support writing.</value>
- </data>
<data name="net_allocate_ssl_context_failed" xml:space="preserve">
<value>Failed to allocate SSL/TLS context, OpenSSL error - {0}.</value>
</data>
@@ -370,4 +355,10 @@
<data name="net_nego_not_supported_empty_target_with_defaultcreds" xml:space="preserve">
<value>Target name should be non empty if default credentials are passed.</value>
</data>
+ <data name="net_security_sslprotocol_contiguous">
+ <value>The requested combination of SslProtocols ({0}) is not valid for this platform because it skips intermediate versions.</value>
+ </data>
+ <data name="net_encryptionpolicy_notsupported" xml:space="preserve">
+ <value>The '{0}' encryption policy is not supported on this platform.</value>
+ </data>
</root>
diff --git a/src/System.Net.Security/src/System.Net.Security.csproj b/src/System.Net.Security/src/System.Net.Security.csproj
index d912c51e88..f120ff2583 100644
--- a/src/System.Net.Security/src/System.Net.Security.csproj
+++ b/src/System.Net.Security/src/System.Net.Security.csproj
@@ -6,6 +6,9 @@
<ProjectGuid>{89F37791-6254-4D60-AB96-ACD3CCA0E771}</ProjectGuid>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
+ <PropertyGroup Condition=" '$(TargetsOSX)' == 'true' ">
+ <DefineConstants>$(DefineConstants);SYSNETSECURITY_NO_OPENSSL</DefineConstants>
+ </PropertyGroup>
<!-- Help VS understand available configurations -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Unix-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Unix-Release|AnyCPU'" />
@@ -15,6 +18,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Windows_NT-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="System\PinnableBufferCache.cs" />
+ <Compile Include="System\Net\CertificateValidationPal.cs" />
<Compile Include="System\Net\FixedSizeReader.cs" />
<Compile Include="System\Net\HelperAsyncResults.cs" />
<Compile Include="System\Net\SslStreamContext.cs" />
@@ -46,19 +50,12 @@
<Compile Include="$(CommonPath)\System\Net\Logging\DebugThreadTracking.cs">
<Link>Common\System\Net\Logging\DebugThreadTracking.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\Net\Shims\TraceSource.cs">
- <Link>Common\System\Net\Shims\TraceSource.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\System\Net\Logging\NetEventSource.Common.cs">
<Link>Common\System\Net\Logging\NetEventSource.Common.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Net\InternalException.cs">
<Link>Common\System\Net\InternalException.cs</Link>
</Compile>
- <!-- HTTP -->
- <Compile Include="$(CommonPath)\System\Net\Http\TlsCertificateExtensions.cs">
- <Link>Common\System\Net\Http\TlsCertificateExtensions.cs</Link>
- </Compile>
<!-- Debug only -->
<Compile Include="$(CommonPath)\System\Net\DebugSafeHandle.cs">
<Link>Common\System\Net\DebugSafeHandle.cs</Link>
@@ -70,9 +67,6 @@
<Link>Common\System\Net\DebugCriticalHandleZeroOrMinusOneIsInvalid.cs</Link>
</Compile>
<!-- System.Net common -->
- <Compile Include="$(CommonPath)\System\Net\ContextAwareResult.cs">
- <Link>Common\System\Net\ContextAwareResult.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\System\Net\ExceptionCheck.cs">
<Link>Common\System\Net\ExceptionCheck.cs</Link>
</Compile>
@@ -95,9 +89,6 @@
<Compile Include="$(CommonPath)\System\Threading\Tasks\TaskToApm.cs">
<Link>Common\System\Threading\Tasks\TaskToApm.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\IO\Error.cs">
- <Link>Common\System\IO\Error.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\System\Net\Security\SecurityBuffer.cs">
<Link>Common\System\Net\Security\SecurityBuffer.cs</Link>
</Compile>
@@ -219,32 +210,61 @@
<Compile Include="$(CommonPath)\Interop\Windows\sspicli\SecPkgContext_StreamSizes.cs">
<Link>Common\Interop\Windows\sspicli\SecPkgContext_StreamSizes.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\Net\ContextAwareResult.Windows.cs">
- <Link>Common\System\Net\ContextAwareResult.Windows.cs</Link>
- </Compile>
</ItemGroup>
<ItemGroup Condition=" '$(TargetsUnix)' == 'true' ">
- <Compile Include="System\Net\CertificateValidationPal.Unix.cs" />
- <Compile Include="System\Net\Security\SslStreamPal.Unix.cs" />
- <Compile Include="System\Net\Security\SslConnectionInfo.Unix.cs" />
- <Compile Include="System\Net\Security\StreamSizes.Unix.cs" />
- <Compile Include="$(CommonPath)\System\Net\Security\CertificateValidation.Unix.cs">
- <Link>Common\System\Net\Security\CertificateValidation.Unix.cs</Link>
+ <Compile Include="$(CommonPath)\Interop\Unix\Interop.Libraries.cs">
+ <Link>Common\Interop\Unix\Interop.Libraries.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\Unix\Interop.Errors.cs">
+ <Link>Common\Interop\Unix\Interop.Errors.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\Unix\System.Net.Security.Native\Interop.Initialization.cs">
+ <Link>Common\Interop\Unix\System.Net.Security.Native\Interop.Initialization.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\Unix\System.Net.Security.Native\Interop.GssApiException.cs">
+ <Link>Common\Interop\Unix\System.Net.Security.Native\Interop.GssApiException.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\Unix\System.Net.Security.Native\Interop.GssBuffer.cs">
+ <Link>Common\Interop\Unix\System.Net.Security.Native\Interop.GssBuffer.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\GssSafeHandles.cs">
+ <Link>Common\Microsoft\Win32\SafeHandles\GssSafeHandles.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.cs">
+ <Link>Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.cs</Link>
</Compile>
- <!-- NegotiateStream -->
<Compile Include="$(CommonPath)\System\Net\ContextFlagsAdapterPal.Unix.cs">
<Link>Common\System\Net\ContextFlagsAdapterPal.Unix.cs</Link>
</Compile>
- <Compile Include="System\Net\Security\NegotiateStreamPal.Unix.cs" />
+ <Compile Include="$(CommonPath)\System\Net\Http\TlsCertificateExtensions.cs">
+ <Link>Common\System\Net\Http\TlsCertificateExtensions.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)\System\Net\Security\NegotiateStreamPal.Unix.cs">
<Link>Common\System\Net\Security\NegotiateStreamPal.Unix.cs</Link>
</Compile>
- <!-- Interop -->
- <Compile Include="$(CommonPath)\Interop\Unix\Interop.Libraries.cs">
- <Link>Common\Interop\Unix\Interop.Libraries.cs</Link>
+ <Compile Include="$(CommonPath)\System\Net\Security\Unix\SafeDeleteContext.cs">
+ <Link>Common\System\Net\Security\Unix\SafeDeleteContext.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\Interop\Unix\Interop.Errors.cs">
- <Link>Common\Interop\Unix\Interop.Errors.cs</Link>
+ <Compile Include="$(CommonPath)\System\Net\Security\Unix\SafeDeleteNegoContext.cs">
+ <Link>Common\System\Net\Security\Unix\SafeDeleteNegoContext.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Net\Security\Unix\SafeFreeCredentials.cs">
+ <Link>Common\System\Net\Security\Unix\SafeFreeCredentials.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Net\Security\Unix\SafeFreeNegoCredentials.cs">
+ <Link>Common\System\Net\Security\Unix\SafeFreeNegoCredentials.cs</Link>
+ </Compile>
+ <Compile Include="System\Net\Security\NegotiateStreamPal.Unix.cs" />
+ <Compile Include="System\Net\Security\Pal.Managed\EndpointChannelBindingToken.cs" />
+ <Compile Include="System\Net\Security\Pal.Managed\SafeChannelBindingHandle.cs" />
+ </ItemGroup>
+ <ItemGroup Condition=" '$(TargetsUnix)' == 'true' AND '$(TargetsOSX)' != 'true' ">
+ <Compile Include="System\Net\CertificateValidationPal.Unix.cs" />
+ <Compile Include="System\Net\Security\SslStreamPal.Unix.cs" />
+ <Compile Include="System\Net\Security\SslConnectionInfo.Unix.cs" />
+ <Compile Include="System\Net\Security\StreamSizes.Unix.cs" />
+ <Compile Include="$(CommonPath)\System\Net\Security\CertificateValidation.Unix.cs">
+ <Link>Common\System\Net\Security\CertificateValidation.Unix.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.ASN1.cs">
<Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.ASN1.cs</Link>
@@ -279,9 +299,6 @@
<Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.X509.cs">
<Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.X509.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.X509ChannelBindingHash.cs">
- <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.X509ChannelBindingHash.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.X509Name.cs">
<Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.X509Name.cs</Link>
</Compile>
@@ -294,21 +311,6 @@
<Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.X509StoreCtx.cs">
<Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.X509StoreCtx.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\Interop\Unix\System.Net.Security.Native\Interop.Initialization.cs">
- <Link>Common\Interop\Unix\System.Net.Security.Native\Interop.Initialization.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\Interop\Unix\System.Net.Security.Native\Interop.GssApiException.cs">
- <Link>Common\Interop\Unix\System.Net.Security.Native\Interop.GssApiException.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\Interop\Unix\System.Net.Security.Native\Interop.GssBuffer.cs">
- <Link>Common\Interop\Unix\System.Net.Security.Native\Interop.GssBuffer.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\GssSafeHandles.cs">
- <Link>Common\Microsoft\Win32\SafeHandles\GssSafeHandles.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.cs">
- <Link>Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeX509Handles.Unix.cs">
<Link>Common\Microsoft\Win32\SafeHandles\SafeX509Handles.Unix.cs</Link>
</Compile>
@@ -327,33 +329,59 @@
<Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\Asn1SafeHandles.Unix.cs">
<Link>Common\Microsoft\Win32\SafeHandles\Asn1SafeHandles.Unix.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\Net\ContextAwareResult.Unix.cs">
- <Link>Common\System\Net\ContextAwareResult.Unix.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\System\Net\Security\Unix\SafeDeleteContext.cs">
- <Link>Common\System\Net\Security\Unix\SafeDeleteContext.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\System\Net\Security\Unix\SafeDeleteSslContext.cs">
<Link>Common\System\Net\Security\Unix\SafeDeleteSslContext.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\Net\Security\Unix\SafeDeleteNegoContext.cs">
- <Link>Common\System\Net\Security\Unix\SafeDeleteNegoContext.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\System\Net\Security\Unix\SafeFreeCertContext.cs">
<Link>Common\System\Net\Security\Unix\SafeFreeCertContext.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\Net\Security\Unix\SafeFreeContextBufferChannelBinding.cs">
- <Link>Common\System\Net\Security\Unix\SafeFreeContextBufferChannelBinding.cs</Link>
+ <Compile Include="$(CommonPath)\System\Net\Security\Unix\SafeFreeSslCredentials.cs">
+ <Link>Common\System\Net\Security\Unix\SafeFreeSslCredentials.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\Net\Security\Unix\SafeFreeCredentials.cs">
- <Link>Common\System\Net\Security\Unix\SafeFreeCredentials.cs</Link>
+ </ItemGroup>
+ <ItemGroup Condition=" '$(TargetsOSX)' == 'true' ">
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\Net\Security\Unix\SafeFreeNegoCredentials.cs">
- <Link>Common\System\Net\Security\Unix\SafeFreeNegoCredentials.cs</Link>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFArray.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFArray.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\Net\Security\Unix\SafeFreeSslCredentials.cs">
- <Link>Common\System\Net\Security\Unix\SafeFreeSslCredentials.cs</Link>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFData.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFData.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFDate.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFDate.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFError.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFError.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFString.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFString.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.Libraries.cs">
+ <Link>Common\Interop\OSX\Interop.Libraries.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecErrMessage.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecErrMessage.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SslErr.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SslErr.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Ssl.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Ssl.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.X509Chain.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.X509Chain.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeCreateHandle.OSX.cs">
+ <Link>Common\Microsoft\Win32\SafeHandles\SafeCreateHandle.OSX.cs</Link>
</Compile>
+ <Compile Include="System\Net\CertificateValidationPal.OSX.cs" />
+ <Compile Include="System\Net\Security\Pal.OSX\SafeDeleteSslContext.cs" />
+ <Compile Include="System\Net\Security\Pal.OSX\SafeFreeSslCredentials.cs" />
+ <Compile Include="System\Net\Security\SslConnectionInfo.OSX.cs" />
+ <Compile Include="System\Net\Security\SslStreamPal.OSX.cs" />
+ <Compile Include="System\Net\Security\StreamSizes.OSX.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Win32.Primitives" />
@@ -373,6 +401,7 @@
<Reference Include="System.Security.Principal" />
<Reference Include="System.Security.Principal.Windows" />
<Reference Include="System.Threading" />
+ <Reference Include="System.Threading.Tasks" />
<Reference Include="System.Threading.ThreadPool" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsUnix)' == 'true'">
@@ -381,4 +410,4 @@
<Reference Include="System.Security.Cryptography.Primitives" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/System.Net.Security/src/System/Net/CertificateValidationPal.OSX.cs b/src/System.Net.Security/src/System/Net/CertificateValidationPal.OSX.cs
new file mode 100644
index 0000000000..20be908314
--- /dev/null
+++ b/src/System.Net.Security/src/System/Net/CertificateValidationPal.OSX.cs
@@ -0,0 +1,167 @@
+// 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.Net.Security;
+using System.Security.Cryptography.X509Certificates;
+using Microsoft.Win32.SafeHandles;
+
+namespace System.Net
+{
+ internal static partial class CertificateValidationPal
+ {
+ internal static SslPolicyErrors VerifyCertificateProperties(
+ SafeDeleteContext securityContext,
+ X509Chain chain,
+ X509Certificate2 remoteCertificate,
+ bool checkCertName,
+ bool isServer,
+ string hostName)
+ {
+ SslPolicyErrors errors = SslPolicyErrors.None;
+
+ if (remoteCertificate == null)
+ {
+ errors |= SslPolicyErrors.RemoteCertificateNotAvailable;
+ }
+ else
+ {
+ if (!chain.Build(remoteCertificate))
+ {
+ errors |= SslPolicyErrors.RemoteCertificateChainErrors;
+ }
+
+ if (!isServer && checkCertName)
+ {
+ SafeDeleteSslContext sslContext = (SafeDeleteSslContext)securityContext;
+
+ if (!Interop.AppleCrypto.SslCheckHostnameMatch(sslContext.SslContext, hostName, remoteCertificate.NotBefore))
+ {
+ errors |= SslPolicyErrors.RemoteCertificateNameMismatch;
+ }
+ }
+ }
+
+ return errors;
+ }
+
+ //
+ // Extracts a remote certificate upon request.
+ //
+ internal static X509Certificate2 GetRemoteCertificate(SafeDeleteContext securityContext)
+ {
+ return GetRemoteCertificate(securityContext, null);
+ }
+
+ internal static X509Certificate2 GetRemoteCertificate(
+ SafeDeleteContext securityContext,
+ out X509Certificate2Collection remoteCertificateStore)
+ {
+ if (securityContext == null)
+ {
+ remoteCertificateStore = null;
+ return null;
+ }
+
+ remoteCertificateStore = new X509Certificate2Collection();
+ return GetRemoteCertificate(securityContext, remoteCertificateStore);
+ }
+
+ private static X509Certificate2 GetRemoteCertificate(
+ SafeDeleteContext securityContext,
+ X509Certificate2Collection remoteCertificateStore)
+ {
+ if (securityContext == null)
+ {
+ return null;
+ }
+
+ if (NetEventSource.IsEnabled) NetEventSource.Enter(securityContext);
+
+ SafeSslHandle sslContext = ((SafeDeleteSslContext)securityContext).SslContext;
+
+ if (sslContext == null)
+ {
+ return null;
+ }
+
+ X509Certificate2 result = null;
+
+ using (SafeX509ChainHandle chainHandle = Interop.AppleCrypto.SslCopyCertChain(sslContext))
+ {
+ long chainSize = Interop.AppleCrypto.X509ChainGetChainSize(chainHandle);
+
+ if (remoteCertificateStore != null)
+ {
+ for (int i = 0; i < chainSize; i++)
+ {
+ IntPtr certHandle = Interop.AppleCrypto.X509ChainGetCertificateAtIndex(chainHandle, i);
+ remoteCertificateStore.Add(new X509Certificate2(certHandle));
+ }
+ }
+
+ // This will be a distinct object than remoteCertificateStore[0] (if applicable),
+ // to match what the Windows and Unix PALs do.
+ if (chainSize > 0)
+ {
+ IntPtr certHandle = Interop.AppleCrypto.X509ChainGetCertificateAtIndex(chainHandle, 0);
+ result = new X509Certificate2(certHandle);
+ }
+ }
+
+ if (NetEventSource.IsEnabled)
+ {
+ NetEventSource.Log.RemoteCertificate(result);
+ NetEventSource.Exit(securityContext, result);
+ }
+
+ return result;
+ }
+
+ //
+ // Used only by client SSL code, never returns null.
+ //
+ internal static string[] GetRequestCertificateAuthorities(SafeDeleteContext securityContext)
+ {
+ SafeSslHandle sslContext = ((SafeDeleteSslContext)securityContext).SslContext;
+
+ if (sslContext == null)
+ {
+ return Array.Empty<string>();
+ }
+
+ using (SafeCFArrayHandle dnArray = Interop.AppleCrypto.SslCopyCADistinguishedNames(sslContext))
+ {
+ long size = Interop.CoreFoundation.CFArrayGetCount(dnArray);
+
+ if (size == 0)
+ {
+ return Array.Empty<string>();
+ }
+
+ string[] distinguishedNames = new string[size];
+
+ for (int i = 0; i < size; i++)
+ {
+ IntPtr element = Interop.CoreFoundation.CFArrayGetValueAtIndex(dnArray, i);
+
+ using (SafeCFDataHandle cfData = new SafeCFDataHandle(element, ownsHandle: false))
+ {
+ byte[] dnData = Interop.CoreFoundation.CFGetData(cfData);
+ X500DistinguishedName dn = new X500DistinguishedName(dnData);
+ distinguishedNames[i] = dn.Name;
+ }
+ }
+
+ return distinguishedNames;
+ }
+ }
+
+ private static X509Store OpenStore(StoreLocation storeLocation)
+ {
+ X509Store store = new X509Store(StoreName.My, storeLocation);
+ store.Open(OpenFlags.ReadOnly);
+ return store;
+ }
+ }
+}
diff --git a/src/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs b/src/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs
index 64b20442c2..e56873ae4a 100644
--- a/src/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs
+++ b/src/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs
@@ -3,21 +3,16 @@
// See the LICENSE file in the project root for more information.
using System.Diagnostics;
-using System.Globalization;
using Microsoft.Win32.SafeHandles;
using System.Net.Security;
-using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
-using System.Threading;
namespace System.Net
{
internal static partial class CertificateValidationPal
{
- private static object s_lockObject = new object();
- private static X509Store s_userCertStore;
-
internal static SslPolicyErrors VerifyCertificateProperties(
+ SafeDeleteContext securityContext,
X509Chain chain,
X509Certificate2 remoteCertificate,
bool checkCertName,
@@ -30,9 +25,27 @@ namespace System.Net
//
// Extracts a remote certificate upon request.
//
- internal static X509Certificate2 GetRemoteCertificate(SafeDeleteContext securityContext, out X509Certificate2Collection remoteCertificateStore)
+ internal static X509Certificate2 GetRemoteCertificate(SafeDeleteContext securityContext)
+ {
+ return GetRemoteCertificate(securityContext, null);
+ }
+
+ internal static X509Certificate2 GetRemoteCertificate(
+ SafeDeleteContext securityContext,
+ out X509Certificate2Collection remoteCertificateStore)
+ {
+ if (securityContext == null)
+ {
+ remoteCertificateStore = null;
+ return null;
+ }
+
+ remoteCertificateStore = new X509Certificate2Collection();
+ return GetRemoteCertificate(securityContext, remoteCertificateStore);
+ }
+
+ private static X509Certificate2 GetRemoteCertificate(SafeDeleteContext securityContext, X509Certificate2Collection remoteCertificateStore)
{
- remoteCertificateStore = null;
bool gotReference = false;
if (securityContext == null)
@@ -54,24 +67,25 @@ namespace System.Net
result = new X509Certificate2(remoteContext.DangerousGetHandle());
}
- remoteCertificateStore = new X509Certificate2Collection();
-
- using (SafeSharedX509StackHandle chainStack =
- Interop.OpenSsl.GetPeerCertificateChain(((SafeDeleteSslContext)securityContext).SslContext))
+ if (remoteCertificateStore != null)
{
- if (!chainStack.IsInvalid)
+ using (SafeSharedX509StackHandle chainStack =
+ Interop.OpenSsl.GetPeerCertificateChain(((SafeDeleteSslContext)securityContext).SslContext))
{
- int count = Interop.Crypto.GetX509StackFieldCount(chainStack);
-
- for (int i = 0; i < count; i++)
+ if (!chainStack.IsInvalid)
{
- IntPtr certPtr = Interop.Crypto.GetX509StackField(chainStack, i);
+ int count = Interop.Crypto.GetX509StackFieldCount(chainStack);
- if (certPtr != IntPtr.Zero)
+ for (int i = 0; i < count; i++)
{
- // X509Certificate2(IntPtr) calls X509_dup, so the reference is appropriately tracked.
- X509Certificate2 chainCert = new X509Certificate2(certPtr);
- remoteCertificateStore.Add(chainCert);
+ IntPtr certPtr = Interop.Crypto.GetX509StackField(chainStack, i);
+
+ if (certPtr != IntPtr.Zero)
+ {
+ // X509Certificate2(IntPtr) calls X509_dup, so the reference is appropriately tracked.
+ X509Certificate2 chainCert = new X509Certificate2(certPtr);
+ remoteCertificateStore.Add(chainCert);
+ }
}
}
}
@@ -132,38 +146,24 @@ namespace System.Net
}
}
- internal static X509Store EnsureStoreOpened(bool isMachineStore)
+ static partial void CheckSupportsStore(StoreLocation storeLocation, ref bool hasSupport)
{
- if (isMachineStore)
- {
- // There's not currently a LocalMachine\My store on Unix, so don't bother trying
- // and having to deal with the exception.
- //
- // https://github.com/dotnet/corefx/issues/3690 tracks the lack of this store.
- return null;
- }
-
- return EnsureStoreOpened(ref s_userCertStore, StoreLocation.CurrentUser);
+ // There's not currently a LocalMachine\My store on Unix, so don't bother trying
+ // and having to deal with the exception.
+ //
+ // https://github.com/dotnet/corefx/issues/3690 tracks the lack of this store.
+ if (storeLocation == StoreLocation.LocalMachine)
+ hasSupport = false;
}
- private static X509Store EnsureStoreOpened(ref X509Store storeField, StoreLocation storeLocation)
+ private static X509Store OpenStore(StoreLocation storeLocation)
{
- return LazyInitializer.EnsureInitialized(ref storeField, ref s_lockObject, () =>
- {
- try
- {
- X509Store store = new X509Store(StoreName.My, storeLocation);
- store.Open(OpenFlags.ReadOnly);
+ Debug.Assert(storeLocation == StoreLocation.CurrentUser);
- if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"storeLocation: {storeLocation} returned store {store}");
- return store;
- }
- catch (CryptographicException e)
- {
- NetEventSource.Fail(null, $"Failed to open cert store, location: {storeLocation} exception {e}");
- throw;
- }
- });
+ X509Store store = new X509Store(StoreName.My, storeLocation);
+ store.Open(OpenFlags.ReadOnly);
+
+ return store;
}
private static int QueryContextRemoteCertificate(SafeDeleteContext securityContext, out SafeFreeCertContext remoteCertContext)
diff --git a/src/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs b/src/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs
index c9f4dea283..ada2c1330a 100644
--- a/src/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs
+++ b/src/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs
@@ -3,25 +3,18 @@
// See the LICENSE file in the project root for more information.
using Microsoft.Win32.SafeHandles;
-using System.Diagnostics;
using System.Net.Security;
using System.Runtime.InteropServices;
-using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Principal;
-using System.Threading;
namespace System.Net
{
internal static partial class CertificateValidationPal
{
- private static readonly object s_syncObject = new object();
-
- private static volatile X509Store s_myCertStoreEx;
- private static volatile X509Store s_myMachineCertStoreEx;
-
internal static SslPolicyErrors VerifyCertificateProperties(
+ SafeDeleteContext securityContext,
X509Chain chain,
X509Certificate2 remoteCertificate,
bool checkCertName,
@@ -186,61 +179,21 @@ namespace System.Net
//
// Security: We temporarily reset thread token to open the cert store under process account.
//
- internal static X509Store EnsureStoreOpened(bool isMachineStore)
+ internal static X509Store OpenStore(StoreLocation storeLocation)
{
- X509Store store = isMachineStore ? s_myMachineCertStoreEx : s_myCertStoreEx;
+ X509Store store = new X509Store(StoreName.My, storeLocation);
- // TODO #3862 Investigate if this can be switched to either the static or Lazy<T> patterns.
- if (store == null)
+ // For app-compat We want to ensure the store is opened under the **process** account.
+ try
{
- lock (s_syncObject)
+ WindowsIdentity.RunImpersonated(SafeAccessTokenHandle.InvalidHandle, () =>
{
- store = isMachineStore ? s_myMachineCertStoreEx : s_myCertStoreEx;
- if (store == null)
- {
- // NOTE: that if this call fails we won't keep track and the next time we enter we will try to open the store again.
- StoreLocation storeLocation = isMachineStore ? StoreLocation.LocalMachine : StoreLocation.CurrentUser;
- store = new X509Store(StoreName.My, storeLocation);
- try
- {
- // For app-compat We want to ensure the store is opened under the **process** account.
- try
- {
- WindowsIdentity.RunImpersonated(SafeAccessTokenHandle.InvalidHandle, () =>
- {
- store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
- if (NetEventSource.IsEnabled) NetEventSource.Info(null, $"storeLocation {storeLocation} returned store: {store}");
- });
- }
- catch
- {
- throw;
- }
-
- if (isMachineStore)
- {
- s_myMachineCertStoreEx = store;
- }
- else
- {
- s_myCertStoreEx = store;
- }
-
- return store;
- }
- catch (Exception exception)
- {
- if (exception is CryptographicException || exception is SecurityException)
- {
- NetEventSource.Fail(null, $"Failed to open cert store, location: {storeLocation} exception: {exception}");
- return null;
- }
-
- if (NetEventSource.IsEnabled) NetEventSource.Error(null, SR.Format(SR.net_log_open_store_failed, storeLocation, exception));
- throw;
- }
- }
- }
+ store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
+ });
+ }
+ catch
+ {
+ throw;
}
return store;
diff --git a/src/System.Net.Security/src/System/Net/CertificateValidationPal.cs b/src/System.Net.Security/src/System/Net/CertificateValidationPal.cs
new file mode 100644
index 0000000000..d96390dda0
--- /dev/null
+++ b/src/System.Net.Security/src/System/Net/CertificateValidationPal.cs
@@ -0,0 +1,85 @@
+// 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.Security;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+
+namespace System.Net
+{
+ internal static partial class CertificateValidationPal
+ {
+ private static readonly object s_syncObject = new object();
+
+ private static volatile X509Store s_myCertStoreEx;
+ private static volatile X509Store s_myMachineCertStoreEx;
+
+ static partial void CheckSupportsStore(StoreLocation storeLocation, ref bool hasSupport);
+
+ internal static X509Store EnsureStoreOpened(bool isMachineStore)
+ {
+ X509Store store = isMachineStore ? s_myMachineCertStoreEx : s_myCertStoreEx;
+
+ // TODO #3862 Investigate if this can be switched to either the static or Lazy<T> patterns.
+ if (store == null)
+ {
+ StoreLocation storeLocation = isMachineStore ? StoreLocation.LocalMachine : StoreLocation.CurrentUser;
+
+ // On Windows and OSX CheckSupportsStore is not defined, so the call is eliminated and the
+ // if should be folded out.
+ //
+ // On Unix it will prevent the lock from being held and released over and over for the LocalMachine store.
+ bool supportsStore = true;
+ CheckSupportsStore(storeLocation, ref supportsStore);
+
+ if (!supportsStore)
+ {
+ return null;
+ }
+
+ lock (s_syncObject)
+ {
+ store = isMachineStore ? s_myMachineCertStoreEx : s_myCertStoreEx;
+
+ if (store == null)
+ {
+ try
+ {
+ // NOTE: that if this call fails we won't keep track and the next time we enter we will try to open the store again.
+ store = OpenStore(storeLocation);
+
+ if (NetEventSource.IsEnabled)
+ NetEventSource.Info(null, $"storeLocation: {storeLocation} returned store {store}");
+
+ if (isMachineStore)
+ {
+ s_myMachineCertStoreEx = store;
+ }
+ else
+ {
+ s_myCertStoreEx = store;
+ }
+ }
+ catch (Exception exception)
+ {
+ if (exception is CryptographicException || exception is SecurityException)
+ {
+ NetEventSource.Fail(null,
+ $"Failed to open cert store, location: {storeLocation} exception: {exception}");
+ return null;
+ }
+
+ if (NetEventSource.IsEnabled)
+ NetEventSource.Error(null, SR.Format(SR.net_log_open_store_failed, storeLocation, exception));
+
+ throw;
+ }
+ }
+ }
+ }
+
+ return store;
+ }
+ }
+}
diff --git a/src/System.Net.Security/src/System/Net/FixedSizeReader.cs b/src/System.Net.Security/src/System/Net/FixedSizeReader.cs
index d1ffb829b6..94dc81225e 100644
--- a/src/System.Net.Security/src/System/Net/FixedSizeReader.cs
+++ b/src/System.Net.Security/src/System/Net/FixedSizeReader.cs
@@ -4,41 +4,29 @@
using System.Diagnostics;
using System.IO;
-using System.Threading.Tasks;
+using System.Threading;
namespace System.Net
{
- //
- // The class is a simple wrapper on top of a read stream. It will read the exact number of bytes requested.
- // It will throw if EOF is reached before the expected number of bytes is returned.
- //
- internal class FixedSizeReader
+ /// <summary>
+ /// The class is a simple wrapper on top of a read stream. It will read the exact number of bytes requested.
+ /// It will throw if EOF is reached before the expected number of bytes is returned.
+ /// </summary>
+ internal static class FixedSizeReader
{
- private static readonly AsyncCallback s_readCallback = new AsyncCallback(ReadCallback);
-
- private readonly Stream _transport;
- private AsyncProtocolRequest _request;
- private int _totalRead;
-
- public FixedSizeReader(Stream transport)
+ /// <summary>
+ /// Returns 0 on legitimate EOF or if 0 bytes were requested, otherwise reads as directed or throws.
+ /// Returns count on success.
+ /// </summary>
+ public static int ReadPacket(Stream transport, byte[] buffer, int offset, int count)
{
- _transport = transport;
- }
-
- //
- // Returns 0 on legitimate EOF or if 0 bytes were requested, otherwise reads as directed or throws.
- // Returns count on success.
- //
- public int ReadPacket(byte[] buffer, int offset, int count)
- {
- int tempCount = count;
+ int remainingCount = count;
do
{
- int bytes = _transport.Read(buffer, offset, tempCount);
-
+ int bytes = transport.Read(buffer, offset, remainingCount);
if (bytes == 0)
{
- if (tempCount != count)
+ if (remainingCount != count)
{
throw new IOException(SR.net_io_eof);
}
@@ -46,121 +34,45 @@ namespace System.Net
return 0;
}
- tempCount -= bytes;
+ remainingCount -= bytes;
offset += bytes;
- } while (tempCount != 0);
+ } while (remainingCount > 0);
+ Debug.Assert(remainingCount == 0);
return count;
}
- //
- // Completes "_Request" with 0 if 0 bytes was requested or legitimate EOF received.
- // Otherwise, reads as directed or completes "_Request" with an Exception or throws.
- //
- public void AsyncReadPacket(AsyncProtocolRequest request)
+ /// <summary>
+ /// Completes "request" with 0 if 0 bytes was requested or legitimate EOF received.
+ /// Otherwise, reads as directed or completes "request" with an Exception.
+ /// </summary>
+ public static async void ReadPacketAsync(Stream transport, AsyncProtocolRequest request) // "async Task" might result in additional, unnecessary allocation
{
- _request = request;
- _totalRead = 0;
- StartReading();
- }
-
- //
- // Loops while subsequent completions are sync.
- //
- private void StartReading()
- {
- while (true)
+ try
{
- int bytes;
-
- Task<int> t = _transport.ReadAsync(_request.Buffer, _request.Offset + _totalRead, _request.Count - _totalRead);
- if (t.IsCompleted)
- {
- bytes = t.GetAwaiter().GetResult();
- }
- else
+ int remainingCount = request.Count, offset = request.Offset;
+ do
{
- IAsyncResult ar = TaskToApm.Begin(t, s_readCallback, this);
- if (!ar.CompletedSynchronously)
+ int bytes = await transport.ReadAsync(request.Buffer, offset, remainingCount, CancellationToken.None).ConfigureAwait(false);
+ if (bytes == 0)
{
-#if DEBUG
- _request._DebugAsyncChain = ar;
-#endif
- break;
+ if (remainingCount != request.Count)
+ {
+ throw new IOException(SR.net_io_eof);
+ }
+ request.CompleteRequest(0);
+ return;
}
- bytes = TaskToApm.End<int>(ar);
- }
-
- if (CheckCompletionBeforeNextRead(bytes))
- {
- break;
- }
- }
- }
-
- private bool CheckCompletionBeforeNextRead(int bytes)
- {
- if (bytes == 0)
- {
- // 0 bytes was requested or EOF in the beginning of a frame, the caller should decide whether it's OK.
- if (_totalRead == 0)
- {
- _request.CompleteRequest(0);
- return true;
- }
-
- // EOF in the middle of a frame.
- throw new IOException(SR.net_io_eof);
- }
- if (_totalRead + bytes > _request.Count)
- {
- NetEventSource.Fail(this, $"State got out of range. Total:{_totalRead + bytes} Count:{_request.Count}");
- }
+ offset += bytes;
+ remainingCount -= bytes;
+ } while (remainingCount > 0);
- if ((_totalRead += bytes) == _request.Count)
- {
- _request.CompleteRequest(_request.Count);
- return true;
- }
-
- return false;
- }
-
- private static void ReadCallback(IAsyncResult transportResult)
- {
- if (!(transportResult.AsyncState is FixedSizeReader))
- {
- NetEventSource.Fail(null, "State type is wrong, expected FixedSizeReader.");
- }
-
- if (transportResult.CompletedSynchronously)
- {
- return;
- }
-
- FixedSizeReader reader = (FixedSizeReader)transportResult.AsyncState;
- AsyncProtocolRequest request = reader._request;
-
- // Async completion.
- try
- {
- int bytes = TaskToApm.End<int>(transportResult);
-
- if (reader.CheckCompletionBeforeNextRead(bytes))
- {
- return;
- }
-
- reader.StartReading();
+ Debug.Assert(remainingCount == 0);
+ request.CompleteRequest(request.Count);
}
catch (Exception e)
{
- if (request.IsUserCompleted)
- {
- throw;
- }
-
request.CompleteUserWithError(e);
}
}
diff --git a/src/System.Net.Security/src/System/Net/Security/InternalNegotiateStream.cs b/src/System.Net.Security/src/System/Net/Security/InternalNegotiateStream.cs
index b23fc4e27c..6e4d0f1dad 100644
--- a/src/System.Net.Security/src/System/Net/Security/InternalNegotiateStream.cs
+++ b/src/System.Net.Security/src/System/Net/Security/InternalNegotiateStream.cs
@@ -27,12 +27,9 @@ namespace System.Net.Security
private int _InternalOffset;
private int _InternalBufferCount;
- private FixedSizeReader _FrameReader;
-
private void InitializeStreamPart()
{
_ReadHeader = new byte[4];
- _FrameReader = new FixedSizeReader(InnerStream);
}
private byte[] InternalBuffer
@@ -267,7 +264,7 @@ namespace System.Net.Security
if (asyncRequest != null)
{
asyncRequest.SetNextRequest(_ReadHeader, 0, _ReadHeader.Length, s_readCallback);
- _FrameReader.AsyncReadPacket(asyncRequest);
+ FixedSizeReader.ReadPacketAsync(InnerStream, asyncRequest);
if (!asyncRequest.MustCompleteSynchronously)
{
return 0;
@@ -277,7 +274,7 @@ namespace System.Net.Security
}
else
{
- readBytes = _FrameReader.ReadPacket(_ReadHeader, 0, _ReadHeader.Length);
+ readBytes = FixedSizeReader.ReadPacket(InnerStream, _ReadHeader, 0, _ReadHeader.Length);
}
return StartFrameBody(readBytes, buffer, offset, count, asyncRequest);
@@ -321,7 +318,7 @@ namespace System.Net.Security
{
asyncRequest.SetNextRequest(InternalBuffer, 0, readBytes, s_readCallback);
- _FrameReader.AsyncReadPacket(asyncRequest);
+ FixedSizeReader.ReadPacketAsync(InnerStream, asyncRequest);
if (!asyncRequest.MustCompleteSynchronously)
{
@@ -332,7 +329,7 @@ namespace System.Net.Security
}
else //Sync
{
- readBytes = _FrameReader.ReadPacket(InternalBuffer, 0, readBytes);
+ readBytes = FixedSizeReader.ReadPacket(InnerStream, InternalBuffer, 0, readBytes);
}
return ProcessFrameBody(readBytes, buffer, offset, count, asyncRequest);
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509ChannelBindingHash.cs b/src/System.Net.Security/src/System/Net/Security/Pal.Managed/EndpointChannelBindingToken.cs
index 430746708a..00b413f493 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.X509ChannelBindingHash.cs
+++ b/src/System.Net.Security/src/System/Net/Security/Pal.Managed/EndpointChannelBindingToken.cs
@@ -2,21 +2,39 @@
// 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;
+using System.Security.Authentication.ExtendedProtection;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
-
-internal static partial class Interop
+namespace System.Net.Security
{
- internal static partial class OpenSsl
+ internal static class EndpointChannelBindingToken
{
- internal static HashAlgorithm GetHashForChannelBinding(X509Certificate2 cert)
+ internal static ChannelBinding Build(SafeDeleteContext securityContext)
+ {
+ using (X509Certificate2 cert = CertificateValidationPal.GetRemoteCertificate(securityContext))
+ {
+ if (cert == null)
+ return null;
+
+ SafeChannelBindingHandle bindingHandle = new SafeChannelBindingHandle(ChannelBindingKind.Unique);
+
+ using (HashAlgorithm hashAlgo = GetHashForChannelBinding(cert))
+ {
+ byte[] bindingHash = hashAlgo.ComputeHash(cert.RawData);
+ bindingHandle.SetCertHash(bindingHash);
+ }
+
+ return bindingHandle;
+ }
+ }
+
+ private static HashAlgorithm GetHashForChannelBinding(X509Certificate2 cert)
{
Oid signatureAlgorithm = cert.SignatureAlgorithm;
switch (signatureAlgorithm.Value)
{
- // RFC 5929 4.1 says that MD5 and SHA1 both upgrade to EvpSha256 for cbt calculation
+ // RFC 5929 4.1 says that MD5 and SHA1 both upgrade to SHA256 for cbt calculation
case "1.2.840.113549.2.5": // MD5
case "1.2.840.113549.1.1.4": // MD5RSA
case "1.3.14.3.2.26": // SHA1
@@ -43,4 +61,4 @@ internal static partial class Interop
}
}
}
-}
+} \ No newline at end of file
diff --git a/src/System.Net.Security/src/System/Net/Security/Pal.Managed/SafeChannelBindingHandle.cs b/src/System.Net.Security/src/System/Net/Security/Pal.Managed/SafeChannelBindingHandle.cs
new file mode 100644
index 0000000000..ad5bd763da
--- /dev/null
+++ b/src/System.Net.Security/src/System/Net/Security/Pal.Managed/SafeChannelBindingHandle.cs
@@ -0,0 +1,87 @@
+// 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.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Authentication.ExtendedProtection;
+using System.Text;
+
+namespace System.Net.Security
+{
+ internal sealed class SafeChannelBindingHandle : ChannelBinding
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ private struct SecChannelBindings
+ {
+ internal int InitiatorLength;
+ internal int InitiatorOffset;
+ internal int AcceptorAddrType;
+ internal int AcceptorLength;
+ internal int AcceptorOffset;
+ internal int ApplicationDataLength;
+ internal int ApplicationDataOffset;
+ }
+
+ private const int CertHashMaxSize = 128;
+ private static readonly byte[] s_tlsServerEndPointByteArray = Encoding.UTF8.GetBytes("tls-server-end-point:");
+ private static readonly byte[] s_tlsUniqueByteArray = Encoding.UTF8.GetBytes("tls-unique:");
+ private static readonly int s_secChannelBindingSize = Marshal.SizeOf<SecChannelBindings>();
+
+ private readonly int _cbtPrefixByteArraySize;
+ internal int Length { get; private set; }
+ internal IntPtr CertHashPtr { get; }
+ public override int Size => Length;
+
+ internal void SetCertHash(byte[] certHashBytes)
+ {
+ Debug.Assert(certHashBytes != null, "check certHashBytes is not null");
+ Debug.Assert(certHashBytes.Length <= CertHashMaxSize);
+
+ int length = certHashBytes.Length;
+ Marshal.Copy(certHashBytes, 0, CertHashPtr, length);
+ SetCertHashLength(length);
+ }
+
+ private byte[] GetPrefixBytes(ChannelBindingKind kind)
+ {
+ Debug.Assert(kind == ChannelBindingKind.Endpoint || kind == ChannelBindingKind.Unique);
+ return kind == ChannelBindingKind.Endpoint
+ ? s_tlsServerEndPointByteArray
+ : s_tlsUniqueByteArray;
+ }
+
+ internal SafeChannelBindingHandle(ChannelBindingKind kind)
+ {
+ byte[] cbtPrefix = GetPrefixBytes(kind);
+ _cbtPrefixByteArraySize = cbtPrefix.Length;
+ handle = Marshal.AllocHGlobal(s_secChannelBindingSize + _cbtPrefixByteArraySize + CertHashMaxSize);
+ IntPtr cbtPrefixPtr = handle + s_secChannelBindingSize;
+ Marshal.Copy(cbtPrefix, 0, cbtPrefixPtr, _cbtPrefixByteArraySize);
+ CertHashPtr = cbtPrefixPtr + _cbtPrefixByteArraySize;
+ Length = CertHashMaxSize;
+ }
+
+ internal void SetCertHashLength(int certHashLength)
+ {
+ int cbtLength = _cbtPrefixByteArraySize + certHashLength;
+ Length = s_secChannelBindingSize + cbtLength;
+
+ SecChannelBindings channelBindings = new SecChannelBindings()
+ {
+ ApplicationDataLength = cbtLength,
+ ApplicationDataOffset = s_secChannelBindingSize
+ };
+ Marshal.StructureToPtr(channelBindings, handle, true);
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+
+ protected override bool ReleaseHandle()
+ {
+ Marshal.FreeHGlobal(handle);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs b/src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs
new file mode 100644
index 0000000000..bb6d29c6f8
--- /dev/null
+++ b/src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeDeleteSslContext.cs
@@ -0,0 +1,348 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.Net.Http;
+using System.Net.Security;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+using Microsoft.Win32.SafeHandles;
+
+namespace System.Net
+{
+ internal sealed class SafeDeleteSslContext : SafeDeleteContext
+ {
+ private SafeSslHandle _sslContext;
+ private Interop.AppleCrypto.SSLReadFunc _readCallback;
+ private Interop.AppleCrypto.SSLWriteFunc _writeCallback;
+ private Queue<byte> _fromConnection = new Queue<byte>();
+ private Queue<byte> _toConnection = new Queue<byte>();
+
+ public SafeSslHandle SslContext => _sslContext;
+
+ public SafeDeleteSslContext(SafeFreeSslCredentials credential, bool isServer)
+ : base(credential)
+ {
+ Debug.Assert((null != credential) && !credential.IsInvalid, "Invalid credential used in SafeDeleteSslContext");
+
+ try
+ {
+ unsafe
+ {
+ _readCallback = ReadFromConnection;
+ _writeCallback = WriteToConnection;
+ }
+
+ _sslContext = CreateSslContext(credential, isServer);
+
+ int osStatus = Interop.AppleCrypto.SslSetIoCallbacks(
+ _sslContext,
+ _readCallback,
+ _writeCallback);
+
+ if (osStatus != 0)
+ {
+ throw Interop.AppleCrypto.CreateExceptionForOSStatus(osStatus);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Write("Exception Caught. - " + ex);
+ Dispose();
+ throw;
+ }
+ }
+
+ private static SafeSslHandle CreateSslContext(SafeFreeSslCredentials credential, bool isServer)
+ {
+ switch (credential.Policy)
+ {
+ case EncryptionPolicy.RequireEncryption:
+ case EncryptionPolicy.AllowNoEncryption:
+ // SecureTransport doesn't allow TLS_NULL_NULL_WITH_NULL, but
+ // since AllowNoEncryption intersect OS-supported isn't nothing,
+ // let it pass.
+ break;
+ default:
+ throw new PlatformNotSupportedException(SR.net_encryptionpolicy_notsupported);
+ }
+
+ SafeSslHandle sslContext = Interop.AppleCrypto.SslCreateContext(isServer ? 1 : 0);
+
+ try
+ {
+ if (sslContext.IsInvalid)
+ {
+ // This is as likely as anything. No error conditions are defined for
+ // the OS function, and our shim only adds a NULL if isServer isn't a normalized bool.
+ throw new OutOfMemoryException();
+ }
+
+ // Let None mean "system default"
+ if (credential.Protocols != SslProtocols.None)
+ {
+ SetProtocols(sslContext, credential.Protocols);
+ }
+
+ if (credential.Certificate != null)
+ {
+ SetCertificate(sslContext, credential.Certificate);
+ }
+
+ Interop.AppleCrypto.SslBreakOnServerAuth(sslContext, true);
+ Interop.AppleCrypto.SslBreakOnClientAuth(sslContext, true);
+ }
+ catch
+ {
+ sslContext.Dispose();
+ throw;
+ }
+
+ return sslContext;
+ }
+
+ public override bool IsInvalid => _sslContext?.IsInvalid ?? true;
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (null != _sslContext)
+ {
+ _sslContext.Dispose();
+ _sslContext = null;
+ }
+
+ _toConnection = null;
+ _fromConnection = null;
+ _writeCallback = null;
+ _readCallback = null;
+ }
+
+ base.Dispose(disposing);
+ }
+
+ private unsafe int WriteToConnection(void* connection, byte* data, void** dataLength)
+ {
+ ulong toWrite = (ulong)*dataLength;
+ byte* readFrom = data;
+
+ lock (_toConnection)
+ {
+ while (toWrite > 0)
+ {
+ _toConnection.Enqueue(*readFrom);
+ readFrom++;
+ toWrite--;
+ }
+ }
+
+ // Since we can enqueue everything, no need to re-assign *dataLength.
+ const int noErr = 0;
+ return noErr;
+ }
+
+ private unsafe int ReadFromConnection(void* connection, byte* data, void** dataLength)
+ {
+ const int noErr = 0;
+ const int errSSLWouldBlock = -9803;
+
+ ulong toRead = (ulong)*dataLength;
+
+ if (toRead == 0)
+ {
+
+ return noErr;
+ }
+
+ uint transferred = 0;
+
+ lock (_fromConnection)
+ {
+
+ if (_fromConnection.Count == 0)
+ {
+
+ *dataLength = (void*)0;
+ return errSSLWouldBlock;
+ }
+
+ byte* writePos = data;
+
+ while (transferred < toRead && _fromConnection.Count > 0)
+ {
+ *writePos = _fromConnection.Dequeue();
+ writePos++;
+ transferred++;
+ }
+ }
+
+ *dataLength = (void*)transferred;
+ return noErr;
+ }
+
+ internal void Write(byte[] buf, int offset, int count)
+ {
+ Debug.Assert(buf != null);
+ Debug.Assert(offset >= 0);
+ Debug.Assert(count >= 0);
+ Debug.Assert(count <= buf.Length - offset);
+
+
+ lock (_fromConnection)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ _fromConnection.Enqueue(buf[offset + i]);
+ }
+ }
+
+ }
+
+ internal int BytesReadyForConnection => _toConnection.Count;
+
+ internal byte[] ReadPendingWrites()
+ {
+ lock (_toConnection)
+ {
+ if (_toConnection.Count == 0)
+ {
+ return null;
+ }
+
+ byte[] data = _toConnection.ToArray();
+ _toConnection.Clear();
+
+ return data;
+ }
+ }
+
+ internal int ReadPendingWrites(byte[] buf, int offset, int count)
+ {
+ Debug.Assert(buf != null);
+ Debug.Assert(offset >= 0);
+ Debug.Assert(count >= 0);
+ Debug.Assert(count <= buf.Length - offset);
+
+ lock (_toConnection)
+ {
+ int limit = Math.Min(count, _toConnection.Count);
+
+ for (int i = 0; i < limit; i++)
+ {
+ buf[offset + i] = _toConnection.Dequeue();
+ }
+
+ return limit;
+ }
+ }
+
+ private static void SetProtocols(SafeSslHandle sslContext, SslProtocols protocols)
+ {
+ const SslProtocols SupportedProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
+ SslProtocols minProtocolId;
+ SslProtocols maxProtocolId;
+
+ switch (protocols & SupportedProtocols)
+ {
+ case SslProtocols.None:
+ throw new PlatformNotSupportedException(SR.net_securityprotocolnotsupported);
+ case SslProtocols.Tls:
+ minProtocolId = SslProtocols.Tls;
+ maxProtocolId = SslProtocols.Tls;
+ break;
+ case SslProtocols.Tls11:
+ minProtocolId = SslProtocols.Tls11;
+ maxProtocolId = SslProtocols.Tls11;
+ break;
+ case SslProtocols.Tls12:
+ minProtocolId = SslProtocols.Tls12;
+ maxProtocolId = SslProtocols.Tls12;
+ break;
+ case SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12:
+ minProtocolId = SslProtocols.Tls;
+ maxProtocolId = SslProtocols.Tls12;
+ break;
+ case SslProtocols.Tls11 | SslProtocols.Tls12:
+ minProtocolId = SslProtocols.Tls11;
+ maxProtocolId = SslProtocols.Tls12;
+ break;
+ case SslProtocols.Tls | SslProtocols.Tls11:
+ minProtocolId = SslProtocols.Tls;
+ maxProtocolId = SslProtocols.Tls11;
+ break;
+ default:
+ throw new PlatformNotSupportedException(SR.net_security_sslprotocol_contiguous);
+ }
+
+ Interop.AppleCrypto.SslSetMinProtocolVersion(sslContext, minProtocolId);
+ Interop.AppleCrypto.SslSetMaxProtocolVersion(sslContext, maxProtocolId);
+ }
+
+ private static void SetCertificate(SafeSslHandle sslContext, X509Certificate2 certificate)
+ {
+ Debug.Assert(sslContext != null, "sslContext != null");
+ Debug.Assert(certificate != null, "certificate != null");
+ Debug.Assert(certificate.HasPrivateKey, "certificate.HasPrivateKey");
+
+ X509Chain chain = TLSCertificateExtensions.BuildNewChain(
+ certificate,
+ includeClientApplicationPolicy: false);
+
+ using (chain)
+ {
+ X509ChainElementCollection elements = chain.ChainElements;
+
+ // We need to leave off the EE (first) and root (last) certificate from the intermediates.
+ X509Certificate2[] intermediateCerts = elements.Count < 3
+ ? Array.Empty<X509Certificate2>()
+ : new X509Certificate2[elements.Count - 2];
+
+ // Build an array which is [
+ // SecIdentityRef for EE cert,
+ // SecCertificateRef for intermed0,
+ // SecCertificateREf for intermed1,
+ // ...
+ // ]
+ IntPtr[] ptrs = new IntPtr[intermediateCerts.Length + 1];
+
+ for (int i = 0; i < intermediateCerts.Length; i++)
+ {
+ X509Certificate2 intermediateCert = elements[i + 1].Certificate;
+
+ if (intermediateCert.HasPrivateKey)
+ {
+ // In the unlikely event that we get a certificate with a private key from
+ // a chain, clear it to the certificate.
+ //
+ // The current value of intermediateCert is still in elements, which will
+ // get Disposed at the end of this method. The new value will be
+ // in the intermediate certs array, which also gets serially Disposed.
+ intermediateCert = new X509Certificate2(intermediateCert.RawData);
+ }
+
+ intermediateCerts[i] = intermediateCert;
+ ptrs[i + 1] = intermediateCert.Handle;
+ }
+
+ ptrs[0] = certificate.Handle;
+
+ Interop.AppleCrypto.SslSetCertificate(sslContext, ptrs);
+
+ // The X509Chain created all new certs for us, so Dispose them.
+ // And since the intermediateCerts could have been new instances, Dispose them, too
+ for (int i = 0; i < elements.Count; i++)
+ {
+ elements[i].Certificate.Dispose();
+
+ if (i < intermediateCerts.Length)
+ {
+ intermediateCerts[i].Dispose();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeFreeSslCredentials.cs b/src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeFreeSslCredentials.cs
new file mode 100644
index 0000000000..488ffc9acf
--- /dev/null
+++ b/src/System.Net.Security/src/System/Net/Security/Pal.OSX/SafeFreeSslCredentials.cs
@@ -0,0 +1,56 @@
+// 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.Diagnostics;
+using System.Net.Security;
+using System.Security.Authentication;
+using System.Security.Cryptography.X509Certificates;
+
+namespace System.Net
+{
+ internal sealed class SafeFreeSslCredentials : SafeFreeCredentials
+ {
+ public SafeFreeSslCredentials(X509Certificate certificate, SslProtocols protocols, EncryptionPolicy policy)
+ : base(IntPtr.Zero, true)
+ {
+ Debug.Assert(
+ certificate == null || certificate is X509Certificate2,
+ "Only X509Certificate2 certificates are supported at this time");
+
+ X509Certificate2 cert = (X509Certificate2)certificate;
+
+ if (cert != null)
+ {
+ Debug.Assert(cert.HasPrivateKey, "cert.HasPrivateKey");
+
+ // Make a defensive copy of the certificate. In some async cases the
+ // certificate can have been disposed before being provided to the handshake.
+ //
+ // This meshes with the Unix (OpenSSL) PAL, because it extracts the private key
+ // and cert handle (which get up-reffed) to match the API expectations.
+ cert = new X509Certificate2(cert);
+
+ Debug.Assert(cert.HasPrivateKey, "cert clone.HasPrivateKey");
+ }
+
+ Certificate = cert;
+ Protocols = protocols;
+ Policy = policy;
+ }
+
+ public EncryptionPolicy Policy { get; }
+
+ public SslProtocols Protocols { get; }
+
+ public X509Certificate2 Certificate { get; }
+
+ public override bool IsInvalid => false;
+
+ protected override bool ReleaseHandle()
+ {
+ Certificate?.Dispose();
+ return true;
+ }
+ }
+}
diff --git a/src/System.Net.Security/src/System/Net/Security/SecureChannel.cs b/src/System.Net.Security/src/System/Net/Security/SecureChannel.cs
index 9642f1af65..44deb8558b 100644
--- a/src/System.Net.Security/src/System/Net/Security/SecureChannel.cs
+++ b/src/System.Net.Security/src/System/Net/Security/SecureChannel.cs
@@ -870,6 +870,8 @@ namespace System.Net.Security
_headerSize = streamSizes.Header;
_trailerSize = streamSizes.Trailer;
_maxDataSize = checked(streamSizes.MaximumMessage - (_headerSize + _trailerSize));
+
+ Debug.Assert(_maxDataSize > 0, "_maxDataSize > 0");
}
catch (Exception e) when (!ExceptionCheck.IsFatal(e))
{
@@ -1013,6 +1015,7 @@ namespace System.Net.Security
}
sslPolicyErrors |= CertificateValidationPal.VerifyCertificateProperties(
+ _securityContext,
chain,
remoteCertificateEx,
_checkCertName,
diff --git a/src/System.Net.Security/src/System/Net/Security/SslConnectionInfo.OSX.cs b/src/System.Net.Security/src/System/Net/Security/SslConnectionInfo.OSX.cs
new file mode 100644
index 0000000000..3875d9bbee
--- /dev/null
+++ b/src/System.Net.Security/src/System/Net/Security/SslConnectionInfo.OSX.cs
@@ -0,0 +1,857 @@
+// 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.Collections.Generic;
+using System.Diagnostics;
+using System.Security.Authentication;
+
+using TlsCipherSuite = Interop.AppleCrypto.TlsCipherSuite;
+
+namespace System.Net.Security
+{
+ internal partial class SslConnectionInfo
+ {
+ private const int LookupMapCount = 133;
+
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx
+ // CALG_DH_SF
+ private const ExchangeAlgorithmType DiffieHellmanStatic = (ExchangeAlgorithmType)0xAA01;
+ // CALG_ECDH
+ private const ExchangeAlgorithmType EcDhAlgorithm = (ExchangeAlgorithmType)0xAA05;
+ // CALG_ECDH_EPHEM
+ private const ExchangeAlgorithmType EcDheAlgorithm = (ExchangeAlgorithmType)0xAA06;
+
+#if DEBUG
+ static SslConnectionInfo()
+ {
+ Debug.Assert(
+ s_tlsLookup.Count == LookupMapCount,
+ $"Lookup dictionary was of size {s_tlsLookup.Count} instead of {LookupMapCount}");
+
+ Array enumValues = Enum.GetValues(typeof(TlsCipherSuite));
+
+ foreach (TlsCipherSuite val in enumValues)
+ {
+ Debug.Assert(s_tlsLookup.ContainsKey(val), $"No mapping found for {val} ({(int)val})");
+ }
+ }
+#endif
+
+ public SslConnectionInfo(SafeSslHandle sslContext)
+ {
+ SslProtocols protocol;
+ TlsCipherSuite cipherSuite;
+
+ int osStatus = Interop.AppleCrypto.SslGetProtocolVersion(sslContext, out protocol);
+
+ if (osStatus != 0)
+ throw Interop.AppleCrypto.CreateExceptionForOSStatus(osStatus);
+
+ osStatus = Interop.AppleCrypto.SslGetCipherSuite(sslContext, out cipherSuite);
+
+ if (osStatus != 0)
+ throw Interop.AppleCrypto.CreateExceptionForOSStatus(osStatus);
+
+ Protocol = (int)protocol;
+
+ MapCipherSuite(cipherSuite);
+ }
+
+ private void MapCipherSuite(TlsCipherSuite cipherSuite)
+ {
+ TlsMapping mapping;
+
+ if (!s_tlsLookup.TryGetValue(cipherSuite, out mapping))
+ {
+ Debug.Fail($"No mapping found for cipherSuite {cipherSuite}");
+ }
+
+ KeyExchangeAlg = (int)mapping.KeyExchangeAlgorithm;
+ KeyExchKeySize = 0;
+ DataCipherAlg = (int)mapping.CipherAlgorithm;
+ DataKeySize = mapping.CipherAlgorithmStrength;
+ DataHashAlg = (int)mapping.HashAlgorithm;
+ DataHashKeySize = (int)mapping.HashAlgorithmStrength;
+ }
+
+ private struct TlsMapping
+ {
+ internal ExchangeAlgorithmType KeyExchangeAlgorithm;
+ // The Key Exchange size isn't part of the CipherSuite
+ internal CipherAlgorithmType CipherAlgorithm;
+ internal int CipherAlgorithmStrength;
+ internal HashAlgorithmType HashAlgorithm;
+ internal int HashAlgorithmStrength;
+
+ internal static TlsMapping DhEphem(CipherAlgorithmType cipher, HashAlgorithmType hash) =>
+ Build(ExchangeAlgorithmType.DiffieHellman, cipher, GetCipherSize(cipher), hash);
+
+ internal static TlsMapping DhEphem(CipherAlgorithmType cipher, int cipherSize, HashAlgorithmType hash) =>
+ Build(ExchangeAlgorithmType.DiffieHellman, cipher, cipherSize, hash);
+
+ internal static TlsMapping DhStatic(CipherAlgorithmType cipher, HashAlgorithmType hash) =>
+ Build(DiffieHellmanStatic, cipher, GetCipherSize(cipher), hash);
+
+ internal static TlsMapping DhStatic(CipherAlgorithmType cipher, int cipherSize, HashAlgorithmType hash) =>
+ Build(DiffieHellmanStatic, cipher, cipherSize, hash);
+
+ internal static TlsMapping EcDhEphem(CipherAlgorithmType cipher, HashAlgorithmType hash) =>
+ Build(EcDheAlgorithm, cipher, GetCipherSize(cipher), hash);
+
+ internal static TlsMapping EcDhEphem(CipherAlgorithmType cipher, int cipherSize, HashAlgorithmType hash) =>
+ Build(EcDheAlgorithm, cipher, cipherSize, hash);
+
+ internal static TlsMapping EcDhStatic(CipherAlgorithmType cipher, HashAlgorithmType hash) =>
+ Build(EcDhAlgorithm, cipher, GetCipherSize(cipher), hash);
+
+ internal static TlsMapping EcDhStatic(CipherAlgorithmType cipher, int cipherSize, HashAlgorithmType hash) =>
+ Build(EcDhAlgorithm, cipher, cipherSize, hash);
+
+ internal static TlsMapping NoExchange(CipherAlgorithmType cipher, HashAlgorithmType hash) =>
+ Build(ExchangeAlgorithmType.None, cipher, GetCipherSize(cipher), hash);
+
+ internal static TlsMapping NoExchange(CipherAlgorithmType cipher, int cipherSize, HashAlgorithmType hash) =>
+ Build(ExchangeAlgorithmType.None, cipher, cipherSize, hash);
+
+ internal static TlsMapping Rsa(CipherAlgorithmType cipher, HashAlgorithmType hash) =>
+ Build(ExchangeAlgorithmType.RsaKeyX, cipher, GetCipherSize(cipher), hash);
+
+ internal static TlsMapping Rsa(CipherAlgorithmType cipher, int cipherSize, HashAlgorithmType hash) =>
+ Build(ExchangeAlgorithmType.RsaKeyX, cipher, cipherSize, hash);
+
+ private static TlsMapping Build(
+ ExchangeAlgorithmType exchange,
+ CipherAlgorithmType cipher,
+ int cipherSize,
+ HashAlgorithmType hash)
+ {
+ int hashSize = GetHashSize(hash);
+
+ return new TlsMapping
+ {
+ KeyExchangeAlgorithm = exchange,
+ CipherAlgorithm = cipher,
+ CipherAlgorithmStrength = cipherSize,
+ HashAlgorithm = hash,
+ HashAlgorithmStrength = hashSize,
+ };
+ }
+
+ private static int GetHashSize(HashAlgorithmType hash)
+ {
+ switch (hash)
+ {
+ case HashAlgorithmType.Md5:
+ return 128;
+ case HashAlgorithmType.Sha1:
+ return 160;
+ case HashAlgorithmType.Sha256:
+ return 256;
+ case HashAlgorithmType.Sha384:
+ return 384;
+ case HashAlgorithmType.Sha512:
+ return 512;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(hash));
+ }
+ }
+
+ private static int GetCipherSize(CipherAlgorithmType cipher)
+ {
+ switch (cipher)
+ {
+ case CipherAlgorithmType.None:
+ case CipherAlgorithmType.Null:
+ return 0;
+ case CipherAlgorithmType.Rc2:
+ Debug.Fail($"RC2 should always include the keysize");
+ return 0;
+ case CipherAlgorithmType.Des:
+ return 56;
+ case CipherAlgorithmType.Rc4:
+ Debug.Fail($"RC4 should always include the keysize");
+ return 0;
+ case CipherAlgorithmType.TripleDes:
+ return 168;
+ case CipherAlgorithmType.Aes128:
+ return 128;
+ case CipherAlgorithmType.Aes192:
+ return 192;
+ case CipherAlgorithmType.Aes256:
+ return 256;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(cipher));
+ }
+ }
+ }
+
+ private static readonly Dictionary<TlsCipherSuite, TlsMapping> s_tlsLookup =
+ new Dictionary<TlsCipherSuite, TlsMapping>(LookupMapCount)
+ {
+ {
+ TlsCipherSuite.TLS_NULL_WITH_NULL_NULL,
+ new TlsMapping()
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_WITH_NULL_MD5,
+ TlsMapping.Rsa(CipherAlgorithmType.None, HashAlgorithmType.Md5)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_WITH_NULL_SHA,
+ TlsMapping.Rsa(CipherAlgorithmType.None, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.SSL_RSA_EXPORT_WITH_RC4_40_MD5,
+ TlsMapping.Rsa(CipherAlgorithmType.Rc4, 40, HashAlgorithmType.Md5)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_WITH_RC4_128_MD5,
+ TlsMapping.Rsa(CipherAlgorithmType.Rc4, 128, HashAlgorithmType.Md5)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_WITH_RC4_128_SHA,
+ TlsMapping.Rsa(CipherAlgorithmType.Rc4, 128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5,
+ TlsMapping.Rsa(CipherAlgorithmType.Rc2, 40, HashAlgorithmType.Md5)
+ },
+
+ {
+ TlsCipherSuite.SSL_RSA_EXPORT_WITH_DES40_CBC_SHA,
+ TlsMapping.Rsa(CipherAlgorithmType.Des, 40, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.SSL_RSA_WITH_DES_CBC_SHA,
+ TlsMapping.Rsa(CipherAlgorithmType.Des, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+ TlsMapping.Rsa(CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA,
+ TlsMapping.DhStatic(CipherAlgorithmType.Des, 40, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.SSL_DH_DSS_WITH_DES_CBC_SHA,
+ TlsMapping.DhStatic(CipherAlgorithmType.Des, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA,
+ TlsMapping.DhStatic(CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA,
+ TlsMapping.DhStatic(CipherAlgorithmType.Des, 40, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.SSL_DH_RSA_WITH_DES_CBC_SHA,
+ TlsMapping.DhStatic(CipherAlgorithmType.Des, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA,
+ TlsMapping.DhStatic(CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA,
+ TlsMapping.DhEphem(CipherAlgorithmType.Des, 40, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.SSL_DHE_DSS_WITH_DES_CBC_SHA,
+ TlsMapping.DhEphem(CipherAlgorithmType.Des, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+ TlsMapping.DhEphem(CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA,
+ TlsMapping.DhEphem(CipherAlgorithmType.Des, 40, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.SSL_DHE_RSA_WITH_DES_CBC_SHA,
+ TlsMapping.DhEphem(CipherAlgorithmType.Des, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ TlsMapping.DhEphem(CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.SSL_DH_anon_EXPORT_WITH_RC4_40_MD5,
+ TlsMapping.DhStatic(CipherAlgorithmType.Rc4, 40, HashAlgorithmType.Md5)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_anon_WITH_RC4_128_MD5,
+ TlsMapping.DhStatic(CipherAlgorithmType.Rc4, 128, HashAlgorithmType.Md5)
+ },
+
+ {
+ TlsCipherSuite.SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA,
+ TlsMapping.DhStatic(CipherAlgorithmType.Des, 40, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.SSL_DH_anon_WITH_DES_CBC_SHA,
+ TlsMapping.DhStatic(CipherAlgorithmType.Des, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_anon_WITH_3DES_EDE_CBC_SHA,
+ TlsMapping.DhStatic(CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_PSK_WITH_NULL_SHA,
+ TlsMapping.NoExchange(CipherAlgorithmType.None, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_PSK_WITH_NULL_SHA,
+ TlsMapping.DhEphem(CipherAlgorithmType.None, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_PSK_WITH_NULL_SHA,
+ TlsMapping.Rsa(CipherAlgorithmType.None, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
+ TlsMapping.Rsa(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_anon_WITH_AES_128_CBC_SHA,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
+ TlsMapping.Rsa(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_anon_WITH_AES_256_CBC_SHA,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_WITH_NULL_SHA256,
+ TlsMapping.Rsa(CipherAlgorithmType.None, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
+ TlsMapping.Rsa(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256,
+ TlsMapping.Rsa(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_anon_WITH_AES_128_CBC_SHA256,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_anon_WITH_AES_256_CBC_SHA256,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_PSK_WITH_RC4_128_SHA,
+ TlsMapping.NoExchange(CipherAlgorithmType.Rc4, 128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA,
+ TlsMapping.NoExchange(CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA,
+ TlsMapping.NoExchange(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA,
+ TlsMapping.NoExchange(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA,
+ TlsMapping.DhEphem(CipherAlgorithmType.Rc4, 128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA,
+ TlsMapping.DhEphem(CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA,
+ TlsMapping.Rsa(CipherAlgorithmType.Rc4, 128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
+ TlsMapping.Rsa(CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
+ TlsMapping.Rsa(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
+ TlsMapping.Rsa(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
+ TlsMapping.Rsa(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
+ TlsMapping.Rsa(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_anon_WITH_AES_128_GCM_SHA256,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DH_anon_WITH_AES_256_GCM_SHA384,
+ TlsMapping.DhStatic(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256,
+ TlsMapping.NoExchange(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384,
+ TlsMapping.NoExchange(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,
+ TlsMapping.Rsa(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
+ TlsMapping.Rsa(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256,
+ TlsMapping.NoExchange(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384,
+ TlsMapping.NoExchange(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_PSK_WITH_NULL_SHA256,
+ TlsMapping.NoExchange(CipherAlgorithmType.None, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_PSK_WITH_NULL_SHA384,
+ TlsMapping.NoExchange(CipherAlgorithmType.None, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,
+ TlsMapping.DhEphem(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256,
+ TlsMapping.DhEphem(CipherAlgorithmType.None, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384,
+ TlsMapping.DhEphem(CipherAlgorithmType.None, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
+ TlsMapping.Rsa(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
+ TlsMapping.Rsa(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256,
+ TlsMapping.Rsa(CipherAlgorithmType.None, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384,
+ TlsMapping.Rsa(CipherAlgorithmType.None, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.None, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.Rc4, 128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA,
+ TlsMapping.EcDhEphem(CipherAlgorithmType.None, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
+ TlsMapping.EcDhEphem(CipherAlgorithmType.Rc4, 128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
+ TlsMapping.EcDhEphem(CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
+ TlsMapping.EcDhEphem(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
+ TlsMapping.EcDhEphem(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.None, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.Rc4, 128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+ TlsMapping.EcDhEphem(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
+ TlsMapping.EcDhEphem(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_anon_WITH_NULL_SHA,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.None, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_anon_WITH_RC4_128_SHA,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.Rc4, 128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_anon_WITH_AES_128_CBC_SHA,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_anon_WITH_AES_256_CBC_SHA,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha1)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+ TlsMapping.EcDhEphem(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
+ TlsMapping.EcDhEphem(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+ TlsMapping.EcDhEphem(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
+ TlsMapping.EcDhEphem(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ TlsMapping.EcDhEphem(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+ TlsMapping.EcDhEphem(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+ {
+ TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ TlsMapping.EcDhEphem(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ TlsMapping.EcDhEphem(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.Aes128, HashAlgorithmType.Sha256)
+ },
+
+ {
+ TlsCipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
+ TlsMapping.EcDhStatic(CipherAlgorithmType.Aes256, HashAlgorithmType.Sha384)
+ },
+ };
+ }
+}
diff --git a/src/System.Net.Security/src/System/Net/Security/SslState.cs b/src/System.Net.Security/src/System/Net/Security/SslState.cs
index cdc7f2f2ba..409b9f581c 100644
--- a/src/System.Net.Security/src/System/Net/Security/SslState.cs
+++ b/src/System.Net.Security/src/System/Net/Security/SslState.cs
@@ -29,8 +29,6 @@ namespace System.Net.Security
private SslStreamInternal _secureStream;
- private FixedSizeReader _reader;
-
private int _nestedAuth;
private SecureChannel _context;
@@ -81,7 +79,6 @@ namespace System.Net.Security
internal SslState(Stream innerStream, RemoteCertValidationCallback certValidationCallback, LocalCertSelectionCallback certSelectionCallback, EncryptionPolicy encryptionPolicy)
{
_innerStream = innerStream;
- _reader = new FixedSizeReader(innerStream);
_certValidationDelegate = certValidationCallback;
_certSelectionDelegate = certSelectionCallback;
_encryptionPolicy = encryptionPolicy;
@@ -868,12 +865,12 @@ namespace System.Net.Security
int readBytes = 0;
if (asyncRequest == null)
{
- readBytes = _reader.ReadPacket(buffer, 0, SecureChannel.ReadHeaderSize);
+ readBytes = FixedSizeReader.ReadPacket(_innerStream, buffer, 0, SecureChannel.ReadHeaderSize);
}
else
{
asyncRequest.SetNextRequest(buffer, 0, SecureChannel.ReadHeaderSize, s_partialFrameCallback);
- _reader.AsyncReadPacket(asyncRequest);
+ FixedSizeReader.ReadPacketAsync(_innerStream, asyncRequest);
if (!asyncRequest.MustCompleteSynchronously)
{
return;
@@ -916,12 +913,12 @@ namespace System.Net.Security
if (asyncRequest == null)
{
- restBytes = _reader.ReadPacket(buffer, readBytes, restBytes);
+ restBytes = FixedSizeReader.ReadPacket(_innerStream, buffer, readBytes, restBytes);
}
else
{
asyncRequest.SetNextRequest(buffer, readBytes, restBytes, s_readFrameCallback);
- _reader.AsyncReadPacket(asyncRequest);
+ FixedSizeReader.ReadPacketAsync(_innerStream, asyncRequest);
if (!asyncRequest.MustCompleteSynchronously)
{
return;
diff --git a/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs b/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs
index 8e201ddec6..b96a46d11e 100644
--- a/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs
+++ b/src/System.Net.Security/src/System/Net/Security/SslStreamInternal.cs
@@ -42,8 +42,6 @@ namespace System.Net.Security
private int _internalOffset;
private int _internalBufferCount;
- private FixedSizeReader _reader;
-
internal SslStreamInternal(SslState sslState)
{
if (PinnableBufferCacheEventSource.Log.IsEnabled())
@@ -52,7 +50,6 @@ namespace System.Net.Security
}
_sslState = sslState;
- _reader = new FixedSizeReader(_sslState.InnerStream);
}
// If we have a read buffer from the pinnable cache, return it.
@@ -613,7 +610,7 @@ namespace System.Net.Security
if (asyncRequest != null)
{
asyncRequest.SetNextRequest(InternalBuffer, 0, SecureChannel.ReadHeaderSize, s_readHeaderCallback);
- _reader.AsyncReadPacket(asyncRequest);
+ FixedSizeReader.ReadPacketAsync(_sslState.InnerStream, asyncRequest);
if (!asyncRequest.MustCompleteSynchronously)
{
@@ -624,7 +621,7 @@ namespace System.Net.Security
}
else
{
- readBytes = _reader.ReadPacket(InternalBuffer, 0, SecureChannel.ReadHeaderSize);
+ readBytes = FixedSizeReader.ReadPacket(_sslState.InnerStream, InternalBuffer, 0, SecureChannel.ReadHeaderSize);
}
return StartFrameBody(readBytes, buffer, offset, count, asyncRequest);
@@ -655,7 +652,7 @@ namespace System.Net.Security
{
asyncRequest.SetNextRequest(InternalBuffer, SecureChannel.ReadHeaderSize, readBytes, s_readFrameCallback);
- _reader.AsyncReadPacket(asyncRequest);
+ FixedSizeReader.ReadPacketAsync(_sslState.InnerStream, asyncRequest);
if (!asyncRequest.MustCompleteSynchronously)
{
@@ -666,7 +663,7 @@ namespace System.Net.Security
}
else
{
- readBytes = _reader.ReadPacket(InternalBuffer, SecureChannel.ReadHeaderSize, readBytes);
+ readBytes = FixedSizeReader.ReadPacket(_sslState.InnerStream, InternalBuffer, SecureChannel.ReadHeaderSize, readBytes);
}
return ProcessFrameBody(readBytes, buffer, offset, count, asyncRequest);
diff --git a/src/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs b/src/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs
new file mode 100644
index 0000000000..f32cc26b35
--- /dev/null
+++ b/src/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs
@@ -0,0 +1,332 @@
+// 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.ComponentModel;
+using System.Diagnostics;
+using System.Security.Authentication;
+using System.Security.Authentication.ExtendedProtection;
+using System.Security.Cryptography.X509Certificates;
+
+using PAL_TlsHandshakeState=Interop.AppleCrypto.PAL_TlsHandshakeState;
+using PAL_TlsIo=Interop.AppleCrypto.PAL_TlsIo;
+
+namespace System.Net.Security
+{
+ internal static class SslStreamPal
+ {
+ private static readonly StreamSizes s_streamSizes = new StreamSizes();
+
+ public static Exception GetException(SecurityStatusPal status)
+ {
+ return status.Exception ?? new Win32Exception((int)status.ErrorCode);
+ }
+
+ internal const bool StartMutualAuthAsAnonymous = false;
+
+ // SecureTransport is okay with a 0 byte input, but it produces a 0 byte output.
+ // Since ST is not producing the framed empty message just call this false and avoid the
+ // special case of an empty array being passed to the `fixed` statement.
+ internal const bool CanEncryptEmptyMessage = false;
+
+ public static void VerifyPackageInfo()
+ {
+ }
+
+ public static SecurityStatusPal AcceptSecurityContext(
+ ref SafeFreeCredentials credential,
+ ref SafeDeleteContext context,
+ SecurityBuffer inputBuffer,
+ SecurityBuffer outputBuffer,
+ bool remoteCertRequired)
+ {
+ return HandshakeInternal(credential, ref context, inputBuffer, outputBuffer, true, remoteCertRequired, null);
+ }
+
+ public static SecurityStatusPal InitializeSecurityContext(
+ ref SafeFreeCredentials credential,
+ ref SafeDeleteContext context,
+ string targetName,
+ SecurityBuffer inputBuffer,
+ SecurityBuffer outputBuffer)
+ {
+ return HandshakeInternal(credential, ref context, inputBuffer, outputBuffer, false, false, targetName);
+ }
+
+ public static SecurityStatusPal InitializeSecurityContext(
+ SafeFreeCredentials credential,
+ ref SafeDeleteContext context,
+ string targetName,
+ SecurityBuffer[] inputBuffers,
+ SecurityBuffer outputBuffer)
+ {
+ Debug.Assert(inputBuffers.Length == 2);
+ Debug.Assert(inputBuffers[1].token == null);
+ return HandshakeInternal(credential, ref context, inputBuffers[0], outputBuffer, false, false, targetName);
+ }
+
+ public static SafeFreeCredentials AcquireCredentialsHandle(
+ X509Certificate certificate,
+ SslProtocols protocols,
+ EncryptionPolicy policy,
+ bool isServer)
+ {
+ return new SafeFreeSslCredentials(certificate, protocols, policy);
+ }
+
+ public static SecurityStatusPal EncryptMessage(
+ SafeDeleteContext securityContext,
+ byte[] input,
+ int offset,
+ int size,
+ int headerSize,
+ int trailerSize,
+ ref byte[] output,
+ out int resultSize)
+ {
+ resultSize = 0;
+
+ Debug.Assert(size > 0, $"{nameof(size)} > 0 since {nameof(CanEncryptEmptyMessage)} is false");
+
+ try
+ {
+ SafeDeleteSslContext sslContext = (SafeDeleteSslContext)securityContext;
+ SafeSslHandle sslHandle = sslContext.SslContext;
+
+ unsafe
+ {
+ fixed (byte* offsetInput = &input[offset])
+ {
+ int written;
+ PAL_TlsIo status = Interop.AppleCrypto.SslWrite(sslHandle, offsetInput, size, out written);
+
+ if (status < 0)
+ {
+ return new SecurityStatusPal(
+ SecurityStatusPalErrorCode.InternalError,
+ Interop.AppleCrypto.CreateExceptionForOSStatus((int)status));
+ }
+
+ if (sslContext.BytesReadyForConnection <= output?.Length)
+ {
+ resultSize = sslContext.ReadPendingWrites(output, 0, output.Length);
+ }
+ else
+ {
+ output = sslContext.ReadPendingWrites();
+ resultSize = output.Length;
+ }
+
+ switch (status)
+ {
+ case PAL_TlsIo.Success:
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.OK);
+ case PAL_TlsIo.WouldBlock:
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.ContinueNeeded);
+ default:
+ Debug.Fail($"Unknown status value: {status}");
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError);
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, e);
+ }
+ }
+
+ public static SecurityStatusPal DecryptMessage(
+ SafeDeleteContext securityContext,
+ byte[] buffer,
+ ref int offset,
+ ref int count)
+ {
+ try
+ {
+ SafeDeleteSslContext sslContext = (SafeDeleteSslContext)securityContext;
+ SafeSslHandle sslHandle = sslContext.SslContext;
+
+ sslContext.Write(buffer, offset, count);
+
+ unsafe
+ {
+ fixed (byte* offsetInput = &buffer[offset])
+ {
+ int written;
+ PAL_TlsIo status = Interop.AppleCrypto.SslRead(sslHandle, offsetInput, count, out written);
+
+ if (status < 0)
+ {
+ return new SecurityStatusPal(
+ SecurityStatusPalErrorCode.InternalError,
+ Interop.AppleCrypto.CreateExceptionForOSStatus((int)status));
+ }
+
+ count = written;
+
+ switch (status)
+ {
+ case PAL_TlsIo.Success:
+ case PAL_TlsIo.WouldBlock:
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.OK);
+ case PAL_TlsIo.ClosedGracefully:
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.ContextExpired);
+ case PAL_TlsIo.Renegotiate:
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.Renegotiate);
+ default:
+ Debug.Fail($"Unknown status value: {status}");
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError);
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, e);
+ }
+ }
+
+ public static ChannelBinding QueryContextChannelBinding(
+ SafeDeleteContext securityContext,
+ ChannelBindingKind attribute)
+ {
+ switch (attribute)
+ {
+ case ChannelBindingKind.Endpoint:
+ return EndpointChannelBindingToken.Build(securityContext);
+ }
+
+ // SecureTransport doesn't expose the Finished messages, so a Unique binding token
+ // cannot be built.
+ //
+ // Windows/netfx compat says to return null for not supported kinds (including unmapped enum values).
+ return null;
+ }
+
+ public static void QueryContextStreamSizes(
+ SafeDeleteContext securityContext,
+ out StreamSizes streamSizes)
+ {
+ streamSizes = s_streamSizes;
+ }
+
+ public static void QueryContextConnectionInfo(
+ SafeDeleteContext securityContext,
+ out SslConnectionInfo connectionInfo)
+ {
+ connectionInfo = new SslConnectionInfo(((SafeDeleteSslContext)securityContext).SslContext);
+ }
+
+ private static SecurityStatusPal HandshakeInternal(
+ SafeFreeCredentials credential,
+ ref SafeDeleteContext context,
+ SecurityBuffer inputBuffer,
+ SecurityBuffer outputBuffer,
+ bool isServer,
+ bool remoteCertRequired,
+ string targetName)
+ {
+ Debug.Assert(!credential.IsInvalid);
+
+ try
+ {
+ SafeDeleteSslContext sslContext = ((SafeDeleteSslContext)context);
+
+ if ((null == context) || context.IsInvalid)
+ {
+ sslContext = new SafeDeleteSslContext(credential as SafeFreeSslCredentials, isServer);
+ context = sslContext;
+
+ if (!string.IsNullOrEmpty(targetName))
+ {
+ Debug.Assert(!isServer, "targetName should not be set for server-side handshakes");
+ Interop.AppleCrypto.SslSetTargetName(sslContext.SslContext, targetName);
+ }
+
+ if (remoteCertRequired)
+ {
+ Debug.Assert(isServer, "remoteCertRequired should not be set for client-side handshakes");
+ Interop.AppleCrypto.SslSetAcceptClientCert(sslContext.SslContext);
+ }
+ }
+
+ if (inputBuffer != null && inputBuffer.size > 0)
+ {
+ sslContext.Write(inputBuffer.token, inputBuffer.offset, inputBuffer.size);
+ }
+
+ SecurityStatusPal status = PerformHandshake(sslContext.SslContext);
+
+ byte[] output = sslContext.ReadPendingWrites();
+ outputBuffer.offset = 0;
+ outputBuffer.size = output?.Length ?? 0;
+ outputBuffer.token = output;
+
+ return status;
+ }
+ catch (Exception exc)
+ {
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, exc);
+ }
+ }
+
+ private static SecurityStatusPal PerformHandshake(SafeSslHandle sslHandle)
+ {
+ while (true)
+ {
+ PAL_TlsHandshakeState handshakeState = Interop.AppleCrypto.SslHandshake(sslHandle);
+
+ switch (handshakeState)
+ {
+ case PAL_TlsHandshakeState.Complete:
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.OK);
+ case PAL_TlsHandshakeState.WouldBlock:
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.ContinueNeeded);
+ case PAL_TlsHandshakeState.ServerAuthCompleted:
+ case PAL_TlsHandshakeState.ClientAuthCompleted:
+ // The standard flow would be to call the verification callback now, and
+ // possibly abort. But the library is set up to call this "success" and
+ // do verification between "handshake complete" and "first send/receive".
+ //
+ // So, call SslHandshake again to indicate to Secure Transport that we've
+ // accepted this handshake and it should go into the ready state.
+ break;
+ default:
+ return new SecurityStatusPal(
+ SecurityStatusPalErrorCode.InternalError,
+ Interop.AppleCrypto.CreateExceptionForOSStatus((int)handshakeState));
+ }
+ }
+ }
+
+ public static SecurityStatusPal ApplyAlertToken(
+ ref SafeFreeCredentials credentialsHandle,
+ SafeDeleteContext securityContext,
+ TlsAlertType alertType,
+ TlsAlertMessage alertMessage)
+ {
+ // There doesn't seem to be an exposed API for writing an alert,
+ // the API seems to assume that all alerts are generated internally by
+ // SSLHandshake.
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.OK);
+ }
+
+ public static SecurityStatusPal ApplyShutdownToken(
+ ref SafeFreeCredentials credentialsHandle,
+ SafeDeleteContext securityContext)
+ {
+ SafeDeleteSslContext sslContext = ((SafeDeleteSslContext)securityContext);
+ int osStatus = Interop.AppleCrypto.SslShutdown(sslContext.SslContext);
+
+ if (osStatus == 0)
+ {
+ return new SecurityStatusPal(SecurityStatusPalErrorCode.OK);
+ }
+
+ return new SecurityStatusPal(
+ SecurityStatusPalErrorCode.InternalError,
+ Interop.AppleCrypto.CreateExceptionForOSStatus(osStatus));
+ }
+ }
+}
diff --git a/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs b/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs
index 70917ac414..b1d1b617e5 100644
--- a/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs
+++ b/src/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs
@@ -70,11 +70,27 @@ namespace System.Net.Security
return retVal;
}
- public static SafeFreeContextBufferChannelBinding QueryContextChannelBinding(SafeDeleteContext securityContext, ChannelBindingKind attribute)
+ public static ChannelBinding QueryContextChannelBinding(SafeDeleteContext securityContext, ChannelBindingKind attribute)
{
- SafeChannelBindingHandle bindingHandle = Interop.OpenSsl.QueryChannelBinding(((SafeDeleteSslContext)securityContext).SslContext, attribute);
- var refHandle = bindingHandle == null ? null : new SafeFreeContextBufferChannelBinding(bindingHandle);
- return refHandle;
+ ChannelBinding bindingHandle;
+
+ if (attribute == ChannelBindingKind.Endpoint)
+ {
+ bindingHandle = EndpointChannelBindingToken.Build(securityContext);
+
+ if (bindingHandle == null)
+ {
+ throw Interop.OpenSsl.CreateSslException(SR.net_ssl_invalid_certificate);
+ }
+ }
+ else
+ {
+ bindingHandle = Interop.OpenSsl.QueryChannelBinding(
+ ((SafeDeleteSslContext)securityContext).SslContext,
+ attribute);
+ }
+
+ return bindingHandle;
}
public static void QueryContextStreamSizes(SafeDeleteContext securityContext, out StreamSizes streamSizes)
diff --git a/src/System.Net.Security/src/System/Net/Security/StreamSizes.OSX.cs b/src/System.Net.Security/src/System/Net/Security/StreamSizes.OSX.cs
new file mode 100644
index 0000000000..ed34467fe9
--- /dev/null
+++ b/src/System.Net.Security/src/System/Net/Security/StreamSizes.OSX.cs
@@ -0,0 +1,29 @@
+// 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.
+
+namespace System.Net
+{
+ internal partial class StreamSizes
+ {
+ // Windows SChannel requires that you pass it a buffer big enough to hold
+ // the header, the trailer, and the payload. You're also required to do your
+ // own chunking, not to exceed the maximum message size (which includes header+trailer).
+ //
+ // Secure Transport seems to be more like the OpenSSL model, where it does its own chunking,
+ // so MaximumMessage just controls the maximum amount of data that will be sent to that
+ // library in a single call.
+ //
+ // 16k is the maximum frame size in Ssl3, Tls1, Tls11, and Tls12.
+ // We could really set maximumMessage to int.MaxValue and have everything still work,
+ // but using a bound of 32k means that if we were to switch from pointers to temporary
+ // arrays, we'd be maintaining a reasonable upper bound.
+
+ public StreamSizes()
+ {
+ Header = 0;
+ Trailer = 0;
+ MaximumMessage = 32 * 1024;
+ }
+ }
+}
diff --git a/src/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs b/src/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs
index d898512b59..e89925d910 100644
--- a/src/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs
+++ b/src/System.Net.Security/tests/FunctionalTests/ClientAsyncAuthenticateTest.cs
@@ -89,6 +89,7 @@ namespace System.Net.Security.Tests
[Theory]
[ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))]
+ [ActiveIssue(16534, TestPlatforms.Windows)]
public async Task ClientAsyncAuthenticate_AllServerVsIndividualClientSupportedProtocols_Success(
SslProtocols clientProtocol)
{
diff --git a/src/System.Net.Security/tests/FunctionalTests/LoggingTest.cs b/src/System.Net.Security/tests/FunctionalTests/LoggingTest.cs
index adec601aa0..c0ef7dc1ab 100644
--- a/src/System.Net.Security/tests/FunctionalTests/LoggingTest.cs
+++ b/src/System.Net.Security/tests/FunctionalTests/LoggingTest.cs
@@ -24,6 +24,7 @@ namespace System.Net.Security.Tests
}
[Fact]
+ [ActiveIssue(16516, TestPlatforms.Windows)]
public void EventSource_EventsRaisedAsExpected()
{
RemoteInvoke(() =>
diff --git a/src/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs b/src/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs
index 1c4456d2e9..70f17ce309 100644
--- a/src/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs
+++ b/src/System.Net.Security/tests/FunctionalTests/ServerAsyncAuthenticateTest.cs
@@ -45,6 +45,7 @@ namespace System.Net.Security.Tests
[Theory]
[ClassData(typeof(SslProtocolSupport.UnsupportedSslProtocolsTestData))]
+ [ActiveIssue(16516, TestPlatforms.Windows)]
public async Task ServerAsyncAuthenticate_EachServerUnsupportedProtocol_Fail(SslProtocols protocol)
{
await Assert.ThrowsAsync<NotSupportedException>(() =>
@@ -76,6 +77,7 @@ namespace System.Net.Security.Tests
}
[Fact]
+ [ActiveIssue(16516, TestPlatforms.Windows)]
public async Task ServerAsyncAuthenticate_UnsupportedAllServer_Fail()
{
await Assert.ThrowsAsync<NotSupportedException>(() =>
diff --git a/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs b/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs
index 0f25755015..0df725b8d1 100644
--- a/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs
+++ b/src/System.Net.Security/tests/FunctionalTests/SslStreamStreamToStreamTest.cs
@@ -23,6 +23,7 @@ namespace System.Net.Security.Tests
protected abstract bool DoHandshake(SslStream clientSslStream, SslStream serverSslStream);
[Fact]
+ [ActiveIssue(16516, TestPlatforms.Windows)]
public void SslStream_StreamToStream_Authentication_Success()
{
VirtualNetwork network = new VirtualNetwork();
diff --git a/src/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs b/src/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs
index 5c149cd216..a986c11798 100644
--- a/src/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs
+++ b/src/System.Net.Security/tests/FunctionalTests/TestConfiguration.cs
@@ -37,6 +37,12 @@ namespace System.Net.Security.Tests
return true;
}
+ // On macOS, the null cipher (no encryption) is not supported.
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ return false;
+ }
+
// On Unix, it depends on how openssl was built. So we ask openssl if it has any.
try
{
diff --git a/src/System.Net.Security/tests/FunctionalTests/TransportContextTest.cs b/src/System.Net.Security/tests/FunctionalTests/TransportContextTest.cs
index f57bb33f20..865b4bac6a 100644
--- a/src/System.Net.Security/tests/FunctionalTests/TransportContextTest.cs
+++ b/src/System.Net.Security/tests/FunctionalTests/TransportContextTest.cs
@@ -54,7 +54,16 @@ namespace System.Net.Security.Tests
CheckChannelBinding(cbt3);
Assert.True(cbt1 != null, "ChannelBindingKind.Endpoint token data should be returned from SCHANNEL.");
- Assert.True(cbt2 != null, "ChannelBindingKind.Unique token data should be returned from SCHANNEL.");
+
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ Assert.True(cbt2 == null, "ChannelBindingKind.Unique token data is not expected from SecureTransport");
+ }
+ else
+ {
+ Assert.True(cbt2 != null, "ChannelBindingKind.Unique token data should be returned from SCHANNEL.");
+ }
+
Assert.True(cbt3 == null, "ChannelBindingKind.Unknown token data should not be returned from SCHANNEL since it does not map to a defined context attribute.");
}
diff --git a/src/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj b/src/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj
index a55ecdeb12..ce209e0886 100644
--- a/src/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj
+++ b/src/System.Net.Security/tests/UnitTests/System.Net.Security.Unit.Tests.csproj
@@ -50,9 +50,6 @@
<Link>Common\Interop\Windows\SChannel\Interop.Alerts.cs</Link>
</Compile>
<!-- Logging -->
- <Compile Include="$(CommonPath)\System\Net\Shims\TraceSource.cs">
- <Link>ProductionCode\Common\System\Net\Shims\TraceSource.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\System\Net\Logging\NetEventSource.Common.cs">
<Link>ProductionCode\Common\System\Net\Logging\NetEventSource.Common.cs</Link>
</Compile>
diff --git a/src/System.Net.WebHeaderCollection/System.Net.WebHeaderCollection.sln b/src/System.Net.WebHeaderCollection/System.Net.WebHeaderCollection.sln
index 46247594ba..38be489396 100644
--- a/src/System.Net.WebHeaderCollection/System.Net.WebHeaderCollection.sln
+++ b/src/System.Net.WebHeaderCollection/System.Net.WebHeaderCollection.sln
@@ -26,10 +26,10 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {F8C21EE8-B271-4014-B9D9-B2C31520AF3F}.Debug|Any CPU.ActiveCfg = netstandard-Windows_NT-Debug|Any CPU
- {F8C21EE8-B271-4014-B9D9-B2C31520AF3F}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU
- {F8C21EE8-B271-4014-B9D9-B2C31520AF3F}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU
- {F8C21EE8-B271-4014-B9D9-B2C31520AF3F}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU
+ {F8C21EE8-B271-4014-B9D9-B2C31520AF3F}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU
+ {F8C21EE8-B271-4014-B9D9-B2C31520AF3F}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU
+ {F8C21EE8-B271-4014-B9D9-B2C31520AF3F}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU
+ {F8C21EE8-B271-4014-B9D9-B2C31520AF3F}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU
{C7DB0DF2-9CF2-42FB-89A7-450550B52C48}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU
{C7DB0DF2-9CF2-42FB-89A7-450550B52C48}.Debug|Any CPU.Build.0 = netcoreapp-Debug|Any CPU
{C7DB0DF2-9CF2-42FB-89A7-450550B52C48}.Release|Any CPU.ActiveCfg = netcoreapp-Release|Any CPU
diff --git a/src/System.Net.WebHeaderCollection/tests/Configurations.props b/src/System.Net.WebHeaderCollection/tests/Configurations.props
index 249c8c18b4..c398e42e89 100644
--- a/src/System.Net.WebHeaderCollection/tests/Configurations.props
+++ b/src/System.Net.WebHeaderCollection/tests/Configurations.props
@@ -2,7 +2,7 @@
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildConfigurations>
- netstandard-Windows_NT;
+ netstandard;
</BuildConfigurations>
</PropertyGroup>
</Project> \ No newline at end of file
diff --git a/src/System.Net.WebHeaderCollection/tests/System.Net.WebHeaderCollection.Tests.csproj b/src/System.Net.WebHeaderCollection/tests/System.Net.WebHeaderCollection.Tests.csproj
index 932eb7c144..b75e35a285 100644
--- a/src/System.Net.WebHeaderCollection/tests/System.Net.WebHeaderCollection.Tests.csproj
+++ b/src/System.Net.WebHeaderCollection/tests/System.Net.WebHeaderCollection.Tests.csproj
@@ -5,8 +5,8 @@
<ProjectGuid>{F8C21EE8-B271-4014-B9D9-B2C31520AF3F}</ProjectGuid>
</PropertyGroup>
<!-- Help VS understand available configurations -->
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Windows_NT-Debug|AnyCPU'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Windows_NT-Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="WebHeaderCollectionTest.cs" />
<Compile Include="LoggingTest.cs" />
diff --git a/src/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj b/src/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj
index 198a2a2da5..3700d62506 100644
--- a/src/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj
+++ b/src/System.Net.WebSockets.Client/src/System.Net.WebSockets.Client.csproj
@@ -89,7 +89,6 @@
<Compile Include="System\Net\WebSockets\WinRTWebSocket.cs" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetsUnix)' == 'true' ">
- <Compile Include="System\Net\Sockets\AsyncEventArgsNetworkStream.cs" />
<Compile Include="System\Net\WebSockets\WebSocketHandle.Managed.cs" />
<Compile Include="$(CommonPath)\System\Net\WebSockets\ManagedWebSocket.cs">
<Link>Common\System\Net\WebSockets\ManagedWebSocket.cs</Link>
@@ -133,14 +132,16 @@
<Reference Include="System.Threading.Tasks" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsUnix)' == 'true'">
+ <Reference Include="System.Buffers" />
<Reference Include="System.Net.NameResolution" />
<Reference Include="System.Net.Security" />
<Reference Include="System.Net.Sockets" />
+ <Reference Include="System.Numerics.Vectors" />
+ <Reference Include="System.Runtime.CompilerServices.Unsafe" />
<Reference Include="System.Security.Cryptography.Algorithms" />
<Reference Include="System.Security.Cryptography.Primitives" />
<Reference Include="System.Text.Encoding.Extensions" />
<Reference Include="System.Threading.Timer" />
- <Reference Include="System.Buffers" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
diff --git a/src/System.Net.WebSockets.Client/src/System/Net/Sockets/AsyncEventArgsNetworkStream.cs b/src/System.Net.WebSockets.Client/src/System/Net/Sockets/AsyncEventArgsNetworkStream.cs
deleted file mode 100644
index f127fbca8b..0000000000
--- a/src/System.Net.WebSockets.Client/src/System/Net/Sockets/AsyncEventArgsNetworkStream.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-// 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.IO;
-using System.Runtime.CompilerServices;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace System.Net.Sockets
-{
- /// <summary>
- /// A custom network stream that stores and reuses a single SocketAsyncEventArgs instance
- /// for reads and a single SocketAsyncEventArgs instance for writes. This limits it to
- /// supporting a single read and a single write at a time, but with much less per-operation
- /// overhead than with System.Net.Sockets.NetworkStream.
- /// </summary>
- internal sealed class AsyncEventArgsNetworkStream : NetworkStream
- {
- private readonly Socket _socket;
- private readonly SocketAsyncEventArgs _readArgs;
- private readonly SocketAsyncEventArgs _writeArgs;
-
- private AsyncTaskMethodBuilder<int> _readAtmb;
- private AsyncTaskMethodBuilder _writeAtmb;
- private bool _disposed;
-
- public AsyncEventArgsNetworkStream(Socket socket) : base(socket, ownsSocket: true)
- {
- _socket = socket;
-
- _readArgs = new SocketAsyncEventArgs();
- _readArgs.Completed += ReadCompleted;
-
- _writeArgs = new SocketAsyncEventArgs();
- _writeArgs.Completed += WriteCompleted;
- }
-
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
-
- if (disposing && !_disposed)
- {
- _disposed = true;
- try
- {
- _readArgs.Dispose();
- _writeArgs.Dispose();
- }
- catch (ObjectDisposedException) { }
- }
- }
-
- public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return Task.FromCanceled<int>(cancellationToken);
- }
-
- _readAtmb = new AsyncTaskMethodBuilder<int>();
- Task<int> t = _readAtmb.Task;
-
- _readArgs.SetBuffer(buffer, offset, count);
- if (!_socket.ReceiveAsync(_readArgs))
- {
- ReadCompleted(null, _readArgs);
- }
-
- return t;
- }
-
- private void ReadCompleted(object sender, SocketAsyncEventArgs e)
- {
- if (e.SocketError == SocketError.Success)
- {
- _readAtmb.SetResult(e.BytesTransferred);
- }
- else
- {
- _readAtmb.SetException(CreateException(e.SocketError));
- }
- }
-
- public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
- {
- if (cancellationToken.IsCancellationRequested)
- {
- return Task.FromCanceled(cancellationToken);
- }
-
- _writeAtmb = new AsyncTaskMethodBuilder();
- Task t = _writeAtmb.Task;
-
- _writeArgs.SetBuffer(buffer, offset, count);
- if (!_socket.SendAsync(_writeArgs))
- {
- // TODO: #4900 This path should be hit very frequently (sends should very frequently simply
- // write into the kernel's send buffer), but it's practically never getting hit due to the current
- // System.Net.Sockets.dll implementation that always completing asynchronously on success :(
- // If that doesn't get fixed, we should try to come up with some alternative here. This is
- // an important path, in part as it means the caller will complete awaits synchronously rather
- // than spending the costs associated with yielding in each async method up the call chain.
- // (This applies to ReadAsync as well, but typically to a much less extent.)
- WriteCompleted(null, _writeArgs);
- }
-
- return t;
- }
-
- private void WriteCompleted(object sender, SocketAsyncEventArgs e)
- {
- if (e.SocketError == SocketError.Success)
- {
- _writeAtmb.SetResult();
- }
- else
- {
- _writeAtmb.SetException(CreateException(e.SocketError));
- }
- }
-
- private Exception CreateException(SocketError error)
- {
- if (_disposed)
- {
- return new ObjectDisposedException(GetType().Name);
- }
- else if (error == SocketError.OperationAborted)
- {
- return new OperationCanceledException();
- }
- else
- {
- return new IOException(SR.net_WebSockets_Generic, new SocketException((int)error));
- }
- }
- }
-}
diff --git a/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs b/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs
index 1c6d95bb20..abf01d1a71 100644
--- a/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs
+++ b/src/System.Net.WebSockets.Client/src/System/Net/WebSockets/WebSocketHandle.Managed.cs
@@ -85,7 +85,7 @@ namespace System.Net.WebSockets
{
// Connect to the remote server
Socket connectedSocket = await ConnectSocketAsync(uri.Host, uri.Port, cancellationToken).ConfigureAwait(false);
- Stream stream = new AsyncEventArgsNetworkStream(connectedSocket);
+ Stream stream = new NetworkStream(connectedSocket, ownsSocket:true);
// Upgrade to SSL if needed
if (uri.Scheme == UriScheme.Wss)
diff --git a/src/System.Net.WebSockets.Client/tests/SendReceiveTest.cs b/src/System.Net.WebSockets.Client/tests/SendReceiveTest.cs
index 0cd4001945..d2fbcf3812 100644
--- a/src/System.Net.WebSockets.Client/tests/SendReceiveTest.cs
+++ b/src/System.Net.WebSockets.Client/tests/SendReceiveTest.cs
@@ -245,8 +245,9 @@ namespace System.Net.WebSockets.Client.Tests
var rand = new Random();
var ctsDefault = new CancellationTokenSource(TimeOutMilliseconds);
- // Values chosen close to boundaries in websockets message length handling.
- foreach (int bufferSize in new int[] { 1, 125, 126, 127, 128, ushort.MaxValue - 1, ushort.MaxValue, ushort.MaxValue + 1, ushort.MaxValue * 2 })
+ // Values chosen close to boundaries in websockets message length handling as well
+ // as in vectors used in mask application.
+ foreach (int bufferSize in new int[] { 1, 3, 4, 5, 31, 32, 33, 125, 126, 127, 128, ushort.MaxValue - 1, ushort.MaxValue, ushort.MaxValue + 1, ushort.MaxValue * 2 })
{
byte[] sendBuffer = new byte[bufferSize];
rand.NextBytes(sendBuffer);
diff --git a/src/System.Private.Uri/System.Private.Uri.sln b/src/System.Private.Uri/System.Private.Uri.sln
index bcdf69345a..a5f0d19a66 100644
--- a/src/System.Private.Uri/System.Private.Uri.sln
+++ b/src/System.Private.Uri/System.Private.Uri.sln
@@ -29,18 +29,18 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {0febe054-68ac-446f-b999-9068736d3cec}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {0febe054-68ac-446f-b999-9068736d3cec}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {0febe054-68ac-446f-b999-9068736d3cec}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {0febe054-68ac-446f-b999-9068736d3cec}.Release|Any CPU.Build.0 = Release|Any CPU
- {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}.Release|Any CPU.Build.0 = Release|Any CPU
- {96AF3242-A368-4F13-B006-A722CC3B8517}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {96AF3242-A368-4F13-B006-A722CC3B8517}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {96AF3242-A368-4F13-B006-A722CC3B8517}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {96AF3242-A368-4F13-B006-A722CC3B8517}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0febe054-68ac-446f-b999-9068736d3cec}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU
+ {0febe054-68ac-446f-b999-9068736d3cec}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU
+ {0febe054-68ac-446f-b999-9068736d3cec}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU
+ {0febe054-68ac-446f-b999-9068736d3cec}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU
+ {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU
+ {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU
+ {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU
+ {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU
+ {96AF3242-A368-4F13-B006-A722CC3B8517}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU
+ {96AF3242-A368-4F13-B006-A722CC3B8517}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU
+ {96AF3242-A368-4F13-B006-A722CC3B8517}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU
+ {96AF3242-A368-4F13-B006-A722CC3B8517}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU
{4AC5343E-6E31-4BA5-A795-0493AE7E9008}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU
{4AC5343E-6E31-4BA5-A795-0493AE7E9008}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU
{4AC5343E-6E31-4BA5-A795-0493AE7E9008}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU
diff --git a/src/System.Private.Uri/tests/ExtendedFunctionalTests/Configurations.props b/src/System.Private.Uri/tests/ExtendedFunctionalTests/Configurations.props
new file mode 100644
index 0000000000..78953dfc88
--- /dev/null
+++ b/src/System.Private.Uri/tests/ExtendedFunctionalTests/Configurations.props
@@ -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>
+ netstandard;
+ </BuildConfigurations>
+ </PropertyGroup>
+</Project>
diff --git a/src/System.Private.Uri/tests/ExtendedFunctionalTests/System.Private.Uri.ExtendedFunctional.Tests.csproj b/src/System.Private.Uri/tests/ExtendedFunctionalTests/System.Private.Uri.ExtendedFunctional.Tests.csproj
index 26f7ebeb63..6461573454 100644
--- a/src/System.Private.Uri/tests/ExtendedFunctionalTests/System.Private.Uri.ExtendedFunctional.Tests.csproj
+++ b/src/System.Private.Uri/tests/ExtendedFunctionalTests/System.Private.Uri.ExtendedFunctional.Tests.csproj
@@ -4,8 +4,8 @@
<PropertyGroup>
<ProjectGuid>{0febe054-68ac-446f-b999-9068736d3cec}</ProjectGuid>
</PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="UriRelativeResolutionTest.cs" />
<Compile Include="UriTests.cs" />
diff --git a/src/System.Private.Uri/tests/FunctionalTests/Configurations.props b/src/System.Private.Uri/tests/FunctionalTests/Configurations.props
new file mode 100644
index 0000000000..78953dfc88
--- /dev/null
+++ b/src/System.Private.Uri/tests/FunctionalTests/Configurations.props
@@ -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>
+ netstandard;
+ </BuildConfigurations>
+ </PropertyGroup>
+</Project>
diff --git a/src/System.Private.Uri/tests/FunctionalTests/System.Private.Uri.Functional.Tests.csproj b/src/System.Private.Uri/tests/FunctionalTests/System.Private.Uri.Functional.Tests.csproj
index 3a72fc6386..dafeadbd56 100644
--- a/src/System.Private.Uri/tests/FunctionalTests/System.Private.Uri.Functional.Tests.csproj
+++ b/src/System.Private.Uri/tests/FunctionalTests/System.Private.Uri.Functional.Tests.csproj
@@ -4,8 +4,8 @@
<PropertyGroup>
<ProjectGuid>{B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}</ProjectGuid>
</PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="AppxUriValue.cs" />
<Compile Include="IdnCheckHostNameTest.cs" />
diff --git a/src/System.Private.Uri/tests/UnitTests/Configurations.props b/src/System.Private.Uri/tests/UnitTests/Configurations.props
new file mode 100644
index 0000000000..78953dfc88
--- /dev/null
+++ b/src/System.Private.Uri/tests/UnitTests/Configurations.props
@@ -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>
+ netstandard;
+ </BuildConfigurations>
+ </PropertyGroup>
+</Project>
diff --git a/src/System.Private.Uri/tests/UnitTests/System.Private.Uri.Unit.Tests.csproj b/src/System.Private.Uri/tests/UnitTests/System.Private.Uri.Unit.Tests.csproj
index 3bb0f68d10..9dd37c2616 100644
--- a/src/System.Private.Uri/tests/UnitTests/System.Private.Uri.Unit.Tests.csproj
+++ b/src/System.Private.Uri/tests/UnitTests/System.Private.Uri.Unit.Tests.csproj
@@ -8,8 +8,8 @@
<StringResourcesPath>../../src/Resources/Strings.resx</StringResourcesPath>
</PropertyGroup>
<!-- Help VS understand available configurations -->
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="Fakes\FakeUri.cs" />
<Compile Include="Fakes\FakeUriParser.cs" />
diff --git a/src/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs b/src/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs
index 0c09af3d86..5fcab26a8f 100644
--- a/src/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs
+++ b/src/System.Private.Xml.Linq/tests/misc/LoadSaveAsyncTests.cs
@@ -49,7 +49,7 @@ namespace CoreXml.Test.XLinq
}
[Theory]
- [MemberData("RoundtripOptions_MemberData")]
+ [MemberData(nameof(RoundtripOptions_MemberData))]
public static async Task RoundtripSyncAsyncMatches_XmlReader(bool document, LoadOptions loadOptions, SaveOptions saveOptions)
{
// Create reader and writer settings
@@ -106,7 +106,7 @@ namespace CoreXml.Test.XLinq
}
[Theory]
- [MemberData("RoundtripOptions_MemberData")]
+ [MemberData(nameof(RoundtripOptions_MemberData))]
public static async Task RoundtripSyncAsyncMatches_StreamReader(bool document, LoadOptions loadOptions, SaveOptions saveOptions)
{
// Roundtrip XML using synchronous and StreamReader/Writer
@@ -148,7 +148,7 @@ namespace CoreXml.Test.XLinq
}
[Theory]
- [MemberData("RoundtripOptions_MemberData")]
+ [MemberData(nameof(RoundtripOptions_MemberData))]
public static async Task RoundtripSyncAsyncMatches_Stream(bool document, LoadOptions loadOptions, SaveOptions saveOptions)
{
// Roundtrip XML using synchronous and Stream
diff --git a/src/System.Private.Xml/tests/XmlConvert/ToTypeTests.cs b/src/System.Private.Xml/tests/XmlConvert/ToTypeTests.cs
index a289f79c5d..22f173f903 100644
--- a/src/System.Private.Xml/tests/XmlConvert/ToTypeTests.cs
+++ b/src/System.Private.Xml/tests/XmlConvert/ToTypeTests.cs
@@ -836,7 +836,7 @@ namespace System.Xml.Tests
var param = (string)CurVariation.Param;
object[] array0 = { "2002-12-30", "23:15:55", "2002-01-09T04:02:08", "2002-01-09T04:02:08Z", "2002-01-09Z", "2002-01-09T04:02:08-05:00", "0002-01", "2016-02-29", "9999", "9999Z", "9999-12-31T12:59:59+14:00", "9999-12-31T12:59:59-11:00", "9999-12-31T12:59:59-10:59", "9999-12-31T12:59:59+13:59", "9999-12-31T23:59:59-00:00", "9999-12-31T23:59:59+14:00", "9998-12-31T12:59:59+14:00", "9998-12-31T12:59:59-14:00", "0002", "0001Z", "0002-01-01T00:00:00-14:00", "0002-01-01T00:00:00-13:59", "0002-01-01T00:00:00+00:00", "0002-01-01T00:00:00-00:00", "2008-02-29T23:59:59-14:00", "2012-02-29T23:59:59+14:00" };
- object[] array1 = { new DateTimeOffset(2002, 12, 30, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2002, 12, 30))), new DateTimeOffset(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 23, 15, 55, TimeZoneInfo.Local.GetUtcOffset(DateTime.Now)), (new DateTimeOffset(2002, 1, 9, 4, 2, 8, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2002, 1, 9)))).AddMilliseconds(0.1458925435), (new DateTimeOffset(2002, 1, 9, 4, 2, 8, TimeSpan.FromHours(0))).ToLocalTime(), (new DateTimeOffset(2002, 1, 9, 0, 0, 0, TimeSpan.FromHours(0))).ToLocalTime(), (new DateTimeOffset(2002, 1, 9, 4, 2, 8, new TimeSpan(-5, 0, 0))), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2, 1, 1))), new DateTimeOffset(2016, 2, 29, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2016, 2, 29))), new DateTimeOffset(9999, 1, 1, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(9999, 1, 1))), new DateTimeOffset(9999, 1, 1, 0, 0, 0, TimeSpan.FromHours(0)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, TimeSpan.FromHours(14.0)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, TimeSpan.FromHours(-11.0)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, TimeSpan.FromHours(-10) + TimeSpan.FromMinutes(-59)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, new TimeSpan(13, 59, 0)), new DateTimeOffset(9999, 12, 31, 23, 59, 59, TimeSpan.Zero), new DateTimeOffset(9999, 12, 31, 23, 59, 59, TimeSpan.FromHours(14.0)), new DateTimeOffset(9998, 12, 31, 12, 59, 59, TimeSpan.FromHours(14.0)), new DateTimeOffset(9998, 12, 31, 12, 59, 59, TimeSpan.FromHours(-14.0)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2, 1, 1))), new DateTimeOffset(1, 1, 1, 0, 0, 0, TimeSpan.FromHours(0)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.FromHours(-14.0)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.FromHours(-13) + TimeSpan.FromMinutes(-59)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.Zero), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.Zero), new DateTimeOffset(2008, 2, 29, 23, 59, 59, TimeSpan.FromHours(-14)), new DateTimeOffset(2012, 2, 29, 23, 59, 59, TimeSpan.FromHours(14)) };
+ object[] array1 = { new DateTimeOffset(2002, 12, 30, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2002, 12, 30))), new DateTimeOffset(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 23, 15, 55, TimeZoneInfo.Local.GetUtcOffset(new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 23, 15, 55))), (new DateTimeOffset(2002, 1, 9, 4, 2, 8, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2002, 1, 9)))).AddMilliseconds(0.1458925435), (new DateTimeOffset(2002, 1, 9, 4, 2, 8, TimeSpan.FromHours(0))).ToLocalTime(), (new DateTimeOffset(2002, 1, 9, 0, 0, 0, TimeSpan.FromHours(0))).ToLocalTime(), (new DateTimeOffset(2002, 1, 9, 4, 2, 8, new TimeSpan(-5, 0, 0))), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2, 1, 1))), new DateTimeOffset(2016, 2, 29, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2016, 2, 29))), new DateTimeOffset(9999, 1, 1, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(9999, 1, 1))), new DateTimeOffset(9999, 1, 1, 0, 0, 0, TimeSpan.FromHours(0)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, TimeSpan.FromHours(14.0)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, TimeSpan.FromHours(-11.0)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, TimeSpan.FromHours(-10) + TimeSpan.FromMinutes(-59)), new DateTimeOffset(9999, 12, 31, 12, 59, 59, new TimeSpan(13, 59, 0)), new DateTimeOffset(9999, 12, 31, 23, 59, 59, TimeSpan.Zero), new DateTimeOffset(9999, 12, 31, 23, 59, 59, TimeSpan.FromHours(14.0)), new DateTimeOffset(9998, 12, 31, 12, 59, 59, TimeSpan.FromHours(14.0)), new DateTimeOffset(9998, 12, 31, 12, 59, 59, TimeSpan.FromHours(-14.0)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeZoneInfo.Local.GetUtcOffset(new DateTime(2, 1, 1))), new DateTimeOffset(1, 1, 1, 0, 0, 0, TimeSpan.FromHours(0)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.FromHours(-14.0)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.FromHours(-13) + TimeSpan.FromMinutes(-59)), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.Zero), new DateTimeOffset(2, 1, 1, 0, 0, 0, TimeSpan.Zero), new DateTimeOffset(2008, 2, 29, 23, 59, 59, TimeSpan.FromHours(-14)), new DateTimeOffset(2012, 2, 29, 23, 59, 59, TimeSpan.FromHours(14)) };
string[] format = { "yyyy-MM-dd", "HH:mm:ss", "yyyy-MM-ddTHH:mm:ss", "yyyy-MM-ddTHH:mm:ssZ", "yyyy-MM-ddZ", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM", "yyyy-MM-dd", "yyyy", "yyyyZ", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy", "yyyyZ", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz", "yyyy-MM-ddTHH:mm:sszzzzzz" };
return TestValid(array0, array1, param, format);
}
diff --git a/src/System.Reflection.Emit/ref/System.Reflection.Emit.cs b/src/System.Reflection.Emit/ref/System.Reflection.Emit.cs
index 38ac6b2b44..6d25fd5b96 100644
--- a/src/System.Reflection.Emit/ref/System.Reflection.Emit.cs
+++ b/src/System.Reflection.Emit/ref/System.Reflection.Emit.cs
@@ -109,6 +109,7 @@ namespace System.Reflection.Emit
public override bool IsDefined(System.Type attributeType, bool inherit) { throw null; }
protected override bool IsPointerImpl() { throw null; }
protected override bool IsPrimitiveImpl() { throw null; }
+ public override bool IsSZArray { get { throw null; } }
protected override bool IsValueTypeImpl() { throw null; }
public override System.Type MakeArrayType() { throw null; }
public override System.Type MakeArrayType(int rank) { throw null; }
@@ -206,6 +207,7 @@ namespace System.Reflection.Emit
protected override bool IsPointerImpl() { throw null; }
protected override bool IsPrimitiveImpl() { throw null; }
public override bool IsSubclassOf(System.Type c) { throw null; }
+ public override bool IsSZArray { get { throw null; } }
protected override bool IsValueTypeImpl() { throw null; }
public override System.Type MakeArrayType() { throw null; }
public override System.Type MakeArrayType(int rank) { throw null; }
@@ -412,6 +414,7 @@ namespace System.Reflection.Emit
protected override bool IsPointerImpl() { throw null; }
protected override bool IsPrimitiveImpl() { throw null; }
public override bool IsSubclassOf(System.Type c) { throw null; }
+ public override bool IsSZArray { get { throw null; } }
public override System.Type MakeArrayType() { throw null; }
public override System.Type MakeArrayType(int rank) { throw null; }
public override System.Type MakeByRefType() { throw null; }
diff --git a/src/System.Reflection.Emit/tests/EnumBuilder/EnumBuilder.Properties.Tests.cs b/src/System.Reflection.Emit/tests/EnumBuilder/EnumBuilder.Properties.Tests.cs
index b0f44437e1..6767aa0e20 100644
--- a/src/System.Reflection.Emit/tests/EnumBuilder/EnumBuilder.Properties.Tests.cs
+++ b/src/System.Reflection.Emit/tests/EnumBuilder/EnumBuilder.Properties.Tests.cs
@@ -30,5 +30,29 @@ namespace System.Reflection.Emit.Tests
enumBuilder.AsType();
Assert.Empty(enumBuilder.Namespace);
}
+
+ [Fact]
+ public void IsArray()
+ {
+ EnumBuilder enumBuilder = Helpers.DynamicEnum(TypeAttributes.Public, typeof(int));
+ Assert.False(enumBuilder.IsArray);
+ Assert.False(enumBuilder.IsSZArray);
+
+ Type asType = enumBuilder.AsType();
+ Assert.False(asType.IsArray);
+ Assert.False(asType.IsSZArray);
+
+ Type arrType = enumBuilder.MakeArrayType();
+ Assert.True(arrType.IsArray);
+ Assert.True(arrType.IsSZArray);
+
+ arrType = enumBuilder.MakeArrayType(1);
+ Assert.True(arrType.IsArray);
+ Assert.False(arrType.IsSZArray);
+
+ arrType = enumBuilder.MakeArrayType(2);
+ Assert.True(arrType.IsArray);
+ Assert.False(arrType.IsSZArray);
+ }
}
}
diff --git a/src/System.Reflection.Emit/tests/GenericTypeParameterBuilder/GenericTypeParameterBuilderMakeArrayType.cs b/src/System.Reflection.Emit/tests/GenericTypeParameterBuilder/GenericTypeParameterBuilderMakeArrayType.cs
index 32961c9fe3..869526aa08 100644
--- a/src/System.Reflection.Emit/tests/GenericTypeParameterBuilder/GenericTypeParameterBuilderMakeArrayType.cs
+++ b/src/System.Reflection.Emit/tests/GenericTypeParameterBuilder/GenericTypeParameterBuilderMakeArrayType.cs
@@ -49,5 +49,29 @@ namespace System.Reflection.Emit.Tests
Assert.Throws<IndexOutOfRangeException>(() => typeParams[0].MakeArrayType(rank));
}
+
+ [Fact]
+ public void IsArray()
+ {
+ GenericTypeParameterBuilder typeParam = Helpers.DynamicType(TypeAttributes.Public).DefineGenericParameters("TFirst")[0];
+ Assert.False(typeParam.IsArray);
+ Assert.False(typeParam.IsSZArray);
+
+ Type asType = typeParam.AsType();
+ Assert.False(asType.IsArray);
+ Assert.False(asType.IsSZArray);
+
+ Type arrType = typeParam.MakeArrayType();
+ Assert.True(arrType.IsArray);
+ Assert.True(arrType.IsSZArray);
+
+ arrType = typeParam.MakeArrayType(1);
+ Assert.True(arrType.IsArray);
+ Assert.False(arrType.IsSZArray);
+
+ arrType = typeParam.MakeArrayType(2);
+ Assert.True(arrType.IsArray);
+ Assert.False(arrType.IsSZArray);
+ }
}
}
diff --git a/src/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderMakeArrayType.cs b/src/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderMakeArrayType.cs
index ed9a1bbd63..b24fe191b0 100644
--- a/src/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderMakeArrayType.cs
+++ b/src/System.Reflection.Emit/tests/TypeBuilder/TypeBuilderMakeArrayType.cs
@@ -49,5 +49,29 @@ namespace System.Reflection.Emit.Tests
TypeBuilder type = Helpers.DynamicType(TypeAttributes.Public | TypeAttributes.Abstract);
Assert.Throws<IndexOutOfRangeException>(() => type.MakeArrayType(rank));
}
+
+ [Fact]
+ public void IsArray()
+ {
+ TypeBuilder typeBuilder = Helpers.DynamicType(TypeAttributes.Public | TypeAttributes.Abstract);
+ Assert.False(typeBuilder.IsArray);
+ Assert.False(typeBuilder.IsSZArray);
+
+ Type asType = typeBuilder.AsType();
+ Assert.False(asType.IsArray);
+ Assert.False(asType.IsSZArray);
+
+ Type arrType = typeBuilder.MakeArrayType();
+ Assert.True(arrType.IsArray);
+ Assert.True(arrType.IsSZArray);
+
+ arrType = typeBuilder.MakeArrayType(1);
+ Assert.True(arrType.IsArray);
+ Assert.False(arrType.IsSZArray);
+
+ arrType = typeBuilder.MakeArrayType(2);
+ Assert.True(arrType.IsArray);
+ Assert.False(arrType.IsSZArray);
+ }
}
}
diff --git a/src/System.Runtime.Extensions/src/ApiCompatBaseline.uapaot.txt b/src/System.Runtime.Extensions/src/ApiCompatBaseline.uapaot.txt
index 9c2d81ddaf..a64afb0132 100644
--- a/src/System.Runtime.Extensions/src/ApiCompatBaseline.uapaot.txt
+++ b/src/System.Runtime.Extensions/src/ApiCompatBaseline.uapaot.txt
@@ -1,5 +1,25 @@
-Compat issues with assembly System.Runtime.Extensions:
-TypesMustExist : Type 'System.AppDomain' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_AssemblyLoad(System.AssemblyLoadEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_AssemblyResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_FirstChanceException(System.EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs>)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_ProcessExit(System.EventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_ReflectionOnlyAssemblyResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_ResourceResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_TypeResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_UnhandledException(System.UnhandledExceptionEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.BaseDirectory.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.GetAssemblies()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.GetData(System.String)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.IsCompatibilitySwitchSet(System.String)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.RelativeSearchPath.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_AssemblyLoad(System.AssemblyLoadEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_AssemblyResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_FirstChanceException(System.EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs>)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_ProcessExit(System.EventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_ReflectionOnlyAssemblyResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_ResourceResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_TypeResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_UnhandledException(System.UnhandledExceptionEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.SetData(System.String, System.Object)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.AssemblyLoadEventArgs' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.AssemblyLoadEventHandler' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Base64FormattingOptions' does not exist in the implementation but it does exist in the contract.
@@ -62,7 +82,6 @@ MembersMustExist : Member 'System.Math.Clamp(System.UInt64, System.UInt64, Syste
MembersMustExist : Member 'System.Math.DivRem(System.Int32, System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Math.DivRem(System.Int64, System.Int64, System.Int64)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.MathF' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Progress<T>' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.ResolveEventHandler' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.StringComparer.Compare(System.Object, System.Object)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.StringComparer.Create(System.Globalization.CultureInfo, System.Boolean)' does not exist in the implementation but it does exist in the contract.
@@ -70,8 +89,4 @@ MembersMustExist : Member 'System.StringComparer.GetHashCode(System.Object)' doe
MembersMustExist : Member 'System.StringComparer.InvariantCulture.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.StringComparer.InvariantCultureIgnoreCase.get()' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.IO.BufferedStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
-MembersMustExist : Member 'System.IO.EndOfStreamException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.IO.MemoryStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
-MembersMustExist : Member 'System.IO.TextReader.Synchronized(System.IO.TextReader)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.IO.TextWriter.Synchronized(System.IO.TextWriter)' does not exist in the implementation but it does exist in the contract.
-Total Issues: 75
diff --git a/src/System.Runtime.Extensions/src/System.Runtime.Extensions.csproj b/src/System.Runtime.Extensions/src/System.Runtime.Extensions.csproj
index bd5161e8da..32ce898598 100644
--- a/src/System.Runtime.Extensions/src/System.Runtime.Extensions.csproj
+++ b/src/System.Runtime.Extensions/src/System.Runtime.Extensions.csproj
@@ -8,9 +8,9 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsPartialFacadeAssembly>true</IsPartialFacadeAssembly>
<GenFacadesIgnoreMissingTypes Condition="'$(TargetGroup)'=='uapaot'">true</GenFacadesIgnoreMissingTypes>
+ <DefineConstants Condition="'$(TargetGroup)' == 'uapaot'">$(DefineConstants);uapaot</DefineConstants>
<!-- System.IO.Path conflicts between type in partial facade and in mscorlib -->
<NoWarn>0436</NoWarn>
- <TargetsWindowsUap Condition="'$(TargetGroup)'=='uapaot'">true</TargetsWindowsUap>
</PropertyGroup>
<!-- Default configurations to help VS understand the configurations -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Unix-Debug|AnyCPU'" />
@@ -22,7 +22,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uapaot-Windows_NT-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uapaot-Windows_NT-Release|AnyCPU'" />
<ItemGroup>
- <Compile Include="System\AppDomain.cs" Condition="'$(TargetGroup)' != 'uapaot'" />
+ <Compile Include="System\AppDomain.cs" />
<Compile Include="System\Context.cs" />
<Compile Include="System\AppDomainUnloadedException.cs" />
<Compile Include="System\ApplicationId.cs" />
@@ -302,7 +302,7 @@
<Compile Include="System\StringNormalizationExtensions.cs" />
<Compile Include="System\Globalization\Extensions.cs" />
</ItemGroup>
- <ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp' or '$(TargetGroup)' == 'uap'">
+ <ItemGroup>
<ProjectReference Include="..\..\System.Private.Uri\src\System.Private.Uri.csproj" />
<ProjectReference Include="..\..\System.Diagnostics.Debug\src\System.Diagnostics.Debug.csproj" />
<ProjectReference Include="..\..\System.Security.Principal\src\System.Security.Principal.csproj">
@@ -314,8 +314,6 @@
<Reference Include="mscorlib" />
<Reference Include="Windows" />
<ReferenceFromRuntime Include="System.Private.Interop" />
- <ProjectReference Include="..\..\System.Runtime\src\System.Runtime.csproj" />
- <ProjectReference Include="..\..\System.Private.Uri\src\System.Private.Uri.csproj" />
</ItemGroup>
<ItemGroup>
<ReferenceFromRuntime Include="System.Private.CoreLib" />
diff --git a/src/System.Runtime.Extensions/src/System/AppDomain.cs b/src/System.Runtime.Extensions/src/System/AppDomain.cs
index 3ed566b053..413c83cb59 100644
--- a/src/System.Runtime.Extensions/src/System/AppDomain.cs
+++ b/src/System.Runtime.Extensions/src/System/AppDomain.cs
@@ -9,7 +9,9 @@ extern alias System_Security_Principal;
using System;
using System.Reflection;
using System.Runtime.ExceptionServices;
+#if !uapaot
using System.Runtime.Loader;
+#endif
using System.IO;
namespace System
@@ -27,6 +29,7 @@ namespace System
public static AppDomain CurrentDomain => s_domain;
+#if !uapaot
public string BaseDirectory => AppContext.BaseDirectory;
public string RelativeSearchPath => null;
@@ -36,6 +39,7 @@ namespace System
add { AppContext.UnhandledException += value; }
remove { AppContext.UnhandledException -= value; }
}
+#endif
public string DynamicDirectory => null;
@@ -59,6 +63,7 @@ namespace System
public event EventHandler DomainUnload;
+#if !uapaot
public event EventHandler<FirstChanceExceptionEventArgs> FirstChanceException
{
add { AppContext.FirstChanceException += value; }
@@ -70,6 +75,7 @@ namespace System
add { AppContext.ProcessExit += value; }
remove { AppContext.ProcessExit -= value; }
}
+#endif
public string ApplyPolicy(string assemblyName)
{
@@ -149,6 +155,7 @@ namespace System
public int ExecuteAssemblyByName(string assemblyName, params string[] args) =>
ExecuteAssembly(Assembly.Load(assemblyName), args);
+#if !uapaot
public object GetData(string name) => AppContext.GetData(name);
public void SetData(string name, object data) => AppContext.SetData(name, data);
@@ -158,6 +165,7 @@ namespace System
bool result;
return AppContext.TryGetSwitch(value, out result) ? result : default(bool?);
}
+#endif
public bool IsDefaultAppDomain() => true;
@@ -231,6 +239,7 @@ namespace System
[ObsoleteAttribute("AppDomain.SetShadowCopyPath has been deprecated. Please investigate the use of AppDomainSetup.ShadowCopyDirectories instead. http://go.microsoft.com/fwlink/?linkid=14202")]
public void SetShadowCopyPath(string path) { }
+#if !uapaot
public Assembly[] GetAssemblies() => AssemblyLoadContext.GetLoadedAssemblies();
public event AssemblyLoadEventHandler AssemblyLoad
@@ -239,6 +248,14 @@ namespace System
remove { AssemblyLoadContext.AssemblyLoad -= value; }
}
+ public event ResolveEventHandler AssemblyResolve
+ {
+ add { AssemblyLoadContext.AssemblyResolve += value; }
+ remove { AssemblyLoadContext.AssemblyResolve -= value; }
+ }
+
+ public event ResolveEventHandler ReflectionOnlyAssemblyResolve;
+
public event ResolveEventHandler TypeResolve
{
add { AssemblyLoadContext.TypeResolve += value; }
@@ -250,6 +267,7 @@ namespace System
add { AssemblyLoadContext.ResourceResolve += value; }
remove { AssemblyLoadContext.ResourceResolve -= value; }
}
+#endif
public void SetPrincipalPolicy(PrincipalPolicy policy) { }
@@ -270,13 +288,5 @@ namespace System
_defaultPrincipal = principal;
}
}
-
- public event ResolveEventHandler AssemblyResolve
- {
- add { AssemblyLoadContext.AssemblyResolve += value; }
- remove { AssemblyLoadContext.AssemblyResolve -= value; }
- }
-
- public event ResolveEventHandler ReflectionOnlyAssemblyResolve;
}
}
diff --git a/src/System.Runtime.Numerics/tests/ComplexTests.cs b/src/System.Runtime.Numerics/tests/ComplexTests.cs
index f4d64c9bea..977605eb0c 100644
--- a/src/System.Runtime.Numerics/tests/ComplexTests.cs
+++ b/src/System.Runtime.Numerics/tests/ComplexTests.cs
@@ -320,7 +320,7 @@ namespace System.Numerics.Tests
}
}
- [Theory, MemberData("ACos_Advanced_TestData")]
+ [Theory, MemberData(nameof(ACos_Advanced_TestData))]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
public static void ACos_Advanced(double real, double imaginary, double expectedReal, double expectedImaginary)
{
@@ -407,7 +407,7 @@ namespace System.Numerics.Tests
}
[ActiveIssue(15455)]
- [Theory, MemberData("ASin_Advanced_TestData")]
+ [Theory, MemberData(nameof(ASin_Advanced_TestData))]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
public static void ASin_Advanced(double real, double imaginary, double expectedReal, double expectedImaginary)
{
@@ -451,7 +451,7 @@ namespace System.Numerics.Tests
}
}
- [Theory, MemberData("ATan_Advanced_TestData")]
+ [Theory, MemberData(nameof(ATan_Advanced_TestData))]
public static void ATan_Advanced(double real, double imaginary, double expectedReal, double expectedImaginary)
{
var complex = new Complex(real, imaginary);
@@ -523,7 +523,7 @@ namespace System.Numerics.Tests
}
}
- [ConditionalTheory(nameof(Is64Bit)), MemberData("Cos_Advanced_TestData")]
+ [ConditionalTheory(nameof(Is64Bit)), MemberData(nameof(Cos_Advanced_TestData))]
public static void Cos_Advanced(double real, double imaginary, double expectedReal, double expectedImaginary)
{
var complex = new Complex(real, imaginary);
@@ -575,7 +575,7 @@ namespace System.Numerics.Tests
}
}
- [ConditionalTheory(nameof(Is64Bit)), MemberData("Cosh_Advanced_TestData")]
+ [ConditionalTheory(nameof(Is64Bit)), MemberData(nameof(Cosh_Advanced_TestData))]
public static void Cosh_Advanced(double real, double imaginary, double expectedReal, double expectedImaginary)
{
var complex = new Complex(real, imaginary);
@@ -1176,7 +1176,7 @@ namespace System.Numerics.Tests
}
}
- [ConditionalTheory(nameof(Is64Bit)), MemberData("Sin_Advanced_TestData")]
+ [ConditionalTheory(nameof(Is64Bit)), MemberData(nameof(Sin_Advanced_TestData))]
public static void Sin_Advanced(double real, double imaginary, double expectedReal, double expectedImaginary)
{
var complex = new Complex(real, imaginary);
@@ -1228,7 +1228,7 @@ namespace System.Numerics.Tests
}
}
- [ConditionalTheory(nameof(Is64Bit)), MemberData("Sinh_Advanced_TestData")]
+ [ConditionalTheory(nameof(Is64Bit)), MemberData(nameof(Sinh_Advanced_TestData))]
public static void Sinh_Advanced(double real, double imaginary, double expectedReal, double expectedImaginary)
{
var complex = new Complex(real, imaginary);
@@ -1320,7 +1320,7 @@ namespace System.Numerics.Tests
}
[Theory]
- [MemberData("Sqrt_TestData")]
+ [MemberData(nameof(Sqrt_TestData))]
public static void Sqrt(double real, double imaginary, double expectedReal, double expectedImaginary)
{
var complex = new Complex(real, imaginary);
@@ -1329,7 +1329,7 @@ namespace System.Numerics.Tests
}
[Theory]
- [MemberData("Sqrt_AdvancedTestData")]
+ [MemberData(nameof(Sqrt_AdvancedTestData))]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
public static void Sqrt_Advanced(double real, double imaginary, double expectedReal, double expectedImaginary)
{
@@ -1400,7 +1400,7 @@ namespace System.Numerics.Tests
}
}
- [Theory, MemberData("Tan_Advanced_TestData")]
+ [Theory, MemberData(nameof(Tan_Advanced_TestData))]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
public static void Tan_Advanced(double real, double imaginary, double expectedReal, double expectedImaginary)
{
@@ -1409,7 +1409,7 @@ namespace System.Numerics.Tests
VerifyRealImaginaryProperties(result, expectedReal, expectedImaginary);
}
- [Theory, MemberData("Tan_Legacy_TestData")]
+ [Theory, MemberData(nameof(Tan_Legacy_TestData))]
[SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
public static void Tan_Legacy (double real, double imaginary, double expectedReal, double expectedImaginary) {
var complex = new Complex(real, imaginary);
@@ -1472,7 +1472,7 @@ namespace System.Numerics.Tests
}
}
- [Theory, MemberData("Tanh_Advanced_TestData")]
+ [Theory, MemberData(nameof(Tanh_Advanced_TestData))]
[SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
public static void Tanh_Advanced(double real, double imaginary, double expectedReal, double expectedImaginary)
{
@@ -1481,7 +1481,7 @@ namespace System.Numerics.Tests
VerifyRealImaginaryProperties(result, expectedReal, expectedImaginary);
}
- [Theory, MemberData("Tanh_Legacy_TestData")]
+ [Theory, MemberData(nameof(Tanh_Legacy_TestData))]
[SkipOnTargetFramework(~TargetFrameworkMonikers.NetFramework)]
public static void Tanh_Legacy (double real, double imaginary, double expectedReal, double expectedImaginary) {
var complex = new Complex(real, imaginary);
@@ -1644,7 +1644,7 @@ namespace System.Numerics.Tests
yield return new object[] { (BigInteger)RandomNegativeDouble() };
}
- [Theory, MemberData("Cast_BigInteger_TestData")]
+ [Theory, MemberData(nameof(Cast_BigInteger_TestData))]
public static void Cast_BigInteger(BigInteger value)
{
Complex complex = (Complex)value;
@@ -1669,7 +1669,7 @@ namespace System.Numerics.Tests
yield return new object[] { -positiveDecimal };
}
- [Theory, MemberData("Cast_Decimal_TestData")]
+ [Theory, MemberData(nameof(Cast_Decimal_TestData))]
public static void Cast_Decimal(decimal value)
{
Complex complex = (Complex)value;
diff --git a/src/System.Runtime/ref/System.Runtime.cs b/src/System.Runtime/ref/System.Runtime.cs
index 7fa2c2bd5a..6c17365e66 100644
--- a/src/System.Runtime/ref/System.Runtime.cs
+++ b/src/System.Runtime/ref/System.Runtime.cs
@@ -2622,6 +2622,7 @@ namespace System
public virtual bool IsSecurityTransparent { get { throw null; } }
public virtual bool IsSerializable { get { throw null; } }
public bool IsSpecialName { get { throw null; } }
+ public virtual bool IsSZArray { get { throw null; } }
public bool IsUnicodeClass { get { throw null; } }
public bool IsValueType { get { throw null; } }
public bool IsVisible { get { throw null; } }
@@ -6119,6 +6120,7 @@ namespace System.Reflection
protected override bool IsValueTypeImpl() { throw null; }
protected override bool IsCOMObjectImpl() { throw null; }
public override bool IsConstructedGenericType { get { throw null; } }
+ public override bool IsSZArray { get { throw null; } }
public override System.Type GetElementType() { throw null; }
protected override bool HasElementTypeImpl() { throw null; }
public override System.Type UnderlyingSystemType { get { throw null; } }
diff --git a/src/System.Runtime/tests/System.Runtime.Tests.csproj b/src/System.Runtime/tests/System.Runtime.Tests.csproj
index bc062baaf0..7e86ef2b0c 100644
--- a/src/System.Runtime/tests/System.Runtime.Tests.csproj
+++ b/src/System.Runtime/tests/System.Runtime.Tests.csproj
@@ -134,11 +134,14 @@
<Compile Include="System\LazyTests.netcoreapp.cs" />
<Compile Include="System\StringTests.netcoreapp.cs" />
<Compile Include="System\TimeSpanTests.netcoreapp.cs" />
+ <Compile Include="System\Reflection\TypeDelegatorTests.netcoreapp.cs" />
+ <Compile Include="System\Reflection\TypeInfoTests.netcoreapp.cs" />
<Compile Include="System\Runtime\CompilerServices\RuntimeHelpersTests.netcoreapp.cs" />
<Compile Include="System\Runtime\CompilerServices\ConditionalWeakTableTests.netcoreapp.cs" />
<Compile Include="System\UIntPtrTests.netcoreapp.cs" />
<Compile Include="System\ComponentModel\DefaultValueAttributeTests.netcoreapp.cs" />
<Compile Include="System\TypedReferenceTests.cs" />
+ <Compile Include="System\TypeTests.netcoreapp.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetGroup)' != 'netcoreapp'">
<Compile Include="System\StringSplitExtensions.cs" />
diff --git a/src/System.Runtime/tests/System/Reflection/TypeDelegatorTests.netcoreapp.cs b/src/System.Runtime/tests/System/Reflection/TypeDelegatorTests.netcoreapp.cs
new file mode 100644
index 0000000000..651d5ea7a7
--- /dev/null
+++ b/src/System.Runtime/tests/System/Reflection/TypeDelegatorTests.netcoreapp.cs
@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+using Xunit;
+
+namespace System.Reflection.Tests
+{
+ public class TypeDelegatorNetcoreTests
+ {
+ private static IEnumerable<object[]> SZArrayOrNotTypes()
+ {
+ yield return new object[] { typeof(int[]), true };
+ yield return new object[] { typeof(string[]), true };
+ yield return new object[] { typeof(void), false };
+ yield return new object[] { typeof(int), false };
+ yield return new object[] { typeof(int[]).MakeByRefType(), false };
+ yield return new object[] { typeof(int[,]), false };
+ yield return new object[] { typeof(TypeInfoTests), false };
+ yield return new object[] { Array.CreateInstance(typeof(int), new[] { 2 }, new[] { -1 }).GetType(), false };
+ yield return new object[] { Array.CreateInstance(typeof(int), new[] { 2 }, new[] { 1 }).GetType(), false };
+ yield return new object[] { Array.CreateInstance(typeof(int), new[] { 2 }, new[] { 0 }).GetType(), true };
+ yield return new object[] { typeof(int[][]), true };
+ yield return new object[] { Type.GetType("System.Int32[]"), true };
+ yield return new object[] { Type.GetType("System.Int32[*]"), false };
+ yield return new object[] { Type.GetType("System.Int32"), false };
+ yield return new object[] { typeof(int).MakeArrayType(), true };
+ yield return new object[] { typeof(int).MakeArrayType(1), false };
+ yield return new object[] { typeof(int).MakeArrayType().MakeArrayType(), true };
+ yield return new object[] { typeof(int).MakeArrayType(2), false };
+ yield return new object[] { typeof(Outside<int>.Inside<string>), false };
+ yield return new object[] { typeof(Outside<int>.Inside<string>[]), true };
+ yield return new object[] { typeof(Outside<int>.Inside<string>[,]), false };
+ yield return new object[] { Array.CreateInstance(typeof(Outside<int>.Inside<string>), new[] { 2 }, new[] { -1 }).GetType(), false };
+ }
+
+ [Theory, MemberData(nameof(SZArrayOrNotTypes))]
+ public void IsSZArray(Type type, bool expected)
+ {
+ Assert.Equal(expected, new TypeDelegator(type).IsSZArray);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/System.Runtime/tests/System/Reflection/TypeInfoTests.netcoreapp.cs b/src/System.Runtime/tests/System/Reflection/TypeInfoTests.netcoreapp.cs
new file mode 100644
index 0000000000..90e09cf967
--- /dev/null
+++ b/src/System.Runtime/tests/System/Reflection/TypeInfoTests.netcoreapp.cs
@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+using Xunit;
+
+namespace System.Reflection.Tests
+{
+ public class TypeInfoNetcoreTests
+ {
+ private static IEnumerable<object[]> SZArrayOrNotTypes()
+ {
+ yield return new object[] { typeof(int[]), true };
+ yield return new object[] { typeof(string[]), true };
+ yield return new object[] { typeof(void), false };
+ yield return new object[] { typeof(int), false };
+ yield return new object[] { typeof(int[]).MakeByRefType(), false };
+ yield return new object[] { typeof(int[,]), false };
+ yield return new object[] { typeof(TypeInfoTests), false };
+ yield return new object[] { Array.CreateInstance(typeof(int), new[] { 2 }, new[] { -1 }).GetType(), false };
+ yield return new object[] { Array.CreateInstance(typeof(int), new[] { 2 }, new[] { 1 }).GetType(), false };
+ yield return new object[] { Array.CreateInstance(typeof(int), new[] { 2 }, new[] { 0 }).GetType(), true };
+ yield return new object[] { typeof(int[][]), true };
+ yield return new object[] { Type.GetType("System.Int32[]"), true };
+ yield return new object[] { Type.GetType("System.Int32[*]"), false };
+ yield return new object[] { Type.GetType("System.Int32"), false };
+ yield return new object[] { typeof(int).MakeArrayType(), true };
+ yield return new object[] { typeof(int).MakeArrayType(1), false };
+ yield return new object[] { typeof(int).MakeArrayType().MakeArrayType(), true };
+ yield return new object[] { typeof(int).MakeArrayType(2), false };
+ yield return new object[] { typeof(Outside<int>.Inside<string>), false };
+ yield return new object[] { typeof(Outside<int>.Inside<string>[]), true };
+ yield return new object[] { typeof(Outside<int>.Inside<string>[,]), false };
+ yield return new object[] { Array.CreateInstance(typeof(Outside<int>.Inside<string>), new[] { 2 }, new[] { -1 }).GetType(), false };
+ }
+
+ [Theory, MemberData(nameof(SZArrayOrNotTypes))]
+ public void IsSZArray(Type type, bool expected)
+ {
+ Assert.Equal(expected, type.GetTypeInfo().IsSZArray);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/System.Runtime/tests/System/TypeTests.netcoreapp.cs b/src/System.Runtime/tests/System/TypeTests.netcoreapp.cs
new file mode 100644
index 0000000000..3edb495c39
--- /dev/null
+++ b/src/System.Runtime/tests/System/TypeTests.netcoreapp.cs
@@ -0,0 +1,40 @@
+using System.Collections.Generic;
+using Xunit;
+
+namespace System.Tests
+{
+ public class TypeTestsNetcore
+ {
+ private static IEnumerable<object[]> SZArrayOrNotTypes()
+ {
+ yield return new object[] {typeof(int[]), true};
+ yield return new object[] {typeof(string[]), true};
+ yield return new object[] {typeof(void), false};
+ yield return new object[] {typeof(int), false};
+ yield return new object[] {typeof(int[]).MakeByRefType(), false};
+ yield return new object[] {typeof(int[,]), false};
+ yield return new object[] {typeof(TypeTests), false};
+ yield return new object[] {Array.CreateInstance(typeof(int), new[] {2}, new[] {-1}).GetType(), false};
+ yield return new object[] {Array.CreateInstance(typeof(int), new[] {2}, new[] {1}).GetType(), false};
+ yield return new object[] {Array.CreateInstance(typeof(int), new[] {2}, new[] {0}).GetType(), true};
+ yield return new object[] {typeof(int[][]), true};
+ yield return new object[] {Type.GetType("System.Int32[]"), true};
+ yield return new object[] {Type.GetType("System.Int32[*]"), false};
+ yield return new object[] {Type.GetType("System.Int32"), false};
+ yield return new object[] {typeof(int).MakeArrayType(), true};
+ yield return new object[] {typeof(int).MakeArrayType(1), false};
+ yield return new object[] {typeof(int).MakeArrayType().MakeArrayType(), true};
+ yield return new object[] {typeof(int).MakeArrayType(2), false};
+ yield return new object[] {typeof(Outside<int>.Inside<string>), false};
+ yield return new object[] {typeof(Outside<int>.Inside<string>[]), true};
+ yield return new object[] {typeof(Outside<int>.Inside<string>[,]), false};
+ yield return new object[] {Array.CreateInstance(typeof(Outside<int>.Inside<string>), new[] {2}, new[] {-1}).GetType(), false};
+ }
+
+ [Theory, MemberData(nameof(SZArrayOrNotTypes))]
+ public void IsSZArray(Type type, bool expected)
+ {
+ Assert.Equal(expected, type.IsSZArray);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/System.Security.Cryptography.Algorithms/src/Resources/Strings.resx b/src/System.Security.Cryptography.Algorithms/src/Resources/Strings.resx
index fa5970c7ac..9b00a92019 100644
--- a/src/System.Security.Cryptography.Algorithms/src/Resources/Strings.resx
+++ b/src/System.Security.Cryptography.Algorithms/src/Resources/Strings.resx
@@ -85,9 +85,18 @@
<data name="Cryptography_CSP_NoPrivateKey" xml:space="preserve">
<value>Object contains only the public half of a key pair. A private key must also be provided.</value>
</data>
+ <data name="Cryptography_Der_Invalid_Encoding" xml:space="preserve">
+ <value>ASN1 corrupted data.</value>
+ </data>
+ <data name="Cryptography_DSA_KeyGenNotSupported" xml:space="preserve">
+ <value>DSA keys can be imported, but new key generation is not supported on this platform.</value>
+ </data>
<data name="Cryptography_ECXmlSerializationFormatRequired" xml:space="preserve">
<value>XML serialization of an elliptic curve key requires using an overload which specifies the XML format to be used.</value>
</data>
+ <data name="Cryptography_ECC_NamedCurvesOnly" xml:space="preserve">
+ <value>Only named curves are supported on this platform.</value>
+ </data>
<data name="Cryptography_HashAlgorithmNameNullOrEmpty" xml:space="preserve">
<value>The hash algorithm name cannot be null or empty.</value>
</data>
diff --git a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
index e78bf79eb3..8a2a365eb8 100644
--- a/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
+++ b/src/System.Security.Cryptography.Algorithms/src/System.Security.Cryptography.Algorithms.csproj
@@ -279,67 +279,6 @@
</Compile>
</ItemGroup>
<ItemGroup Condition=" '$(TargetsUnix)' == 'true' AND '$(TargetsOSX)' != 'true' ">
- <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs">
- <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.Cipher.cs">
- <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.Cipher.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.Hmac.cs">
- <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.Hmac.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeEvpCipherCtxHandle.Unix.cs">
- <Link>Common\Microsoft\Win32\SafeHandles\SafeEvpCipherCtxHandle.Unix.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeEvpMdCtxHandle.Unix.cs">
- <Link>Common\Microsoft\Win32\SafeHandles\SafeEvpMdCtxHandle.Unix.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeHmacCtxHandle.Unix.cs">
- <Link>Common\Microsoft\Win32\SafeHandles\SafeHmacCtxHandle.Unix.cs</Link>
- </Compile>
- <Compile Include="Internal\Cryptography\AesImplementation.Unix.cs" />
- <Compile Include="Internal\Cryptography\DesImplementation.Unix.cs" />
- <Compile Include="Internal\Cryptography\HashProviderDispenser.Unix.cs" />
- <Compile Include="Internal\Cryptography\OpenSslCipher.cs" />
- <Compile Include="Internal\Cryptography\RandomNumberGeneratorImplementation.Unix.cs" />
- <Compile Include="Internal\Cryptography\RC2Implementation.Unix.cs" />
- <Compile Include="Internal\Cryptography\TripleDesImplementation.Unix.cs" />
- </ItemGroup>
- <ItemGroup Condition=" '$(TargetsOSX)' == 'true' ">
- <Compile Include="$(CommonPath)\Interop\OSX\Interop.Libraries.cs">
- <Link>Common\Interop\OSX\Interop.Libraries.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Digest.cs">
- <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Digest.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Err.cs">
- <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Err.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Hmac.cs">
- <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Hmac.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.PAL_HashAlgorithm.cs">
- <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.PAL_HashAlgorithm.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Random.cs">
- <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Random.cs</Link>
- </Compile>
- <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Symmetric.cs">
- <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Symmetric.cs</Link>
- </Compile>
- <Compile Include="Internal\Cryptography\AesImplementation.OSX.cs" />
- <Compile Include="Internal\Cryptography\AppleCCCryptor.cs" />
- <Compile Include="Internal\Cryptography\DesImplementation.OSX.cs" />
- <Compile Include="Internal\Cryptography\HashProviderDispenser.OSX.cs" />
- <Compile Include="Internal\Cryptography\RandomNumberGeneratorImplementation.OSX.cs" />
- <Compile Include="Internal\Cryptography\RC2Implementation.OSX.cs" />
- <Compile Include="Internal\Cryptography\TripleDesImplementation.OSX.cs" />
- </ItemGroup>
- <ItemGroup Condition=" '$(TargetsUnix)' == 'true' ">
- <Compile Include="System\Security\Cryptography\ECDsaOpenSsl.cs" />
- <Compile Include="$(CommonPath)\Internal\Cryptography\OpenSslAsymmetricAlgorithmCore.cs">
- <Link>Common\Internal\Cryptography\OpenSslAsymmetricAlgorithmCore.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\Interop\Unix\Interop.Libraries.cs">
<Link>Common\Interop\Unix\Interop.Libraries.cs</Link>
</Compile>
@@ -367,6 +306,15 @@
<Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.ERR.cs">
<Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.ERR.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.Cipher.cs">
+ <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.Cipher.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs">
+ <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.Hmac.cs">
+ <Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.Hmac.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Security.Cryptography.Native\Interop.Initialization.cs">
<Link>Common\Interop\Unix\System.Security.Cryptography.Native\Interop.Initialization.cs</Link>
</Compile>
@@ -385,20 +333,23 @@
<Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeDsaHandle.Unix.cs">
<Link>Common\Microsoft\Win32\SafeHandles\SafeDsaHandle.Unix.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeInteriorHandle.cs">
- <Link>Common\Microsoft\Win32\SafeHandles\SafeInteriorHandle.cs</Link>
- </Compile>
<Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeEcKeyHandle.Unix.cs">
<Link>Common\Microsoft\Win32\SafeHandles\SafeEcKeyHandle.Unix.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeRsaHandle.Unix.cs">
- <Link>Common\Microsoft\Win32\SafeHandles\SafeRsaHandle.Unix.cs</Link>
+ <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeEvpCipherCtxHandle.Unix.cs">
+ <Link>Common\Microsoft\Win32\SafeHandles\SafeEvpCipherCtxHandle.Unix.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\Security\Cryptography\DerEncoder.cs">
- <Link>Common\System\Security\Cryptography\DerEncoder.cs</Link>
+ <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeEvpMdCtxHandle.Unix.cs">
+ <Link>Common\Microsoft\Win32\SafeHandles\SafeEvpMdCtxHandle.Unix.cs</Link>
</Compile>
- <Compile Include="$(CommonPath)\System\Security\Cryptography\DerSequenceReader.cs">
- <Link>Common\System\Security\Cryptography\DerSequenceReader.cs</Link>
+ <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeHmacCtxHandle.Unix.cs">
+ <Link>Common\Microsoft\Win32\SafeHandles\SafeHmacCtxHandle.Unix.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeInteriorHandle.cs">
+ <Link>Common\Microsoft\Win32\SafeHandles\SafeInteriorHandle.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeRsaHandle.Unix.cs">
+ <Link>Common\Microsoft\Win32\SafeHandles\SafeRsaHandle.Unix.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Security\Cryptography\DSAOpenSsl.cs">
<Link>Common\System\Security\Cryptography\DSAOpenSsl.cs</Link>
@@ -412,6 +363,112 @@
<Compile Include="$(CommonPath)\System\Security\Cryptography\RSAOpenSsl.cs">
<Link>Common\System\Security\Cryptography\RSAOpenSsl.cs</Link>
</Compile>
+ <Compile Include="Internal\Cryptography\AesImplementation.Unix.cs" />
+ <Compile Include="Internal\Cryptography\DesImplementation.Unix.cs" />
+ <Compile Include="Internal\Cryptography\HashProviderDispenser.Unix.cs" />
+ <Compile Include="Internal\Cryptography\OpenSslCipher.cs" />
+ <Compile Include="Internal\Cryptography\RandomNumberGeneratorImplementation.Unix.cs" />
+ <Compile Include="Internal\Cryptography\RC2Implementation.Unix.cs" />
+ <Compile Include="Internal\Cryptography\TripleDesImplementation.Unix.cs" />
+ <Compile Include="System\Security\Cryptography\ECDsaOpenSsl.cs" />
+ </ItemGroup>
+ <ItemGroup Condition=" '$(TargetsOSX)' == 'true' ">
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFArray.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFArray.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFData.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFData.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFError.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFError.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFString.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFString.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.Libraries.cs">
+ <Link>Common\Interop\OSX\Interop.Libraries.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Digest.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Digest.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Ecc.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Ecc.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Err.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Err.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Hmac.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Hmac.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Keychain.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Keychain.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.PAL_HashAlgorithm.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.PAL_HashAlgorithm.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Random.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Random.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.RSA.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.RSA.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecErr.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecErr.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecErrMessage.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecErrMessage.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecKeyRef.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecKeyRef.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecKeyRef.Export.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecKeyRef.Export.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Symmetric.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Symmetric.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeCreateHandle.OSX.cs">
+ <Link>Common\Microsoft\Win32\SafeHandles\SafeCreateHandle.OSX.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeHandleCache.cs">
+ <Link>Common\Microsoft\Win32\SafeHandles\SafeHandleCache.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\DSASecurityTransforms.cs">
+ <Link>Common\System\Security\Cryptography\DSASecurityTransforms.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\ECDsaSecurityTransforms.cs">
+ <Link>Common\System\Security\Cryptography\ECDsaSecurityTransforms.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\KeyBlobHelpers.cs">
+ <Link>Common\System\Security\Cryptography\KeyBlobHelpers.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\RSASecurityTransforms.cs">
+ <Link>Common\System\Security\Cryptography\RSASecurityTransforms.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\SecKeyPair.cs">
+ <Link>Common\System\Security\Cryptography\SecKeyPair.cs</Link>
+ </Compile>
+ <Compile Include="Internal\Cryptography\AesImplementation.OSX.cs" />
+ <Compile Include="Internal\Cryptography\AppleCCCryptor.cs" />
+ <Compile Include="Internal\Cryptography\DesImplementation.OSX.cs" />
+ <Compile Include="Internal\Cryptography\HashProviderDispenser.OSX.cs" />
+ <Compile Include="Internal\Cryptography\RandomNumberGeneratorImplementation.OSX.cs" />
+ <Compile Include="Internal\Cryptography\RC2Implementation.OSX.cs" />
+ <Compile Include="Internal\Cryptography\TripleDesImplementation.OSX.cs" />
+ </ItemGroup>
+ <ItemGroup Condition=" '$(TargetsUnix)' == 'true'">
+ <Compile Include="$(CommonPath)\Internal\Cryptography\AsymmetricAlgorithmHelpers.cs">
+ <Link>Common\Internal\Cryptography\AsymmetricAlgorithmHelpers.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\DerEncoder.cs">
+ <Link>Common\System\Security\Cryptography\DerEncoder.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\DerSequenceReader.cs">
+ <Link>Common\System\Security\Cryptography\DerSequenceReader.cs</Link>
+ </Compile>
</ItemGroup>
<ItemGroup>
<Reference Include="System.Collections" />
@@ -428,6 +485,7 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetsUnix)' == 'true'">
<Reference Include="System.Runtime.Numerics" />
+ <Reference Include="System.Threading" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
diff --git a/src/System.Security.Cryptography.Algorithms/tests/DefaultDSAProvider.cs b/src/System.Security.Cryptography.Algorithms/tests/DefaultDSAProvider.cs
index 1f9360f5d7..c2939fd13b 100644
--- a/src/System.Security.Cryptography.Algorithms/tests/DefaultDSAProvider.cs
+++ b/src/System.Security.Cryptography.Algorithms/tests/DefaultDSAProvider.cs
@@ -22,9 +22,11 @@ namespace System.Security.Cryptography.Dsa.Tests
{
get
{
- return (!PlatformDetection.IsWindows7);
+ return !(PlatformDetection.IsWindows7 || PlatformDetection.IsOSX);
}
}
+
+ public bool SupportsKeyGeneration => !PlatformDetection.IsOSX;
}
public partial class DSAFactory
diff --git a/src/System.Security.Cryptography.Algorithms/tests/DefaultECDsaProvider.cs b/src/System.Security.Cryptography.Algorithms/tests/DefaultECDsaProvider.cs
index f7d8dde19e..02cbf732e8 100644
--- a/src/System.Security.Cryptography.Algorithms/tests/DefaultECDsaProvider.cs
+++ b/src/System.Security.Cryptography.Algorithms/tests/DefaultECDsaProvider.cs
@@ -32,6 +32,10 @@ namespace System.Security.Cryptography.EcDsa.Tests
// Friendly name required for windows
return NativeOidFriendlyNameExists(oid.FriendlyName);
}
+ if (PlatformDetection.IsOSX)
+ {
+ return false;
+ }
if (!string.IsNullOrEmpty(oid.Value))
{
// Value is passed before FriendlyName
@@ -48,6 +52,12 @@ namespace System.Security.Cryptography.EcDsa.Tests
{
return PlatformDetection.WindowsVersion >= 10;
}
+
+ if (PlatformDetection.IsOSX)
+ {
+ return false;
+ }
+
return true;
}
}
diff --git a/src/System.Security.Cryptography.Cng/tests/DSACngProvider.cs b/src/System.Security.Cryptography.Cng/tests/DSACngProvider.cs
index 58ef0dfdf7..15f13bff9e 100644
--- a/src/System.Security.Cryptography.Cng/tests/DSACngProvider.cs
+++ b/src/System.Security.Cryptography.Cng/tests/DSACngProvider.cs
@@ -16,13 +16,8 @@ namespace System.Security.Cryptography.Dsa.Tests
return new DSACng(keySize);
}
- public bool SupportsFips186_3
- {
- get
- {
- return (!PlatformDetection.IsWindows7);
- }
- }
+ public bool SupportsFips186_3 => (!PlatformDetection.IsWindows7);
+ public bool SupportsKeyGeneration => true;
}
public partial class DSAFactory
diff --git a/src/System.Security.Cryptography.Csp/tests/DSACryptoServiceProviderProvider.cs b/src/System.Security.Cryptography.Csp/tests/DSACryptoServiceProviderProvider.cs
index 4b747c9d55..5d5a4d852c 100644
--- a/src/System.Security.Cryptography.Csp/tests/DSACryptoServiceProviderProvider.cs
+++ b/src/System.Security.Cryptography.Csp/tests/DSACryptoServiceProviderProvider.cs
@@ -16,13 +16,8 @@ namespace System.Security.Cryptography.Dsa.Tests
return new DSACryptoServiceProvider(keySize);
}
- public bool SupportsFips186_3
- {
- get
- {
- return false;
- }
- }
+ public bool SupportsFips186_3 => false;
+ public bool SupportsKeyGeneration => true;
}
public partial class DSAFactory
diff --git a/src/System.Security.Cryptography.Encoding/src/Configurations.props b/src/System.Security.Cryptography.Encoding/src/Configurations.props
index 62eb1c4776..05d3ab66c3 100644
--- a/src/System.Security.Cryptography.Encoding/src/Configurations.props
+++ b/src/System.Security.Cryptography.Encoding/src/Configurations.props
@@ -2,9 +2,10 @@
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildConfigurations>
+ netcoreapp-OSX;
netcoreapp-Unix;
netcoreapp-Windows_NT;
uap-Windows_NT;
</BuildConfigurations>
</PropertyGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/System.Security.Cryptography.Encoding/src/Internal/Cryptography/AsnFormatter.OSX.cs b/src/System.Security.Cryptography.Encoding/src/Internal/Cryptography/AsnFormatter.OSX.cs
new file mode 100644
index 0000000000..1df487f829
--- /dev/null
+++ b/src/System.Security.Cryptography.Encoding/src/Internal/Cryptography/AsnFormatter.OSX.cs
@@ -0,0 +1,217 @@
+// 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.Collections.Generic;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Internal.Cryptography
+{
+ internal abstract partial class AsnFormatter
+ {
+ private static readonly AsnFormatter s_instance = new AppleAsnFormatter();
+
+ // -----------------------------
+ // ---- PAL layer ends here ----
+ // -----------------------------
+ }
+
+ internal class AppleAsnFormatter : AsnFormatter
+ {
+ protected override string FormatNative(Oid oid, byte[] rawData, bool multiLine)
+ {
+ if (oid == null || string.IsNullOrEmpty(oid.Value))
+ {
+ return EncodeHexString(rawData, true);
+ }
+
+ switch (oid.Value)
+ {
+ case "2.5.29.17":
+ return FormatSubjectAlternativeName(rawData);
+ }
+
+ return null;
+ }
+
+ private const string CommaSpace = ", ";
+
+ internal enum GeneralNameType
+ {
+ OtherName = 0,
+ Rfc822Name = 1,
+ // RFC 822: Standard for the format of ARPA Internet Text Messages.
+ // That means "email", and an RFC 822 Name: "Email address"
+ Email = Rfc822Name,
+ DnsName = 2,
+ X400Address = 3,
+ DirectoryName = 4,
+ EdiPartyName = 5,
+ UniformResourceIdentifier = 6,
+ IPAddress = 7,
+ RegisteredId = 8,
+ }
+
+ private string FormatSubjectAlternativeName(byte[] rawData)
+ {
+ // Because SubjectAlternativeName is a commonly parsed structure, we'll
+ // specifically format this one. And we'll match the OpenSSL format, which
+ // includes not localizing any of the values (or respecting the multiLine boolean)
+ //
+ // The intent here is to be functionally equivalent to OpenSSL GENERAL_NAME_print.
+
+ // The end size of this string is hard to predict.
+ // * dNSName values have a tag that takes four characters to represent ("DNS:")
+ // and then their payload is ASCII encoded (so one byte -> one char), so they
+ // work out to be about equal (in chars) to their DER encoded length (in bytes).
+ // * iPAddress values have a tag that takes 11 characters ("IP Address:") and then
+ // grow from 4 bytes to up to 15 characters for IPv4, or 16 bytes to 47 characters
+ // for IPv6
+ //
+ // So use a List<string> and just Concat them all when we're done, and we reduce the
+ // number of times we copy the header values (vs pointers to the header values).
+ List<string> segments = new List<string>();
+
+ try
+ {
+ // SubjectAltName ::= GeneralNames
+ //
+ // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+ //
+ // GeneralName ::= CHOICE {
+ // otherName [0] OtherName,
+ // rfc822Name [1] IA5String,
+ // dNSName [2] IA5String,
+ // x400Address [3] ORAddress,
+ // directoryName [4] Name,
+ // ediPartyName [5] EDIPartyName,
+ // uniformResourceIdentifier [6] IA5String,
+ // iPAddress [7] OCTET STRING,
+ // registeredID [8] OBJECT IDENTIFIER }
+ //
+ // OtherName::= SEQUENCE {
+ // type - id OBJECT IDENTIFIER,
+ // value[0] EXPLICIT ANY DEFINED BY type - id }
+ DerSequenceReader altNameReader = new DerSequenceReader(rawData);
+
+ while (altNameReader.HasData)
+ {
+ if (segments.Count != 0)
+ {
+ segments.Add(CommaSpace);
+ }
+
+ byte tag = altNameReader.PeekTag();
+
+ if ((tag & DerSequenceReader.ContextSpecificTagFlag) == 0)
+ {
+ // All GeneralName values need the ContextSpecific flag.
+ return null;
+ }
+
+ GeneralNameType nameType = (GeneralNameType)(tag & DerSequenceReader.TagNumberMask);
+
+ bool needsConstructedFlag = false;
+
+ switch (nameType)
+ {
+ case GeneralNameType.OtherName:
+ case GeneralNameType.X400Address:
+ case GeneralNameType.DirectoryName:
+ case GeneralNameType.EdiPartyName:
+ needsConstructedFlag = true;
+ break;
+ }
+
+ if (needsConstructedFlag &&
+ (tag & DerSequenceReader.ConstructedFlag) == 0)
+ {
+ // All of the SEQUENCE types require the constructed bit,
+ // or OpenSSL will have refused to print it.
+ return null;
+ }
+
+ switch (nameType)
+ {
+ case GeneralNameType.OtherName:
+ segments.Add("othername:<unsupported>");
+ altNameReader.SkipValue();
+ break;
+ case GeneralNameType.Rfc822Name:
+ segments.Add("email:");
+ segments.Add(altNameReader.ReadIA5String());
+ break;
+ case GeneralNameType.DnsName:
+ segments.Add("DNS:");
+ segments.Add(altNameReader.ReadIA5String());
+ break;
+ case GeneralNameType.X400Address:
+ segments.Add("X400Name:<unsupported>");
+ altNameReader.SkipValue();
+ break;
+ case GeneralNameType.DirectoryName:
+ // OpenSSL supports printing one of these, but the logic lives in X509Certificates,
+ // and it isn't very common. So we'll skip this one until someone asks for it.
+ segments.Add("DirName:<unsupported>");
+ altNameReader.SkipValue();
+ break;
+ case GeneralNameType.EdiPartyName:
+ segments.Add("EdiPartyName:<unsupported>");
+ altNameReader.SkipValue();
+ break;
+ case GeneralNameType.UniformResourceIdentifier:
+ segments.Add("URI:");
+ segments.Add(altNameReader.ReadIA5String());
+ break;
+ case GeneralNameType.IPAddress:
+ segments.Add("IP Address");
+
+ byte[] ipAddressBytes = altNameReader.ReadOctetString();
+
+ if (ipAddressBytes.Length == 4)
+ {
+ // Add the colon and dotted-decimal representation of IPv4.
+ segments.Add(
+ $":{ipAddressBytes[0]}.{ipAddressBytes[1]}.{ipAddressBytes[2]}.{ipAddressBytes[3]}");
+ }
+ else if (ipAddressBytes.Length == 16)
+ {
+ // Print the IP Address value as colon separated UInt16 hex values without leading zeroes.
+ // 20 01 0D B8 AC 10 FE 01 00 00 00 00 00 00 00 00
+ //
+ // IP Address:2001:DB8:AC10:FE01:0:0:0:0
+ for (int i = 0; i < ipAddressBytes.Length; i += 2)
+ {
+ segments.Add($":{ipAddressBytes[i] << 8 | ipAddressBytes[i + 1]:X}");
+ }
+ }
+ else
+ {
+ segments.Add(":<invalid>");
+ }
+
+ break;
+ case GeneralNameType.RegisteredId:
+ segments.Add("Registered ID:");
+ segments.Add(altNameReader.ReadOidAsString());
+ break;
+ default:
+ // A new extension to GeneralName could legitimately hit this,
+ // but it's correct to say that until we know what that is that
+ // the pretty-print has failed, and we should fall back to hex.
+ //
+ // But it could also simply be poorly encoded user data.
+ return null;
+ }
+ }
+
+ return string.Concat(segments);
+ }
+ catch (CryptographicException)
+ {
+ return null;
+ }
+ }
+ }
+}
diff --git a/src/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.OSX.cs b/src/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.OSX.cs
new file mode 100644
index 0000000000..ab9465349d
--- /dev/null
+++ b/src/System.Security.Cryptography.Encoding/src/Internal/Cryptography/OidLookup.OSX.cs
@@ -0,0 +1,67 @@
+// 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;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography;
+
+namespace Internal.Cryptography
+{
+ internal static partial class OidLookup
+ {
+ private static bool ShouldUseCache(OidGroup oidGroup)
+ {
+ return true;
+ }
+
+ private static string NativeOidToFriendlyName(string oid, OidGroup oidGroup, bool fallBackToAllGroups)
+ {
+ string friendlyName;
+
+ if (s_extraOidToFriendlyName.TryGetValue(oid, out friendlyName))
+ {
+ return friendlyName;
+ }
+
+ return null;
+ }
+
+ private static string NativeFriendlyNameToOid(string friendlyName, OidGroup oidGroup, bool fallBackToAllGroups)
+ {
+ string oid;
+
+ if (s_extraFriendlyNameToOid.TryGetValue(friendlyName, out oid))
+ {
+ return oid;
+ }
+
+ return null;
+ }
+
+ // -----------------------------
+ // ---- PAL layer ends here ----
+ // -----------------------------
+
+ // There are places inside the framework where Oid.FromFriendlyName is called
+ // (to pass in an OID group restriction for Windows) and an exception is not tolerated.
+ //
+ // The main place for this is X509Extension's internal ctor.
+ //
+ // These Name/OID pairs are not "universal", in that either Windows localizes it or Windows
+ // and OpenSSL produce different answers. Since the answers originally came from OpenSSL
+ // on macOS, this preserves the OpenSSL names.
+ private static readonly Dictionary<string, string> s_extraFriendlyNameToOid =
+ new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
+ {
+ { "X509v3 Subject Key Identifier", "2.5.29.14" },
+ { "X509v3 Key Usage", "2.5.29.15" },
+ { "X509v3 Basic Constraints", "2.5.29.19" },
+ { "X509v3 Extended Key Usage", "2.5.29.37" },
+ };
+
+ private static readonly Dictionary<string, string> s_extraOidToFriendlyName =
+ s_extraFriendlyNameToOid.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
+ }
+}
diff --git a/src/System.Security.Cryptography.Encoding/src/Resources/Strings.resx b/src/System.Security.Cryptography.Encoding/src/Resources/Strings.resx
index a09f02612e..b28f9896a0 100644
--- a/src/System.Security.Cryptography.Encoding/src/Resources/Strings.resx
+++ b/src/System.Security.Cryptography.Encoding/src/Resources/Strings.resx
@@ -82,7 +82,10 @@
<data name="Argument_InvalidValue" xml:space="preserve">
<value>Value was invalid.</value>
</data>
+ <data name="Cryptography_Der_Invalid_Encoding" xml:space="preserve">
+ <value>ASN1 corrupted data.</value>
+ </data>
<data name="ObjectDisposed_Generic" xml:space="preserve">
<value>Cannot access a disposed object.</value>
</data>
-</root> \ No newline at end of file
+</root>
diff --git a/src/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj b/src/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj
index 02d47f873b..dbe3c951c3 100644
--- a/src/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj
+++ b/src/System.Security.Cryptography.Encoding/src/System.Security.Cryptography.Encoding.csproj
@@ -6,6 +6,8 @@
<AssemblyName>System.Security.Cryptography.Encoding</AssemblyName>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-OSX-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-OSX-Release|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Unix-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Unix-Release|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Windows_NT-Debug|AnyCPU'" />
@@ -48,7 +50,7 @@
<Link>Common\Interop\Windows\Interop.Libraries.cs</Link>
</Compile>
</ItemGroup>
- <ItemGroup Condition=" '$(TargetsUnix)' == 'true' ">
+ <ItemGroup Condition=" '$(TargetsUnix)' == 'true' AND '$(TargetsOSX)' != 'true' ">
<Compile Include="Internal\Cryptography\AsnFormatter.Unix.cs" />
<Compile Include="Internal\Cryptography\OidLookup.Unix.cs" />
<Compile Include="Internal\Cryptography\OpenSslAsnFormatter.cs" />
@@ -86,6 +88,13 @@
<Link>Common\Microsoft\Win32\SafeHandles\X509ExtensionSafeHandles.Unix.cs</Link>
</Compile>
</ItemGroup>
+ <ItemGroup Condition=" '$(TargetsOSX)' == 'true' ">
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\DerSequenceReader.cs">
+ <Link>Common\System\Security\Cryptography\DerSequenceReader.cs</Link>
+ </Compile>
+ <Compile Include="Internal\Cryptography\AsnFormatter.OSX.cs" />
+ <Compile Include="Internal\Cryptography\OidLookup.OSX.cs" />
+ </ItemGroup>
<ItemGroup>
<Reference Include="System.Collections" />
<Reference Include="System.Collections.Concurrent" />
@@ -98,5 +107,9 @@
<Reference Include="System.Runtime.InteropServices" />
<Reference Include="System.Security.Cryptography.Primitives" />
</ItemGroup>
+ <ItemGroup Condition=" '$(TargetsOSX)' == 'true' ">
+ <Reference Include="System.Runtime.Numerics" />
+ <Reference Include="System.Threading" />
+ </ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
diff --git a/src/System.Security.Cryptography.Encoding/tests/AsnEncodedData.cs b/src/System.Security.Cryptography.Encoding/tests/AsnEncodedData.cs
index 0c5c29c2c2..6b86ed87da 100644
--- a/src/System.Security.Cryptography.Encoding/tests/AsnEncodedData.cs
+++ b/src/System.Security.Cryptography.Encoding/tests/AsnEncodedData.cs
@@ -4,6 +4,7 @@
using System.IO;
using System.Text;
+using Test.Cryptography;
using Xunit;
namespace System.Security.Cryptography.Encoding.Tests
@@ -66,5 +67,65 @@ namespace System.Security.Cryptography.Encoding.Tests
Assert.Equal(new[] { "example.org", "sub.example.org", "*.sub.example.org" }, output);
}
+
+ [Fact]
+ [PlatformSpecific(TestPlatforms.AnyUnix)]
+ public static void TestSubjectAlternativeName_Unix()
+ {
+ byte[] sanExtension = (
+ "3081D3A027060A2B0601040182371402" +
+ "03A0190C177375626A65637475706E31" +
+ "406578616D706C652E6F726781157361" +
+ "6E656D61696C31406578616D706C652E" +
+ "6F72678218646E73312E7375626A6563" +
+ "742E6578616D706C652E6F7267A30630" +
+ "0441027573A500863168747470733A2F" +
+ "2F7777772E6578616D706C652E6F7267" +
+ "2F706174682F746F2F612F7265736F75" +
+ "72636523616E63686F7287047F000001" +
+ "871020010DB8AC10FE01000000000000" +
+ "0000870F20010DB8AC10FE0100000000" +
+ "0000008704FFFFFFFF8704020F636488" +
+ "052901020203").HexToByteArray();
+
+ AsnEncodedData asnData = new AsnEncodedData(
+ new Oid("2.5.29.17"),
+ sanExtension);
+
+ string s = asnData.Format(false);
+
+ string expected = string.Join(
+ ", ",
+ // Choice[0]: OtherName
+ "othername:<unsupported>",
+ // Choice[1]: Rfc822Name (EmailAddress)
+ "email:sanemail1@example.org",
+ // Choice[2]: DnsName
+ "DNS:dns1.subject.example.org",
+ // Choice[3]: X400Name
+ "X400Name:<unsupported>",
+ // Skip Choice[4]: DirName
+ // (Supported by OpenSSL, but not by our Apple version)
+ // Choice[5]: EdiName
+ "EdiPartyName:<unsupported>",
+ // Choice[6]: URI
+ "URI:https://www.example.org/path/to/a/resource#anchor",
+ // Choice[7]: IPAddress (IPv4)
+ "IP Address:127.0.0.1",
+ // Choice[7]: IPAddress (IPv6)
+ "IP Address:2001:DB8:AC10:FE01:0:0:0:0",
+ // Choice[7]: IPAddress (unknown type)
+ "IP Address:<invalid>",
+ // Choice[7]: IPAddress (IPv4, longer string)
+ "IP Address:255.255.255.255",
+ // Choice[7]: IPAddress (IPv4, medium string)
+ // Note that between this, 127.0.0.1, and 255.255.255.255 all fields
+ // had both length-1 and length-3 (and some had length-2)
+ "IP Address:2.15.99.100",
+ // Choice[8]: RegisteredID
+ "Registered ID:1.1.1.2.2.3");
+
+ Assert.Equal(expected, s);
+ }
}
}
diff --git a/src/System.Security.Cryptography.Encoding/tests/Oid.cs b/src/System.Security.Cryptography.Encoding/tests/Oid.cs
index 60745d297a..baedd579a0 100644
--- a/src/System.Security.Cryptography.Encoding/tests/Oid.cs
+++ b/src/System.Security.Cryptography.Encoding/tests/Oid.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
+using System.Runtime.InteropServices;
using Xunit;
namespace System.Security.Cryptography.Encoding.Tests
@@ -263,8 +264,28 @@ namespace System.Security.Cryptography.Encoding.Tests
// This needs to be an OID not in the static lookup table. The purpose is to verify the
// NativeOidToFriendlyName fallback for Unix. For Windows this is accomplished by
// using FromOidValue with an OidGroup other than OidGroup.All.
-
- Oid oid = Oid.FromOidValue(ObsoleteSmime3desWrap_Oid, OidGroup.All);
+
+ Oid oid;
+
+ try
+ {
+ oid = Oid.FromOidValue(ObsoleteSmime3desWrap_Oid, OidGroup.All);
+ }
+ catch (CryptographicException)
+ {
+ bool isMac = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
+
+ Assert.True(isMac, "Exception is only raised on macOS");
+
+ if (isMac)
+ {
+ return;
+ }
+ else
+ {
+ throw;
+ }
+ }
Assert.Equal(ObsoleteSmime3desWrap_Oid, oid.Value);
Assert.Equal(ObsoleteSmime3desWrap_Name, oid.FriendlyName);
@@ -277,7 +298,27 @@ namespace System.Security.Cryptography.Encoding.Tests
// This needs to be a name not in the static lookup table. The purpose is to verify the
// NativeFriendlyNameToOid fallback for Unix. For Windows this is accomplished by
// using FromOidValue with an OidGroup other than OidGroup.All.
- Oid oid = Oid.FromFriendlyName(ObsoleteSmime3desWrap_Name, OidGroup.All);
+ Oid oid;
+
+ try
+ {
+ oid = Oid.FromFriendlyName(ObsoleteSmime3desWrap_Name, OidGroup.All);
+ }
+ catch (CryptographicException)
+ {
+ bool isMac = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
+
+ Assert.True(isMac, "Exception is only raised on macOS");
+
+ if (isMac)
+ {
+ return;
+ }
+ else
+ {
+ throw;
+ }
+ }
Assert.Equal(ObsoleteSmime3desWrap_Oid, oid.Value);
Assert.Equal(ObsoleteSmime3desWrap_Name, oid.FriendlyName);
diff --git a/src/System.Security.Cryptography.Encoding/tests/Resources/Strings.resx b/src/System.Security.Cryptography.Encoding/tests/Resources/Strings.resx
index fa1fac2d85..59c5bc4f97 100644
--- a/src/System.Security.Cryptography.Encoding/tests/Resources/Strings.resx
+++ b/src/System.Security.Cryptography.Encoding/tests/Resources/Strings.resx
@@ -61,6 +61,9 @@
<data name="Argument_InvalidOidValue" xml:space="preserve">
<value>The OID value was invalid.</value>
</data>
+ <data name="Cryptography_Der_Invalid_Encoding" xml:space="preserve">
+ <value>ASN1 corrupted data.</value>
+ </data>
<data name="Cryptography_Invalid_IA5String" xml:space="preserve">
<value>The string contains a character not in the 7 bit ASCII character set.</value>
</data>
diff --git a/src/System.Security.Cryptography.OpenSsl/src/Resources/Strings.resx b/src/System.Security.Cryptography.OpenSsl/src/Resources/Strings.resx
index 97e17b7a70..032549c48e 100644
--- a/src/System.Security.Cryptography.OpenSsl/src/Resources/Strings.resx
+++ b/src/System.Security.Cryptography.OpenSsl/src/Resources/Strings.resx
@@ -135,6 +135,9 @@
<data name="Cryptography_CurveNotSupported" xml:space="preserve">
<value>The specified curve '{0}' or its parameters are not valid for this platform.</value>
</data>
+ <data name="Cryptography_Der_Invalid_Encoding" xml:space="preserve">
+ <value>ASN1 corrupted data.</value>
+ </data>
<data name="Cryptography_InvalidDsaParameters_MissingFields" xml:space="preserve">
<value>The specified DSA parameters are not valid; P, Q, G and Y are all required.</value>
</data>
diff --git a/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj b/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj
index 8706ecd9f3..950e91ba78 100644
--- a/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj
+++ b/src/System.Security.Cryptography.OpenSsl/src/System.Security.Cryptography.OpenSsl.csproj
@@ -27,8 +27,8 @@
<Compile Include="System\Security\Cryptography\ECDsaOpenSsl.cs" />
<Compile Include="System\Security\Cryptography\RSAOpenSsl.cs" />
<Compile Include="System\Security\Cryptography\SafeEvpPKeyHandle.Unix.cs" />
- <Compile Include="$(CommonPath)\Internal\Cryptography\OpenSslAsymmetricAlgorithmCore.cs">
- <Link>Common\Internal\Cryptography\OpenSslAsymmetricAlgorithmCore.cs</Link>
+ <Compile Include="$(CommonPath)\Internal\Cryptography\AsymmetricAlgorithmHelpers.cs">
+ <Link>Common\Internal\Cryptography\AsymmetricAlgorithmHelpers.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\Interop.Libraries.cs">
<Link>Common\Interop\Unix\Interop.Libraries.cs</Link>
@@ -125,6 +125,7 @@
<Reference Include="System.Security.Cryptography.Algorithms" />
<Reference Include="System.Security.Cryptography.Encoding" />
<Reference Include="System.Security.Cryptography.Primitives" />
+ <Reference Include="System.Threading" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/System.Security.Cryptography.OpenSsl/tests/DsaOpenSslProvider.cs b/src/System.Security.Cryptography.OpenSsl/tests/DsaOpenSslProvider.cs
index 2cb1f09cf9..54b150e07e 100644
--- a/src/System.Security.Cryptography.OpenSsl/tests/DsaOpenSslProvider.cs
+++ b/src/System.Security.Cryptography.OpenSsl/tests/DsaOpenSslProvider.cs
@@ -16,13 +16,8 @@ namespace System.Security.Cryptography.Dsa.Tests
return new DSAOpenSsl(keySize);
}
- public bool SupportsFips186_3
- {
- get
- {
- return true;
- }
- }
+ public bool SupportsFips186_3 => true;
+ public bool SupportsKeyGeneration => true;
}
public partial class DSAFactory
diff --git a/src/System.Security.Cryptography.Primitives/System.Security.Cryptography.Primitives.sln b/src/System.Security.Cryptography.Primitives/System.Security.Cryptography.Primitives.sln
index 2de4bce9fa..c90a901aea 100644
--- a/src/System.Security.Cryptography.Primitives/System.Security.Cryptography.Primitives.sln
+++ b/src/System.Security.Cryptography.Primitives/System.Security.Cryptography.Primitives.sln
@@ -26,10 +26,10 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {101EB757-55A4-4F48-841C-C088640B8F57}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU
- {101EB757-55A4-4F48-841C-C088640B8F57}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU
- {101EB757-55A4-4F48-841C-C088640B8F57}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU
- {101EB757-55A4-4F48-841C-C088640B8F57}.Release|Any CPU.Build.0 = netcoreapp-Windows_NT-Release|Any CPU
+ {101EB757-55A4-4F48-841C-C088640B8F57}.Debug|Any CPU.ActiveCfg = netcoreapp-Debug|Any CPU
+ {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
{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
diff --git a/src/System.Security.Cryptography.Primitives/tests/Configurations.props b/src/System.Security.Cryptography.Primitives/tests/Configurations.props
index 162fbd464c..8b803e0772 100644
--- a/src/System.Security.Cryptography.Primitives/tests/Configurations.props
+++ b/src/System.Security.Cryptography.Primitives/tests/Configurations.props
@@ -2,8 +2,8 @@
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildConfigurations>
- netcoreapp-Windows_NT;;
- netstandard-Windows_NT;;
+ netcoreapp;
+ netstandard;
</BuildConfigurations>
</PropertyGroup>
</Project> \ No newline at end of file
diff --git a/src/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.csproj b/src/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.csproj
index 4fb97835d8..7152496bc0 100644
--- a/src/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.csproj
+++ b/src/System.Security.Cryptography.Primitives/tests/System.Security.Cryptography.Primitives.Tests.csproj
@@ -6,10 +6,10 @@
<ProjectGuid>{101EB757-55A4-4F48-841C-C088640B8F57}</ProjectGuid>
<DefineConstants Condition="'$(TargetGroup)' == 'netcoreapp'">$(DefineConstants);netcoreapp</DefineConstants>
</PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Windows_NT-Debug|AnyCPU'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Windows_NT-Release|AnyCPU'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Windows_NT-Debug|AnyCPU'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Windows_NT-Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="AsymmetricAlgorithm\Trivial.cs" />
<Compile Include="CryptoStream.cs" />
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Configurations.props b/src/System.Security.Cryptography.X509Certificates/src/Configurations.props
index 62eb1c4776..05d3ab66c3 100644
--- a/src/System.Security.Cryptography.X509Certificates/src/Configurations.props
+++ b/src/System.Security.Cryptography.X509Certificates/src/Configurations.props
@@ -2,9 +2,10 @@
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildConfigurations>
+ netcoreapp-OSX;
netcoreapp-Unix;
netcoreapp-Windows_NT;
uap-Windows_NT;
</BuildConfigurations>
</PropertyGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Oids.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Oids.cs
index 4e489503e5..69b30bc603 100644
--- a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Oids.cs
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Oids.cs
@@ -15,8 +15,13 @@ namespace Internal.Cryptography
//
internal static class Oids
{
+ public const string CommonName = "2.5.4.3";
+ public const string Organization = "2.5.4.10";
+ public const string OrganizationalUnit = "2.5.4.11";
public const string BasicConstraints = "2.5.29.10";
public const string SubjectKeyIdentifier = "2.5.29.14";
+ public const string SubjectAltName = "2.5.29.17";
+ public const string IssuerAltName = "2.5.29.18";
public const string KeyUsage = "2.5.29.15";
public const string BasicConstraints2 = "2.5.29.19";
public const string CrlDistributionPoints = "2.5.29.31";
@@ -31,6 +36,7 @@ namespace Internal.Cryptography
public const string DsaDsa = "1.2.840.10040.4.1";
public const string EmailAddress = "1.2.840.113549.1.9.1";
public const string EnrollCertTypeExtension = "1.3.6.1.4.1.311.20.2";
+ public const string UserPrincipalName = "1.3.6.1.4.1.311.20.2.3";
public const string CertificateTemplate = "1.3.6.1.4.1.311.21.7";
public const string ApplicationCertPolicies = "1.3.6.1.4.1.311.21.10";
public const string AuthorityInformationAccess = "1.3.6.1.5.5.7.1.1";
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/CertificatePal.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/CertificatePal.cs
new file mode 100644
index 0000000000..f2a383a052
--- /dev/null
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/CertificatePal.cs
@@ -0,0 +1,930 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Apple;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+
+namespace Internal.Cryptography.Pal
+{
+ internal sealed partial class CertificatePal
+ {
+ public static ICertificatePal FromHandle(IntPtr handle)
+ {
+ return FromHandle(handle, true);
+ }
+
+ internal static ICertificatePal FromHandle(IntPtr handle, bool throwOnFail)
+ {
+ if (handle == IntPtr.Zero)
+ throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle));
+
+ SafeSecCertificateHandle certHandle;
+ SafeSecIdentityHandle identityHandle;
+
+ if (Interop.AppleCrypto.X509DemuxAndRetainHandle(handle, out certHandle, out identityHandle))
+ {
+ Debug.Assert(
+ certHandle.IsInvalid != identityHandle.IsInvalid,
+ $"certHandle.IsInvalid ({certHandle.IsInvalid}) should differ from identityHandle.IsInvalid ({identityHandle.IsInvalid})");
+
+ if (certHandle.IsInvalid)
+ {
+ certHandle.Dispose();
+ return new AppleCertificatePal(identityHandle);
+ }
+
+ identityHandle.Dispose();
+ return new AppleCertificatePal(certHandle);
+ }
+
+ certHandle.Dispose();
+ identityHandle.Dispose();
+
+ if (throwOnFail)
+ {
+ throw new ArgumentException(SR.Arg_InvalidHandle, nameof(handle));
+ }
+
+ return null;
+ }
+
+ public static ICertificatePal FromOtherCert(X509Certificate cert)
+ {
+ Debug.Assert(cert.Pal != null);
+
+ return FromHandle(cert.Handle);
+ }
+
+ public static ICertificatePal FromBlob(
+ byte[] rawData,
+ SafePasswordHandle password,
+ X509KeyStorageFlags keyStorageFlags)
+ {
+ Debug.Assert(password != null);
+
+ X509ContentType contentType = X509Certificate2.GetCertContentType(rawData);
+
+ if (contentType == X509ContentType.Pkcs7)
+ {
+ // In single mode for a PKCS#7 signed or signed-and-enveloped file we're supposed to return
+ // the certificate which signed the PKCS#7 file.
+ //
+ // X509Certificate2Collection::Export(X509ContentType.Pkcs7) claims to be a signed PKCS#7,
+ // but doesn't emit a signature block. So this is hard to test.
+ //
+ // TODO(2910): Figure out how to extract the signing certificate, when it's present.
+ throw new CryptographicException(SR.Cryptography_X509_PKCS7_NoSigner);
+ }
+
+ bool exportable = true;
+
+ SafeKeychainHandle keychain;
+
+ if (contentType == X509ContentType.Pkcs12)
+ {
+ if ((keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) == X509KeyStorageFlags.EphemeralKeySet)
+ {
+ throw new PlatformNotSupportedException(SR.Cryptography_X509_NoEphemeralPfx);
+ }
+
+ exportable = (keyStorageFlags & X509KeyStorageFlags.Exportable) == X509KeyStorageFlags.Exportable;
+
+ bool persist =
+ (keyStorageFlags & X509KeyStorageFlags.PersistKeySet) == X509KeyStorageFlags.PersistKeySet;
+
+ keychain = persist
+ ? Interop.AppleCrypto.SecKeychainCopyDefault()
+ : Interop.AppleCrypto.CreateTemporaryKeychain();
+ }
+ else
+ {
+ keychain = SafeTemporaryKeychainHandle.InvalidHandle;
+ password = SafePasswordHandle.InvalidHandle;
+ }
+
+ using (keychain)
+ {
+ SafeSecIdentityHandle identityHandle;
+ SafeSecCertificateHandle certHandle = Interop.AppleCrypto.X509ImportCertificate(
+ rawData,
+ contentType,
+ password,
+ keychain,
+ exportable,
+ out identityHandle);
+
+ if (identityHandle.IsInvalid)
+ {
+ identityHandle.Dispose();
+ return new AppleCertificatePal(certHandle);
+ }
+
+ if (contentType != X509ContentType.Pkcs12)
+ {
+ Debug.Fail("Non-PKCS12 import produced an identity handle");
+
+ identityHandle.Dispose();
+ certHandle.Dispose();
+ throw new CryptographicException();
+ }
+
+ Debug.Assert(certHandle.IsInvalid);
+ certHandle.Dispose();
+ return new AppleCertificatePal(identityHandle);
+ }
+ }
+
+ public static ICertificatePal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
+ {
+ Debug.Assert(password != null);
+
+ byte[] fileBytes = System.IO.File.ReadAllBytes(fileName);
+ return FromBlob(fileBytes, password, keyStorageFlags);
+ }
+ }
+
+ internal sealed class AppleCertificatePal : ICertificatePal
+ {
+ private SafeSecIdentityHandle _identityHandle;
+ private SafeSecCertificateHandle _certHandle;
+ private CertificateData _certData;
+ private bool _readCertData;
+
+ internal AppleCertificatePal(SafeSecCertificateHandle certHandle)
+ {
+ Debug.Assert(!certHandle.IsInvalid);
+
+ _certHandle = certHandle;
+ }
+
+ internal AppleCertificatePal(SafeSecIdentityHandle identityHandle)
+ {
+ Debug.Assert(!identityHandle.IsInvalid);
+
+ _identityHandle = identityHandle;
+ _certHandle = Interop.AppleCrypto.X509GetCertFromIdentity(identityHandle);
+ }
+
+ public void Dispose()
+ {
+ _certHandle?.Dispose();
+ _identityHandle?.Dispose();
+
+ _certHandle = null;
+ _identityHandle = null;
+ }
+
+ internal SafeSecCertificateHandle CertificateHandle => _certHandle;
+ internal SafeSecIdentityHandle IdentityHandle => _identityHandle;
+
+ public bool HasPrivateKey => !(_identityHandle?.IsInvalid ?? true);
+
+ public IntPtr Handle
+ {
+ get
+ {
+ if (HasPrivateKey)
+ {
+ return _identityHandle.DangerousGetHandle();
+ }
+
+ return _certHandle?.DangerousGetHandle() ?? IntPtr.Zero;
+ }
+ }
+
+
+ public string Issuer => IssuerName.Name;
+
+ public string Subject => SubjectName.Name;
+
+ public string KeyAlgorithm
+ {
+ get
+ {
+ EnsureCertData();
+ return _certData.PublicKeyAlgorithm.AlgorithmId;
+ }
+ }
+
+ public byte[] KeyAlgorithmParameters
+ {
+ get
+ {
+ EnsureCertData();
+ return _certData.PublicKeyAlgorithm.Parameters;
+ }
+ }
+
+ public byte[] PublicKeyValue
+ {
+ get
+ {
+ EnsureCertData();
+ return _certData.PublicKey;
+ }
+ }
+
+ public byte[] SerialNumber
+ {
+ get
+ {
+ EnsureCertData();
+ byte[] serial = _certData.SerialNumber;
+ Array.Reverse(serial);
+ return serial;
+ }
+ }
+
+ public string SignatureAlgorithm
+ {
+ get
+ {
+ EnsureCertData();
+ return _certData.SignatureAlgorithm.AlgorithmId;
+ }
+ }
+
+ public string FriendlyName
+ {
+ get { return ""; }
+ set
+ {
+ throw new PlatformNotSupportedException(
+ SR.Format(SR.Cryptography_Unix_X509_PropertyNotSettable, nameof(FriendlyName)));
+ }
+ }
+
+ public int Version
+ {
+ get
+ {
+ EnsureCertData();
+ return _certData.Version + 1;
+ }
+ }
+
+ public X500DistinguishedName SubjectName
+ {
+ get
+ {
+ EnsureCertData();
+ return _certData.Subject;
+ }
+ }
+
+ public X500DistinguishedName IssuerName
+ {
+ get
+ {
+ EnsureCertData();
+ return _certData.Issuer;
+ }
+ }
+
+ public IEnumerable<X509Extension> Extensions {
+ get
+ {
+ EnsureCertData();
+ return _certData.Extensions;
+ }
+ }
+
+ public byte[] RawData
+ {
+ get
+ {
+ EnsureCertData();
+ return _certData.RawData;
+ }
+ }
+
+ public DateTime NotAfter
+ {
+ get
+ {
+ EnsureCertData();
+ return _certData.NotAfter.ToLocalTime();
+ }
+ }
+
+ public DateTime NotBefore
+ {
+ get
+ {
+ EnsureCertData();
+ return _certData.NotBefore.ToLocalTime();
+ }
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350",
+ Justification = "SHA1 is required for Compat")]
+ public byte[] Thumbprint
+ {
+ get
+ {
+ EnsureCertData();
+
+ using (SHA1 hash = SHA1.Create())
+ {
+ return hash.ComputeHash(_certData.RawData);
+ }
+ }
+ }
+
+ public bool Archived
+ {
+ get { return false; }
+ set
+ {
+ throw new PlatformNotSupportedException(
+ SR.Format(SR.Cryptography_Unix_X509_PropertyNotSettable, nameof(Archived)));
+ }
+ }
+
+ public byte[] SubjectPublicKeyInfo
+ {
+ get
+ {
+ EnsureCertData();
+
+ return _certData.SubjectPublicKeyInfo;
+ }
+ }
+
+ public AsymmetricAlgorithm GetPrivateKey()
+ {
+ switch (KeyAlgorithm)
+ {
+ case Oids.RsaRsa:
+ return GetRSAPrivateKey();
+ case Oids.DsaDsa:
+ return GetDSAPrivateKey();
+ case Oids.Ecc:
+ return GetECDsaPrivateKey();
+ }
+
+ throw new NotSupportedException(SR.NotSupported_KeyAlgorithm);
+ }
+
+ public RSA GetRSAPrivateKey()
+ {
+ if (_identityHandle == null)
+ return null;
+
+ Debug.Assert(!_identityHandle.IsInvalid);
+ SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle);
+ SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle);
+
+ return new RSAImplementation.RSASecurityTransforms(publicKey, privateKey);
+ }
+
+ public DSA GetDSAPrivateKey()
+ {
+ if (_identityHandle == null)
+ return null;
+
+ Debug.Assert(!_identityHandle.IsInvalid);
+ SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle);
+ SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle);
+
+ return new DSAImplementation.DSASecurityTransforms(publicKey, privateKey);
+ }
+
+ public ECDsa GetECDsaPrivateKey()
+ {
+ if (_identityHandle == null)
+ return null;
+
+ Debug.Assert(!_identityHandle.IsInvalid);
+ SafeSecKeyRefHandle publicKey = Interop.AppleCrypto.X509GetPublicKey(_certHandle);
+ SafeSecKeyRefHandle privateKey = Interop.AppleCrypto.X509GetPrivateKeyFromIdentity(_identityHandle);
+
+ return new ECDsaImplementation.ECDsaSecurityTransforms(publicKey, privateKey);
+ }
+
+ public string GetNameInfo(X509NameType nameType, bool forIssuer)
+ {
+ // Algorithm behaviors (pseudocode). When forIssuer is true, replace "Subject" with "Issuer" and
+ // SAN (Subject Alternative Names) with IAN (Issuer Alternative Names).
+ //
+ // SimpleName: Subject[CN] ?? Subject[OU] ?? Subject[O] ?? Subject[E] ?? Subject.Rdns.FirstOrDefault() ??
+ // SAN.Entries.FirstOrDefault(type == GEN_EMAIL);
+ // EmailName: SAN.Entries.FirstOrDefault(type == GEN_EMAIL) ?? Subject[E];
+ // UpnName: SAN.Entries.FirsOrDefaultt(type == GEN_OTHER && entry.AsOther().OID == szOidUpn).AsOther().Value;
+ // DnsName: SAN.Entries.FirstOrDefault(type == GEN_DNS) ?? Subject[CN];
+ // DnsFromAlternativeName: SAN.Entries.FirstOrDefault(type == GEN_DNS);
+ // UrlName: SAN.Entries.FirstOrDefault(type == GEN_URI);
+
+ EnsureCertData();
+
+ if (nameType == X509NameType.SimpleName)
+ {
+ X500DistinguishedName name = forIssuer ? _certData.Issuer : _certData.Subject;
+ string candidate = GetSimpleNameInfo(name);
+
+ if (candidate != null)
+ {
+ return candidate;
+ }
+ }
+
+ // Check the Subject Alternative Name (or Issuer Alternative Name) for the right value;
+ {
+ string extensionId = forIssuer ? Oids.IssuerAltName : Oids.SubjectAltName;
+ GeneralNameType? matchType = null;
+ string otherOid = null;
+
+ // Currently all X509NameType types have a path where they look at the SAN/IAN,
+ // but we need to figure out which kind they want.
+ switch (nameType)
+ {
+ case X509NameType.DnsName:
+ case X509NameType.DnsFromAlternativeName:
+ matchType = GeneralNameType.DnsName;
+ break;
+ case X509NameType.SimpleName:
+ case X509NameType.EmailName:
+ matchType = GeneralNameType.Email;
+ break;
+ case X509NameType.UpnName:
+ matchType = GeneralNameType.OtherName;
+ otherOid = Oids.UserPrincipalName;
+ break;
+ case X509NameType.UrlName:
+ matchType = GeneralNameType.UniformResourceIdentifier;
+ break;
+ }
+
+ if (matchType.HasValue)
+ {
+ foreach (X509Extension extension in _certData.Extensions)
+ {
+ if (extension.Oid.Value == extensionId)
+ {
+ string candidate = FindAltNameMatch(extension.RawData, matchType.Value, otherOid);
+
+ if (candidate != null)
+ {
+ return candidate;
+ }
+ }
+ }
+ }
+ else
+ {
+ Debug.Fail($"Unresolved matchType for X509NameType.{nameType}");
+ }
+ }
+
+ // Subject-based fallback
+ {
+ string expectedKey = null;
+
+ switch (nameType)
+ {
+ case X509NameType.EmailName:
+ expectedKey = Oids.EmailAddress;
+ break;
+ case X509NameType.DnsName:
+ // Note: This does not include DnsFromAlternativeName, since
+ // the subject (or issuer) is not the Alternative Name.
+ expectedKey = Oids.CommonName;
+ break;
+ }
+
+ if (expectedKey != null)
+ {
+ X500DistinguishedName name = forIssuer ? _certData.Issuer : _certData.Subject;
+
+ foreach (var kvp in ReadReverseRdns(name))
+ {
+ if (kvp.Key == expectedKey)
+ {
+ return kvp.Value;
+ }
+ }
+ }
+ }
+
+ return "";
+ }
+
+ private static string GetSimpleNameInfo(X500DistinguishedName name)
+ {
+ string ou = null;
+ string o = null;
+ string e = null;
+ string firstRdn = null;
+
+ foreach (var kvp in ReadReverseRdns(name))
+ {
+ string oid = kvp.Key;
+ string value = kvp.Value;
+
+ // TODO: Check this (and the OpenSSL-using version) if OU/etc are specified more than once.
+ // (Compare against Windows)
+ switch (oid)
+ {
+ case Oids.CommonName:
+ return value;
+ case Oids.OrganizationalUnit:
+ ou = value;
+ break;
+ case Oids.Organization:
+ o = value;
+ break;
+ case Oids.EmailAddress:
+ e = value;
+ break;
+ default:
+ if (firstRdn == null)
+ {
+ firstRdn = value;
+ }
+
+ break;
+ }
+ }
+
+ return ou ?? o ?? e ?? firstRdn;
+ }
+
+ private static string FindAltNameMatch(byte[] extensionBytes, GeneralNameType matchType, string otherOid)
+ {
+ // If Other, have OID, else, no OID.
+ Debug.Assert(
+ (otherOid == null) == (matchType != GeneralNameType.OtherName),
+ $"otherOid has incorrect nullarity for matchType {matchType}");
+
+ Debug.Assert(
+ matchType == GeneralNameType.UniformResourceIdentifier ||
+ matchType == GeneralNameType.DnsName ||
+ matchType == GeneralNameType.Email ||
+ matchType == GeneralNameType.OtherName,
+ $"matchType ({matchType}) is not currently supported");
+
+ Debug.Assert(
+ otherOid == null || otherOid == Oids.UserPrincipalName,
+ $"otherOid ({otherOid}) is not supported");
+
+ // SubjectAltName ::= GeneralNames
+ //
+ // IssuerAltName ::= GeneralNames
+ //
+ // GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+ //
+ // GeneralName ::= CHOICE {
+ // otherName [0] OtherName,
+ // rfc822Name [1] IA5String,
+ // dNSName [2] IA5String,
+ // x400Address [3] ORAddress,
+ // directoryName [4] Name,
+ // ediPartyName [5] EDIPartyName,
+ // uniformResourceIdentifier [6] IA5String,
+ // iPAddress [7] OCTET STRING,
+ // registeredID [8] OBJECT IDENTIFIER }
+ //
+ // OtherName::= SEQUENCE {
+ // type - id OBJECT IDENTIFIER,
+ // value[0] EXPLICIT ANY DEFINED BY type - id }
+
+ byte expectedTag = (byte)(DerSequenceReader.ContextSpecificTagFlag | (byte)matchType);
+
+ if (matchType == GeneralNameType.OtherName)
+ {
+ expectedTag |= DerSequenceReader.ConstructedFlag;
+ }
+
+ DerSequenceReader altNameReader = new DerSequenceReader(extensionBytes);
+
+ while (altNameReader.HasData)
+ {
+ if (altNameReader.PeekTag() != expectedTag)
+ {
+ altNameReader.SkipValue();
+ continue;
+ }
+
+ switch (matchType)
+ {
+ case GeneralNameType.OtherName:
+ {
+ DerSequenceReader otherNameReader = altNameReader.ReadSequence();
+ string oid = otherNameReader.ReadOidAsString();
+
+ if (oid == otherOid)
+ {
+ // Payload is value[0] EXPLICIT, meaning
+ // a) it'll be tagged as ContextSpecific0
+ // b) that's interpretable as a Sequence (EXPLICIT)
+ // c) the payload will then be retagged as the correct type (EXPLICIT)
+ if (otherNameReader.PeekTag() != DerSequenceReader.ContextSpecificConstructedTag0)
+ {
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
+
+ otherNameReader = otherNameReader.ReadSequence();
+
+ // Currently only UPN is supported, which is a UTF8 string per
+ // https://msdn.microsoft.com/en-us/library/ff842518.aspx
+ return otherNameReader.ReadUtf8String();
+ }
+
+ // If the OtherName OID didn't match, move to the next entry.
+ continue;
+ }
+ case GeneralNameType.Rfc822Name:
+ case GeneralNameType.DnsName:
+ case GeneralNameType.UniformResourceIdentifier:
+ return altNameReader.ReadIA5String();
+ default:
+ altNameReader.SkipValue();
+ continue;
+ }
+ }
+
+ return null;
+ }
+
+ public void AppendPrivateKeyInfo(StringBuilder sb)
+ {
+ if (!HasPrivateKey)
+ {
+ return;
+ }
+
+ // There's nothing really to say about the key, just acknowledge there is one.
+ sb.AppendLine();
+ sb.AppendLine();
+ sb.AppendLine("[Private Key]");
+ }
+
+ private void EnsureCertData()
+ {
+ if (_readCertData)
+ return;
+
+ Debug.Assert(!_certHandle.IsInvalid);
+ _certData = new CertificateData(Interop.AppleCrypto.X509GetRawData(_certHandle));
+ _readCertData = true;
+ }
+
+ private static IEnumerable<KeyValuePair<string, string>> ReadReverseRdns(X500DistinguishedName name)
+ {
+ DerSequenceReader x500NameReader = new DerSequenceReader(name.RawData);
+ var rdnReaders = new Stack<DerSequenceReader>();
+
+ while (x500NameReader.HasData)
+ {
+ rdnReaders.Push(x500NameReader.ReadSet());
+ }
+
+
+ while (rdnReaders.Count > 0)
+ {
+ DerSequenceReader rdnReader = rdnReaders.Pop();
+
+ while (rdnReader.HasData)
+ {
+ DerSequenceReader tavReader = rdnReader.ReadSequence();
+ string oid = tavReader.ReadOidAsString();
+
+ var tag = (DerSequenceReader.DerTag)tavReader.PeekTag();
+ string value = null;
+
+ switch (tag)
+ {
+ case DerSequenceReader.DerTag.BMPString:
+ value = tavReader.ReadBMPString();
+ break;
+ case DerSequenceReader.DerTag.IA5String:
+ value = tavReader.ReadIA5String();
+ break;
+ case DerSequenceReader.DerTag.PrintableString:
+ value = tavReader.ReadPrintableString();
+ break;
+ case DerSequenceReader.DerTag.UTF8String:
+ value = tavReader.ReadUtf8String();
+ break;
+
+ // Ignore anything we don't know how to read.
+ }
+
+ if (value != null)
+ {
+ yield return new KeyValuePair<string, string>(oid, value);
+ }
+ }
+ }
+ }
+ }
+
+ internal enum GeneralNameType
+ {
+ OtherName = 0,
+ Rfc822Name = 1,
+ // RFC 822: Standard for the format of ARPA Internet Text Messages.
+ // That means "email", and an RFC 822 Name: "Email address"
+ Email = Rfc822Name,
+ DnsName = 2,
+ X400Address = 3,
+ DirectoryName = 4,
+ EdiPartyName = 5,
+ UniformResourceIdentifier = 6,
+ IPAddress = 7,
+ RegisteredId = 8,
+ }
+
+ internal struct CertificateData
+ {
+ internal struct AlgorithmIdentifier
+ {
+ internal string AlgorithmId;
+ internal byte[] Parameters;
+ }
+
+ internal byte[] RawData;
+ internal byte[] SubjectPublicKeyInfo;
+
+ internal int Version;
+ internal byte[] SerialNumber;
+ internal AlgorithmIdentifier TbsSignature;
+ internal X500DistinguishedName Issuer;
+ internal DateTime NotBefore;
+ internal DateTime NotAfter;
+ internal X500DistinguishedName Subject;
+ internal AlgorithmIdentifier PublicKeyAlgorithm;
+ internal byte[] PublicKey;
+ internal byte[] IssuerUniqueId;
+ internal byte[] SubjectUniqueId;
+ internal List<X509Extension> Extensions;
+ internal AlgorithmIdentifier SignatureAlgorithm;
+ internal byte[] SignatureValue;
+
+ internal CertificateData(byte[] rawData)
+ {
+#if DEBUG
+ try
+ {
+#endif
+ DerSequenceReader reader = new DerSequenceReader(rawData);
+
+ DerSequenceReader tbsCertificate = reader.ReadSequence();
+
+ if (tbsCertificate.PeekTag() == DerSequenceReader.ContextSpecificConstructedTag0)
+ {
+ DerSequenceReader version = tbsCertificate.ReadSequence();
+ Version = version.ReadInteger();
+ }
+ else if (tbsCertificate.PeekTag() != (byte)DerSequenceReader.DerTag.Integer)
+ {
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
+ else
+ {
+ Version = 0;
+ }
+
+ if (Version < 0 || Version > 2)
+ throw new CryptographicException();
+
+ SerialNumber = tbsCertificate.ReadIntegerBytes();
+
+ DerSequenceReader tbsSignature = tbsCertificate.ReadSequence();
+ TbsSignature.AlgorithmId = tbsSignature.ReadOidAsString();
+ TbsSignature.Parameters = tbsSignature.HasData ? tbsSignature.ReadNextEncodedValue() : Array.Empty<byte>();
+
+ if (tbsSignature.HasData)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
+ Issuer = new X500DistinguishedName(tbsCertificate.ReadNextEncodedValue());
+
+ DerSequenceReader validity = tbsCertificate.ReadSequence();
+ NotBefore = validity.ReadX509Date();
+ NotAfter = validity.ReadX509Date();
+
+ if (validity.HasData)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
+ Subject = new X500DistinguishedName(tbsCertificate.ReadNextEncodedValue());
+
+ SubjectPublicKeyInfo = tbsCertificate.ReadNextEncodedValue();
+ DerSequenceReader subjectPublicKeyInfo = new DerSequenceReader(SubjectPublicKeyInfo);
+ DerSequenceReader subjectKeyAlgorithm = subjectPublicKeyInfo.ReadSequence();
+ PublicKeyAlgorithm.AlgorithmId = subjectKeyAlgorithm.ReadOidAsString();
+ PublicKeyAlgorithm.Parameters = subjectKeyAlgorithm.HasData ? subjectKeyAlgorithm.ReadNextEncodedValue() : Array.Empty<byte>();
+
+ if (subjectKeyAlgorithm.HasData)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
+ PublicKey = subjectPublicKeyInfo.ReadBitString();
+
+ if (subjectPublicKeyInfo.HasData)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
+ if (Version > 0 &&
+ tbsCertificate.HasData &&
+ tbsCertificate.PeekTag() == DerSequenceReader.ContextSpecificConstructedTag1)
+ {
+ IssuerUniqueId = tbsCertificate.ReadBitString();
+ }
+ else
+ {
+ IssuerUniqueId = null;
+ }
+
+ if (Version > 0 &&
+ tbsCertificate.HasData &&
+ tbsCertificate.PeekTag() == DerSequenceReader.ContextSpecificConstructedTag2)
+ {
+ SubjectUniqueId = tbsCertificate.ReadBitString();
+ }
+ else
+ {
+ SubjectUniqueId = null;
+ }
+
+ Extensions = new List<X509Extension>();
+
+ if (Version > 1 &&
+ tbsCertificate.HasData &&
+ tbsCertificate.PeekTag() == DerSequenceReader.ContextSpecificConstructedTag3)
+ {
+ DerSequenceReader extensions = tbsCertificate.ReadSequence();
+ extensions = extensions.ReadSequence();
+
+ while (extensions.HasData)
+ {
+ DerSequenceReader extensionReader = extensions.ReadSequence();
+ string oid = extensionReader.ReadOidAsString();
+ bool critical = false;
+
+ if (extensionReader.PeekTag() == (byte)DerSequenceReader.DerTag.Boolean)
+ {
+ critical = extensionReader.ReadBoolean();
+ }
+
+ byte[] extensionData = extensionReader.ReadOctetString();
+
+ Extensions.Add(new X509Extension(oid, extensionData, critical));
+
+ if (extensionReader.HasData)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
+ }
+
+ if (tbsCertificate.HasData)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
+ DerSequenceReader signatureAlgorithm = reader.ReadSequence();
+ SignatureAlgorithm.AlgorithmId = signatureAlgorithm.ReadOidAsString();
+ SignatureAlgorithm.Parameters = signatureAlgorithm.HasData ? signatureAlgorithm.ReadNextEncodedValue() : Array.Empty<byte>();
+
+ if (signatureAlgorithm.HasData)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
+ SignatureValue = reader.ReadBitString();
+
+ if (reader.HasData)
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+
+ RawData = rawData;
+#if DEBUG
+ }
+ catch (Exception e)
+ {
+ throw new CryptographicException(
+ $"Error in reading certificate:{Environment.NewLine}{PemPrintCert(rawData)}",
+ e);
+ }
+#endif
+ }
+
+#if DEBUG
+ private static string PemPrintCert(byte[] rawData)
+ {
+ const string PemHeader = "-----BEGIN CERTIFICATE-----";
+ const string PemFooter = "-----END CERTIFICATE-----";
+
+ StringBuilder builder = new StringBuilder(PemHeader.Length + PemFooter.Length + rawData.Length * 2);
+ builder.Append(PemHeader);
+ builder.AppendLine();
+
+ builder.Append(Convert.ToBase64String(rawData, Base64FormattingOptions.InsertLineBreaks));
+ builder.AppendLine();
+
+ builder.Append(PemFooter);
+ builder.AppendLine();
+
+ return builder.ToString();
+ }
+#endif
+ }
+}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ChainPal.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ChainPal.cs
new file mode 100644
index 0000000000..eace2ffbc2
--- /dev/null
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/ChainPal.cs
@@ -0,0 +1,522 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using Microsoft.Win32.SafeHandles;
+
+namespace Internal.Cryptography.Pal
+{
+ internal sealed class SecTrustChainPal : IChainPal
+ {
+ private Stack<SafeHandle> _extraHandles;
+ private SafeX509ChainHandle _chainHandle;
+ public X509ChainElement[] ChainElements { get; private set; }
+ public X509ChainStatus[] ChainStatus { get; private set; }
+ private DateTime _verificationTime;
+
+ internal SecTrustChainPal()
+ {
+ _extraHandles = new Stack<SafeHandle>();
+ }
+
+ public SafeX509ChainHandle SafeHandle => null;
+
+ internal void OpenTrustHandle(
+ ICertificatePal leafCert,
+ X509Certificate2Collection extraStore,
+ bool checkRevocation)
+ {
+ SafeCreateHandle policiesArray = PreparePoliciesArray(checkRevocation);
+ SafeCreateHandle certsArray = PrepareCertsArray(leafCert, extraStore);
+
+ int osStatus;
+
+ SafeX509ChainHandle chain;
+ int ret = Interop.AppleCrypto.AppleCryptoNative_X509ChainCreate(
+ certsArray,
+ policiesArray,
+ out chain,
+ out osStatus);
+
+ if (ret == 1)
+ {
+ _chainHandle = chain;
+ return;
+ }
+
+ chain.Dispose();
+
+ if (ret == 0)
+ {
+ throw Interop.AppleCrypto.CreateExceptionForOSStatus(osStatus);
+ }
+
+ Debug.Fail($"AppleCryptoNative_X509ChainCreate returned unexpected return value {ret}");
+ throw new CryptographicException();
+ }
+
+ public void Dispose()
+ {
+ if (_extraHandles == null)
+ return;
+
+ Stack<SafeHandle> extraHandles = _extraHandles;
+ _extraHandles = null;
+
+ _chainHandle?.Dispose();
+
+ while (extraHandles.Count > 0)
+ {
+ extraHandles.Pop().Dispose();
+ }
+ }
+
+ public bool? Verify(X509VerificationFlags flags, out Exception exception)
+ {
+ exception = null;
+
+ return ChainVerifier.Verify(ChainElements, flags);
+ }
+
+ private SafeCreateHandle PreparePoliciesArray(bool checkRevocation)
+ {
+ IntPtr[] policies = new IntPtr[checkRevocation ? 2 : 1];
+
+ SafeHandle defaultPolicy = Interop.AppleCrypto.X509ChainCreateDefaultPolicy();
+
+ if (defaultPolicy.IsInvalid)
+ {
+ defaultPolicy.Dispose();
+ throw new PlatformNotSupportedException(nameof(X509Chain));
+ }
+
+ _extraHandles.Push(defaultPolicy);
+ policies[0] = defaultPolicy.DangerousGetHandle();
+
+ if (checkRevocation)
+ {
+ SafeHandle revPolicy = Interop.AppleCrypto.X509ChainCreateRevocationPolicy();
+ _extraHandles.Push(revPolicy);
+ policies[1] = revPolicy.DangerousGetHandle();
+ }
+
+ SafeCreateHandle policiesArray =
+ Interop.CoreFoundation.CFArrayCreate(policies, (UIntPtr)policies.Length);
+
+ _extraHandles.Push(policiesArray);
+ return policiesArray;
+ }
+
+ private SafeCreateHandle PrepareCertsArray(ICertificatePal cert, X509Certificate2Collection extraStore)
+ {
+ IntPtr[] ptrs = new IntPtr[1 + (extraStore?.Count ?? 0)];
+ SafeHandle[] safeHandles = new SafeHandle[ptrs.Length];
+
+ AppleCertificatePal applePal = (AppleCertificatePal)cert;
+
+ safeHandles[0] = applePal.CertificateHandle;
+
+ if (extraStore != null)
+ {
+ for (int i = 0; i < extraStore.Count; i++)
+ {
+ AppleCertificatePal extraCertPal = (AppleCertificatePal)extraStore[i].Pal;
+
+ safeHandles[i + 1] = extraCertPal.CertificateHandle;
+ }
+ }
+
+ int idx = 0;
+ bool addedRef = false;
+
+ try
+ {
+ for (idx = 0; idx < safeHandles.Length; idx++)
+ {
+ SafeHandle handle = safeHandles[idx];
+ handle.DangerousAddRef(ref addedRef);
+ ptrs[idx] = handle.DangerousGetHandle();
+ }
+ }
+ catch
+ {
+ // If any DangerousAddRef failed, idx will be on the one that failed, so we'll start off
+ // by subtracing one.
+ for (idx--; idx >= 0; idx--)
+ {
+ safeHandles[idx].DangerousRelease();
+ }
+
+ throw;
+ }
+
+ // Creating the array has the effect of calling CFRetain() on all of the pointers, so the native
+ // resource is safe even if we DangerousRelease=>ReleaseHandle them.
+ SafeCreateHandle certsArray = Interop.CoreFoundation.CFArrayCreate(ptrs, (UIntPtr)ptrs.Length);
+ _extraHandles.Push(certsArray);
+
+ for (idx = 0; idx < safeHandles.Length; idx++)
+ {
+ safeHandles[idx].DangerousRelease();
+ }
+
+ return certsArray;
+ }
+
+ internal void Execute(
+ DateTime verificationTime,
+ bool allowNetwork,
+ OidCollection applicationPolicy,
+ OidCollection certificatePolicy)
+ {
+ int osStatus;
+
+ // Save the time code for determining which message to load for NotTimeValid.
+ _verificationTime = verificationTime;
+ int ret;
+
+ using (SafeCFDateHandle cfEvaluationTime = Interop.CoreFoundation.CFDateCreate(verificationTime))
+ {
+ ret = Interop.AppleCrypto.AppleCryptoNative_X509ChainEvaluate(
+ _chainHandle,
+ cfEvaluationTime,
+ allowNetwork,
+ out osStatus);
+ }
+
+ if (ret == 0)
+ throw Interop.AppleCrypto.CreateExceptionForOSStatus(osStatus);
+
+ if (ret != 1)
+ {
+ Debug.Fail($"AppleCryptoNative_X509ChainEvaluate returned unknown result {ret}");
+ throw new CryptographicException();
+ }
+
+ Tuple<X509Certificate2, int>[] elements = ParseResults(_chainHandle);
+
+ if (!IsPolicyMatch(elements, applicationPolicy, certificatePolicy))
+ {
+ Tuple<X509Certificate2, int> currentValue = elements[0];
+
+ elements[0] = Tuple.Create(
+ currentValue.Item1,
+ currentValue.Item2 | (int)X509ChainStatusFlags.NotValidForUsage);
+ }
+
+ BuildAndSetProperties(elements);
+ }
+
+ private static Tuple<X509Certificate2,int>[] ParseResults(SafeX509ChainHandle chainHandle)
+ {
+ long elementCount = Interop.AppleCrypto.X509ChainGetChainSize(chainHandle);
+ var elements = new Tuple<X509Certificate2, int>[elementCount];
+
+ using (var trustResults = Interop.AppleCrypto.X509ChainGetTrustResults(chainHandle))
+ {
+ for (long elementIdx = 0; elementIdx < elementCount; elementIdx++)
+ {
+ IntPtr certHandle =
+ Interop.AppleCrypto.X509ChainGetCertificateAtIndex(chainHandle, elementIdx);
+
+ int dwStatus;
+ int ret = Interop.AppleCrypto.X509ChainGetStatusAtIndex(trustResults, elementIdx, out dwStatus);
+
+ // A return value of zero means no errors happened in locating the status (negative) or in
+ // parsing the status (positive).
+ if (ret != 0)
+ {
+ Debug.Fail($"X509ChainGetStatusAtIndex returned unexpected error {ret}");
+ throw new CryptographicException();
+ }
+
+ X509Certificate2 cert = new X509Certificate2(certHandle);
+
+ FixupStatus(cert, ref dwStatus);
+
+ elements[elementIdx] = Tuple.Create(cert, dwStatus);
+ }
+ }
+
+ return elements;
+ }
+
+ private bool IsPolicyMatch(
+ Tuple<X509Certificate2, int>[] elements,
+ OidCollection applicationPolicy,
+ OidCollection certificatePolicy)
+ {
+ if (applicationPolicy?.Count > 0 || certificatePolicy?.Count > 0)
+ {
+ List<X509Certificate2> certsToRead = new List<X509Certificate2>();
+
+ foreach (var element in elements)
+ {
+ certsToRead.Add(element.Item1);
+ }
+
+ CertificatePolicyChain policyChain = new CertificatePolicyChain(certsToRead);
+
+ if (certificatePolicy?.Count > 0)
+ {
+ if (!policyChain.MatchesCertificatePolicies(certificatePolicy))
+ {
+ return false;
+ }
+ }
+
+ if (applicationPolicy?.Count > 0)
+ {
+ if (!policyChain.MatchesApplicationPolicies(applicationPolicy))
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private void BuildAndSetProperties(Tuple<X509Certificate2, int>[] elementTuples)
+ {
+ X509ChainElement[] elements = new X509ChainElement[elementTuples.Length];
+ int allStatus = 0;
+
+ for (int i = 0; i < elementTuples.Length; i++)
+ {
+ Tuple<X509Certificate2, int> tuple = elementTuples[i];
+
+ elements[i] = BuildElement(tuple.Item1, tuple.Item2);
+ allStatus |= tuple.Item2;
+ }
+
+ ChainElements = elements;
+
+ X509ChainElement rollupElement = BuildElement(null, allStatus);
+ ChainStatus = rollupElement.ChainElementStatus;
+ }
+
+ private static void FixupStatus(X509Certificate2 cert, ref int dwStatus)
+ {
+ X509ChainStatusFlags flags = (X509ChainStatusFlags)dwStatus;
+
+ if ((flags & X509ChainStatusFlags.UntrustedRoot) != 0)
+ {
+ X509ChainStatusFlags newFlag = FindUntrustedRootReason(cert);
+
+ if (newFlag != X509ChainStatusFlags.UntrustedRoot)
+ {
+ flags &= ~X509ChainStatusFlags.UntrustedRoot;
+ flags |= newFlag;
+
+ dwStatus = (int)flags;
+ }
+ }
+ }
+
+ private static X509ChainStatusFlags FindUntrustedRootReason(X509Certificate2 cert)
+ {
+ // UntrustedRoot comes back for at least the following reasons:
+ // 1. The parent cert could not be found (no network, no AIA, etc) (PartialChain)
+ // 2. The root cert was found, and wasn't trusted (UntrustedRoot)
+ // 3. The certificate was tampered with, so the parent was declared invalid.
+ //
+ // In the #3 case we'd like to call it NotSignatureValid, but since we didn't get
+ // the parent certificate we can't recompute that, so it'll just get called
+ // PartialChain.
+ if (!cert.SubjectName.RawData.ContentsEqual(cert.IssuerName.RawData))
+ {
+ return X509ChainStatusFlags.PartialChain;
+ }
+
+ // Okay, so we're looking at a self-signed certificate.
+ // What are some situations?
+ // 1. A private / new root certificate was matched which is not trusted (UntrustedRoot)
+ // 2. A valid root certificate is tampered with (NotSignatureValid)
+ // 3. A valid certificate is created which has the same subject name as
+ // an existing root cert (UntrustedRoot)
+ //
+ // To a user, case 2 and 3 aren't really distinguishable:
+ // "What do you mean [my favorite CA] isn't trusted?".
+ // NotSignatureValid would reveal the tamper, but since whoever was tampering can
+ // easily re-sign a self-signed cert, it's not worth duplicating the signature
+ // computation here.
+ return X509ChainStatusFlags.UntrustedRoot;
+ }
+
+ private X509ChainElement BuildElement(X509Certificate2 cert, int dwStatus)
+ {
+ const int errSecCertificateExpired = -67818;
+ const int errSecCertificateNotValidYet = -67819;
+
+ if (dwStatus == 0)
+ {
+ return new X509ChainElement(cert, Array.Empty<X509ChainStatus>(), "");
+ }
+
+ List<X509ChainStatus> statuses = new List<X509ChainStatus>();
+ X509ChainStatusFlags flags = (X509ChainStatusFlags)dwStatus;
+
+ foreach (X509ChainErrorMapping mapping in X509ChainErrorMapping.s_chainErrorMappings)
+ {
+ if ((mapping.ChainStatusFlag & flags) == mapping.ChainStatusFlag)
+ {
+ int osStatus;
+
+ // Disambiguate the NotTimeValid code to get the right string.
+ if (mapping.ChainStatusFlag == X509ChainStatusFlags.NotTimeValid)
+ {
+ if (cert != null && cert.NotBefore > _verificationTime)
+ {
+ osStatus = errSecCertificateNotValidYet;
+ }
+ else
+ {
+ osStatus = errSecCertificateExpired;
+ }
+ }
+ else
+ {
+ osStatus = mapping.OSStatus;
+ }
+
+ statuses.Add(
+ new X509ChainStatus
+ {
+ Status = mapping.ChainStatusFlag,
+ StatusInformation = Interop.AppleCrypto.GetSecErrorString(osStatus),
+ });
+ }
+ }
+
+ return new X509ChainElement(cert, statuses.ToArray(), "");
+ }
+
+ private struct X509ChainErrorMapping
+ {
+ internal static readonly X509ChainErrorMapping[] s_chainErrorMappings =
+ {
+ new X509ChainErrorMapping(X509ChainStatusFlags.NotTimeValid),
+ new X509ChainErrorMapping(X509ChainStatusFlags.NotTimeNested),
+ new X509ChainErrorMapping(X509ChainStatusFlags.Revoked),
+ new X509ChainErrorMapping(X509ChainStatusFlags.NotSignatureValid),
+ new X509ChainErrorMapping(X509ChainStatusFlags.NotValidForUsage),
+ new X509ChainErrorMapping(X509ChainStatusFlags.UntrustedRoot),
+ new X509ChainErrorMapping(X509ChainStatusFlags.RevocationStatusUnknown),
+ new X509ChainErrorMapping(X509ChainStatusFlags.Cyclic),
+ new X509ChainErrorMapping(X509ChainStatusFlags.InvalidExtension),
+ new X509ChainErrorMapping(X509ChainStatusFlags.InvalidPolicyConstraints),
+ new X509ChainErrorMapping(X509ChainStatusFlags.InvalidBasicConstraints),
+ new X509ChainErrorMapping(X509ChainStatusFlags.InvalidNameConstraints),
+ new X509ChainErrorMapping(X509ChainStatusFlags.HasNotSupportedNameConstraint),
+ new X509ChainErrorMapping(X509ChainStatusFlags.HasNotDefinedNameConstraint),
+ new X509ChainErrorMapping(X509ChainStatusFlags.HasNotPermittedNameConstraint),
+ new X509ChainErrorMapping(X509ChainStatusFlags.HasExcludedNameConstraint),
+ new X509ChainErrorMapping(X509ChainStatusFlags.PartialChain),
+ new X509ChainErrorMapping(X509ChainStatusFlags.CtlNotTimeValid),
+ new X509ChainErrorMapping(X509ChainStatusFlags.CtlNotSignatureValid),
+ new X509ChainErrorMapping(X509ChainStatusFlags.CtlNotValidForUsage),
+ new X509ChainErrorMapping(X509ChainStatusFlags.OfflineRevocation),
+ new X509ChainErrorMapping(X509ChainStatusFlags.NoIssuanceChainPolicy),
+ new X509ChainErrorMapping(X509ChainStatusFlags.ExplicitDistrust),
+ new X509ChainErrorMapping(X509ChainStatusFlags.HasNotSupportedCriticalExtension),
+ new X509ChainErrorMapping(X509ChainStatusFlags.HasWeakSignature),
+ };
+
+ internal readonly X509ChainStatusFlags ChainStatusFlag;
+ internal readonly int OSStatus;
+
+ private X509ChainErrorMapping(X509ChainStatusFlags flag)
+ {
+ ChainStatusFlag = flag;
+ OSStatus = Interop.AppleCrypto.GetOSStatusForChainStatus(flag);
+ }
+ }
+ }
+
+ internal sealed partial class ChainPal
+ {
+ public static IChainPal FromHandle(IntPtr chainContext)
+ {
+ // This is possible to do on Apple's platform, but is tricky in execution.
+ // In Windows, CertGetCertificateChain is what allocates the handle, and it does
+ // * Chain building
+ // * Revocation checking as directed
+ // But notably does not apply any policy rules (TLS hostname matching, etc), or
+ // even inquire as to what policies should be applied.
+ //
+ // On Apple, the order is reversed. Creating the SecTrust(Ref) object requires
+ // the policy to match against, but when the handle is created it might not have
+ // built the chain. Then a call to SecTrustEvaluate actually does the chain building.
+ //
+ // This means that Windows never had to handle the "unevaluated chain" pointer, but
+ // on Apple we would. And so it means that the .NET API doesn't understand it can be in
+ // that state.
+ // * Is that an exception on querying the status or elements?
+ // * An exception in this call chain (new X509Chain(IntPtr))?
+ // * Should we build the chain on first data query?
+ // * Should we build the chain now?
+ //
+ // The only thing that is known is that if this method succeeds it does not take ownership
+ // of the handle. So it should CFRetain the handle and let the PAL object's SafeHandle
+ // still Dispose/CFRelease.
+ //
+ // For now, just PNSE, it didn't work when we used OpenSSL, and we can add this when we
+ // decide what it should do.
+ throw new PlatformNotSupportedException();
+ }
+
+ public static bool ReleaseSafeX509ChainHandle(IntPtr handle)
+ {
+ Interop.CoreFoundation.CFRelease(handle);
+ return true;
+ }
+
+ public static IChainPal BuildChain(
+ bool useMachineContext,
+ ICertificatePal cert,
+ X509Certificate2Collection extraStore,
+ OidCollection applicationPolicy,
+ OidCollection certificatePolicy,
+ X509RevocationMode revocationMode,
+ X509RevocationFlag revocationFlag,
+ DateTime verificationTime,
+ TimeSpan timeout)
+ {
+ // If the time was given in Universal, it will stay Universal.
+ // If the time was given in Local, it will be converted.
+ // If the time was given in Unspecified, it will be assumed local, and converted.
+ //
+ // This matches the "assume Local unless explicitly Universal" implicit contract.
+ verificationTime = verificationTime.ToUniversalTime();
+
+ // The Windows (and other-Unix-PAL) behavior is to allow network until network operations
+ // have exceeded the specified timeout. For Apple it's either on (and AIA fetching works),
+ // or off (and AIA fetching doesn't work). And once an SSL policy is used, or revocation is
+ // being checked, the value is on anyways.
+ const bool allowNetwork = true;
+ bool checkRevocation = revocationMode != X509RevocationMode.NoCheck;
+
+ SecTrustChainPal chainPal = new SecTrustChainPal();
+
+ try
+ {
+ chainPal.OpenTrustHandle(cert, extraStore, checkRevocation);
+ chainPal.Execute(verificationTime, allowNetwork, applicationPolicy, certificatePolicy);
+ }
+ catch
+ {
+ chainPal.Dispose();
+ throw;
+ }
+
+ return chainPal;
+ }
+ }
+}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/FindPal.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/FindPal.cs
new file mode 100644
index 0000000000..98a6b61583
--- /dev/null
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/FindPal.cs
@@ -0,0 +1,64 @@
+// 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.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Internal.Cryptography.Pal
+{
+ internal sealed partial class FindPal
+ {
+ internal static IFindPal OpenPal(X509Certificate2Collection findFrom, X509Certificate2Collection copyTo, bool validOnly)
+ {
+ return new AppleCertificateFinder(findFrom, copyTo, validOnly);
+ }
+
+ private sealed class AppleCertificateFinder : ManagedCertificateFinder
+ {
+ public AppleCertificateFinder(X509Certificate2Collection findFrom, X509Certificate2Collection copyTo, bool validOnly)
+ : base(findFrom, copyTo, validOnly)
+ {
+ }
+
+ protected override string DerStringToManagedString(byte[] anyString)
+ {
+ DerSequenceReader reader = DerSequenceReader.CreateForPayload(anyString);
+
+ var tag = (DerSequenceReader.DerTag)reader.PeekTag();
+ string value = null;
+
+ switch (tag)
+ {
+ case DerSequenceReader.DerTag.BMPString:
+ value = reader.ReadBMPString();
+ break;
+ case DerSequenceReader.DerTag.IA5String:
+ value = reader.ReadIA5String();
+ break;
+ case DerSequenceReader.DerTag.PrintableString:
+ value = reader.ReadPrintableString();
+ break;
+ case DerSequenceReader.DerTag.UTF8String:
+ value = reader.ReadUtf8String();
+ break;
+
+ // Ignore anything we don't know how to read.
+ }
+
+ return value;
+ }
+
+ protected override byte[] GetSubjectPublicKeyInfo(X509Certificate2 cert)
+ {
+ AppleCertificatePal pal = (AppleCertificatePal)cert.Pal;
+ return pal.SubjectPublicKeyInfo;
+ }
+
+ protected override X509Certificate2 CloneCertificate(X509Certificate2 cert)
+ {
+ return new X509Certificate2(cert.Handle);
+ }
+ }
+ }
+}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.AppleKeychainStore.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.AppleKeychainStore.cs
new file mode 100644
index 0000000000..980ddb0488
--- /dev/null
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.AppleKeychainStore.cs
@@ -0,0 +1,98 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Apple;
+using System.Security.Cryptography.X509Certificates;
+using Microsoft.Win32.SafeHandles;
+
+namespace Internal.Cryptography.Pal
+{
+ internal sealed partial class StorePal
+ {
+ private sealed class AppleKeychainStore : IStorePal
+ {
+ private SafeKeychainHandle _keychainHandle;
+ private readonly bool _readonly;
+
+ internal AppleKeychainStore(SafeKeychainHandle keychainHandle, OpenFlags openFlags)
+ {
+ Debug.Assert(keychainHandle != null && !keychainHandle.IsInvalid);
+
+ _keychainHandle = keychainHandle;
+
+ _readonly = (openFlags & (OpenFlags.ReadWrite | OpenFlags.MaxAllowed)) == 0;
+ }
+
+ public void Dispose()
+ {
+ _keychainHandle?.Dispose();
+ _keychainHandle = null;
+ }
+
+ public void CloneTo(X509Certificate2Collection collection)
+ {
+ HashSet<X509Certificate2> dedupedCerts = new HashSet<X509Certificate2>();
+
+ using (SafeCFArrayHandle identities = Interop.AppleCrypto.KeychainEnumerateIdentities(_keychainHandle))
+ {
+ ReadCollection(identities, dedupedCerts);
+ }
+
+ using (SafeCFArrayHandle certs = Interop.AppleCrypto.KeychainEnumerateCerts(_keychainHandle))
+ {
+ ReadCollection(certs, dedupedCerts);
+ }
+
+ foreach (X509Certificate2 cert in dedupedCerts)
+ {
+ collection.Add(cert);
+ }
+ }
+
+ public void Add(ICertificatePal cert)
+ {
+ if (_readonly)
+ throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly);
+
+ AppleCertificatePal applePal = (AppleCertificatePal)cert;
+
+ var handle = (SafeKeychainItemHandle)applePal.IdentityHandle ?? applePal.CertificateHandle;
+ Interop.AppleCrypto.X509StoreAddCertificate(handle, _keychainHandle);
+ }
+
+ public void Remove(ICertificatePal cert)
+ {
+ if (_readonly)
+ throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly);
+
+ AppleCertificatePal applePal = (AppleCertificatePal)cert;
+
+ Interop.AppleCrypto.X509StoreRemoveCertificate(applePal.CertificateHandle, _keychainHandle);
+ }
+
+ public SafeHandle SafeHandle => _keychainHandle;
+
+ public static AppleKeychainStore OpenDefaultKeychain(OpenFlags openFlags)
+ {
+ return new AppleKeychainStore(Interop.AppleCrypto.SecKeychainCopyDefault(), openFlags);
+ }
+
+ public static AppleKeychainStore OpenSystemSharedKeychain(OpenFlags openFlags)
+ {
+ const string SharedSystemKeychainPath = "/Library/Keychains/System.keychain";
+ return OpenKeychain(SharedSystemKeychainPath, openFlags);
+ }
+
+ private static AppleKeychainStore OpenKeychain(string keychainPath, OpenFlags openFlags)
+ {
+ return new AppleKeychainStore(Interop.AppleCrypto.SecKeychainOpen(keychainPath), openFlags);
+ }
+ }
+ }
+}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.AppleTrustStore.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.AppleTrustStore.cs
new file mode 100644
index 0000000000..b668967840
--- /dev/null
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.AppleTrustStore.cs
@@ -0,0 +1,85 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using Microsoft.Win32.SafeHandles;
+
+namespace Internal.Cryptography.Pal
+{
+ internal sealed partial class StorePal
+ {
+ private sealed class AppleTrustStore : IStorePal
+ {
+ private readonly StoreName _storeName;
+ private readonly StoreLocation _location;
+
+ private AppleTrustStore(StoreName storeName, StoreLocation location)
+ {
+ Debug.Assert(storeName == StoreName.Root || storeName == StoreName.Disallowed);
+
+ _storeName = storeName;
+ _location = location;
+ }
+
+ public void Dispose()
+ {
+ // Nothing to do.
+ }
+
+ public void CloneTo(X509Certificate2Collection collection)
+ {
+ HashSet<X509Certificate2> dedupedCerts = new HashSet<X509Certificate2>();
+
+ if (_storeName == StoreName.Root)
+ {
+ using (SafeCFArrayHandle certs = Interop.AppleCrypto.StoreEnumerateRoot(_location))
+ {
+ ReadCollection(certs, dedupedCerts);
+ }
+ }
+ else if (_storeName == StoreName.Disallowed)
+ {
+ using (SafeCFArrayHandle certs = Interop.AppleCrypto.StoreEnumerateDisallowed(_location))
+ {
+ ReadCollection(certs, dedupedCerts);
+ }
+ }
+ else
+ {
+ Debug.Fail($"No handler for trust store {_storeName}");
+ }
+
+ foreach (X509Certificate2 cert in dedupedCerts)
+ {
+ collection.Add(cert);
+ }
+ }
+
+ public void Add(ICertificatePal cert)
+ {
+ throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly);
+ }
+
+ public void Remove(ICertificatePal cert)
+ {
+ throw new CryptographicException(SR.Cryptography_X509_StoreReadOnly);
+ }
+
+ public SafeHandle SafeHandle => null;
+
+ internal static AppleTrustStore OpenStore(StoreName storeName, StoreLocation location, OpenFlags openFlags)
+ {
+ if ((openFlags & OpenFlags.ReadWrite) == OpenFlags.ReadWrite)
+ throw new CryptographicException(SR.Security_AccessDenied);
+
+ return new AppleTrustStore(storeName, location);
+ }
+ }
+ }
+}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.ExportPal.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.ExportPal.cs
new file mode 100644
index 0000000000..f3fdab7768
--- /dev/null
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.ExportPal.cs
@@ -0,0 +1,119 @@
+// 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;
+using System.Diagnostics;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using Microsoft.Win32.SafeHandles;
+
+namespace Internal.Cryptography.Pal
+{
+ internal sealed partial class StorePal
+ {
+ private sealed class AppleCertificateExporter : IExportPal
+ {
+ private X509Certificate2Collection _certs;
+ private ICertificatePal _singleCertPal;
+
+ public AppleCertificateExporter(ICertificatePal cert)
+ {
+ _singleCertPal = cert;
+ }
+
+ public AppleCertificateExporter(X509Certificate2Collection certs)
+ {
+ _certs = certs;
+ }
+
+ public void Dispose()
+ {
+ // Don't dispose any of the resources, they're still owned by the caller.
+ _singleCertPal = null;
+ _certs = null;
+ }
+
+ public byte[] Export(X509ContentType contentType, SafePasswordHandle password)
+ {
+ Debug.Assert(password != null);
+ switch (contentType)
+ {
+ case X509ContentType.Cert:
+ return ExportX509Der();
+ case X509ContentType.Pkcs12:
+ return ExportPkcs12(password);
+ case X509ContentType.Pkcs7:
+ return ExportPkcs7();
+ case X509ContentType.SerializedCert:
+ case X509ContentType.SerializedStore:
+ throw new PlatformNotSupportedException(SR.Cryptography_Unix_X509_SerializedExport);
+ default:
+ throw new CryptographicException(SR.Cryptography_X509_InvalidContentType);
+ }
+ }
+
+ private byte[] ExportX509Der()
+ {
+ if (_singleCertPal != null)
+ {
+ return _singleCertPal.RawData;
+ }
+
+ // Windows/Desktop compatibility: Exporting a collection (or store) as
+ // X509ContentType.Cert returns the equivalent of FirstOrDefault(),
+ // so anything past _certs[0] is ignored, and an empty collection is
+ // null (not an Exception)
+ if (_certs.Count == 0)
+ {
+ return null;
+ }
+
+ return _certs[0].RawData;
+ }
+
+ private byte[] ExportPkcs12(SafePasswordHandle password)
+ {
+ IntPtr[] certHandles;
+
+ if (_singleCertPal != null)
+ {
+ certHandles = new[] { _singleCertPal.Handle };
+ }
+ else
+ {
+ certHandles = new IntPtr[_certs.Count];
+
+ for (int i = 0; i < _certs.Count; i++)
+ {
+ certHandles[i] = _certs[i].Handle;
+ }
+ }
+
+ return Interop.AppleCrypto.X509ExportPfx(certHandles, password);
+ }
+
+ private byte[] ExportPkcs7()
+ {
+ IntPtr[] certHandles;
+
+ if (_singleCertPal != null)
+ {
+ certHandles = new[] { ((AppleCertificatePal)_singleCertPal).CertificateHandle.DangerousGetHandle() };
+ }
+ else
+ {
+ certHandles = new IntPtr[_certs.Count];
+
+ for (int i = 0; i < _certs.Count; i++)
+ {
+ AppleCertificatePal pal = (AppleCertificatePal)_certs[i].Pal;
+ certHandles[i] = pal.CertificateHandle.DangerousGetHandle();
+ }
+ }
+
+ return Interop.AppleCrypto.X509ExportPkcs7(certHandles);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.LoaderPal.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.LoaderPal.cs
new file mode 100644
index 0000000000..0657ce677a
--- /dev/null
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.LoaderPal.cs
@@ -0,0 +1,60 @@
+// 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;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Apple;
+using System.Security.Cryptography.X509Certificates;
+using Microsoft.Win32.SafeHandles;
+
+namespace Internal.Cryptography.Pal
+{
+ internal sealed partial class StorePal
+ {
+ private sealed class AppleCertLoader : ILoaderPal
+ {
+ private readonly SafeCFArrayHandle _collectionHandle;
+ private readonly SafeTemporaryKeychainHandle _tmpKeychain;
+
+ public AppleCertLoader(SafeCFArrayHandle collectionHandle, SafeTemporaryKeychainHandle tmpKeychain)
+ {
+ _collectionHandle = collectionHandle;
+ _tmpKeychain = tmpKeychain;
+ }
+
+ public void Dispose()
+ {
+ _collectionHandle.Dispose();
+ _tmpKeychain?.Dispose();
+ }
+
+ public void MoveTo(X509Certificate2Collection collection)
+ {
+ long longCount = Interop.CoreFoundation.CFArrayGetCount(_collectionHandle);
+
+ if (longCount > int.MaxValue)
+ throw new CryptographicException();
+
+ int count = (int)longCount;
+
+ // Apple returns things in the opposite order from Windows, so read backwards.
+ for (int i = count - 1; i >= 0; i--)
+ {
+ IntPtr handle = Interop.CoreFoundation.CFArrayGetValueAtIndex(_collectionHandle, i);
+
+ if (handle != IntPtr.Zero)
+ {
+ ICertificatePal certPal = CertificatePal.FromHandle(handle, throwOnFail: false);
+
+ if (certPal != null)
+ {
+ X509Certificate2 cert = new X509Certificate2(certPal);
+ collection.Add(cert);
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.cs
new file mode 100644
index 0000000000..86ce9cd7b2
--- /dev/null
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/StorePal.cs
@@ -0,0 +1,172 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Apple;
+using System.Security.Cryptography.X509Certificates;
+using Microsoft.Win32.SafeHandles;
+
+namespace Internal.Cryptography.Pal
+{
+ internal sealed partial class StorePal
+ {
+ public static IStorePal FromHandle(IntPtr storeHandle)
+ {
+ if (storeHandle == IntPtr.Zero)
+ throw new ArgumentNullException(nameof(storeHandle));
+
+ var keychainHandle = new SafeKeychainHandle(storeHandle);
+ Interop.CoreFoundation.CFRetain(storeHandle);
+
+ return new AppleKeychainStore(keychainHandle, OpenFlags.MaxAllowed);
+ }
+
+ public static ILoaderPal FromBlob(byte[] rawData, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
+ {
+ Debug.Assert(password != null);
+
+ X509ContentType contentType = X509Certificate2.GetCertContentType(rawData);
+
+ SafeKeychainHandle keychain;
+ bool exportable = true;
+
+ if (contentType == X509ContentType.Pkcs12)
+ {
+ if ((keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) == X509KeyStorageFlags.EphemeralKeySet)
+ {
+ throw new PlatformNotSupportedException(SR.Cryptography_X509_NoEphemeralPfx);
+ }
+
+ exportable = (keyStorageFlags & X509KeyStorageFlags.Exportable) == X509KeyStorageFlags.Exportable;
+
+ bool persist =
+ (keyStorageFlags & X509KeyStorageFlags.PersistKeySet) == X509KeyStorageFlags.PersistKeySet;
+
+ keychain = persist
+ ? Interop.AppleCrypto.SecKeychainCopyDefault()
+ : Interop.AppleCrypto.CreateTemporaryKeychain();
+ }
+ else
+ {
+ keychain = SafeTemporaryKeychainHandle.InvalidHandle;
+ password = SafePasswordHandle.InvalidHandle;
+ }
+
+ // Only dispose tmpKeychain on the exception path, otherwise it's managed by AppleCertLoader.
+ try
+ {
+ SafeCFArrayHandle certs = Interop.AppleCrypto.X509ImportCollection(
+ rawData,
+ contentType,
+ password,
+ keychain,
+ exportable);
+
+ // If the default keychain was used, null will be passed to the loader.
+ return new AppleCertLoader(certs, keychain as SafeTemporaryKeychainHandle);
+ }
+ catch
+ {
+ keychain.Dispose();
+ throw;
+ }
+ }
+
+ public static ILoaderPal FromFile(string fileName, SafePasswordHandle password, X509KeyStorageFlags keyStorageFlags)
+ {
+ Debug.Assert(password != null);
+
+ byte[] fileBytes = File.ReadAllBytes(fileName);
+ return FromBlob(fileBytes, password, keyStorageFlags);
+ }
+
+ public static IExportPal FromCertificate(ICertificatePal cert)
+ {
+ return new AppleCertificateExporter(cert);
+ }
+
+ public static IExportPal LinkFromCertificateCollection(X509Certificate2Collection certificates)
+ {
+ return new AppleCertificateExporter(certificates);
+ }
+
+ public static IStorePal FromSystemStore(string storeName, StoreLocation storeLocation, OpenFlags openFlags)
+ {
+ StringComparer ordinalIgnoreCase = StringComparer.OrdinalIgnoreCase;
+
+ switch (storeLocation)
+ {
+ case StoreLocation.CurrentUser:
+ if (ordinalIgnoreCase.Equals("My", storeName))
+ return AppleKeychainStore.OpenDefaultKeychain(openFlags);
+ if (ordinalIgnoreCase.Equals("Root", storeName))
+ return AppleTrustStore.OpenStore(StoreName.Root, storeLocation, openFlags);
+ if (ordinalIgnoreCase.Equals("Disallowed", storeName))
+ return AppleTrustStore.OpenStore(StoreName.Disallowed, storeLocation, openFlags);
+
+ break;
+ case StoreLocation.LocalMachine:
+ if (ordinalIgnoreCase.Equals("My", storeName))
+ return AppleKeychainStore.OpenSystemSharedKeychain(openFlags);
+ if (ordinalIgnoreCase.Equals("Root", storeName))
+ return AppleTrustStore.OpenStore(StoreName.Root, storeLocation, openFlags);
+ if (ordinalIgnoreCase.Equals("Disallowed", storeName))
+ return AppleTrustStore.OpenStore(StoreName.Disallowed, storeLocation, openFlags);
+ break;
+ }
+
+ if ((openFlags & OpenFlags.OpenExistingOnly) == OpenFlags.OpenExistingOnly)
+ throw new CryptographicException(SR.Cryptography_X509_StoreNotFound);
+
+ throw new PlatformNotSupportedException(
+ SR.Format(
+ SR.Cryptography_X509_StoreCannotCreate,
+ storeName,
+ storeLocation));
+ }
+
+ private static void ReadCollection(SafeCFArrayHandle matches, HashSet<X509Certificate2> collection)
+ {
+ if (matches.IsInvalid)
+ {
+ return;
+ }
+
+ long count = Interop.CoreFoundation.CFArrayGetCount(matches);
+
+ for (int i = 0; i < count; i++)
+ {
+ IntPtr handle = Interop.CoreFoundation.CFArrayGetValueAtIndex(matches, i);
+
+ SafeSecCertificateHandle certHandle;
+ SafeSecIdentityHandle identityHandle;
+
+ if (Interop.AppleCrypto.X509DemuxAndRetainHandle(handle, out certHandle, out identityHandle))
+ {
+ X509Certificate2 cert;
+
+ if (certHandle.IsInvalid)
+ {
+ certHandle.Dispose();
+ cert = new X509Certificate2(new AppleCertificatePal(identityHandle));
+ }
+ else
+ {
+ identityHandle.Dispose();
+ cert = new X509Certificate2(new AppleCertificatePal(certHandle));
+ }
+
+ if (!collection.Add(cert))
+ {
+ cert.Dispose();
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs
new file mode 100644
index 0000000000..1056f0ccbb
--- /dev/null
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs
@@ -0,0 +1,131 @@
+// 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;
+using System.Security.Cryptography;
+using System.Security.Cryptography.Apple;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Internal.Cryptography.Pal
+{
+ internal sealed partial class X509Pal
+ {
+ public static IX509Pal Instance = new AppleX509Pal();
+
+ private X509Pal()
+ {
+ }
+
+ private partial class AppleX509Pal : ManagedX509ExtensionProcessor, IX509Pal
+ {
+ public AsymmetricAlgorithm DecodePublicKey(Oid oid, byte[] encodedKeyValue, byte[] encodedParameters,
+ ICertificatePal certificatePal)
+ {
+ AppleCertificatePal applePal = certificatePal as AppleCertificatePal;
+
+ if (applePal != null)
+ {
+ SafeSecKeyRefHandle key = Interop.AppleCrypto.X509GetPublicKey(applePal.CertificateHandle);
+
+ switch (oid.Value)
+ {
+ case Oids.RsaRsa:
+ return new RSAImplementation.RSASecurityTransforms(key);
+ case Oids.DsaDsa:
+ return new DSAImplementation.DSASecurityTransforms(key);
+ case Oids.Ecc:
+ return new ECDsaImplementation.ECDsaSecurityTransforms(key);
+ }
+
+ key.Dispose();
+ }
+ else
+ {
+ switch (oid.Value)
+ {
+ case Oids.RsaRsa:
+ return DecodeRsaPublicKey(encodedKeyValue);
+ case Oids.DsaDsa:
+ return DecodeDsaPublicKey(encodedKeyValue, encodedParameters);
+ }
+ }
+
+ throw new NotSupportedException(SR.NotSupported_KeyAlgorithm);
+ }
+
+ private static AsymmetricAlgorithm DecodeRsaPublicKey(byte[] encodedKeyValue)
+ {
+ DerSequenceReader reader = new DerSequenceReader(encodedKeyValue);
+ RSAParameters rsaParameters = new RSAParameters();
+ reader.ReadPkcs1PublicBlob(ref rsaParameters);
+
+ RSA rsa = RSA.Create();
+ try
+ {
+ rsa.ImportParameters(rsaParameters);
+ return rsa;
+ }
+ catch (Exception)
+ {
+ rsa.Dispose();
+ throw;
+ }
+ }
+
+ private static AsymmetricAlgorithm DecodeDsaPublicKey(byte[] encodedKeyValue, byte[] encodedParameters)
+ {
+ DSAParameters dsaParameters = new DSAParameters();
+ DerSequenceReader parameterReader = new DerSequenceReader(encodedParameters);
+
+ parameterReader.ReadSubjectPublicKeyInfo(encodedKeyValue, ref dsaParameters);
+
+ DSA dsa = DSA.Create();
+ try
+ {
+ dsa.ImportParameters(dsaParameters);
+ return dsa;
+ }
+ catch (Exception)
+ {
+ dsa.Dispose();
+ throw;
+ }
+ }
+
+ public string X500DistinguishedNameDecode(byte[] encodedDistinguishedName, X500DistinguishedNameFlags flag)
+ {
+ return X500NameEncoder.X500DistinguishedNameDecode(encodedDistinguishedName, true, flag);
+ }
+
+ public byte[] X500DistinguishedNameEncode(string distinguishedName, X500DistinguishedNameFlags flag)
+ {
+ return X500NameEncoder.X500DistinguishedNameEncode(distinguishedName, flag);
+ }
+
+ public string X500DistinguishedNameFormat(byte[] encodedDistinguishedName, bool multiLine)
+ {
+ return X500NameEncoder.X500DistinguishedNameDecode(
+ encodedDistinguishedName,
+ true,
+ multiLine ? X500DistinguishedNameFlags.UseNewLines : X500DistinguishedNameFlags.None,
+ multiLine);
+ }
+
+ public X509ContentType GetCertContentType(byte[] rawData)
+ {
+ if (rawData == null || rawData.Length == 0)
+ {
+ return X509ContentType.Unknown;
+ }
+
+ return Interop.AppleCrypto.X509GetContentType(rawData, rawData.Length);
+ }
+
+ public X509ContentType GetCertContentType(string fileName)
+ {
+ return GetCertContentType(System.IO.File.ReadAllBytes(fileName));
+ }
+ }
+ }
+}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ChainVerifier.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ChainVerifier.cs
new file mode 100644
index 0000000000..fa341d24b7
--- /dev/null
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ChainVerifier.cs
@@ -0,0 +1,132 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Internal.Cryptography.Pal
+{
+ internal static class ChainVerifier
+ {
+ public static bool Verify(X509ChainElement[] chainElements, X509VerificationFlags flags)
+ {
+ bool isEndEntity = true;
+
+ foreach (X509ChainElement element in chainElements)
+ {
+ if (HasUnsuppressedError(flags, element, isEndEntity))
+ {
+ return false;
+ }
+
+ isEndEntity = false;
+ }
+
+ return true;
+ }
+
+ private static bool HasUnsuppressedError(X509VerificationFlags flags, X509ChainElement element, bool isEndEntity)
+ {
+ foreach (X509ChainStatus status in element.ChainElementStatus)
+ {
+ if (status.Status == X509ChainStatusFlags.NoError)
+ {
+ return false;
+ }
+
+ Debug.Assert(
+ (status.Status & (status.Status - 1)) == 0,
+ $"Only one bit should be set in status.Status ({status})");
+
+ // The Windows certificate store API only checks the time error for a "peer trust" certificate,
+ // but we don't have a concept for that in Unix. If we did, we'd need to do that logic that here.
+ // Note also that that logic is skipped if CERT_CHAIN_POLICY_IGNORE_PEER_TRUST_FLAG is set.
+
+ X509VerificationFlags? suppressionFlag;
+
+ if (status.Status == X509ChainStatusFlags.RevocationStatusUnknown)
+ {
+ if (isEndEntity)
+ {
+ suppressionFlag = X509VerificationFlags.IgnoreEndRevocationUnknown;
+ }
+ else if (IsSelfSigned(element.Certificate))
+ {
+ suppressionFlag = X509VerificationFlags.IgnoreRootRevocationUnknown;
+ }
+ else
+ {
+ suppressionFlag = X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown;
+ }
+ }
+ else
+ {
+ suppressionFlag = GetSuppressionFlag(status.Status);
+ }
+
+ // If an error was found, and we do NOT have the suppression flag for it enabled,
+ // we have an unsuppressed error, so return true. (If there's no suppression for a given code,
+ // we (by definition) don't have that flag set.
+ if (!suppressionFlag.HasValue ||
+ (flags & suppressionFlag) == 0)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static bool IsSelfSigned(X509Certificate2 cert)
+ {
+ return cert.SubjectName.RawData.ContentsEqual(cert.IssuerName.RawData);
+ }
+
+ private static X509VerificationFlags? GetSuppressionFlag(X509ChainStatusFlags status)
+ {
+ switch (status)
+ {
+ case X509ChainStatusFlags.UntrustedRoot:
+ case X509ChainStatusFlags.PartialChain:
+ return X509VerificationFlags.AllowUnknownCertificateAuthority;
+
+ case X509ChainStatusFlags.NotValidForUsage:
+ case X509ChainStatusFlags.CtlNotValidForUsage:
+ return X509VerificationFlags.IgnoreWrongUsage;
+
+ case X509ChainStatusFlags.NotTimeValid:
+ return X509VerificationFlags.IgnoreNotTimeValid;
+
+ case X509ChainStatusFlags.CtlNotTimeValid:
+ return X509VerificationFlags.IgnoreCtlNotTimeValid;
+
+ case X509ChainStatusFlags.InvalidNameConstraints:
+ case X509ChainStatusFlags.HasNotSupportedNameConstraint:
+ case X509ChainStatusFlags.HasNotDefinedNameConstraint:
+ case X509ChainStatusFlags.HasNotPermittedNameConstraint:
+ case X509ChainStatusFlags.HasExcludedNameConstraint:
+ return X509VerificationFlags.IgnoreInvalidName;
+
+ case X509ChainStatusFlags.InvalidPolicyConstraints:
+ case X509ChainStatusFlags.NoIssuanceChainPolicy:
+ return X509VerificationFlags.IgnoreInvalidPolicy;
+
+ case X509ChainStatusFlags.InvalidBasicConstraints:
+ return X509VerificationFlags.IgnoreInvalidBasicConstraints;
+
+ case X509ChainStatusFlags.HasNotSupportedCriticalExtension:
+ // This field would be mapped in by AllFlags, but we don't have a name for it currently.
+ return (X509VerificationFlags)0x00002000;
+
+ case X509ChainStatusFlags.NotTimeNested:
+ return X509VerificationFlags.IgnoreNotTimeNested;
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedCertificateFinder.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedCertificateFinder.cs
new file mode 100644
index 0000000000..ebc5ee48d1
--- /dev/null
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedCertificateFinder.cs
@@ -0,0 +1,363 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Numerics;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Internal.Cryptography.Pal
+{
+ internal abstract class ManagedCertificateFinder : IFindPal
+ {
+ private readonly X509Certificate2Collection _findFrom;
+ private readonly X509Certificate2Collection _copyTo;
+ private readonly bool _validOnly;
+
+ internal ManagedCertificateFinder(X509Certificate2Collection findFrom, X509Certificate2Collection copyTo, bool validOnly)
+ {
+ _findFrom = findFrom;
+ _copyTo = copyTo;
+ _validOnly = validOnly;
+ }
+
+ public string NormalizeOid(string maybeOid, OidGroup expectedGroup)
+ {
+ Oid oid = new Oid(maybeOid);
+
+ // If maybeOid is interpreted to be a FriendlyName, return the OID.
+ if (!StringComparer.OrdinalIgnoreCase.Equals(oid.Value, maybeOid))
+ {
+ return oid.Value;
+ }
+
+ FindPal.ValidateOidValue(maybeOid);
+ return maybeOid;
+ }
+
+ public void FindByThumbprint(byte[] thumbprint)
+ {
+ FindCore(cert => cert.GetCertHash().ContentsEqual(thumbprint));
+ }
+
+ public void FindBySubjectName(string subjectName)
+ {
+ FindCore(
+ cert =>
+ {
+ string formedSubject = X500NameEncoder.X500DistinguishedNameDecode(cert.SubjectName.RawData, false, X500DistinguishedNameFlags.None);
+
+ return formedSubject.IndexOf(subjectName, StringComparison.OrdinalIgnoreCase) >= 0;
+ });
+ }
+
+ public void FindBySubjectDistinguishedName(string subjectDistinguishedName)
+ {
+ FindCore(cert => StringComparer.OrdinalIgnoreCase.Equals(subjectDistinguishedName, cert.Subject));
+ }
+
+ public void FindByIssuerName(string issuerName)
+ {
+ FindCore(
+ cert =>
+ {
+ string formedIssuer = X500NameEncoder.X500DistinguishedNameDecode(cert.IssuerName.RawData, false, X500DistinguishedNameFlags.None);
+
+ return formedIssuer.IndexOf(issuerName, StringComparison.OrdinalIgnoreCase) >= 0;
+ });
+ }
+
+ public void FindByIssuerDistinguishedName(string issuerDistinguishedName)
+ {
+ FindCore(cert => StringComparer.OrdinalIgnoreCase.Equals(issuerDistinguishedName, cert.Issuer));
+ }
+
+ public void FindBySerialNumber(BigInteger hexValue, BigInteger decimalValue)
+ {
+ FindCore(
+ cert =>
+ {
+ byte[] serialBytes = cert.GetSerialNumber();
+ BigInteger serialNumber = FindPal.PositiveBigIntegerFromByteArray(serialBytes);
+ bool match = hexValue.Equals(serialNumber) || decimalValue.Equals(serialNumber);
+
+ return match;
+ });
+ }
+
+ private static DateTime NormalizeDateTime(DateTime dateTime)
+ {
+ // If it's explicitly UTC, convert it to local before a comparison, since the
+ // NotBefore and NotAfter are in Local time.
+ //
+ // If it was Unknown, assume it was Local.
+ if (dateTime.Kind == DateTimeKind.Utc)
+ {
+ return dateTime.ToLocalTime();
+ }
+
+ return dateTime;
+ }
+
+ public void FindByTimeValid(DateTime dateTime)
+ {
+ DateTime normalized = NormalizeDateTime(dateTime);
+
+ FindCore(cert => cert.NotBefore <= normalized && normalized <= cert.NotAfter);
+ }
+
+ public void FindByTimeNotYetValid(DateTime dateTime)
+ {
+ DateTime normalized = NormalizeDateTime(dateTime);
+
+ FindCore(cert => cert.NotBefore > normalized);
+ }
+
+ public void FindByTimeExpired(DateTime dateTime)
+ {
+ DateTime normalized = NormalizeDateTime(dateTime);
+
+ FindCore(cert => cert.NotAfter < normalized);
+ }
+
+ protected abstract string DerStringToManagedString(byte[] anyString);
+
+ public void FindByTemplateName(string templateName)
+ {
+ FindCore(
+ cert =>
+ {
+ X509Extension ext = FindExtension(cert, Oids.EnrollCertTypeExtension);
+
+ if (ext != null)
+ {
+ // Try a V1 template structure, just a string:
+ string decodedName = DerStringToManagedString(ext.RawData);
+
+ // If this doesn't match, maybe a V2 template will
+ if (StringComparer.OrdinalIgnoreCase.Equals(templateName, decodedName))
+ {
+ return true;
+ }
+ }
+
+ ext = FindExtension(cert, Oids.CertificateTemplate);
+
+ if (ext != null)
+ {
+ DerSequenceReader reader = new DerSequenceReader(ext.RawData);
+ // SEQUENCE (
+ // OID oid,
+ // INTEGER major,
+ // INTEGER minor OPTIONAL
+ // )
+
+ if (reader.PeekTag() == (byte)DerSequenceReader.DerTag.ObjectIdentifier)
+ {
+ Oid oid = reader.ReadOid();
+
+ if (StringComparer.Ordinal.Equals(templateName, oid.Value))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ });
+ }
+
+ public void FindByApplicationPolicy(string oidValue)
+ {
+ FindCore(
+ cert =>
+ {
+ X509Extension ext = FindExtension(cert, Oids.EnhancedKeyUsage);
+
+ if (ext == null)
+ {
+ // A certificate with no EKU is valid for all extended purposes.
+ return true;
+ }
+
+ var ekuExt = (X509EnhancedKeyUsageExtension)ext;
+
+ foreach (Oid usageOid in ekuExt.EnhancedKeyUsages)
+ {
+ if (StringComparer.Ordinal.Equals(oidValue, usageOid.Value))
+ {
+ return true;
+ }
+ }
+
+ // If the certificate had an EKU extension, and the value we wanted was
+ // not present, then it is not valid for that usage.
+ return false;
+ });
+ }
+
+ public void FindByCertificatePolicy(string oidValue)
+ {
+ FindCore(
+ cert =>
+ {
+ X509Extension ext = FindExtension(cert, Oids.CertPolicies);
+
+ if (ext == null)
+ {
+ // Unlike Application Policy, Certificate Policy is "assume false".
+ return false;
+ }
+
+ ISet<string> policyOids = CertificatePolicyChain.ReadCertPolicyExtension(ext);
+ return policyOids.Contains(oidValue);
+ });
+ }
+
+ public void FindByExtension(string oidValue)
+ {
+ FindCore(cert => FindExtension(cert, oidValue) != null);
+ }
+
+ public void FindByKeyUsage(X509KeyUsageFlags keyUsage)
+ {
+ FindCore(
+ cert =>
+ {
+ X509Extension ext = FindExtension(cert, Oids.KeyUsage);
+
+ if (ext == null)
+ {
+ // A certificate with no key usage extension is considered valid for all key usages.
+ return true;
+ }
+
+ var kuExt = (X509KeyUsageExtension)ext;
+
+ return (kuExt.KeyUsages & keyUsage) == keyUsage;
+ });
+ }
+
+ protected abstract byte[] GetSubjectPublicKeyInfo(X509Certificate2 cert);
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is required for Compat")]
+ public void FindBySubjectKeyIdentifier(byte[] keyIdentifier)
+ {
+ FindCore(
+ cert =>
+ {
+ X509Extension ext = FindExtension(cert, Oids.SubjectKeyIdentifier);
+ byte[] certKeyId;
+
+ if (ext != null)
+ {
+ // The extension exposes the value as a hexadecimal string, or we can decode here.
+ // Enough parsing has gone on, let's decode.
+ certKeyId = ManagedX509ExtensionProcessor.DecodeX509SubjectKeyIdentifierExtension(ext.RawData);
+ }
+ else
+ {
+ // The Desktop/Windows version of this method use CertGetCertificateContextProperty
+ // with a property ID of CERT_KEY_IDENTIFIER_PROP_ID.
+ //
+ // MSDN says that when there's no extension, this method takes the SHA-1 of the
+ // SubjectPublicKeyInfo block, and returns that.
+ //
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376079%28v=vs.85%29.aspx
+
+ using (HashAlgorithm hash = SHA1.Create())
+ {
+ byte[] publicKeyInfoBytes = GetSubjectPublicKeyInfo(cert);
+
+ certKeyId = hash.ComputeHash(publicKeyInfoBytes);
+ }
+ }
+
+ return keyIdentifier.ContentsEqual(certKeyId);
+ });
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ }
+
+ private static X509Extension FindExtension(X509Certificate2 cert, string extensionOid)
+ {
+ if (cert.Extensions == null || cert.Extensions.Count == 0)
+ {
+ return null;
+ }
+
+ foreach (X509Extension ext in cert.Extensions)
+ {
+ if (ext != null &&
+ ext.Oid != null &&
+ StringComparer.Ordinal.Equals(extensionOid, ext.Oid.Value))
+ {
+ return ext;
+ }
+ }
+
+ return null;
+ }
+
+ protected abstract X509Certificate2 CloneCertificate(X509Certificate2 cert);
+
+ private void FindCore(Predicate<X509Certificate2> predicate)
+ {
+ foreach (X509Certificate2 cert in _findFrom)
+ {
+ if (predicate(cert))
+ {
+ if (!_validOnly || IsCertValid(cert))
+ {
+ X509Certificate2 clone = CloneCertificate(cert);
+ Debug.Assert(!ReferenceEquals(cert, clone));
+
+ _copyTo.Add(clone);
+ }
+ }
+ }
+ }
+
+ private static bool IsCertValid(X509Certificate2 cert)
+ {
+ try
+ {
+ // This needs to be kept in sync with VerifyCertificateIgnoringErrors in the
+ // Windows PAL version (and potentially any other PALs that come about)
+ using (X509Chain chain = new X509Chain
+ {
+ ChainPolicy =
+ {
+ RevocationMode = X509RevocationMode.NoCheck,
+ RevocationFlag = X509RevocationFlag.ExcludeRoot
+ }
+ })
+ {
+ bool valid = chain.Build(cert);
+ int elementCount = chain.ChainElements.Count;
+
+ for (int i = 0; i < elementCount; i++)
+ {
+ chain.ChainElements[i].Certificate.Dispose();
+ }
+
+ return valid;
+ }
+ }
+ catch (CryptographicException)
+ {
+ return false;
+ }
+ }
+ }
+}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedX509ExtensionProcessor.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedX509ExtensionProcessor.cs
new file mode 100644
index 0000000000..2a06dc3f36
--- /dev/null
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/ManagedX509ExtensionProcessor.cs
@@ -0,0 +1,297 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Internal.Cryptography.Pal
+{
+ internal class ManagedX509ExtensionProcessor
+ {
+ public virtual byte[] EncodeX509KeyUsageExtension(X509KeyUsageFlags keyUsages)
+ {
+ // The numeric values of X509KeyUsageFlags mean that if we interpret it as a little-endian
+ // ushort it will line up with the flags in the spec.
+ ushort ushortValue = unchecked((ushort)(int)keyUsages);
+ byte[] data = BitConverter.GetBytes(ushortValue);
+
+ // RFC 3280 section 4.2.1.3 (https://tools.ietf.org/html/rfc3280#section-4.2.1.3) defines
+ // digitalSignature (0) through decipherOnly (8), making 9 named bits.
+ const int namedBitsCount = 9;
+
+ // The expected output of this method isn't the SEQUENCE value, but just the payload bytes.
+ byte[][] segments = DerEncoder.SegmentedEncodeNamedBitList(data, namedBitsCount);
+ Debug.Assert(segments.Length == 3);
+ return ConcatenateArrays(segments);
+ }
+
+ public virtual void DecodeX509KeyUsageExtension(byte[] encoded, out X509KeyUsageFlags keyUsages)
+ {
+ DerSequenceReader reader = DerSequenceReader.CreateForPayload(encoded);
+ byte[] decoded = reader.ReadBitString();
+
+ // Only 9 bits are defined.
+ if (decoded.Length > 2)
+ {
+ throw new CryptographicException();
+ }
+
+ // DER encodings of BIT_STRING values number the bits as
+ // 01234567 89 (big endian), plus a number saying how many bits of the last byte were padding.
+ //
+ // So digitalSignature (0) doesn't mean 2^0 (0x01), it means the most significant bit
+ // is set in this byte stream.
+ //
+ // BIT_STRING values are compact. So a value of cRLSign (6) | keyEncipherment (2), which
+ // is 0b0010001 => 0b0010 0010 (1 bit padding) => 0x22 encoded is therefore
+ // 0x02 (length remaining) 0x01 (1 bit padding) 0x22.
+ //
+ // We will read that, and return 0x22. 0x22 lines up
+ // exactly with X509KeyUsageFlags.CrlSign (0x20) | X509KeyUsageFlags.KeyEncipherment (0x02)
+ //
+ // Once the decipherOnly (8) bit is added to the mix, the values become:
+ // 0b001000101 => 0b0010 0010 1000 0000 (7 bits padding)
+ // { 0x03 0x07 0x22 0x80 }
+ // And we read new byte[] { 0x22 0x80 }
+ //
+ // The value of X509KeyUsageFlags.DecipherOnly is 0x8000. 0x8000 in a little endian
+ // representation is { 0x00 0x80 }. This means that the DER storage mechanism has effectively
+ // ended up being little-endian for BIT_STRING values. Untwist the bytes, and now the bits all
+ // line up with the existing X509KeyUsageFlags.
+
+ int value = 0;
+
+ if (decoded.Length > 0)
+ {
+ value = decoded[0];
+ }
+
+ if (decoded.Length > 1)
+ {
+ value |= decoded[1] << 8;
+ }
+
+ keyUsages = (X509KeyUsageFlags)value;
+ }
+
+ public virtual byte[] EncodeX509BasicConstraints2Extension(
+ bool certificateAuthority,
+ bool hasPathLengthConstraint,
+ int pathLengthConstraint)
+ {
+ //BasicConstraintsSyntax::= SEQUENCE {
+ // cA BOOLEAN DEFAULT FALSE,
+ // pathLenConstraint INTEGER(0..MAX) OPTIONAL,
+ // ... }
+
+ List<byte[][]> segments = new List<byte[][]>(2);
+
+ if (certificateAuthority)
+ {
+ segments.Add(DerEncoder.SegmentedEncodeBoolean(true));
+ }
+
+ if (hasPathLengthConstraint)
+ {
+ byte[] pathLengthBytes = BitConverter.GetBytes(pathLengthConstraint);
+ // Little-Endian => Big-Endian
+ Array.Reverse(pathLengthBytes);
+ segments.Add(DerEncoder.SegmentedEncodeUnsignedInteger(pathLengthBytes));
+ }
+
+ return DerEncoder.ConstructSequence(segments);
+ }
+
+ public virtual bool SupportsLegacyBasicConstraintsExtension => false;
+
+ public virtual void DecodeX509BasicConstraintsExtension(
+ byte[] encoded,
+ out bool certificateAuthority,
+ out bool hasPathLengthConstraint,
+ out int pathLengthConstraint)
+ {
+ // No RFC nor ITU document describes the layout of the 2.5.29.10 structure,
+ // and OpenSSL doesn't have a decoder for it, either.
+ //
+ // Since it was never published as a standard (2.5.29.19 replaced it before publication)
+ // there shouldn't be too many people upset that we can't decode it for them on Unix.
+ throw new PlatformNotSupportedException(SR.NotSupported_LegacyBasicConstraints);
+ }
+
+ public virtual void DecodeX509BasicConstraints2Extension(
+ byte[] encoded,
+ out bool certificateAuthority,
+ out bool hasPathLengthConstraint,
+ out int pathLengthConstraint)
+ {
+ // BasicConstraints ::= SEQUENCE {
+ // cA BOOLEAN DEFAULT FALSE,
+ // pathLenConstraint INTEGER(0..MAX) OPTIONAL }
+
+ DerSequenceReader constraints = new DerSequenceReader(encoded);
+
+ if (!constraints.HasData)
+ {
+ certificateAuthority = false;
+ hasPathLengthConstraint = false;
+ pathLengthConstraint = 0;
+ return;
+ }
+
+ if (constraints.PeekTag() == (byte)DerSequenceReader.DerTag.Boolean)
+ {
+ certificateAuthority = constraints.ReadBoolean();
+ }
+ else
+ {
+ certificateAuthority = false;
+ }
+
+ if (constraints.HasData)
+ {
+ pathLengthConstraint = constraints.ReadInteger();
+ hasPathLengthConstraint = true;
+ }
+ else
+ {
+ pathLengthConstraint = 0;
+ hasPathLengthConstraint = false;
+ }
+ }
+
+ public virtual byte[] EncodeX509EnhancedKeyUsageExtension(OidCollection usages)
+ {
+ //extKeyUsage EXTENSION ::= {
+ // SYNTAX SEQUENCE SIZE(1..MAX) OF KeyPurposeId
+ // IDENTIFIED BY id-ce-extKeyUsage }
+ //
+ //KeyPurposeId::= OBJECT IDENTIFIER
+
+ List<byte[][]> segments = new List<byte[][]>(usages.Count);
+
+ foreach (Oid usage in usages)
+ {
+ segments.Add(DerEncoder.SegmentedEncodeOid(usage));
+ }
+
+ return DerEncoder.ConstructSequence(segments);
+ }
+
+ public virtual void DecodeX509EnhancedKeyUsageExtension(byte[] encoded, out OidCollection usages)
+ {
+ // ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId
+ //
+ // KeyPurposeId::= OBJECT IDENTIFIER
+
+ OidCollection oids = new OidCollection();
+ DerSequenceReader reader = new DerSequenceReader(encoded);
+
+ while (reader.HasData)
+ {
+ oids.Add(reader.ReadOid());
+ }
+
+ usages = oids;
+ }
+
+ public virtual byte[] EncodeX509SubjectKeyIdentifierExtension(byte[] subjectKeyIdentifier)
+ {
+ //subjectKeyIdentifier EXTENSION ::= {
+ // SYNTAX SubjectKeyIdentifier
+ // IDENTIFIED BY id - ce - subjectKeyIdentifier }
+ //
+ //SubjectKeyIdentifier::= KeyIdentifier
+ //
+ //KeyIdentifier ::= OCTET STRING
+
+ byte[][] segments = DerEncoder.SegmentedEncodeOctetString(subjectKeyIdentifier);
+
+ // The extension is not a sequence, just the octet string
+ return ConcatenateArrays(segments);
+ }
+
+ public virtual void DecodeX509SubjectKeyIdentifierExtension(byte[] encoded, out byte[] subjectKeyIdentifier)
+ {
+ subjectKeyIdentifier = DecodeX509SubjectKeyIdentifierExtension(encoded);
+ }
+
+ internal static byte[] DecodeX509SubjectKeyIdentifierExtension(byte[] encoded)
+ {
+ DerSequenceReader reader = DerSequenceReader.CreateForPayload(encoded);
+ return reader.ReadOctetString();
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is required for Compat")]
+ public virtual byte[] ComputeCapiSha1OfPublicKey(PublicKey key)
+ {
+ // The CapiSha1 value is the SHA-1 of the SubjectPublicKeyInfo field, inclusive
+ // of the DER structural bytes.
+
+ //SubjectPublicKeyInfo::= SEQUENCE {
+ // algorithm AlgorithmIdentifier{ { SupportedAlgorithms} },
+ // subjectPublicKey BIT STRING,
+ // ... }
+ //
+ //AlgorithmIdentifier{ ALGORITHM: SupportedAlgorithms} ::= SEQUENCE {
+ // algorithm ALGORITHM.&id({ SupportedAlgorithms}),
+ // parameters ALGORITHM.&Type({ SupportedAlgorithms}
+ // { @algorithm}) OPTIONAL,
+ // ... }
+ //
+ //ALGORITHM::= CLASS {
+ // &Type OPTIONAL,
+ // &id OBJECT IDENTIFIER UNIQUE }
+ //WITH SYNTAX {
+ // [&Type]
+ //IDENTIFIED BY &id }
+
+ // key.EncodedKeyValue corresponds to SubjectPublicKeyInfo.subjectPublicKey, except it
+ // has had the BIT STRING envelope removed.
+ //
+ // key.EncodedParameters corresponds to AlgorithmIdentifier.Parameters precisely
+ // (DER NULL for RSA, DER Constructed SEQUENCE for DSA)
+
+ byte[] empty = Array.Empty<byte>();
+ byte[][] algorithmOid = DerEncoder.SegmentedEncodeOid(key.Oid);
+ // Because ConstructSegmentedSequence doesn't look to see that it really is tag+length+value (but does check
+ // that the array has length 3), just hide the joined TLV triplet in the last element.
+ byte[][] segmentedParameters = { empty, empty, key.EncodedParameters.RawData };
+ byte[][] algorithmIdentifier = DerEncoder.ConstructSegmentedSequence(algorithmOid, segmentedParameters);
+ byte[][] subjectPublicKey = DerEncoder.SegmentedEncodeBitString(key.EncodedKeyValue.RawData);
+
+ using (SHA1 hash = SHA1.Create())
+ {
+ return hash.ComputeHash(
+ DerEncoder.ConstructSequence(
+ algorithmIdentifier,
+ subjectPublicKey));
+ }
+ }
+
+ private static byte[] ConcatenateArrays(byte[][] segments)
+ {
+ int length = 0;
+
+ foreach (byte[] segment in segments)
+ {
+ length += segment.Length;
+ }
+
+ byte[] concatenated = new byte[length];
+
+ int offset = 0;
+
+ foreach (byte[] segment in segments)
+ {
+ Buffer.BlockCopy(segment, 0, concatenated, offset, segment.Length);
+ offset += segment.Length;
+ }
+
+ return concatenated;
+ }
+ }
+}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslCertificateFinder.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslCertificateFinder.cs
index 6472c4452d..720c298994 100644
--- a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslCertificateFinder.cs
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslCertificateFinder.cs
@@ -2,355 +2,38 @@
// 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;
-using System.Collections.Generic;
-using System.Numerics;
-using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
-using Microsoft.Win32.SafeHandles;
namespace Internal.Cryptography.Pal
{
- internal sealed class OpenSslCertificateFinder : IFindPal
+ internal sealed class OpenSslCertificateFinder : ManagedCertificateFinder
{
- private readonly X509Certificate2Collection _findFrom;
- private readonly X509Certificate2Collection _copyTo;
- private readonly bool _validOnly;
-
internal OpenSslCertificateFinder(X509Certificate2Collection findFrom, X509Certificate2Collection copyTo, bool validOnly)
+ : base(findFrom, copyTo, validOnly)
{
- _findFrom = findFrom;
- _copyTo = copyTo;
- _validOnly = validOnly;
- }
-
- public string NormalizeOid(string maybeOid, OidGroup expectedGroup)
- {
- Oid oid = new Oid(maybeOid);
-
- // If maybeOid is interpreted to be a FriendlyName, return the OID.
- if (!StringComparer.OrdinalIgnoreCase.Equals(oid.Value, maybeOid))
- {
- return oid.Value;
- }
-
- FindPal.ValidateOidValue(maybeOid);
- return maybeOid;
- }
-
- public void FindByThumbprint(byte[] thumbprint)
- {
- FindCore(cert => cert.GetCertHash().ContentsEqual(thumbprint));
- }
-
- public void FindBySubjectName(string subjectName)
- {
- FindCore(
- cert =>
- {
- string formedSubject = X500NameEncoder.X500DistinguishedNameDecode(cert.SubjectName.RawData, false, X500DistinguishedNameFlags.None);
-
- return formedSubject.IndexOf(subjectName, StringComparison.OrdinalIgnoreCase) >= 0;
- });
- }
-
- public void FindBySubjectDistinguishedName(string subjectDistinguishedName)
- {
- FindCore(cert => StringComparer.OrdinalIgnoreCase.Equals(subjectDistinguishedName, cert.Subject));
- }
-
- public void FindByIssuerName(string issuerName)
- {
- FindCore(
- cert =>
- {
- string formedIssuer = X500NameEncoder.X500DistinguishedNameDecode(cert.IssuerName.RawData, false, X500DistinguishedNameFlags.None);
-
- return formedIssuer.IndexOf(issuerName, StringComparison.OrdinalIgnoreCase) >= 0;
- });
- }
-
- public void FindByIssuerDistinguishedName(string issuerDistinguishedName)
- {
- FindCore(cert => StringComparer.OrdinalIgnoreCase.Equals(issuerDistinguishedName, cert.Issuer));
- }
-
- public void FindBySerialNumber(BigInteger hexValue, BigInteger decimalValue)
- {
- FindCore(
- cert =>
- {
- byte[] serialBytes = cert.GetSerialNumber();
- BigInteger serialNumber = FindPal.PositiveBigIntegerFromByteArray(serialBytes);
- bool match = hexValue.Equals(serialNumber) || decimalValue.Equals(serialNumber);
-
- return match;
- });
- }
-
- private static DateTime NormalizeDateTime(DateTime dateTime)
- {
- // If it's explicitly UTC, convert it to local before a comparison, since the
- // NotBefore and NotAfter are in Local time.
- //
- // If it was Unknown, assume it was Local.
- if (dateTime.Kind == DateTimeKind.Utc)
- {
- return dateTime.ToLocalTime();
- }
-
- return dateTime;
- }
-
- public void FindByTimeValid(DateTime dateTime)
- {
- DateTime normalized = NormalizeDateTime(dateTime);
-
- FindCore(cert => cert.NotBefore <= normalized && normalized <= cert.NotAfter);
- }
-
- public void FindByTimeNotYetValid(DateTime dateTime)
- {
- DateTime normalized = NormalizeDateTime(dateTime);
-
- FindCore(cert => cert.NotBefore > normalized);
- }
-
- public void FindByTimeExpired(DateTime dateTime)
- {
- DateTime normalized = NormalizeDateTime(dateTime);
-
- FindCore(cert => cert.NotAfter < normalized);
- }
-
- public void FindByTemplateName(string templateName)
- {
- FindCore(
- cert =>
- {
- X509Extension ext = FindExtension(cert, Oids.EnrollCertTypeExtension);
-
- if (ext != null)
- {
- // Try a V1 template structure, just a string:
- string decodedName = Interop.Crypto.DerStringToManagedString(ext.RawData);
-
- // If this doesn't match, maybe a V2 template will
- if (StringComparer.OrdinalIgnoreCase.Equals(templateName, decodedName))
- {
- return true;
- }
- }
-
- ext = FindExtension(cert, Oids.CertificateTemplate);
-
- if (ext != null)
- {
- DerSequenceReader reader = new DerSequenceReader(ext.RawData);
- // SEQUENCE (
- // OID oid,
- // INTEGER major,
- // INTEGER minor OPTIONAL
- // )
-
- if (reader.PeekTag() == (byte)DerSequenceReader.DerTag.ObjectIdentifier)
- {
- Oid oid = reader.ReadOid();
-
- if (StringComparer.Ordinal.Equals(templateName, oid.Value))
- {
- return true;
- }
- }
- }
-
- return false;
- });
- }
-
- public void FindByApplicationPolicy(string oidValue)
- {
- FindCore(
- cert =>
- {
- X509Extension ext = FindExtension(cert, Oids.EnhancedKeyUsage);
-
- if (ext == null)
- {
- // A certificate with no EKU is valid for all extended purposes.
- return true;
- }
-
- var ekuExt = (X509EnhancedKeyUsageExtension)ext;
-
- foreach (Oid usageOid in ekuExt.EnhancedKeyUsages)
- {
- if (StringComparer.Ordinal.Equals(oidValue, usageOid.Value))
- {
- return true;
- }
- }
-
- // If the certificate had an EKU extension, and the value we wanted was
- // not present, then it is not valid for that usage.
- return false;
- });
- }
-
- public void FindByCertificatePolicy(string oidValue)
- {
- FindCore(
- cert =>
- {
- X509Extension ext = FindExtension(cert, Oids.CertPolicies);
-
- if (ext == null)
- {
- // Unlike Application Policy, Certificate Policy is "assume false".
- return false;
- }
-
- ISet<string> policyOids = CertificatePolicyChain.ReadCertPolicyExtension(ext);
- return policyOids.Contains(oidValue);
- });
- }
-
- public void FindByExtension(string oidValue)
- {
- FindCore(cert => FindExtension(cert, oidValue) != null);
- }
-
- public void FindByKeyUsage(X509KeyUsageFlags keyUsage)
- {
- FindCore(
- cert =>
- {
- X509Extension ext = FindExtension(cert, Oids.KeyUsage);
-
- if (ext == null)
- {
- // A certificate with no key usage extension is considered valid for all key usages.
- return true;
- }
-
- var kuExt = (X509KeyUsageExtension)ext;
-
- return (kuExt.KeyUsages & keyUsage) == keyUsage;
- });
- }
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is required for Compat")]
- public void FindBySubjectKeyIdentifier(byte[] keyIdentifier)
- {
- FindCore(
- cert =>
- {
- X509Extension ext = FindExtension(cert, Oids.SubjectKeyIdentifier);
- byte[] certKeyId;
-
- if (ext != null)
- {
- // The extension exposes the value as a hexadecimal string, or we can decode here.
- // Enough parsing has gone on, let's decode.
- certKeyId = OpenSslX509Encoder.DecodeX509SubjectKeyIdentifierExtension(ext.RawData);
- }
- else
- {
- // The Desktop/Windows version of this method use CertGetCertificateContextProperty
- // with a property ID of CERT_KEY_IDENTIFIER_PROP_ID.
- //
- // MSDN says that when there's no extension, this method takes the SHA-1 of the
- // SubjectPublicKeyInfo block, and returns that.
- //
- // https://msdn.microsoft.com/en-us/library/windows/desktop/aa376079%28v=vs.85%29.aspx
-
- OpenSslX509CertificateReader certPal = (OpenSslX509CertificateReader)cert.Pal;
-
- using (HashAlgorithm hash = SHA1.Create())
- {
- byte[] publicKeyInfoBytes = Interop.Crypto.OpenSslEncode(
- Interop.Crypto.GetX509SubjectPublicKeyInfoDerSize,
- Interop.Crypto.EncodeX509SubjectPublicKeyInfo,
- certPal.SafeHandle);
-
- certKeyId = hash.ComputeHash(publicKeyInfoBytes);
- }
- }
-
- return keyIdentifier.ContentsEqual(certKeyId);
- });
}
- public void Dispose()
+ protected override string DerStringToManagedString(byte[] anyString)
{
- // No resources to release, but required by the interface.
+ return Interop.Crypto.DerStringToManagedString(anyString);
}
- private static X509Extension FindExtension(X509Certificate2 cert, string extensionOid)
+ protected override byte[] GetSubjectPublicKeyInfo(X509Certificate2 cert)
{
- if (cert.Extensions == null || cert.Extensions.Count == 0)
- {
- return null;
- }
+ OpenSslX509CertificateReader certPal = (OpenSslX509CertificateReader)cert.Pal;
- foreach (X509Extension ext in cert.Extensions)
- {
- if (ext != null &&
- ext.Oid != null &&
- StringComparer.Ordinal.Equals(extensionOid, ext.Oid.Value))
- {
- return ext;
- }
- }
+ byte[] publicKeyInfoBytes = Interop.Crypto.OpenSslEncode(
+ Interop.Crypto.GetX509SubjectPublicKeyInfoDerSize,
+ Interop.Crypto.EncodeX509SubjectPublicKeyInfo,
+ certPal.SafeHandle);
- return null;
+ return publicKeyInfoBytes;
}
-
- private void FindCore(Predicate<X509Certificate2> predicate)
+
+ protected override X509Certificate2 CloneCertificate(X509Certificate2 cert)
{
- foreach (X509Certificate2 cert in _findFrom)
- {
- if (predicate(cert))
- {
- if (!_validOnly || IsCertValid(cert))
- {
- OpenSslX509CertificateReader certPal = (OpenSslX509CertificateReader)cert.Pal;
- _copyTo.Add(new X509Certificate2(certPal.DuplicateHandles()));
- }
- }
- }
- }
-
- private static bool IsCertValid(X509Certificate2 cert)
- {
- try
- {
- // This needs to be kept in sync with VerifyCertificateIgnoringErrors in the
- // Windows PAL version (and potentially any other PALs that come about)
- using (X509Chain chain = new X509Chain
- {
- ChainPolicy =
- {
- RevocationMode = X509RevocationMode.NoCheck,
- RevocationFlag = X509RevocationFlag.ExcludeRoot
- }
- })
- {
- bool valid = chain.Build(cert);
- int elementCount = chain.ChainElements.Count;
-
- for (int i = 0; i < elementCount; i++)
- {
- chain.ChainElements[i].Certificate.Dispose();
- }
-
- return valid;
- }
- }
- catch (CryptographicException)
- {
- return false;
- }
+ OpenSslX509CertificateReader certPal = (OpenSslX509CertificateReader)cert.Pal;
+ return new X509Certificate2(certPal.DuplicateHandles());
}
}
}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs
index 16d04b0f7a..ef4236b38c 100644
--- a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs
@@ -23,71 +23,7 @@ namespace Internal.Cryptography.Pal
public bool? Verify(X509VerificationFlags flags, out Exception exception)
{
exception = null;
- bool isEndEntity = true;
-
- foreach (X509ChainElement element in ChainElements)
- {
- if (HasUnsuppressedError(flags, element, isEndEntity))
- {
- return false;
- }
-
- isEndEntity = false;
- }
-
- return true;
- }
-
- private static bool HasUnsuppressedError(X509VerificationFlags flags, X509ChainElement element, bool isEndEntity)
- {
- foreach (X509ChainStatus status in element.ChainElementStatus)
- {
- if (status.Status == X509ChainStatusFlags.NoError)
- {
- return false;
- }
-
- Debug.Assert(
- (status.Status & (status.Status - 1)) == 0,
- "Only one bit is set in status.Status");
-
- // The Windows certificate store API only checks the time error for a "peer trust" certificate,
- // but we don't have a concept for that in Unix. If we did, we'd need to do that logic that here.
- // Note also that that logic is skipped if CERT_CHAIN_POLICY_IGNORE_PEER_TRUST_FLAG is set.
-
- X509VerificationFlags? suppressionFlag;
-
- if (status.Status == X509ChainStatusFlags.RevocationStatusUnknown)
- {
- if (isEndEntity)
- {
- suppressionFlag = X509VerificationFlags.IgnoreEndRevocationUnknown;
- }
- else if (IsSelfSigned(element.Certificate))
- {
- suppressionFlag = X509VerificationFlags.IgnoreRootRevocationUnknown;
- }
- else
- {
- suppressionFlag = X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown;
- }
- }
- else
- {
- suppressionFlag = GetSuppressionFlag(status.Status);
- }
-
- // If an error was found, and we do NOT have the suppression flag for it enabled,
- // we have an unsuppressed error, so return true. (If there's no suppression for a given code,
- // we (by definition) don't have that flag set.
- if (!suppressionFlag.HasValue ||
- (flags & suppressionFlag) == 0)
- {
- return true;
- }
- }
-
- return false;
+ return ChainVerifier.Verify(ChainElements, flags);
}
public X509ChainElement[] ChainElements { get; private set; }
@@ -349,50 +285,6 @@ namespace Internal.Cryptography.Pal
list.Add(status);
}
-
- private static X509VerificationFlags? GetSuppressionFlag(X509ChainStatusFlags status)
- {
- switch (status)
- {
- case X509ChainStatusFlags.UntrustedRoot:
- case X509ChainStatusFlags.PartialChain:
- return X509VerificationFlags.AllowUnknownCertificateAuthority;
-
- case X509ChainStatusFlags.NotValidForUsage:
- case X509ChainStatusFlags.CtlNotValidForUsage:
- return X509VerificationFlags.IgnoreWrongUsage;
-
- case X509ChainStatusFlags.NotTimeValid:
- return X509VerificationFlags.IgnoreNotTimeValid;
-
- case X509ChainStatusFlags.CtlNotTimeValid:
- return X509VerificationFlags.IgnoreCtlNotTimeValid;
-
- case X509ChainStatusFlags.InvalidNameConstraints:
- case X509ChainStatusFlags.HasNotSupportedNameConstraint:
- case X509ChainStatusFlags.HasNotDefinedNameConstraint:
- case X509ChainStatusFlags.HasNotPermittedNameConstraint:
- case X509ChainStatusFlags.HasExcludedNameConstraint:
- return X509VerificationFlags.IgnoreInvalidName;
-
- case X509ChainStatusFlags.InvalidPolicyConstraints:
- case X509ChainStatusFlags.NoIssuanceChainPolicy:
- return X509VerificationFlags.IgnoreInvalidPolicy;
-
- case X509ChainStatusFlags.InvalidBasicConstraints:
- return X509VerificationFlags.IgnoreInvalidBasicConstraints;
-
- case X509ChainStatusFlags.HasNotSupportedCriticalExtension:
- // This field would be mapped in by AllFlags, but we don't have a name for it currently.
- return (X509VerificationFlags)0x00002000;
-
- case X509ChainStatusFlags.NotTimeNested:
- return X509VerificationFlags.IgnoreNotTimeNested;
- }
-
- return null;
- }
-
private static X509ChainStatusFlags MapVerifyErrorToChainStatus(Interop.Crypto.X509VerifyStatusCode code)
{
switch (code)
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509Encoder.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509Encoder.cs
index faa2dfe12f..b0c395642b 100644
--- a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509Encoder.cs
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509Encoder.cs
@@ -3,7 +3,6 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
@@ -11,7 +10,7 @@ using Microsoft.Win32.SafeHandles;
namespace Internal.Cryptography.Pal
{
- internal sealed class OpenSslX509Encoder : IX509Pal
+ internal sealed class OpenSslX509Encoder : ManagedX509ExtensionProcessor, IX509Pal
{
public AsymmetricAlgorithm DecodePublicKey(Oid oid, byte[] encodedKeyValue, byte[] encodedParameters, ICertificatePal certificatePal)
{
@@ -155,24 +154,7 @@ namespace Internal.Cryptography.Pal
throw new CryptographicException();
}
- public byte[] EncodeX509KeyUsageExtension(X509KeyUsageFlags keyUsages)
- {
- // The numeric values of X509KeyUsageFlags mean that if we interpret it as a little-endian
- // ushort it will line up with the flags in the spec.
- ushort ushortValue = unchecked((ushort)(int)keyUsages);
- byte[] data = BitConverter.GetBytes(ushortValue);
-
- // RFC 3280 section 4.2.1.3 (https://tools.ietf.org/html/rfc3280#section-4.2.1.3) defines
- // digitalSignature (0) through decipherOnly (8), making 9 named bits.
- const int namedBitsCount = 9;
-
- // The expected output of this method isn't the SEQUENCE value, but just the payload bytes.
- byte[][] segments = DerEncoder.SegmentedEncodeNamedBitList(data, namedBitsCount);
- Debug.Assert(segments.Length == 3);
- return ConcatenateArrays(segments);
- }
-
- public void DecodeX509KeyUsageExtension(byte[] encoded, out X509KeyUsageFlags keyUsages)
+ public override void DecodeX509KeyUsageExtension(byte[] encoded, out X509KeyUsageFlags keyUsages)
{
using (SafeAsn1BitStringHandle bitString = Interop.Crypto.DecodeAsn1BitString(encoded, encoded.Length))
{
@@ -225,54 +207,7 @@ namespace Internal.Cryptography.Pal
}
}
- public bool SupportsLegacyBasicConstraintsExtension
- {
- get { return false; }
- }
-
- public byte[] EncodeX509BasicConstraints2Extension(
- bool certificateAuthority,
- bool hasPathLengthConstraint,
- int pathLengthConstraint)
- {
- //BasicConstraintsSyntax::= SEQUENCE {
- // cA BOOLEAN DEFAULT FALSE,
- // pathLenConstraint INTEGER(0..MAX) OPTIONAL,
- // ... }
-
- List<byte[][]> segments = new List<byte[][]>(2);
-
- if (certificateAuthority)
- {
- segments.Add(DerEncoder.SegmentedEncodeBoolean(true));
- }
-
- if (hasPathLengthConstraint)
- {
- byte[] pathLengthBytes = BitConverter.GetBytes(pathLengthConstraint);
- // Little-Endian => Big-Endian
- Array.Reverse(pathLengthBytes);
- segments.Add(DerEncoder.SegmentedEncodeUnsignedInteger(pathLengthBytes));
- }
-
- return DerEncoder.ConstructSequence(segments);
- }
-
- public void DecodeX509BasicConstraintsExtension(
- byte[] encoded,
- out bool certificateAuthority,
- out bool hasPathLengthConstraint,
- out int pathLengthConstraint)
- {
- // No RFC nor ITU document describes the layout of the 2.5.29.10 structure,
- // and OpenSSL doesn't have a decoder for it, either.
- //
- // Since it was never published as a standard (2.5.29.19 replaced it before publication)
- // there shouldn't be too many people upset that we can't decode it for them on Unix.
- throw new PlatformNotSupportedException(SR.NotSupported_LegacyBasicConstraints);
- }
-
- public void DecodeX509BasicConstraints2Extension(
+ public override void DecodeX509BasicConstraints2Extension(
byte[] encoded,
out bool certificateAuthority,
out bool hasPathLengthConstraint,
@@ -289,25 +224,7 @@ namespace Internal.Cryptography.Pal
}
}
- public byte[] EncodeX509EnhancedKeyUsageExtension(OidCollection usages)
- {
- //extKeyUsage EXTENSION ::= {
- // SYNTAX SEQUENCE SIZE(1..MAX) OF KeyPurposeId
- // IDENTIFIED BY id - ce - extKeyUsage }
- //
- //KeyPurposeId::= OBJECT IDENTIFIER
-
- List<byte[][]> segments = new List<byte[][]>(usages.Count);
-
- foreach (Oid usage in usages)
- {
- segments.Add(DerEncoder.SegmentedEncodeOid(usage));
- }
-
- return DerEncoder.ConstructSequence(segments);
- }
-
- public void DecodeX509EnhancedKeyUsageExtension(byte[] encoded, out OidCollection usages)
+ public override void DecodeX509EnhancedKeyUsageExtension(byte[] encoded, out OidCollection usages)
{
OidCollection oids = new OidCollection();
@@ -335,80 +252,6 @@ namespace Internal.Cryptography.Pal
usages = oids;
}
- public byte[] EncodeX509SubjectKeyIdentifierExtension(byte[] subjectKeyIdentifier)
- {
- //subjectKeyIdentifier EXTENSION ::= {
- // SYNTAX SubjectKeyIdentifier
- // IDENTIFIED BY id - ce - subjectKeyIdentifier }
- //
- //SubjectKeyIdentifier::= KeyIdentifier
- //
- //KeyIdentifier ::= OCTET STRING
-
- byte[][] segments = DerEncoder.SegmentedEncodeOctetString(subjectKeyIdentifier);
-
- // The extension is not a sequence, just the octet string
- return ConcatenateArrays(segments);
- }
-
- public void DecodeX509SubjectKeyIdentifierExtension(byte[] encoded, out byte[] subjectKeyIdentifier)
- {
- subjectKeyIdentifier = DecodeX509SubjectKeyIdentifierExtension(encoded);
- }
-
- internal static byte[] DecodeX509SubjectKeyIdentifierExtension(byte[] encoded)
- {
- DerSequenceReader reader = DerSequenceReader.CreateForPayload(encoded);
- return reader.ReadOctetString();
- }
-
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "SHA1 is required for Compat")]
- public byte[] ComputeCapiSha1OfPublicKey(PublicKey key)
- {
- // The CapiSha1 value is the SHA-1 of the SubjectPublicKeyInfo field, inclusive
- // of the DER structural bytes.
-
- //SubjectPublicKeyInfo::= SEQUENCE {
- // algorithm AlgorithmIdentifier{ { SupportedAlgorithms} },
- // subjectPublicKey BIT STRING,
- // ... }
- //
- //AlgorithmIdentifier{ ALGORITHM: SupportedAlgorithms} ::= SEQUENCE {
- // algorithm ALGORITHM.&id({ SupportedAlgorithms}),
- // parameters ALGORITHM.&Type({ SupportedAlgorithms}
- // { @algorithm}) OPTIONAL,
- // ... }
- //
- //ALGORITHM::= CLASS {
- // &Type OPTIONAL,
- // &id OBJECT IDENTIFIER UNIQUE }
- //WITH SYNTAX {
- // [&Type]
- //IDENTIFIED BY &id }
-
- // key.EncodedKeyValue corresponds to SubjectPublicKeyInfo.subjectPublicKey, except it
- // has had the BIT STRING envelope removed.
- //
- // key.EncodedParameters corresponds to AlgorithmIdentifier.Parameters precisely
- // (DER NULL for RSA, DER Constructed SEQUENCE for DSA)
-
- byte[] empty = Array.Empty<byte>();
- byte[][] algorithmOid = DerEncoder.SegmentedEncodeOid(key.Oid);
- // Because ConstructSegmentedSequence doesn't look to see that it really is tag+length+value (but does check
- // that the array has length 3), just hide the joined TLV triplet in the last element.
- byte[][] segmentedParameters = { empty, empty, key.EncodedParameters.RawData };
- byte[][] algorithmIdentifier = DerEncoder.ConstructSegmentedSequence(algorithmOid, segmentedParameters);
- byte[][] subjectPublicKey = DerEncoder.SegmentedEncodeBitString(key.EncodedKeyValue.RawData);
-
- using (SHA1 hash = SHA1.Create())
- {
- return hash.ComputeHash(
- DerEncoder.ConstructSequence(
- algorithmIdentifier,
- subjectPublicKey));
- }
- }
-
private static RSA BuildRsaPublicKey(byte[] encodedData)
{
using (SafeRsaHandle rsaHandle = Interop.Crypto.DecodeRsaPublicKey(encodedData, encodedData.Length))
@@ -490,27 +333,5 @@ namespace Internal.Cryptography.Pal
throw new CryptographicException();
}
-
- private static byte[] ConcatenateArrays(byte[][] segments)
- {
- int length = 0;
-
- foreach (byte[] segment in segments)
- {
- length += segment.Length;
- }
-
- byte[] concatenated = new byte[length];
-
- int offset = 0;
-
- foreach (byte[] segment in segments)
- {
- Buffer.BlockCopy(segment, 0, concatenated, offset, segment.Length);
- offset += segment.Length;
- }
-
- return concatenated;
- }
}
}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.ManagedDecode.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.ManagedDecode.cs
new file mode 100644
index 0000000000..094e95c20d
--- /dev/null
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.ManagedDecode.cs
@@ -0,0 +1,159 @@
+// 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;
+using System.Collections.Generic;
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Internal.Cryptography.Pal
+{
+ internal static partial class X500NameEncoder
+ {
+ private static string X500DistinguishedNameDecode(
+ byte[] encodedName,
+ bool printOid,
+ bool reverse,
+ bool quoteIfNeeded,
+ string dnSeparator,
+ string multiValueSeparator,
+ bool addTrailingDelimiter)
+ {
+ DerSequenceReader x500NameReader = new DerSequenceReader(encodedName);
+ var rdnReaders = new List<DerSequenceReader>();
+
+ while (x500NameReader.HasData)
+ {
+ rdnReaders.Add(x500NameReader.ReadSet());
+ }
+
+ // We need to allocate a StringBuilder to hold the data as we're building it, and there's the usual
+ // arbitrary process of choosing a number that's "big enough" to minimize reallocations without wasting
+ // too much space in the average case.
+ //
+ // So, let's look at an example of what our output might be.
+ //
+ // GitHub.com's SSL cert has a "pretty long" subject (partially due to the unknown OIDs):
+ // businessCategory=Private Organization
+ // 1.3.6.1.4.1.311.60.2.1.3=US
+ // 1.3.6.1.4.1.311.60.2.1.2=Delaware
+ // serialNumber=5157550
+ // street=548 4th Street
+ // postalCode=94107
+ // C=US
+ // ST=California
+ // L=San Francisco
+ // O=GitHub, Inc.
+ // CN=github.com
+ //
+ // Which comes out to 228 characters using OpenSSL's default pretty-print
+ // (openssl x509 -in github.cer -text -noout)
+ // Throw in some "maybe-I-need-to-quote-this" quotes, and a couple of extra/extra-long O/OU values
+ // and round that up to the next programmer number, and you get that 512 should avoid reallocations
+ // in all but the most dire of cases.
+ StringBuilder decodedName = new StringBuilder(512);
+ int entryCount = rdnReaders.Count;
+ bool printSpacing = false;
+
+ for (int i = 0; i < entryCount; i++)
+ {
+ int loc = reverse ? entryCount - i - 1 : i;
+
+ // RelativeDistinguishedName ::=
+ // SET SIZE (1..MAX) OF AttributeTypeAndValue
+ //
+ // AttributeTypeAndValue::= SEQUENCE {
+ // type AttributeType,
+ // value AttributeValue }
+ //
+ // AttributeType::= OBJECT IDENTIFIER
+ //
+ // AttributeValue ::= ANY-- DEFINED BY AttributeType
+
+ if (printSpacing)
+ {
+ decodedName.Append(dnSeparator);
+ }
+ else
+ {
+ printSpacing = true;
+ }
+
+ DerSequenceReader rdnReader = rdnReaders[loc];
+ bool hadValue = false;
+
+ while (rdnReader.HasData)
+ {
+ DerSequenceReader tavReader = rdnReader.ReadSequence();
+
+ if (hadValue)
+ {
+ decodedName.Append(multiValueSeparator);
+ }
+ else
+ {
+ hadValue = true;
+ }
+
+ if (printOid)
+ {
+ AppendOid(decodedName, tavReader.ReadOidAsString());
+ }
+ else
+ {
+ tavReader.SkipValue();
+ }
+
+ string attributeValue = ReadString(tavReader);
+
+ bool quote = quoteIfNeeded && NeedsQuoting(attributeValue);
+
+ if (quote)
+ {
+ decodedName.Append('"');
+
+ // If the RDN itself had a quote within it, that quote needs to be escaped
+ // with another quote.
+ attributeValue = attributeValue.Replace("\"", "\"\"");
+ }
+
+ decodedName.Append(attributeValue);
+
+ if (quote)
+ {
+ decodedName.Append('"');
+ }
+ }
+ }
+
+ if (addTrailingDelimiter)
+ {
+ decodedName.Append(dnSeparator);
+ }
+
+ return decodedName.ToString();
+ }
+
+ private static string ReadString(DerSequenceReader tavReader)
+ {
+ var tag = (DerSequenceReader.DerTag)tavReader.PeekTag();
+
+ switch (tag)
+ {
+ case DerSequenceReader.DerTag.BMPString:
+ return tavReader.ReadBMPString();
+ case DerSequenceReader.DerTag.IA5String:
+ return tavReader.ReadIA5String();
+ case DerSequenceReader.DerTag.PrintableString:
+ return tavReader.ReadPrintableString();
+ case DerSequenceReader.DerTag.UTF8String:
+ return tavReader.ReadUtf8String();
+ case DerSequenceReader.DerTag.T61String:
+ return "";
+ default:
+ throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
+ }
+ }
+ }
+}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.OpenSslDecode.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.OpenSslDecode.cs
new file mode 100644
index 0000000000..eeaca53b1f
--- /dev/null
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.OpenSslDecode.cs
@@ -0,0 +1,122 @@
+// 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.Text;
+
+using Microsoft.Win32.SafeHandles;
+
+namespace Internal.Cryptography.Pal
+{
+ internal static partial class X500NameEncoder
+ {
+ private static string X500DistinguishedNameDecode(
+ byte[] encodedName,
+ bool printOid,
+ bool reverse,
+ bool quoteIfNeeded,
+ string dnSeparator,
+ string multiValueSeparator,
+ bool addTrailingDelimiter)
+ {
+ using (SafeX509NameHandle x509Name = Interop.Crypto.DecodeX509Name(encodedName, encodedName.Length))
+ {
+ if (x509Name.IsInvalid)
+ {
+ return "";
+ }
+
+ // We need to allocate a StringBuilder to hold the data as we're building it, and there's the usual
+ // arbitrary process of choosing a number that's "big enough" to minimize reallocations without wasting
+ // too much space in the average case.
+ //
+ // So, let's look at an example of what our output might be.
+ //
+ // GitHub.com's SSL cert has a "pretty long" subject (partially due to the unknown OIDs):
+ // businessCategory=Private Organization
+ // 1.3.6.1.4.1.311.60.2.1.3=US
+ // 1.3.6.1.4.1.311.60.2.1.2=Delaware
+ // serialNumber=5157550
+ // street=548 4th Street
+ // postalCode=94107
+ // C=US
+ // ST=California
+ // L=San Francisco
+ // O=GitHub, Inc.
+ // CN=github.com
+ //
+ // Which comes out to 228 characters using OpenSSL's default pretty-print
+ // (openssl x509 -in github.cer -text -noout)
+ // Throw in some "maybe-I-need-to-quote-this" quotes, and a couple of extra/extra-long O/OU values
+ // and round that up to the next programmer number, and you get that 512 should avoid reallocations
+ // in all but the most dire of cases.
+ StringBuilder decodedName = new StringBuilder(512);
+ int entryCount = Interop.Crypto.GetX509NameEntryCount(x509Name);
+ bool printSpacing = false;
+
+ for (int i = 0; i < entryCount; i++)
+ {
+ int loc = reverse ? entryCount - i - 1 : i;
+
+ using (SafeSharedX509NameEntryHandle nameEntry = Interop.Crypto.GetX509NameEntry(x509Name, loc))
+ {
+ Interop.Crypto.CheckValidOpenSslHandle(nameEntry);
+
+ string thisOidValue;
+
+ using (SafeSharedAsn1ObjectHandle oidHandle = Interop.Crypto.GetX509NameEntryOid(nameEntry))
+ {
+ thisOidValue = Interop.Crypto.GetOidValue(oidHandle);
+ }
+
+ if (printSpacing)
+ {
+ decodedName.Append(dnSeparator);
+ }
+ else
+ {
+ printSpacing = true;
+ }
+
+ if (printOid)
+ {
+ AppendOid(decodedName, thisOidValue);
+ }
+
+ string rdnValue;
+
+ using (SafeSharedAsn1StringHandle valueHandle = Interop.Crypto.GetX509NameEntryData(nameEntry))
+ {
+ rdnValue = Interop.Crypto.Asn1StringToManagedString(valueHandle);
+ }
+
+ bool quote = quoteIfNeeded && NeedsQuoting(rdnValue);
+
+ if (quote)
+ {
+ decodedName.Append('"');
+
+ // If the RDN itself had a quote within it, that quote needs to be escaped
+ // with another quote.
+ rdnValue = rdnValue.Replace("\"", "\"\"");
+ }
+
+ decodedName.Append(rdnValue);
+
+ if (quote)
+ {
+ decodedName.Append('"');
+ }
+ }
+ }
+
+ if (addTrailingDelimiter)
+ {
+ decodedName.Append(dnSeparator);
+ }
+
+ return decodedName.ToString();
+ }
+ }
+ }
+}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.cs b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.cs
index 145fd89ae5..56da9ae40c 100644
--- a/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.cs
+++ b/src/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/X500NameEncoder.cs
@@ -13,7 +13,7 @@ using Microsoft.Win32.SafeHandles;
namespace Internal.Cryptography.Pal
{
- internal static class X500NameEncoder
+ internal static partial class X500NameEncoder
{
private const string OidTagPrefix = "OID.";
@@ -44,6 +44,7 @@ namespace Internal.Cryptography.Pal
{
bool reverse = (flags & X500DistinguishedNameFlags.Reversed) == X500DistinguishedNameFlags.Reversed;
bool quoteIfNeeded = (flags & X500DistinguishedNameFlags.DoNotUseQuotes) != X500DistinguishedNameFlags.DoNotUseQuotes;
+ bool useMultiSeparator = (flags & X500DistinguishedNameFlags.DoNotUsePlusSign) != X500DistinguishedNameFlags.DoNotUsePlusSign;
string dnSeparator;
if ((flags & X500DistinguishedNameFlags.UseSemicolons) == X500DistinguishedNameFlags.UseSemicolons)
@@ -61,103 +62,23 @@ namespace Internal.Cryptography.Pal
dnSeparator = ", ";
}
- using (SafeX509NameHandle x509Name = Interop.Crypto.DecodeX509Name(encodedName, encodedName.Length))
- {
- if (x509Name.IsInvalid)
- {
- return "";
- }
-
- // We need to allocate a StringBuilder to hold the data as we're building it, and there's the usual
- // arbitrary process of choosing a number that's "big enough" to minimize reallocations without wasting
- // too much space in the average case.
- //
- // So, let's look at an example of what our output might be.
- //
- // GitHub.com's SSL cert has a "pretty long" subject (partially due to the unknown OIDs):
- // businessCategory=Private Organization
- // 1.3.6.1.4.1.311.60.2.1.3=US
- // 1.3.6.1.4.1.311.60.2.1.2=Delaware
- // serialNumber=5157550
- // street=548 4th Street
- // postalCode=94107
- // C=US
- // ST=California
- // L=San Francisco
- // O=GitHub, Inc.
- // CN=github.com
- //
- // Which comes out to 228 characters using OpenSSL's default pretty-print
- // (openssl x509 -in github.cer -text -noout)
- // Throw in some "maybe-I-need-to-quote-this" quotes, and a couple of extra/extra-long O/OU values
- // and round that up to the next programmer number, and you get that 512 should avoid reallocations
- // in all but the most dire of cases.
- StringBuilder decodedName = new StringBuilder(512);
- int entryCount = Interop.Crypto.GetX509NameEntryCount(x509Name);
- bool printSpacing = false;
-
- for (int i = 0; i < entryCount; i++)
- {
- int loc = reverse ? entryCount - i - 1 : i;
-
- using (SafeSharedX509NameEntryHandle nameEntry = Interop.Crypto.GetX509NameEntry(x509Name, loc))
- {
- Interop.Crypto.CheckValidOpenSslHandle(nameEntry);
-
- string thisOidValue;
-
- using (SafeSharedAsn1ObjectHandle oidHandle = Interop.Crypto.GetX509NameEntryOid(nameEntry))
- {
- thisOidValue = Interop.Crypto.GetOidValue(oidHandle);
- }
+ string multiValueSparator = useMultiSeparator ? " + " : " ";
- if (printSpacing)
- {
- decodedName.Append(dnSeparator);
- }
- else
- {
- printSpacing = true;
- }
-
- if (printOid)
- {
- AppendOid(decodedName, thisOidValue);
- }
-
- string rdnValue;
-
- using (SafeSharedAsn1StringHandle valueHandle = Interop.Crypto.GetX509NameEntryData(nameEntry))
- {
- rdnValue = Interop.Crypto.Asn1StringToManagedString(valueHandle);
- }
-
- bool quote = quoteIfNeeded && NeedsQuoting(rdnValue);
-
- if (quote)
- {
- decodedName.Append('"');
-
- // If the RDN itself had a quote within it, that quote needs to be escaped
- // with another quote.
- rdnValue = rdnValue.Replace("\"", "\"\"");
- }
-
- decodedName.Append(rdnValue);
-
- if (quote)
- {
- decodedName.Append('"');
- }
- }
- }
-
- if (addTrailingDelimieter)
- {
- decodedName.Append(dnSeparator);
- }
-
- return decodedName.ToString();
+ try
+ {
+ return X500DistinguishedNameDecode(
+ encodedName,
+ printOid,
+ reverse,
+ quoteIfNeeded,
+ dnSeparator,
+ multiValueSparator,
+ addTrailingDelimieter);
+ }
+ catch (CryptographicException)
+ {
+ // Windows compat:
+ return "";
}
}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Microsoft/Win32/SafeHandles/SafePasswordHandle.cs b/src/System.Security.Cryptography.X509Certificates/src/Microsoft/Win32/SafeHandles/SafePasswordHandle.cs
index a594cc072d..f9235193b4 100644
--- a/src/System.Security.Cryptography.X509Certificates/src/Microsoft/Win32/SafeHandles/SafePasswordHandle.cs
+++ b/src/System.Security.Cryptography.X509Certificates/src/Microsoft/Win32/SafeHandles/SafePasswordHandle.cs
@@ -41,6 +41,25 @@ namespace Microsoft.Win32.SafeHandles
return true;
}
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && SafeHandleCache<SafePasswordHandle>.IsCachedInvalidHandle(this))
+ {
+ return;
+ }
+
+ base.Dispose(disposing);
+ }
+
public override bool IsInvalid => handle == (IntPtr)(-1);
+
+ public static SafePasswordHandle InvalidHandle =>
+ SafeHandleCache<SafePasswordHandle>.GetInvalidHandle(
+ () =>
+ {
+ var handle = new SafePasswordHandle((string)null);
+ handle.handle = (IntPtr)(-1);
+ return handle;
+ });
}
}
diff --git a/src/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx b/src/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx
index 9a5a85ec6c..5c6a344613 100644
--- a/src/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx
+++ b/src/System.Security.Cryptography.X509Certificates/src/Resources/Strings.resx
@@ -58,6 +58,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
+ <data name="Arg_CryptographyException" xml:space="preserve">
+ <value>Error occurred during a cryptographic operation.</value>
+ </data>
<data name="Arg_EmptyOrNullArray" xml:space="preserve">
<value>Array may not be empty or null.</value>
</data>
@@ -97,15 +100,39 @@
<data name="Chain_NoPolicyMatch" xml:space="preserve">
<value>The certificate has invalid policy.</value>
</data>
+ <data name="Cryptography_CSP_NoPrivateKey" xml:space="preserve">
+ <value>Object contains only the public half of a key pair. A private key must also be provided.</value>
+ </data>
+ <data name="Cryptography_CurveNotSupported" xml:space="preserve">
+ <value>The specified curve '{0}' or its parameters are not valid for this platform.</value>
+ </data>
+ <data name="Cryptography_ECC_NamedCurvesOnly" xml:space="preserve">
+ <value>Only named curves are supported on this platform.</value>
+ </data>
+ <data name="Cryptography_Der_Invalid_Encoding" xml:space="preserve">
+ <value>ASN1 corrupted data.</value>
+ </data>
<data name="Cryptography_InvalidContextHandle" xml:space="preserve">
<value>The chain context handle is invalid.</value>
</data>
<data name="Cryptography_InvalidHandle" xml:space="preserve">
<value>{0} is an invalid handle.</value>
</data>
+ <data name="Cryptography_InvalidOID" xml:space="preserve">
+ <value>Object identifier (OID) is unknown.</value>
+ </data>
+ <data name="Cryptography_InvalidPaddingMode" xml:space="preserve">
+ <value>Specified padding mode is not valid for this algorithm.</value>
+ </data>
+ <data name="Cryptography_InvalidRsaParameters" xml:space="preserve">
+ <value>The specified RSA parameters are not valid; both Exponent and Modulus are required fields.</value>
+ </data>
<data name="Cryptography_InvalidStoreHandle" xml:space="preserve">
<value>The store handle is invalid.</value>
</data>
+ <data name="Cryptography_OpenInvalidHandle" xml:space="preserve">
+ <value>Cannot open an invalid handle.</value>
+ </data>
<data name="Cryptography_Unix_X509_MachineStoresReadOnly" xml:space="preserve">
<value>Unix LocalMachine X509Stores are read-only for all users.</value>
</data>
@@ -115,9 +142,18 @@
<data name="Cryptography_Unix_X509_PropertyNotSettable" xml:space="preserve">
<value>The {0} value cannot be set on Unix.</value>
</data>
+ <data name="Cryptography_UnknownHashAlgorithm" xml:space="preserve">
+ <value>'{0}' is not a known hash algorithm.</value>
+ </data>
<data name="Cryptography_Unix_X509_SerializedExport" xml:space="preserve">
<value>X509ContentType.SerializedCert and X509ContentType.SerializedStore are not supported on Unix.</value>
</data>
+ <data name="Cryptography_Unmapped_System_Typed_Error" xml:space="preserve">
+ <value>The system cryptographic library returned error '{0}' of type '{1}'</value>
+ </data>
+ <data name="Cryptography_X509_ExportFailed" xml:space="preserve">
+ <value>The certificate export operation failed.</value>
+ </data>
<data name="Cryptography_X509_ExtensionMismatch" xml:space="preserve">
<value>The parameter should be an X509Extension.</value>
</data>
@@ -148,6 +184,9 @@
<data name="Cryptography_X509_StoreReadOnly" xml:space="preserve">
<value>The X509 certificate store is read-only.</value>
</data>
+ <data name="Cryptography_X509_StoreCannotCreate" xml:space="preserve">
+ <value>The platform does not have a definition for an X509 certificate store named '{0}' with a StoreLocation of '{1}', and does not support creating it.</value>
+ </data>
<data name="InvalidOperation_EnumNotStarted" xml:space="preserve">
<value>Enumeration has not started. Call MoveNext.</value>
</data>
@@ -169,6 +208,9 @@
<data name="Security_InvalidValue" xml:space="preserve">
<value>The {0} value was invalid.</value>
</data>
+ <data name="Security_AccessDenied" xml:space="preserve">
+ <value>Access is denied.</value>
+ </data>
<data name="Unknown_Error" xml:space="preserve">
<value>Unknown error.</value>
</data>
@@ -189,5 +231,41 @@
</data>
<data name="Cryptography_Invalid_IA5String" xml:space="preserve">
<value>The string contains a character not in the 7 bit ASCII character set.</value>
- </data>
+ </data>
+ <data name="Cryptography_X509_NoEphemeralPfx" xml:space="preserve">
+ <value>This platform does not support loading with EphemeralKeySet. Remove the flag to allow keys to be temporarily created on disk.</value>
+ </data>
+ <data name="Cryptography_X509Store_WouldModifyUserTrust" xml:space="preserve">
+ <value>Removing the requested certificate would modify user trust settings, and has been denied.</value>
+ </data>
+ <data name="Cryptography_X509Store_WouldModifyAdminTrust" xml:space="preserve">
+ <value>Removing the requested certificate would modify admin trust settings, and has been denied.</value>
+ </data>
+ <data name="Cryptography_InvalidKeySize" xml:space="preserve">
+ <value>Specified key is not a valid size for this algorithm.</value>
+ </data>
+ <data name="Cryptography_DSA_KeyGenNotSupported" xml:space="preserve">
+ <value>DSA keys can be imported, but new key generation is not supported on this platform.</value>
+ </data>
+ <data name="Cryptography_InvalidDsaParameters_MissingFields" xml:space="preserve">
+ <value>The specified DSA parameters are not valid; P, Q, G and Y are all required.</value>
+ </data>
+ <data name="Cryptography_InvalidDsaParameters_MismatchedPGY" xml:space="preserve">
+ <value>The specified DSA parameters are not valid; P, G and Y must be the same length (the key size).</value>
+ </data>
+ <data name="Cryptography_InvalidDsaParameters_MismatchedQX" xml:space="preserve">
+ <value>The specified DSA parameters are not valid; Q and X (if present) must be the same length.</value>
+ </data>
+ <data name="Cryptography_InvalidDsaParameters_MismatchedPJ" xml:space="preserve">
+ <value>The specified DSA parameters are not valid; J (if present) must be shorter than P.</value>
+ </data>
+ <data name="Cryptography_InvalidDsaParameters_SeedRestriction_ShortKey" xml:space="preserve">
+ <value>The specified DSA parameters are not valid; Seed, if present, must be 20 bytes long for keys shorter than 1024 bits.</value>
+ </data>
+ <data name="Cryptography_InvalidDsaParameters_QRestriction_ShortKey" xml:space="preserve">
+ <value>The specified DSA parameters are not valid; Q must be 20 bytes long for keys shorter than 1024 bits.</value>
+ </data>
+ <data name="Cryptography_InvalidDsaParameters_QRestriction_LargeKey" xml:space="preserve">
+ <value>The specified DSA parameters are not valid; Q's length must be one of 20, 32 or 64 bytes.</value>
+ </data>
</root>
diff --git a/src/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj b/src/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj
index 2e1ef4d64d..76642c41d7 100644
--- a/src/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj
+++ b/src/System.Security.Cryptography.X509Certificates/src/System.Security.Cryptography.X509Certificates.csproj
@@ -132,10 +132,12 @@
</Compile>
</ItemGroup>
<ItemGroup Condition=" '$(TargetsUnix)' == 'true'">
+ <Compile Include="Microsoft\Win32\SafeHandles\SafePasswordHandle.Unix.cs" />
+ </ItemGroup>
+ <ItemGroup Condition=" '$(TargetsUnix)' == 'true' AND '$(TargetsOSX)' != 'true' ">
<Compile Include="Internal\Cryptography\Pal.Unix\CertCollectionLoader.cs" />
<Compile Include="Internal\Cryptography\Pal.Unix\CertificateAssetDownloader.cs" />
<Compile Include="Internal\Cryptography\Pal.Unix\CertificatePal.cs" />
- <Compile Include="Internal\Cryptography\Pal.Unix\CertificatePolicy.cs" />
<Compile Include="Internal\Cryptography\Pal.Unix\ChainPal.cs" />
<Compile Include="Internal\Cryptography\Pal.Unix\CollectionBackedStoreProvider.cs" />
<Compile Include="Internal\Cryptography\Pal.Unix\CrlCache.cs" />
@@ -150,10 +152,9 @@
<Compile Include="Internal\Cryptography\Pal.Unix\PkcsFormatReader.cs" />
<Compile Include="Internal\Cryptography\Pal.Unix\SingleCertLoader.cs" />
<Compile Include="Internal\Cryptography\Pal.Unix\StorePal.cs" />
- <Compile Include="Internal\Cryptography\Pal.Unix\X500NameEncoder.cs" />
+ <Compile Include="Internal\Cryptography\Pal.Unix\X500NameEncoder.OpenSslDecode.cs" />
<Compile Include="Internal\Cryptography\Pal.Unix\X509Pal.cs" />
<Compile Include="Internal\Cryptography\Pal.Unix\X509Persistence.cs" />
- <Compile Include="Microsoft\Win32\SafeHandles\SafePasswordHandle.Unix.cs" />
<Compile Include="$(CommonPath)\Interop\Unix\Interop.Libraries.cs">
<Link>Common\Interop\Unix\Interop.Libraries.cs</Link>
</Compile>
@@ -280,12 +281,112 @@
<Compile Include="$(CommonPath)\System\IO\PersistedFiles.Names.Unix.cs">
<Link>Common\System\IO\PersistedFiles.Names.Unix.cs</Link>
</Compile>
+ </ItemGroup>
+ <ItemGroup Condition=" '$(TargetsOSX)' == 'true' AND '$(IsPartialFacadeAssembly)' != 'true'">
+ <Compile Include="$(CommonPath)\Internal\Cryptography\AsymmetricAlgorithmHelpers.cs">
+ <Link>Common\Internal\Cryptography\AsymmetricAlgorithmHelpers.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFArray.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFArray.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFData.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFData.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFDate.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFDate.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFError.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFError.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.CoreFoundation.CFString.cs">
+ <Link>Common\Interop\OSX\Interop.CoreFoundation.CFString.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\Interop.Libraries.cs">
+ <Link>Common\Interop\OSX\Interop.Libraries.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Ecc.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Ecc.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Err.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Err.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Keychain.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Keychain.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.PAL_HashAlgorithm.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.PAL_HashAlgorithm.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.RSA.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.RSA.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecErr.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecErr.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecErrMessage.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecErrMessage.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecKeyRef.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecKeyRef.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecKeyRef.Export.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.SecKeyRef.Export.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Trust.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.Trust.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.X509.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.X509.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.X509Chain.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.X509Chain.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.X509Store.cs">
+ <Link>Common\Interop\OSX\System.Security.Cryptography.Native.Apple\Interop.X509Store.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\Microsoft\Win32\SafeHandles\SafeCreateHandle.OSX.cs">
+ <Link>Common\Microsoft\Win32\SafeHandles\SafeCreateHandle.OSX.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\DSASecurityTransforms.cs">
+ <Link>Common\System\Security\Cryptography\DSASecurityTransforms.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\ECDsaSecurityTransforms.cs">
+ <Link>Common\System\Security\Cryptography\ECDsaSecurityTransforms.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\KeyBlobHelpers.cs">
+ <Link>Common\System\Security\Cryptography\KeyBlobHelpers.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\RSASecurityTransforms.cs">
+ <Link>Common\System\Security\Cryptography\RSASecurityTransforms.cs</Link>
+ </Compile>
+ <Compile Include="$(CommonPath)\System\Security\Cryptography\SecKeyPair.cs">
+ <Link>Common\System\Security\Cryptography\SecKeyPair.cs</Link>
+ </Compile>
+ <Compile Include="Internal\Cryptography\Pal.OSX\CertificatePal.cs" />
+ <Compile Include="Internal\Cryptography\Pal.OSX\ChainPal.cs" />
+ <Compile Include="Internal\Cryptography\Pal.OSX\FindPal.cs" />
+ <Compile Include="Internal\Cryptography\Pal.OSX\StorePal.cs" />
+ <Compile Include="Internal\Cryptography\Pal.OSX\StorePal.AppleTrustStore.cs" />
+ <Compile Include="Internal\Cryptography\Pal.OSX\StorePal.AppleKeychainStore.cs" />
+ <Compile Include="Internal\Cryptography\Pal.OSX\StorePal.ExportPal.cs" />
+ <Compile Include="Internal\Cryptography\Pal.OSX\StorePal.LoaderPal.cs" />
+ <Compile Include="Internal\Cryptography\Pal.OSX\X509Pal.cs" />
+ <Compile Include="Internal\Cryptography\Pal.Unix\X500NameEncoder.ManagedDecode.cs" />
+ </ItemGroup>
+ <ItemGroup Condition=" '$(TargetsUnix)' == 'true'">
<Compile Include="$(CommonPath)\System\Security\Cryptography\DerEncoder.cs">
<Link>Common\System\Security\Cryptography\DerEncoder.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\System\Security\Cryptography\DerSequenceReader.cs">
<Link>Common\System\Security\Cryptography\DerSequenceReader.cs</Link>
</Compile>
+ <Compile Include="Internal\Cryptography\Pal.Unix\CertificatePolicy.cs" />
+ <Compile Include="Internal\Cryptography\Pal.Unix\ChainVerifier.cs" />
+ <Compile Include="Internal\Cryptography\Pal.Unix\ManagedCertificateFinder.cs" />
+ <Compile Include="Internal\Cryptography\Pal.Unix\ManagedX509ExtensionProcessor.cs" />
+ <Compile Include="Internal\Cryptography\Pal.Unix\X500NameEncoder.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Collections" />
@@ -299,13 +400,15 @@
<Reference Include="System.Runtime.InteropServices" />
<Reference Include="System.Runtime.Numerics" />
<Reference Include="System.Security.Cryptography.Algorithms" />
- <Reference Include="System.Security.Cryptography.Cng" />
- <Reference Include="System.Security.Cryptography.Csp" />
<Reference Include="System.Security.Cryptography.Encoding" />
<Reference Include="System.Security.Cryptography.Primitives" />
<Reference Include="System.Threading" />
</ItemGroup>
- <ItemGroup Condition="'$(TargetsUnix)' == 'true'">
+ <ItemGroup Condition="'$(TargetsWindows)' == 'true'">
+ <Reference Include="System.Security.Cryptography.Cng" />
+ <Reference Include="System.Security.Cryptography.Csp" />
+ </ItemGroup>
+ <ItemGroup Condition="'$(TargetsUnix)' == 'true' AND '$(TargetsOSX)' != 'true'">
<Reference Include="System.Security.Cryptography.OpenSsl" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
diff --git a/src/System.Security.Cryptography.X509Certificates/tests/Cert.cs b/src/System.Security.Cryptography.X509Certificates/tests/Cert.cs
index be2be3a98e..5f180b0d78 100644
--- a/src/System.Security.Cryptography.X509Certificates/tests/Cert.cs
+++ b/src/System.Security.Cryptography.X509Certificates/tests/Cert.cs
@@ -2,6 +2,8 @@
// 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.InteropServices;
+
namespace System.Security.Cryptography.X509Certificates.Tests
{
//
@@ -11,12 +13,14 @@ namespace System.Security.Cryptography.X509Certificates.Tests
//
internal static class Cert
{
- internal const X509KeyStorageFlags EphemeralIfPossible =
+ // netstandard: DefaultKeySet
+ // netcoreapp-OSX: DefaultKeySet
+ // netcoreapp-other: EphemeralKeySet
+ internal static readonly X509KeyStorageFlags EphemeralIfPossible =
#if netcoreapp
- X509KeyStorageFlags.EphemeralKeySet;
-#else
- X509KeyStorageFlags.DefaultKeySet;
+ !RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? X509KeyStorageFlags.EphemeralKeySet :
#endif
+ X509KeyStorageFlags.DefaultKeySet;
//
// The Import() methods have an overload for each X509Certificate2Collection.Import() overload.
//
diff --git a/src/System.Security.Cryptography.X509Certificates/tests/CertTests.cs b/src/System.Security.Cryptography.X509Certificates/tests/CertTests.cs
index 2042e8f8a4..0e2126fcaf 100644
--- a/src/System.Security.Cryptography.X509Certificates/tests/CertTests.cs
+++ b/src/System.Security.Cryptography.X509Certificates/tests/CertTests.cs
@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System.IO;
+using System.Runtime.InteropServices;
using Xunit;
using Xunit.Abstractions;
@@ -286,17 +287,31 @@ namespace System.Security.Cryptography.X509Certificates.Tests
// Pre-condition: There's no private key
Assert.False(publicOnly.HasPrivateKey);
- // This won't throw.
- byte[] pkcs12Bytes = publicOnly.Export(X509ContentType.Pkcs12);
+ // macOS 10.12 (Sierra) fails to create a PKCS#12 blob if it has no private keys within it.
+ bool shouldThrow = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
- // Read it back as a collection, there should be only one cert, and it should
- // be equal to the one we started with.
- using (ImportedCollection ic = Cert.Import(pkcs12Bytes))
+ try
{
- X509Certificate2Collection fromPfx = ic.Collection;
+ byte[] pkcs12Bytes = publicOnly.Export(X509ContentType.Pkcs12);
- Assert.Equal(1, fromPfx.Count);
- Assert.Equal(publicOnly, fromPfx[0]);
+ Assert.False(shouldThrow, "PKCS#12 export of a public-only certificate threw as expected");
+
+ // Read it back as a collection, there should be only one cert, and it should
+ // be equal to the one we started with.
+ using (ImportedCollection ic = Cert.Import(pkcs12Bytes))
+ {
+ X509Certificate2Collection fromPfx = ic.Collection;
+
+ Assert.Equal(1, fromPfx.Count);
+ Assert.Equal(publicOnly, fromPfx[0]);
+ }
+ }
+ catch (CryptographicException)
+ {
+ if (!shouldThrow)
+ {
+ throw;
+ }
}
}
}
diff --git a/src/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs b/src/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs
index 2508cc6896..7fa8bbeab8 100644
--- a/src/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs
+++ b/src/System.Security.Cryptography.X509Certificates/tests/ChainTests.cs
@@ -487,7 +487,15 @@ namespace System.Security.Cryptography.X509Certificates.Tests
{
if (shouldInstallCertToUserStore)
{
- userRootStore.Open(OpenFlags.ReadWrite);
+ try
+ {
+ userRootStore.Open(OpenFlags.ReadWrite);
+ }
+ catch (CryptographicException)
+ {
+ return;
+ }
+
userRootStore.Add(microsoftDotComRoot); // throws CryptographicException
installedCertToUserStore = true;
}
diff --git a/src/System.Security.Cryptography.X509Certificates/tests/CollectionImportTests.cs b/src/System.Security.Cryptography.X509Certificates/tests/CollectionImportTests.cs
index 612b3d4e21..ad612b93bb 100644
--- a/src/System.Security.Cryptography.X509Certificates/tests/CollectionImportTests.cs
+++ b/src/System.Security.Cryptography.X509Certificates/tests/CollectionImportTests.cs
@@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Runtime.InteropServices;
using Xunit;
namespace System.Security.Cryptography.X509Certificates.Tests
@@ -116,6 +117,8 @@ namespace System.Security.Cryptography.X509Certificates.Tests
{
X509Certificate2Collection collection = ic.Collection;
Assert.Equal(1, collection.Count);
+
+ Assert.Equal("D5B5BC1C458A558845BFF51CB4DFF31C", collection[0].SerialNumber);
}
}
@@ -126,6 +129,8 @@ namespace System.Security.Cryptography.X509Certificates.Tests
{
X509Certificate2Collection collection = ic.Collection;
Assert.Equal(1, collection.Count);
+
+ Assert.Equal("D5B5BC1C458A558845BFF51CB4DFF31C", collection[0].SerialNumber);
}
}
@@ -136,6 +141,8 @@ namespace System.Security.Cryptography.X509Certificates.Tests
{
X509Certificate2Collection collection = ic.Collection;
Assert.Equal(1, collection.Count);
+
+ Assert.Equal("D5B5BC1C458A558845BFF51CB4DFF31C", collection[0].SerialNumber);
}
}
@@ -146,6 +153,8 @@ namespace System.Security.Cryptography.X509Certificates.Tests
{
X509Certificate2Collection collection = ic.Collection;
Assert.Equal(1, collection.Count);
+
+ Assert.Equal("D5B5BC1C458A558845BFF51CB4DFF31C", collection[0].SerialNumber);
}
}
@@ -301,6 +310,21 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
}
+#if netcoreapp11
+ [Fact]
+ [PlatformSpecific(TestPlatforms.OSX)]
+ public static void EphemeralKeySet_OSX()
+ {
+ // EphemeralKeySet fails when loading a PFX, and is ignored otherwise.
+ using (ImportedCollection coll = Cert.Import(TestData.Pkcs7ChainDerBytes, null, X509KeyStorageFlags.EphemeralKeySet))
+ {
+ Assert.Equal(3, coll.Collection.Count);
+ }
+
+ Assert.Throws<PlatformNotSupportedException>(
+ () => new X509Certificate2(TestData.EmptyPfx, string.Empty, X509KeyStorageFlags.EphemeralKeySet));
+ }
+#endif
[Fact]
public static void InvalidStorageFlags()
@@ -347,7 +371,8 @@ namespace System.Security.Cryptography.X509Certificates.Tests
yield return new object[] { X509KeyStorageFlags.DefaultKeySet };
#if netcoreapp
- yield return new object[] { X509KeyStorageFlags.EphemeralKeySet };
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ yield return new object[] { X509KeyStorageFlags.EphemeralKeySet };
#endif
}
}
diff --git a/src/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs b/src/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs
index 28715a20c0..e0c60a2741 100644
--- a/src/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs
+++ b/src/System.Security.Cryptography.X509Certificates/tests/CollectionTests.cs
@@ -735,6 +735,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
[Fact]
+ [ActiveIssue(16705, TestPlatforms.OSX)]
public static void ExportUnrelatedPfx()
{
// Export multiple certificates which are not part of any kind of certificate chain.
@@ -800,7 +801,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
[Fact]
- [ActiveIssue(2743, TestPlatforms.AnyUnix)]
+ [ActiveIssue(2743, TestPlatforms.AnyUnix & ~TestPlatforms.OSX)]
public static void ExportMultiplePrivateKeys()
{
var collection = new X509Certificate2Collection();
@@ -817,6 +818,10 @@ namespace System.Security.Cryptography.X509Certificates.Tests
// Export, re-import.
byte[] exported;
+ bool expectSuccess =
+ RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ||
+ RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
+
try
{
exported = collection.Export(X509ContentType.Pkcs12);
@@ -829,7 +834,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
//
// If Windows gets here, or any exception other than PlatformNotSupportedException is raised,
// let that fail the test.
- if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ if (expectSuccess)
{
throw;
}
@@ -839,7 +844,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
// As the other half of issue 2743, if we make it this far we better be Windows (or remove the catch
// above)
- Assert.True(RuntimeInformation.IsOSPlatform(OSPlatform.Windows), "RuntimeInformation.IsOSPlatform(OSPlatform.Windows)");
+ Assert.True(expectSuccess, "Test is expected to fail on this platform");
using (ImportedCollection ic = Cert.Import(exported))
{
@@ -1480,17 +1485,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
}
- public static IEnumerable<object[]> StorageFlags
- {
- get
- {
- yield return new object[] { X509KeyStorageFlags.DefaultKeySet };
-
-#if netcoreapp
- yield return new object[] { X509KeyStorageFlags.EphemeralKeySet };
-#endif
- }
- }
+ public static IEnumerable<object[]> StorageFlags => CollectionImportTests.StorageFlags;
private static X509Certificate2[] ToArray(this X509Certificate2Collection col)
{
diff --git a/src/System.Security.Cryptography.X509Certificates/tests/Configurations.props b/src/System.Security.Cryptography.X509Certificates/tests/Configurations.props
index 4625c4baf9..df96729a5b 100644
--- a/src/System.Security.Cryptography.X509Certificates/tests/Configurations.props
+++ b/src/System.Security.Cryptography.X509Certificates/tests/Configurations.props
@@ -2,10 +2,12 @@
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildConfigurations>
+ netcoreapp-OSX;
netcoreapp-Unix;
netcoreapp-Windows_NT;
+ netstandard-OSX;
netstandard-Unix;
netstandard-Windows_NT;
</BuildConfigurations>
</PropertyGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/System.Security.Cryptography.X509Certificates/tests/ContentTypeTests.cs b/src/System.Security.Cryptography.X509Certificates/tests/ContentTypeTests.cs
index 2783174790..9546745c92 100644
--- a/src/System.Security.Cryptography.X509Certificates/tests/ContentTypeTests.cs
+++ b/src/System.Security.Cryptography.X509Certificates/tests/ContentTypeTests.cs
@@ -22,7 +22,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
[Theory]
[MemberData(nameof(GetContentBlobsWithType))]
- public static void TestBlobContentType(byte[] blob, X509ContentType contentType)
+ public static void TestBlobContentType(string caseName, byte[] blob, X509ContentType contentType)
{
X509ContentType blobType = X509Certificate2.GetCertContentType(blob);
Assert.Equal(contentType, blobType);
@@ -32,18 +32,18 @@ namespace System.Security.Cryptography.X509Certificates.Tests
{
return new[]
{
- new object[] { TestData.MsCertificate, X509ContentType.Cert },
- new object[] { TestData.MsCertificatePemBytes, X509ContentType.Cert },
- new object[] { TestData.Pkcs7ChainDerBytes, X509ContentType.Pkcs7 },
- new object[] { TestData.Pkcs7ChainPemBytes, X509ContentType.Pkcs7 },
- new object[] { TestData.Pkcs7EmptyDerBytes, X509ContentType.Pkcs7 },
- new object[] { TestData.Pkcs7EmptyPemBytes, X509ContentType.Pkcs7 },
- new object[] { TestData.Pkcs7SingleDerBytes, X509ContentType.Pkcs7 },
- new object[] { TestData.Pkcs7SinglePemBytes, X509ContentType.Pkcs7 },
- new object[] { TestData.PfxData, X509ContentType.Pkcs12 },
- new object[] { TestData.EmptyPfx, X509ContentType.Pkcs12 },
- new object[] { TestData.MultiPrivateKeyPfx, X509ContentType.Pkcs12 },
- new object[] { TestData.ChainPfxBytes, X509ContentType.Pkcs12 },
+ new object[] { "MsCertificate", TestData.MsCertificate, X509ContentType.Cert },
+ new object[] { "MsCertificatePem", TestData.MsCertificatePemBytes, X509ContentType.Cert },
+ new object[] { "Pkcs7ChainDer", TestData.Pkcs7ChainDerBytes, X509ContentType.Pkcs7 },
+ new object[] { "Pkcs7ChainPem", TestData.Pkcs7ChainPemBytes, X509ContentType.Pkcs7 },
+ new object[] { "Pkcs7EmptyDer", TestData.Pkcs7EmptyDerBytes, X509ContentType.Pkcs7 },
+ new object[] { "Pkcs7EmptyPem", TestData.Pkcs7EmptyPemBytes, X509ContentType.Pkcs7 },
+ new object[] { "Pkcs7SingleDer", TestData.Pkcs7SingleDerBytes, X509ContentType.Pkcs7 },
+ new object[] { "Pkcs7SinglePem", TestData.Pkcs7SinglePemBytes, X509ContentType.Pkcs7 },
+ new object[] { "PfxData", TestData.PfxData, X509ContentType.Pkcs12 },
+ new object[] { "EmptyPfx", TestData.EmptyPfx, X509ContentType.Pkcs12 },
+ new object[] { "MultiPrivatePfx", TestData.MultiPrivateKeyPfx, X509ContentType.Pkcs12 },
+ new object[] { "ChainPfx", TestData.ChainPfxBytes, X509ContentType.Pkcs12 },
};
}
}
diff --git a/src/System.Security.Cryptography.X509Certificates/tests/CtorTests.cs b/src/System.Security.Cryptography.X509Certificates/tests/CtorTests.cs
index 57dc32b906..461bd81263 100644
--- a/src/System.Security.Cryptography.X509Certificates/tests/CtorTests.cs
+++ b/src/System.Security.Cryptography.X509Certificates/tests/CtorTests.cs
@@ -364,12 +364,19 @@ namespace System.Security.Cryptography.X509Certificates.Tests
CryptographicException ex = Assert.ThrowsAny<CryptographicException>(
() => new X509Certificate2(new byte[] { 0x01, 0x02, 0x03 }));
+ CryptographicException defaultException = new CryptographicException();
+ Assert.NotEqual(defaultException.Message, ex.Message);
+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Assert.Equal(unchecked((int)0x80092009), ex.HResult);
// TODO (3233): Test that Message is also set correctly
//Assert.Equal("Cannot find the requested object.", ex.Message);
}
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
+ {
+ Assert.Equal(-25257, ex.HResult);
+ }
else // Any Unix
{
Assert.Equal(0x0D07803A, ex.HResult);
diff --git a/src/System.Security.Cryptography.X509Certificates/tests/ExportTests.cs b/src/System.Security.Cryptography.X509Certificates/tests/ExportTests.cs
index 3df884afe3..24dfa156c5 100644
--- a/src/System.Security.Cryptography.X509Certificates/tests/ExportTests.cs
+++ b/src/System.Security.Cryptography.X509Certificates/tests/ExportTests.cs
@@ -48,6 +48,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
[Fact]
+ [ActiveIssue(16705, TestPlatforms.OSX)]
public static void ExportAsPfx()
{
using (X509Certificate2 c1 = new X509Certificate2(TestData.MsCertificate))
@@ -64,6 +65,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
[Fact]
+ [ActiveIssue(16705, TestPlatforms.OSX)]
public static void ExportAsPfxWithPassword()
{
const string password = "Cotton";
@@ -82,6 +84,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
[Fact]
+ [ActiveIssue(16705, TestPlatforms.OSX)]
public static void ExportAsPfxVerifyPassword()
{
const string password = "Cotton";
@@ -92,5 +95,70 @@ namespace System.Security.Cryptography.X509Certificates.Tests
Assert.ThrowsAny<CryptographicException>(() => new X509Certificate2(pfx, "WRONGPASSWORD"));
}
}
+
+ [Fact]
+ public static void ExportAsPfxWithPrivateKeyVerifyPassword()
+ {
+ using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable))
+ {
+ Assert.True(cert.HasPrivateKey, "cert.HasPrivateKey");
+
+ const string password = "Cotton";
+
+ byte[] pfx = cert.Export(X509ContentType.Pkcs12, password);
+
+ Assert.ThrowsAny<CryptographicException>(() => new X509Certificate2(pfx, "WRONGPASSWORD"));
+
+ using (var cert2 = new X509Certificate2(pfx, password))
+ {
+ Assert.Equal(cert, cert2);
+ Assert.True(cert2.HasPrivateKey, "cert2.HasPrivateKey");
+ }
+ }
+ }
+
+ [Fact]
+ public static void ExportAsPfxWithPrivateKey()
+ {
+ using (X509Certificate2 cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable))
+ {
+ Assert.True(cert.HasPrivateKey, "cert.HasPrivateKey");
+
+ byte[] pfxBytes = cert.Export(X509ContentType.Pkcs12);
+
+ using (X509Certificate2 fromPfx = new X509Certificate2(pfxBytes))
+ {
+ Assert.Equal(cert, fromPfx);
+ Assert.True(fromPfx.HasPrivateKey, "fromPfx.HasPrivateKey");
+
+ byte[] origSign;
+ byte[] copySign;
+
+ using (RSA origPriv = cert.GetRSAPrivateKey())
+ {
+ origSign = origPriv.SignData(pfxBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
+ }
+
+ using (RSA copyPriv = fromPfx.GetRSAPrivateKey())
+ {
+ copySign = copyPriv.SignData(pfxBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
+ }
+
+ using (RSA origPub = cert.GetRSAPublicKey())
+ {
+ Assert.True(
+ origPub.VerifyData(pfxBytes, copySign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1),
+ "oPub v copySig");
+ }
+
+ using (RSA copyPub = fromPfx.GetRSAPublicKey())
+ {
+ Assert.True(
+ copyPub.VerifyData(pfxBytes, origSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1),
+ "copyPub v oSig");
+ }
+ }
+ }
+ }
}
}
diff --git a/src/System.Security.Cryptography.X509Certificates/tests/ExtensionsTests.cs b/src/System.Security.Cryptography.X509Certificates/tests/ExtensionsTests.cs
index 0559f1f613..32cba5be20 100644
--- a/src/System.Security.Cryptography.X509Certificates/tests/ExtensionsTests.cs
+++ b/src/System.Security.Cryptography.X509Certificates/tests/ExtensionsTests.cs
@@ -275,8 +275,8 @@ namespace System.Security.Cryptography.X509Certificates.Tests
[Fact]
public static void EnhancedKeyUsageExtension_2Oids()
{
- Oid oid1 = Oid.FromOidValue("1.3.6.1.5.5.7.3.1", OidGroup.EnhancedKeyUsage);
- Oid oid2 = Oid.FromOidValue("1.3.6.1.4.1.311.10.3.1", OidGroup.EnhancedKeyUsage);
+ Oid oid1 = new Oid("1.3.6.1.5.5.7.3.1");
+ Oid oid2 = new Oid("1.3.6.1.4.1.311.10.3.1");
OidCollection usages = new OidCollection();
usages.Add(oid1);
usages.Add(oid2);
diff --git a/src/System.Security.Cryptography.X509Certificates/tests/PfxTests.cs b/src/System.Security.Cryptography.X509Certificates/tests/PfxTests.cs
index a795707b4c..202ed8e92d 100644
--- a/src/System.Security.Cryptography.X509Certificates/tests/PfxTests.cs
+++ b/src/System.Security.Cryptography.X509Certificates/tests/PfxTests.cs
@@ -124,7 +124,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
[Fact]
public static void TestPrivateKeyProperty()
{
- using (var c = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.EphemeralKeySet))
+ using (var c = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, Cert.EphemeralIfPossible))
{
bool hasPrivateKey = c.HasPrivateKey;
Assert.True(hasPrivateKey);
@@ -184,7 +184,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
[Fact]
public static void ECDsaPrivateKeyProperty_WindowsPfx()
{
- using (var cert = new X509Certificate2(TestData.ECDsaP256_DigitalSignature_Pfx_Windows, "Test", X509KeyStorageFlags.EphemeralKeySet))
+ using (var cert = new X509Certificate2(TestData.ECDsaP256_DigitalSignature_Pfx_Windows, "Test", Cert.EphemeralIfPossible))
using (var pubOnly = new X509Certificate2(cert.RawData))
{
Assert.True(cert.HasPrivateKey, "cert.HasPrivateKey");
@@ -203,6 +203,28 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
}
}
+
+ [Fact]
+ public static void DsaPrivateKeyProperty()
+ {
+ using (var cert = new X509Certificate2(TestData.Dsa1024Pfx, TestData.Dsa1024PfxPassword, Cert.EphemeralIfPossible))
+ {
+ AsymmetricAlgorithm alg = cert.PrivateKey;
+ Assert.NotNull(alg);
+ Assert.Same(alg, cert.PrivateKey);
+ Assert.IsAssignableFrom<DSA>(alg);
+
+ DSA dsa = (DSA)alg;
+ byte[] data = { 1, 2, 3, 4, 5 };
+ byte[] sig = dsa.SignData(data, HashAlgorithmName.SHA1);
+
+ Assert.True(dsa.VerifyData(data, sig, HashAlgorithmName.SHA1), "Key verifies signature");
+
+ data[0] ^= 0xFF;
+
+ Assert.False(dsa.VerifyData(data, sig, HashAlgorithmName.SHA1), "Key verifies tampered data signature");
+ }
+ }
#endif
private static void Verify_ECDsaPrivateKey_WindowsPfx(ECDsa ecdsa)
@@ -235,13 +257,10 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
catch (CryptographicException)
{
- // Windows 7, Windows 8, Ubuntu 14, CentOS can fail. Verify known good platforms don't fail.
- Assert.False(PlatformDetection.IsWindows && PlatformDetection.WindowsVersion >= 10);
- Assert.False(PlatformDetection.IsUbuntu1604);
- Assert.False(PlatformDetection.IsUbuntu1610);
- Assert.False(PlatformDetection.IsOSX);
-
- return;
+ // Windows 7, Windows 8, Ubuntu 14, CentOS, macOS can fail. Verify known good platforms don't fail.
+ Assert.False(PlatformDetection.IsWindows && PlatformDetection.WindowsVersion >= 10, "Is Windows 10");
+ Assert.False(PlatformDetection.IsUbuntu1604, "Is Ubuntu 16.04");
+ Assert.False(PlatformDetection.IsUbuntu1610, "Is Ubuntu 16.10");
}
}
@@ -404,17 +423,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
}
- public static IEnumerable<object[]> StorageFlags
- {
- get
- {
- yield return new object[] { X509KeyStorageFlags.DefaultKeySet };
-
-#if netcoreapp
- yield return new object[] { X509KeyStorageFlags.EphemeralKeySet };
-#endif
- }
- }
+ public static IEnumerable<object[]> StorageFlags => CollectionImportTests.StorageFlags;
private static X509Certificate2 Rewrap(this X509Certificate2 c)
{
diff --git a/src/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs b/src/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs
index d8fa074566..ffdeddc798 100644
--- a/src/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs
+++ b/src/System.Security.Cryptography.X509Certificates/tests/PublicKeyTests.cs
@@ -337,9 +337,6 @@ namespace System.Security.Cryptography.X509Certificates.Tests
Assert.False(PlatformDetection.IsWindows && PlatformDetection.WindowsVersion >= 10);
Assert.False(PlatformDetection.IsUbuntu1604);
Assert.False(PlatformDetection.IsUbuntu1610);
- Assert.False(PlatformDetection.IsOSX);
-
- return;
}
}
@@ -431,9 +428,6 @@ namespace System.Security.Cryptography.X509Certificates.Tests
Assert.False(PlatformDetection.IsWindows && PlatformDetection.WindowsVersion >= 10);
Assert.False(PlatformDetection.IsUbuntu1604);
Assert.False(PlatformDetection.IsUbuntu1610);
- Assert.False(PlatformDetection.IsOSX);
-
- return;
}
}
diff --git a/src/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj b/src/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj
index 0282080c89..b1faa742ea 100644
--- a/src/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj
+++ b/src/System.Security.Cryptography.X509Certificates/tests/System.Security.Cryptography.X509Certificates.Tests.csproj
@@ -35,6 +35,7 @@
<Compile Include="TestData.cs" />
<Compile Include="X500DistinguishedNameEncodingTests.cs" />
<Compile Include="X500DistinguishedNameTests.cs" />
+ <Compile Include="X509StoreMutableTests.OSX.cs" />
<Compile Include="X509StoreTests.cs" />
<Compile Include="$(CommonTestPath)\System\Security\Cryptography\ByteUtils.cs">
<Link>CommonTest\System\Security\Cryptography\ByteUtils.cs</Link>
@@ -50,7 +51,7 @@
<Link>Common\System\Runtime\Serialization\Formatters\BinaryFormatterHelpers.cs</Link>
</Compile>
</ItemGroup>
- <ItemGroup Condition=" '$(TargetsUnix)' == 'true' ">
+ <ItemGroup Condition=" '$(TargetsUnix)' == 'true' AND '$(TargetsOSX)' != 'true' ">
<Compile Include="X509FilesystemTests.Unix.cs" />
<Compile Include="$(CommonPath)\Interop\Unix\Interop.Libraries.cs">
<Link>Common\Interop\Unix\Interop.Libraries.cs</Link>
@@ -75,4 +76,4 @@
<SupplementalTestData Include="$(PackagesDir)System.Security.Cryptography.X509Certificates.TestData\1.0.2-prerelease\content\**\*.*" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.OSX.cs b/src/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.OSX.cs
new file mode 100644
index 0000000000..ba17176a81
--- /dev/null
+++ b/src/System.Security.Cryptography.X509Certificates/tests/X509StoreMutableTests.OSX.cs
@@ -0,0 +1,249 @@
+// 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 Xunit;
+
+namespace System.Security.Cryptography.X509Certificates.Tests
+{
+ [OuterLoop("Modifies system state")]
+ [PlatformSpecific(TestPlatforms.OSX)]
+ public static class X509StoreMutableTests_OSX
+ {
+ [Fact]
+ public static void PersistKeySet_OSX()
+ {
+ using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
+ using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.DefaultKeySet))
+ {
+ store.Open(OpenFlags.ReadWrite);
+
+ // Defensive removal.
+ store.Remove(cert);
+
+ Assert.False(IsCertInStore(cert, store), "PtxData certificate was found on pre-condition");
+
+ // Opening this as persisted has now added it to login.keychain, aka CU\My.
+ using (var persistedCert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.PersistKeySet))
+ {
+ Assert.True(IsCertInStore(cert, store), "PtxData certificate was found upon PersistKeySet import");
+ }
+
+ // And ensure it didn't get removed when the certificate got disposed.
+ Assert.True(IsCertInStore(cert, store), "PtxData certificate was found after PersistKeySet Dispose");
+
+ // Cleanup.
+ store.Remove(cert);
+ }
+ }
+
+ [Fact]
+ public static void AddToStore_NonExportable_OSX()
+ {
+ using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
+ using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.DefaultKeySet))
+ {
+ store.Open(OpenFlags.ReadWrite);
+
+ int countBefore = GetStoreCertificateCount(store);
+
+ // Because this has to export the key from the temporary keychain to the permanent one,
+ // a non-exportable PFX load will fail.
+ Assert.ThrowsAny<CryptographicException>(() => store.Add(cert));
+
+ int countAfter = GetStoreCertificateCount(store);
+
+ Assert.Equal(countBefore, countAfter);
+ }
+ }
+
+ [Fact]
+ public static void AddToStore_Exportable()
+ {
+ using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
+ using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable))
+ using (var certOnly = new X509Certificate2(cert.RawData))
+ {
+ store.Open(OpenFlags.ReadWrite);
+
+ // Defensive removal.
+ store.Remove(certOnly);
+ Assert.False(IsCertInStore(cert, store), "PtxData certificate was found on pre-condition");
+
+ store.Add(cert);
+ Assert.True(IsCertInStore(certOnly, store), "PtxData certificate was found after add");
+
+ // Cleanup
+ store.Remove(certOnly);
+ }
+ }
+
+ [Fact]
+ public static void AddToStoreTwice()
+ {
+ using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
+ using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable))
+ using (var certOnly = new X509Certificate2(cert.RawData))
+ {
+ store.Open(OpenFlags.ReadWrite);
+
+ // Defensive removal.
+ store.Remove(certOnly);
+ Assert.False(IsCertInStore(cert, store), "PtxData certificate was found on pre-condition");
+
+ store.Add(cert);
+ Assert.True(IsCertInStore(certOnly, store), "PtxData certificate was found after add");
+
+ // No exception for duplicate item.
+ store.Add(cert);
+
+ // Cleanup
+ store.Remove(certOnly);
+ }
+ }
+
+ [Fact]
+ public static void AddPrivateAfterPublic()
+ {
+ using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
+ using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable))
+ using (var certOnly = new X509Certificate2(cert.RawData))
+ {
+ store.Open(OpenFlags.ReadWrite);
+
+ // Defensive removal.
+ store.Remove(certOnly);
+ Assert.False(IsCertInStore(cert, store), "PtxData certificate was found on pre-condition");
+
+ store.Add(certOnly);
+ Assert.True(IsCertInStore(certOnly, store), "PtxData certificate was found after add");
+ Assert.False(StoreHasPrivateKey(store, certOnly), "Store has a private key for PfxData after public-only add");
+
+ // Add the private key
+ store.Add(cert);
+ Assert.True(StoreHasPrivateKey(store, certOnly), "Store has a private key for PfxData after PFX add");
+
+ // Cleanup
+ store.Remove(certOnly);
+ }
+ }
+
+ [Fact]
+ public static void AddPublicAfterPrivate()
+ {
+ using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
+ using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable))
+ using (var certOnly = new X509Certificate2(cert.RawData))
+ {
+ store.Open(OpenFlags.ReadWrite);
+
+ // Defensive removal.
+ store.Remove(certOnly);
+ Assert.False(IsCertInStore(cert, store), "PtxData certificate was found on pre-condition");
+
+ // Add the private key
+ store.Add(cert);
+ Assert.True(IsCertInStore(certOnly, store), "PtxData certificate was found after add");
+ Assert.True(StoreHasPrivateKey(store, certOnly), "Store has a private key for PfxData after PFX add");
+
+ // Add the public key with no private key
+ store.Add(certOnly);
+ Assert.True(StoreHasPrivateKey(store, certOnly), "Store has a private key for PfxData after public-only add");
+
+ // Cleanup
+ store.Remove(certOnly);
+ }
+ }
+
+ [Fact]
+ public static void VerifyRemove()
+ {
+ using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
+ using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable))
+ {
+ store.Open(OpenFlags.ReadWrite);
+
+ // Defensive removal. Sort of circular, but it's the best we can do.
+ store.Remove(cert);
+ Assert.False(IsCertInStore(cert, store), "PtxData certificate was found on pre-condition");
+
+ store.Add(cert);
+ Assert.True(IsCertInStore(cert, store), "PtxData certificate was found after add");
+
+ store.Remove(cert);
+ Assert.False(IsCertInStore(cert, store), "PtxData certificate was found after remove");
+ }
+ }
+
+ [Fact]
+ public static void RemovePublicDeletesPrivateKey()
+ {
+ using (var store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
+ using (var cert = new X509Certificate2(TestData.PfxData, TestData.PfxDataPassword, X509KeyStorageFlags.Exportable))
+ using (var certOnly = new X509Certificate2(cert.RawData))
+ {
+ store.Open(OpenFlags.ReadWrite);
+
+ // Defensive removal.
+ store.Remove(cert);
+ Assert.False(IsCertInStore(cert, store), "PtxData certificate was found on pre-condition");
+
+ // Add the private key
+ store.Add(cert);
+ Assert.True(IsCertInStore(cert, store), "PtxData certificate was found after add");
+
+ store.Remove(certOnly);
+ Assert.False(IsCertInStore(cert, store), "PtxData certificate was found after remove");
+
+ // Add back the public key only
+ store.Add(certOnly);
+ Assert.True(IsCertInStore(cert, store), "PtxData certificate was found after public-only add");
+ Assert.False(StoreHasPrivateKey(store, cert), "Store has a private key for cert after public-only add");
+
+ // Cleanup
+ store.Remove(certOnly);
+ }
+ }
+
+ private static bool StoreHasPrivateKey(X509Store store, X509Certificate2 forCert)
+ {
+ using (ImportedCollection coll = new ImportedCollection(store.Certificates))
+ {
+ foreach (X509Certificate2 storeCert in coll.Collection)
+ {
+ if (forCert.Equals(storeCert))
+ {
+ return storeCert.HasPrivateKey;
+ }
+ }
+ }
+
+ Assert.True(false, $"Certificate ({forCert.Subject}) exists in the store");
+ return false;
+ }
+
+ private static bool IsCertInStore(X509Certificate2 cert, X509Store store)
+ {
+ using (ImportedCollection coll = new ImportedCollection(store.Certificates))
+ {
+ foreach (X509Certificate2 storeCert in coll.Collection)
+ {
+ if (cert.Equals(storeCert))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static int GetStoreCertificateCount(X509Store store)
+ {
+ using (var coll = new ImportedCollection(store.Certificates))
+ {
+ return coll.Collection.Count;
+ }
+ }
+ }
+}
diff --git a/src/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs b/src/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs
index a1277c11bd..00e13587ff 100644
--- a/src/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs
+++ b/src/System.Security.Cryptography.X509Certificates/tests/X509StoreTests.cs
@@ -2,7 +2,6 @@
// 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.InteropServices;
using Xunit;
namespace System.Security.Cryptography.X509Certificates.Tests
@@ -41,7 +40,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
}
- [PlatformSpecific(TestPlatforms.Windows)] // Not supported on Unix
+ [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.OSX)] // Not supported via OpenSSL
[Fact]
public static void Constructor_StoreHandle()
{
@@ -67,7 +66,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
}
- [PlatformSpecific(TestPlatforms.AnyUnix)] // API not supported on Unix
+ [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.OSX)] // API not supported via OpenSSL
[Fact]
public static void Constructor_StoreHandle_Unix()
{
@@ -80,7 +79,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
Assert.Throws<PlatformNotSupportedException>(() => new X509Chain(IntPtr.Zero));
}
- [PlatformSpecific(TestPlatforms.Windows)] // Not supported on Unix
+ [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.OSX)] // StoreHandle not supported via OpenSSL
[Fact]
public static void TestDispose()
{
@@ -249,7 +248,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
[Fact]
- [PlatformSpecific(TestPlatforms.Windows)]
+ [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.OSX)]
public static void OpenMachineMyStore_Supported()
{
using (X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
@@ -259,7 +258,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
[Fact]
- [PlatformSpecific(TestPlatforms.AnyUnix)]
+ [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.OSX)]
public static void OpenMachineMyStore_NotSupported()
{
using (X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
@@ -269,7 +268,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
[Theory]
- [PlatformSpecific(TestPlatforms.AnyUnix)]
+ [PlatformSpecific(TestPlatforms.AnyUnix & ~TestPlatforms.OSX)]
[InlineData(OpenFlags.ReadOnly, false)]
[InlineData(OpenFlags.MaxAllowed, false)]
[InlineData(OpenFlags.ReadWrite, true)]
@@ -312,5 +311,23 @@ namespace System.Security.Cryptography.X509Certificates.Tests
}
}
}
+
+ [Theory]
+ [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.OSX)]
+ [InlineData(StoreLocation.CurrentUser)]
+ [InlineData(StoreLocation.LocalMachine)]
+ public static void EnumerateDisallowedStore(StoreLocation location)
+ {
+ using (X509Store store = new X509Store(StoreName.Disallowed, location))
+ {
+ store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
+
+ using (var storeCerts = new ImportedCollection(store.Certificates))
+ {
+ // That's all. We enumerated it.
+ // There might not even be data in it.
+ }
+ }
+ }
}
}
diff --git a/src/System.Security.Cryptography.Xml/tests/DSAKeyValueTest.cs b/src/System.Security.Cryptography.Xml/tests/DSAKeyValueTest.cs
index dc939859b3..cea94757a7 100644
--- a/src/System.Security.Cryptography.Xml/tests/DSAKeyValueTest.cs
+++ b/src/System.Security.Cryptography.Xml/tests/DSAKeyValueTest.cs
@@ -12,6 +12,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.InteropServices;
using System.Xml;
using Xunit;
@@ -50,9 +51,9 @@ namespace System.Security.Cryptography.Xml.Tests
}
[Fact]
+ [ActiveIssue(17001, TestPlatforms.OSX)]
public void GetXml()
{
-
DSAKeyValue dsa = new DSAKeyValue();
XmlElement xmlkey = dsa.GetXml();
@@ -78,6 +79,7 @@ namespace System.Security.Cryptography.Xml.Tests
}
[Fact]
+ [ActiveIssue(17001, TestPlatforms.OSX)]
public void GetXml_SameDsa()
{
using (DSA dsa = DSA.Create())
diff --git a/src/System.Text.Encoding.CodePages/System.Text.Encoding.CodePages.sln b/src/System.Text.Encoding.CodePages/System.Text.Encoding.CodePages.sln
index 0f087e91d4..86b0419e6d 100644
--- a/src/System.Text.Encoding.CodePages/System.Text.Encoding.CodePages.sln
+++ b/src/System.Text.Encoding.CodePages/System.Text.Encoding.CodePages.sln
@@ -26,10 +26,10 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {835AD07B-7C9A-406F-B16F-59B3B0D017A4}.Debug|Any CPU.ActiveCfg = netstandard-Windows_NT-Debug|Any CPU
- {835AD07B-7C9A-406F-B16F-59B3B0D017A4}.Debug|Any CPU.Build.0 = netstandard-Windows_NT-Debug|Any CPU
- {835AD07B-7C9A-406F-B16F-59B3B0D017A4}.Release|Any CPU.ActiveCfg = netstandard-Windows_NT-Release|Any CPU
- {835AD07B-7C9A-406F-B16F-59B3B0D017A4}.Release|Any CPU.Build.0 = netstandard-Windows_NT-Release|Any CPU
+ {835AD07B-7C9A-406F-B16F-59B3B0D017A4}.Debug|Any CPU.ActiveCfg = netstandard-Debug|Any CPU
+ {835AD07B-7C9A-406F-B16F-59B3B0D017A4}.Debug|Any CPU.Build.0 = netstandard-Debug|Any CPU
+ {835AD07B-7C9A-406F-B16F-59B3B0D017A4}.Release|Any CPU.ActiveCfg = netstandard-Release|Any CPU
+ {835AD07B-7C9A-406F-B16F-59B3B0D017A4}.Release|Any CPU.Build.0 = netstandard-Release|Any CPU
{16EE6633-F557-5C9E-9EF3-B5334B044F47}.Debug|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Debug|Any CPU
{16EE6633-F557-5C9E-9EF3-B5334B044F47}.Debug|Any CPU.Build.0 = netcoreapp-Windows_NT-Debug|Any CPU
{16EE6633-F557-5C9E-9EF3-B5334B044F47}.Release|Any CPU.ActiveCfg = netcoreapp-Windows_NT-Release|Any CPU
diff --git a/src/System.Text.Encoding.CodePages/tests/Configurations.props b/src/System.Text.Encoding.CodePages/tests/Configurations.props
index 249c8c18b4..c398e42e89 100644
--- a/src/System.Text.Encoding.CodePages/tests/Configurations.props
+++ b/src/System.Text.Encoding.CodePages/tests/Configurations.props
@@ -2,7 +2,7 @@
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildConfigurations>
- netstandard-Windows_NT;
+ netstandard;
</BuildConfigurations>
</PropertyGroup>
</Project> \ No newline at end of file
diff --git a/src/System.Text.Encoding.CodePages/tests/System.Text.Encoding.CodePages.Tests.csproj b/src/System.Text.Encoding.CodePages/tests/System.Text.Encoding.CodePages.Tests.csproj
index 9e2faaed11..103c724a63 100644
--- a/src/System.Text.Encoding.CodePages/tests/System.Text.Encoding.CodePages.Tests.csproj
+++ b/src/System.Text.Encoding.CodePages/tests/System.Text.Encoding.CodePages.Tests.csproj
@@ -5,8 +5,8 @@
<ProjectGuid>{835AD07B-7C9A-406F-B16F-59B3B0D017A4}</ProjectGuid>
</PropertyGroup>
<!-- Default configurations to help VS understand the configurations -->
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Windows_NT-Debug|AnyCPU'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Windows_NT-Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="EncodingCodePages.cs" />
<Compile Include="EncodingCodePages.netstandard.cs" />
diff --git a/src/System.Threading.Tasks/src/ApiCompatBaseline.uapaot.txt b/src/System.Threading.Tasks/src/ApiCompatBaseline.uapaot.txt
index dbd4c9e174..dc605902ae 100644
--- a/src/System.Threading.Tasks/src/ApiCompatBaseline.uapaot.txt
+++ b/src/System.Threading.Tasks/src/ApiCompatBaseline.uapaot.txt
@@ -1,12 +1,3 @@
-Compat issues with assembly System.Threading.Tasks:
-MembersMustExist : Member 'System.AggregateException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.OperationCanceledException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
MembersMustExist : Member 'System.OperationCanceledException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Tasks.Task' does not implement interface 'System.IDisposable' in the implementation but it does in the contract.
-MembersMustExist : Member 'System.Threading.Tasks.Task.Dispose()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Tasks.Task.Dispose(System.Boolean)' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Tasks.Task<TResult>' does not implement interface 'System.IDisposable' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Tasks.TaskCanceledException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Threading.Tasks.TaskCanceledException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.Tasks.TaskSchedulerException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
-Total Issues: 10
diff --git a/src/System.Threading.Thread/src/ApiCompatBaseline.uap.txt b/src/System.Threading.Thread/src/ApiCompatBaseline.uap.txt
deleted file mode 100644
index 29e756858a..0000000000
--- a/src/System.Threading.Thread/src/ApiCompatBaseline.uap.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-Compat issues with assembly System.Threading.Thread:
-MembersMustExist : Member 'System.Threading.Thread.CurrentPrincipal.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Thread.CurrentPrincipal.set(System.Security.Principal.IPrincipal)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Thread.GetDomain()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Thread.GetDomainID()' does not exist in the implementation but it does exist in the contract.
-Total Issues: 4
diff --git a/src/System.Threading.Thread/src/ApiCompatBaseline.uapaot.txt b/src/System.Threading.Thread/src/ApiCompatBaseline.uapaot.txt
deleted file mode 100644
index 29e756858a..0000000000
--- a/src/System.Threading.Thread/src/ApiCompatBaseline.uapaot.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-Compat issues with assembly System.Threading.Thread:
-MembersMustExist : Member 'System.Threading.Thread.CurrentPrincipal.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Thread.CurrentPrincipal.set(System.Security.Principal.IPrincipal)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Thread.GetDomain()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Thread.GetDomainID()' does not exist in the implementation but it does exist in the contract.
-Total Issues: 4
diff --git a/src/System.Threading.Thread/src/System.Threading.Thread.csproj b/src/System.Threading.Thread/src/System.Threading.Thread.csproj
index ae1c3ffda1..89f26ae5af 100644
--- a/src/System.Threading.Thread/src/System.Threading.Thread.csproj
+++ b/src/System.Threading.Thread/src/System.Threading.Thread.csproj
@@ -8,7 +8,6 @@
<!-- Type being defined conflicts with imported type from dependency assembly -->
<NoWarn>436</NoWarn>
<ProjectGuid>{06197EED-FF48-43F3-976D-463839D43E8C}</ProjectGuid>
- <DefineConstants Condition="'$(TargetGroup)' == 'uapaot'">$(DefineConstants);uapaot</DefineConstants>
</PropertyGroup>
<!-- Help VS understand available configurations -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netcoreapp-Unix-Debug|AnyCPU'" />
@@ -28,7 +27,7 @@
<Compile Include="System\Threading\ThreadAbortException.cs" />
<Compile Include="System\Threading\ThreadExceptionEventArgs.cs" />
</ItemGroup>
- <ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp' or '$(TargetGroup)' == 'uap'">
+ <ItemGroup>
<ProjectReference Include="..\..\System.Runtime\src\System.Runtime.csproj">
<Aliases>System_Runtime</Aliases>
</ProjectReference>
diff --git a/src/System.Threading.Thread/src/System/Threading/Thread.cs b/src/System.Threading.Thread/src/System/Threading/Thread.cs
index 6243a68f01..b18545a2ea 100644
--- a/src/System.Threading.Thread/src/System/Threading/Thread.cs
+++ b/src/System.Threading.Thread/src/System/Threading/Thread.cs
@@ -2,23 +2,19 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-#if !uapaot
extern alias System_Runtime_Extensions;
extern alias System_Security_Principal;
-#endif
-using System.Diagnostics;
+using Internal.Runtime.Augments;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Globalization;
using System.Runtime.ConstrainedExecution;
-using Internal.Runtime.Augments;
namespace System.Threading
{
-#if !uapaot
using AppDomain = System_Runtime_Extensions::System.AppDomain;
using IPrincipal = System_Security_Principal::System.Security.Principal.IPrincipal;
-#endif
public sealed partial class Thread : CriticalFinalizerObject
{
@@ -27,9 +23,7 @@ namespace System.Threading
private readonly RuntimeThread _runtimeThread;
private Delegate _start;
-#if !uapaot
private IPrincipal _principal;
-#endif
private Thread(RuntimeThread runtimeThread)
{
@@ -162,7 +156,6 @@ namespace System.Threading
}
}
-#if !uapaot
public static IPrincipal CurrentPrincipal
{
get
@@ -174,7 +167,6 @@ namespace System.Threading
CurrentThread._principal = value;
}
}
-#endif
public ExecutionContext ExecutionContext => ExecutionContext.Capture();
public bool IsAlive => _runtimeThread.IsAlive;
@@ -285,10 +277,8 @@ namespace System.Threading
throw new InvalidOperationException(SR.Thread_GetSetCompressedStack_NotSupported);
}
-#if !uapaot
public static AppDomain GetDomain() => AppDomain.CurrentDomain;
public static int GetDomainID() => GetDomain().Id;
-#endif
public override int GetHashCode() => ManagedThreadId;
public void Interrupt() => _runtimeThread.Interrupt();
public void Join() => _runtimeThread.Join();
diff --git a/src/System.Threading/src/ApiCompatBaseline.uapaot.txt b/src/System.Threading/src/ApiCompatBaseline.uapaot.txt
index cc04d33637..35b4fb3899 100644
--- a/src/System.Threading/src/ApiCompatBaseline.uapaot.txt
+++ b/src/System.Threading/src/ApiCompatBaseline.uapaot.txt
@@ -1,10 +1,4 @@
-Compat issues with assembly System.Threading:
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.AbandonedMutexException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
TypesMustExist : Type 'System.Threading.AsyncFlowControl' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.AutoResetEvent' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
-TypesMustExist : Type 'System.Threading.Barrier' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Threading.BarrierPostPhaseException' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.EventWaitHandle' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Threading.ExecutionContext' does not implement interface 'System.IDisposable' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.CreateCopy()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.Dispose()' does not exist in the implementation but it does exist in the contract.
@@ -12,18 +6,7 @@ MembersMustExist : Member 'System.Threading.ExecutionContext.GetObjectData(Syste
MembersMustExist : Member 'System.Threading.ExecutionContext.IsFlowSuppressed()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.RestoreFlow()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.SuppressFlow()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.LazyInitializer.EnsureInitialized<T>(T, System.Object, System.Func<T>)' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Threading.LockCookie' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.ManualResetEvent' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
-MembersMustExist : Member 'System.Threading.Monitor.Wait(System.Object, System.Int32, System.Boolean)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Monitor.Wait(System.Object, System.TimeSpan, System.Boolean)' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Mutex' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
-TypesMustExist : Type 'System.Threading.ReaderWriterLock' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Semaphore' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.IsWaitNotificationRequired()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.SetWaitNotificationRequired()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.Wait(System.IntPtr[], System.Boolean, System.Int32)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.WaitHelper(System.IntPtr[], System.Boolean, System.Int32)' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.SynchronizationLockException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.WaitHandleCannotBeOpenedException' does not inherit from base type 'System.ApplicationException' in the implementation but it does in the contract.
-Total Issues: 27
diff --git a/src/System.Xml.XmlSerializer/src/System.Xml.XmlSerializer.csproj b/src/System.Xml.XmlSerializer/src/System.Xml.XmlSerializer.csproj
index d4104c4a42..d292a448b6 100644
--- a/src/System.Xml.XmlSerializer/src/System.Xml.XmlSerializer.csproj
+++ b/src/System.Xml.XmlSerializer/src/System.Xml.XmlSerializer.csproj
@@ -17,7 +17,7 @@
<ItemGroup>
<ProjectReference Include="..\..\System.Private.Xml\src\System.Private.Xml.csproj" />
</ItemGroup>
- <ItemGroup Condition="'$(TargetGroup)' == 'uapaot'">
+ <ItemGroup Condition="'$(TargetGroup)' == 'uap'">
<EmbeddedResource Include="..\..\System.Private.Xml\src\Resources\System.Private.Xml.rd.xml" />
</ItemGroup>
<ItemGroup>
diff --git a/src/packages.builds b/src/packages.builds
index 912d7a94e0..01e538bbe1 100644
--- a/src/packages.builds
+++ b/src/packages.builds
@@ -7,12 +7,6 @@
</PropertyGroup>
<ItemGroup>
- <!--
- <Project Include="Native\pkg\**\*.builds" Condition="'$(SkipNativePackageBuild)' != 'true'">
- <AdditionalProperties>$(AdditionalProperties);SkipCreatePackageOnMissingFiles=true</AdditionalProperties>
- <BuildAllOSGroups>$(BuildAllOSGroups)</BuildAllOSGroups>
- </Project>
- -->
<Project Include="$(MSBuildThisFileDirectory)..\pkg\Microsoft.NETCore.Platforms\Microsoft.NETCore.Platforms.builds" Condition="'$(SkipManagedPackageBuild)' != 'true'">
<AdditionalProperties>$(AdditionalProperties)</AdditionalProperties>
</Project>
@@ -22,7 +16,7 @@
<Project Include="$(MSBuildThisFileDirectory)..\pkg\Microsoft.Private.CoreFx.UAP\Microsoft.Private.CoreFx.UAP.builds" Condition="'$(SkipManagedPackageBuild)' != 'true'">
<AdditionalProperties>$(AdditionalProperties)</AdditionalProperties>
</Project>
- <Project Include="*\pkg\*.pkgproj" Condition="'$(SkipManagedPackageBuild)' != 'true' AND '$(BuildAllConfigurations)' == 'true'">
+ <Project Include="*\pkg\**\*.pkgproj" Condition="'$(SkipManagedPackageBuild)' != 'true' AND '$(BuildAllConfigurations)' == 'true'">
<AdditionalProperties>$(AdditionalProperties)</AdditionalProperties>
</Project>
</ItemGroup>
diff --git a/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt b/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt
index b7869d75bd..e847414143 100644
--- a/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt
+++ b/src/shims/ApiCompatBaseline.uapaot.netstandard20.txt
@@ -2,7 +2,28 @@ DEFAULT_APPNAME Error: 0 : Failed to find or load matching assembly 'System.IO.F
DEFAULT_APPNAME Error: 0 : Unable to resolve assembly 'Assembly(Name=System.IO.FileSystem.AccessControl, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)' referenced by the implementation assembly 'Assembly(Name=mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089)'.
Compat issues with assembly mscorlib:
TypesMustExist : Type 'System.AppContext' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.AppDomain' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_AssemblyLoad(System.AssemblyLoadEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_AssemblyResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_FirstChanceException(System.EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs>)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_ProcessExit(System.EventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_ReflectionOnlyAssemblyResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_ResourceResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_TypeResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_UnhandledException(System.UnhandledExceptionEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.BaseDirectory.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.GetAssemblies()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.GetData(System.String)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.IsCompatibilitySwitchSet(System.String)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.RelativeSearchPath.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_AssemblyLoad(System.AssemblyLoadEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_AssemblyResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_FirstChanceException(System.EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs>)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_ProcessExit(System.EventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_ReflectionOnlyAssemblyResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_ResourceResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_TypeResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_UnhandledException(System.UnhandledExceptionEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.SetData(System.String, System.Object)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.ArgIterator' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Array.CreateInstance(System.Type, System.Int64[])' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Array.IsReadOnly.get()' does not exist in the implementation but it does exist in the contract.
@@ -121,7 +142,6 @@ MembersMustExist : Member 'System.Math.BigMul(System.Int32, System.Int32)' does
MembersMustExist : Member 'System.Math.DivRem(System.Int32, System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Math.DivRem(System.Int64, System.Int64, System.Int64)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.ModuleHandle System.ModuleHandle.EmptyHandle' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.OperationCanceledException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
MembersMustExist : Member 'System.OperationCanceledException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.ResolveEventHandler' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.RuntimeArgumentHandle' does not exist in the implementation but it does exist in the contract.
@@ -224,7 +244,6 @@ TypesMustExist : Type 'System.Diagnostics.Tracing.EventTask' does not exist in t
TypesMustExist : Type 'System.Diagnostics.Tracing.EventWrittenEventArgs' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Diagnostics.Tracing.NonEventAttribute' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Globalization.CompareInfo' does not implement interface 'System.Runtime.Serialization.IDeserializationCallback' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Globalization.TextInfo' does not implement interface 'System.Runtime.Serialization.IDeserializationCallback' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.IO.BufferedStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
TypesMustExist : Type 'System.IO.FileOptions' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.IO.FileStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
@@ -241,10 +260,7 @@ MembersMustExist : Member 'System.Reflection.Assembly.GetFiles()' does not exist
MembersMustExist : Member 'System.Reflection.Assembly.GetFiles(System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Reflection.FieldInfo.GetValueDirect(System.TypedReference)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Reflection.FieldInfo.SetValueDirect(System.TypedReference, System.Object)' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Reflection.ObfuscateAssemblyAttribute' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Reflection.ObfuscationAttribute' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Reflection.StrongNameKeyPair..ctor(System.IO.FileStream)' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Reflection.TypeDelegator' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Runtime.MemoryFailPoint' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Runtime.CompilerServices.CompilationRelaxations' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Runtime.CompilerServices.CompilationRelaxationsAttribute..ctor(System.Runtime.CompilerServices.CompilationRelaxations)' does not exist in the implementation but it does exist in the contract.
@@ -262,9 +278,6 @@ MembersMustExist : Member 'System.Runtime.CompilerServices.MethodImplAttribute..
MembersMustExist : Member 'System.Runtime.CompilerServices.MethodImplAttribute..ctor(System.Int16)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Runtime.CompilerServices.StringFreezingAttribute' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Runtime.CompilerServices.SuppressIldasmAttribute' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Runtime.ConstrainedExecution.Cer' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Runtime.ConstrainedExecution.Consistency' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Runtime.ConstrainedExecution.ReliabilityContractAttribute' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Runtime.InteropServices.AllowReversePInvokeCallsAttribute' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Runtime.InteropServices.COMException' does not inherit from base type 'System.Runtime.InteropServices.ExternalException' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Runtime.InteropServices.COMException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
@@ -300,10 +313,6 @@ MembersMustExist : Member 'System.Runtime.InteropServices.SafeArrayTypeMismatchE
CannotRemoveBaseTypeOrInterface : Type 'System.Runtime.InteropServices.SafeBuffer' does not inherit from base type 'Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Runtime.InteropServices.SEHException' does not inherit from base type 'System.Runtime.InteropServices.ExternalException' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Runtime.InteropServices.SEHException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Runtime.InteropServices.StructLayoutAttribute..ctor(System.Int16)' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Runtime.Serialization.ISafeSerializationData' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Runtime.Serialization.SafeSerializationEventArgs' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Runtime.Serialization.SerializationInfo..ctor(System.Type, System.Runtime.Serialization.IFormatterConverter, System.Boolean)' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Security.Cryptography.CryptoStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Text.Decoder.Convert(System.Byte*, System.Int32, System.Char*, System.Int32, System.Boolean, System.Int32, System.Int32, System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Text.Decoder.GetCharCount(System.Byte*, System.Int32, System.Boolean)' does not exist in the implementation but it does exist in the contract.
@@ -323,10 +332,7 @@ MembersMustExist : Member 'System.Text.Encoding.IsMailNewsSave.get()' does not e
MembersMustExist : Member 'System.Text.Encoding.WindowsCodePage.get()' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Text.EncodingInfo' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Text.StringBuilder' does not implement interface 'System.Runtime.Serialization.ISerializable' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.AbandonedMutexException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
TypesMustExist : Type 'System.Threading.AsyncFlowControl' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.AutoResetEvent' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.EventWaitHandle' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Threading.ExecutionContext' does not implement interface 'System.IDisposable' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.CreateCopy()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.Dispose()' does not exist in the implementation but it does exist in the contract.
@@ -334,21 +340,12 @@ MembersMustExist : Member 'System.Threading.ExecutionContext.GetObjectData(Syste
MembersMustExist : Member 'System.Threading.ExecutionContext.IsFlowSuppressed()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.RestoreFlow()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.SuppressFlow()' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.ManualResetEvent' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
-MembersMustExist : Member 'System.Threading.Monitor.Wait(System.Object, System.Int32, System.Boolean)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Monitor.Wait(System.Object, System.TimeSpan, System.Boolean)' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Mutex' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
TypesMustExist : Type 'System.Threading.Overlapped' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Threading.RegisteredWaitHandle' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.IsWaitNotificationRequired()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.SetWaitNotificationRequired()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.Wait(System.IntPtr[], System.Boolean, System.Int32)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.WaitHelper(System.IntPtr[], System.Boolean, System.Int32)' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.SynchronizationLockException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
-MembersMustExist : Member 'System.Threading.Thread.CurrentPrincipal.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Thread.CurrentPrincipal.set(System.Security.Principal.IPrincipal)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Thread.GetDomain()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Thread.GetDomainID()' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Threading.ThreadPool' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Timer' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Threading.Timer..ctor(System.Threading.TimerCallback)' does not exist in the implementation but it does exist in the contract.
@@ -358,20 +355,34 @@ MembersMustExist : Member 'System.Threading.Timer.Change(System.Int64, System.In
MembersMustExist : Member 'System.Threading.Timer.Change(System.UInt32, System.UInt32)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.Timer.Dispose(System.Threading.WaitHandle)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Threading.WaitCallback' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.WaitHandle' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.WaitHandleCannotBeOpenedException' does not inherit from base type 'System.ApplicationException' in the implementation but it does in the contract.
TypesMustExist : Type 'System.Threading.WaitOrTimerCallback' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Tasks.Task' does not implement interface 'System.IDisposable' in the implementation but it does in the contract.
-MembersMustExist : Member 'System.Threading.Tasks.Task.Dispose()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Tasks.Task.Dispose(System.Boolean)' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Tasks.Task<TResult>' does not implement interface 'System.IDisposable' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Tasks.TaskCanceledException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Threading.Tasks.TaskCanceledException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.Tasks.TaskSchedulerException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
Compat issues with assembly netstandard:
CannotRemoveBaseTypeOrInterface : Type 'Microsoft.Win32.SafeHandles.SafeMemoryMappedViewHandle' does not inherit from base type 'Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid' in the implementation but it does in the contract.
TypesMustExist : Type 'System.AppContext' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.AppDomain' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_AssemblyLoad(System.AssemblyLoadEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_AssemblyResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_FirstChanceException(System.EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs>)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_ProcessExit(System.EventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_ReflectionOnlyAssemblyResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_ResourceResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_TypeResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.add_UnhandledException(System.UnhandledExceptionEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.BaseDirectory.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.GetAssemblies()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.GetData(System.String)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.IsCompatibilitySwitchSet(System.String)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.RelativeSearchPath.get()' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_AssemblyLoad(System.AssemblyLoadEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_AssemblyResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_FirstChanceException(System.EventHandler<System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs>)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_ProcessExit(System.EventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_ReflectionOnlyAssemblyResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_ResourceResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_TypeResolve(System.ResolveEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.remove_UnhandledException(System.UnhandledExceptionEventHandler)' does not exist in the implementation but it does exist in the contract.
+MembersMustExist : Member 'System.AppDomain.SetData(System.String, System.Object)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.ArgIterator' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Array.CreateInstance(System.Type, System.Int64[])' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Array.IsReadOnly.get()' does not exist in the implementation but it does exist in the contract.
@@ -490,7 +501,6 @@ MembersMustExist : Member 'System.Math.BigMul(System.Int32, System.Int32)' does
MembersMustExist : Member 'System.Math.DivRem(System.Int32, System.Int32, System.Int32)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Math.DivRem(System.Int64, System.Int64, System.Int64)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.ModuleHandle System.ModuleHandle.EmptyHandle' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.OperationCanceledException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
MembersMustExist : Member 'System.OperationCanceledException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.ResolveEventHandler' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.RuntimeArgumentHandle' does not exist in the implementation but it does exist in the contract.
@@ -537,7 +547,6 @@ MembersMustExist : Member 'System.UInt64.GetTypeCode()' does not exist in the im
CannotRemoveBaseTypeOrInterface : Type 'System.UIntPtr' does not implement interface 'System.Runtime.Serialization.ISerializable' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Version..ctor()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Version.CompareTo(System.Object)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.ComponentModel.DefaultValueAttribute.SetValue(System.Object)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Diagnostics.Debug.AutoFlush.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Diagnostics.Debug.AutoFlush.set(System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Diagnostics.Debug.Close()' does not exist in the implementation but it does exist in the contract.
@@ -607,7 +616,6 @@ TypesMustExist : Type 'System.Diagnostics.Tracing.EventTask' does not exist in t
TypesMustExist : Type 'System.Diagnostics.Tracing.EventWrittenEventArgs' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Diagnostics.Tracing.NonEventAttribute' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Globalization.CompareInfo' does not implement interface 'System.Runtime.Serialization.IDeserializationCallback' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Globalization.TextInfo' does not implement interface 'System.Runtime.Serialization.IDeserializationCallback' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.IO.BufferedStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
TypesMustExist : Type 'System.IO.FileOptions' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.IO.FileStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
@@ -644,10 +652,7 @@ MembersMustExist : Member 'System.Reflection.Assembly.GetFiles()' does not exist
MembersMustExist : Member 'System.Reflection.Assembly.GetFiles(System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Reflection.FieldInfo.GetValueDirect(System.TypedReference)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Reflection.FieldInfo.SetValueDirect(System.TypedReference, System.Object)' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Reflection.ObfuscateAssemblyAttribute' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Reflection.ObfuscationAttribute' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Reflection.StrongNameKeyPair..ctor(System.IO.FileStream)' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Reflection.TypeDelegator' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Runtime.MemoryFailPoint' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Runtime.CompilerServices.CompilationRelaxations' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Runtime.CompilerServices.CompilationRelaxationsAttribute..ctor(System.Runtime.CompilerServices.CompilationRelaxations)' does not exist in the implementation but it does exist in the contract.
@@ -665,9 +670,6 @@ MembersMustExist : Member 'System.Runtime.CompilerServices.MethodImplAttribute..
MembersMustExist : Member 'System.Runtime.CompilerServices.MethodImplAttribute..ctor(System.Int16)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Runtime.CompilerServices.StringFreezingAttribute' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Runtime.CompilerServices.SuppressIldasmAttribute' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Runtime.ConstrainedExecution.Cer' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Runtime.ConstrainedExecution.Consistency' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Runtime.ConstrainedExecution.ReliabilityContractAttribute' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Runtime.InteropServices.AllowReversePInvokeCallsAttribute' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Runtime.InteropServices.COMException' does not inherit from base type 'System.Runtime.InteropServices.ExternalException' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Runtime.InteropServices.COMException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
@@ -703,10 +705,6 @@ MembersMustExist : Member 'System.Runtime.InteropServices.SafeArrayTypeMismatchE
CannotRemoveBaseTypeOrInterface : Type 'System.Runtime.InteropServices.SafeBuffer' does not inherit from base type 'Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Runtime.InteropServices.SEHException' does not inherit from base type 'System.Runtime.InteropServices.ExternalException' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Runtime.InteropServices.SEHException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Runtime.InteropServices.StructLayoutAttribute..ctor(System.Int16)' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Runtime.Serialization.ISafeSerializationData' does not exist in the implementation but it does exist in the contract.
-TypesMustExist : Type 'System.Runtime.Serialization.SafeSerializationEventArgs' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Runtime.Serialization.SerializationInfo..ctor(System.Type, System.Runtime.Serialization.IFormatterConverter, System.Boolean)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Security.SecureStringMarshal' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Security.Cryptography.CryptoStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Security.Cryptography.ECDsa.Create(System.Security.Cryptography.ECCurve)' does not exist in the implementation but it does exist in the contract.
@@ -729,10 +727,7 @@ MembersMustExist : Member 'System.Text.Encoding.IsMailNewsSave.get()' does not e
MembersMustExist : Member 'System.Text.Encoding.WindowsCodePage.get()' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Text.EncodingInfo' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Text.StringBuilder' does not implement interface 'System.Runtime.Serialization.ISerializable' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.AbandonedMutexException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
TypesMustExist : Type 'System.Threading.AsyncFlowControl' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.AutoResetEvent' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.EventWaitHandle' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Threading.ExecutionContext' does not implement interface 'System.IDisposable' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.CreateCopy()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.Dispose()' does not exist in the implementation but it does exist in the contract.
@@ -740,22 +735,12 @@ MembersMustExist : Member 'System.Threading.ExecutionContext.GetObjectData(Syste
MembersMustExist : Member 'System.Threading.ExecutionContext.IsFlowSuppressed()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.RestoreFlow()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.SuppressFlow()' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.ManualResetEvent' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
-MembersMustExist : Member 'System.Threading.Monitor.Wait(System.Object, System.Int32, System.Boolean)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Monitor.Wait(System.Object, System.TimeSpan, System.Boolean)' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Mutex' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
TypesMustExist : Type 'System.Threading.Overlapped' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Threading.RegisteredWaitHandle' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Semaphore' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.IsWaitNotificationRequired()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.SetWaitNotificationRequired()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.Wait(System.IntPtr[], System.Boolean, System.Int32)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.WaitHelper(System.IntPtr[], System.Boolean, System.Int32)' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.SynchronizationLockException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
-MembersMustExist : Member 'System.Threading.Thread.CurrentPrincipal.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Thread.CurrentPrincipal.set(System.Security.Principal.IPrincipal)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Thread.GetDomain()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Thread.GetDomainID()' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Threading.ThreadPool' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Timer' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Threading.Timer..ctor(System.Threading.TimerCallback)' does not exist in the implementation but it does exist in the contract.
@@ -765,18 +750,10 @@ MembersMustExist : Member 'System.Threading.Timer.Change(System.Int64, System.In
MembersMustExist : Member 'System.Threading.Timer.Change(System.UInt32, System.UInt32)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.Timer.Dispose(System.Threading.WaitHandle)' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Threading.WaitCallback' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.WaitHandle' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.WaitHandleCannotBeOpenedException' does not inherit from base type 'System.ApplicationException' in the implementation but it does in the contract.
TypesMustExist : Type 'System.Threading.WaitOrTimerCallback' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Tasks.Task' does not implement interface 'System.IDisposable' in the implementation but it does in the contract.
-MembersMustExist : Member 'System.Threading.Tasks.Task.Dispose()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Tasks.Task.Dispose(System.Boolean)' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Tasks.Task<TResult>' does not implement interface 'System.IDisposable' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Tasks.TaskCanceledException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Threading.Tasks.TaskCanceledException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.Tasks.TaskSchedulerException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
Compat issues with assembly System:
-MembersMustExist : Member 'System.ComponentModel.DefaultValueAttribute.SetValue(System.Object)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Diagnostics.Debug.AutoFlush.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Diagnostics.Debug.AutoFlush.set(System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Diagnostics.Debug.Close()' does not exist in the implementation but it does exist in the contract.
@@ -803,7 +780,6 @@ CannotRemoveBaseTypeOrInterface : Type 'System.Net.Security.AuthenticatedStream'
CannotRemoveBaseTypeOrInterface : Type 'System.Net.Security.NegotiateStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Net.Security.SslStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Net.Sockets.NetworkStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Semaphore' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
Compat issues with assembly System.AppContext:
TypesMustExist : Type 'System.AppContext' does not exist in the implementation but it does exist in the contract.
Compat issues with assembly System.Core:
@@ -893,7 +869,6 @@ TypesMustExist : Type 'System.Diagnostics.Tracing.EventWrittenEventArgs' does no
TypesMustExist : Type 'System.Diagnostics.Tracing.NonEventAttribute' does not exist in the implementation but it does exist in the contract.
Compat issues with assembly System.Globalization:
CannotRemoveBaseTypeOrInterface : Type 'System.Globalization.CompareInfo' does not implement interface 'System.Runtime.Serialization.IDeserializationCallback' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Globalization.TextInfo' does not implement interface 'System.Runtime.Serialization.IDeserializationCallback' in the implementation but it does in the contract.
Compat issues with assembly System.IO:
CannotRemoveBaseTypeOrInterface : Type 'System.IO.BufferedStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.IO.MemoryStream' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
@@ -1052,16 +1027,13 @@ MembersMustExist : Member 'System.UInt64.GetTypeCode()' does not exist in the im
CannotRemoveBaseTypeOrInterface : Type 'System.UIntPtr' does not implement interface 'System.Runtime.Serialization.ISerializable' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Version..ctor()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Version.CompareTo(System.Object)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.ComponentModel.DefaultValueAttribute.SetValue(System.Object)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Runtime.CompilerServices.CompilationRelaxationsAttribute..ctor(System.Runtime.CompilerServices.CompilationRelaxations)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Runtime.CompilerServices.InternalsVisibleToAttribute.AllInternalsVisible.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Runtime.CompilerServices.InternalsVisibleToAttribute.AllInternalsVisible.set(System.Boolean)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Runtime.CompilerServices.MethodCodeType System.Runtime.CompilerServices.MethodImplAttribute.MethodCodeType' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Runtime.CompilerServices.MethodImplAttribute..ctor()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Runtime.CompilerServices.MethodImplAttribute..ctor(System.Int16)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Runtime.InteropServices.StructLayoutAttribute..ctor(System.Int16)' does not exist in the implementation but it does exist in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Text.StringBuilder' does not implement interface 'System.Runtime.Serialization.ISerializable' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.WaitHandle' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
Compat issues with assembly System.Runtime.Extensions:
MembersMustExist : Member 'System.Object System.Convert.DBNull' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Convert.ChangeType(System.Object, System.TypeCode)' does not exist in the implementation but it does exist in the contract.
@@ -1146,8 +1118,6 @@ MembersMustExist : Member 'System.Runtime.InteropServices.SafeArrayTypeMismatchE
CannotRemoveBaseTypeOrInterface : Type 'System.Runtime.InteropServices.SafeBuffer' does not inherit from base type 'Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Runtime.InteropServices.SEHException' does not inherit from base type 'System.Runtime.InteropServices.ExternalException' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Runtime.InteropServices.SEHException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
-Compat issues with assembly System.Runtime.Serialization.Formatters:
-MembersMustExist : Member 'System.Runtime.Serialization.SerializationInfo..ctor(System.Type, System.Runtime.Serialization.IFormatterConverter, System.Boolean)' does not exist in the implementation but it does exist in the contract.
Compat issues with assembly System.Security.Cryptography.Algorithms:
MembersMustExist : Member 'System.Security.Cryptography.ECDsa.Create(System.Security.Cryptography.ECCurve)' does not exist in the implementation but it does exist in the contract.
Compat issues with assembly System.Security.Cryptography.Cng:
@@ -1174,9 +1144,6 @@ MembersMustExist : Member 'System.Text.Encoding.IsMailNewsDisplay.get()' does no
MembersMustExist : Member 'System.Text.Encoding.IsMailNewsSave.get()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Text.Encoding.WindowsCodePage.get()' does not exist in the implementation but it does exist in the contract.
Compat issues with assembly System.Threading:
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.AbandonedMutexException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.AutoResetEvent' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.EventWaitHandle' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
CannotRemoveBaseTypeOrInterface : Type 'System.Threading.ExecutionContext' does not implement interface 'System.IDisposable' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.CreateCopy()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.Dispose()' does not exist in the implementation but it does exist in the contract.
@@ -1184,32 +1151,14 @@ MembersMustExist : Member 'System.Threading.ExecutionContext.GetObjectData(Syste
MembersMustExist : Member 'System.Threading.ExecutionContext.IsFlowSuppressed()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.RestoreFlow()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.ExecutionContext.SuppressFlow()' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.ManualResetEvent' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
-MembersMustExist : Member 'System.Threading.Monitor.Wait(System.Object, System.Int32, System.Boolean)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Monitor.Wait(System.Object, System.TimeSpan, System.Boolean)' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Mutex' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Semaphore' does not inherit from base type 'System.MarshalByRefObject' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.IsWaitNotificationRequired()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.SetWaitNotificationRequired()' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.Wait(System.IntPtr[], System.Boolean, System.Int32)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.SynchronizationContext.WaitHelper(System.IntPtr[], System.Boolean, System.Int32)' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.SynchronizationLockException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.WaitHandleCannotBeOpenedException' does not inherit from base type 'System.ApplicationException' in the implementation but it does in the contract.
Compat issues with assembly System.Threading.Tasks:
-CannotRemoveBaseTypeOrInterface : Type 'System.OperationCanceledException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
MembersMustExist : Member 'System.OperationCanceledException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Tasks.Task' does not implement interface 'System.IDisposable' in the implementation but it does in the contract.
-MembersMustExist : Member 'System.Threading.Tasks.Task.Dispose()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Tasks.Task.Dispose(System.Boolean)' does not exist in the implementation but it does exist in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Tasks.Task<TResult>' does not implement interface 'System.IDisposable' in the implementation but it does in the contract.
-CannotRemoveBaseTypeOrInterface : Type 'System.Threading.Tasks.TaskCanceledException' does not inherit from base type 'System.SystemException' in the implementation but it does in the contract.
MembersMustExist : Member 'System.Threading.Tasks.TaskCanceledException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.Tasks.TaskSchedulerException..ctor(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)' does not exist in the implementation but it does exist in the contract.
-Compat issues with assembly System.Threading.Thread:
-MembersMustExist : Member 'System.Threading.Thread.CurrentPrincipal.get()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Thread.CurrentPrincipal.set(System.Security.Principal.IPrincipal)' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Thread.GetDomain()' does not exist in the implementation but it does exist in the contract.
-MembersMustExist : Member 'System.Threading.Thread.GetDomainID()' does not exist in the implementation but it does exist in the contract.
Compat issues with assembly System.Threading.ThreadPool:
TypesMustExist : Type 'System.Threading.RegisteredWaitHandle' does not exist in the implementation but it does exist in the contract.
TypesMustExist : Type 'System.Threading.ThreadPool' does not exist in the implementation but it does exist in the contract.
@@ -1223,4 +1172,4 @@ MembersMustExist : Member 'System.Threading.Timer..ctor(System.Threading.TimerCa
MembersMustExist : Member 'System.Threading.Timer.Change(System.Int64, System.Int64)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.Timer.Change(System.UInt32, System.UInt32)' does not exist in the implementation but it does exist in the contract.
MembersMustExist : Member 'System.Threading.Timer.Dispose(System.Threading.WaitHandle)' does not exist in the implementation but it does exist in the contract.
-Total Issues: 1186
+Total Issues: 1137
diff --git a/src/upload-tests.proj b/src/upload-tests.proj
index e1d12502bd..779b4e2cdb 100644
--- a/src/upload-tests.proj
+++ b/src/upload-tests.proj
@@ -63,8 +63,8 @@
<!-- Properties used for submission by CloudTest.Helix.Targets-->
<BuildMoniker>$(CurrentDate)</BuildMoniker>
<BuildMoniker Condition="'$(IsOfficial)'=='true'">$(OfficialBuildId)</BuildMoniker>
- <!-- TODO: This requires some specific understanding of Helix, we should try to generalize this into properties. -->
- <HelixJobProperties>{ &quot;architecture&quot; : &quot;$(ArchGroup)&quot;, &quot;configuration&quot;: &quot;$(ConfigurationGroup)&quot;, &quot;operatingSystem&quot; : &quot;$(TargetOS)&quot; }</HelixJobProperties>
+ <HelixArchLabel>$(ArchGroup)</HelixArchLabel>
+ <HelixConfigLabel>$(ConfigurationGroup)</HelixConfigLabel>
</PropertyGroup>
<Target Name="CoreFXPreCloudBuild" >