diff --git a/NuGet.config b/NuGet.config index 74e7c1411d8a85..8c974d5ecd320b 100644 --- a/NuGet.config +++ b/NuGet.config @@ -9,6 +9,7 @@ + diff --git a/docs/workflow/testing/libraries/testing-android.md b/docs/workflow/testing/libraries/testing-android.md index b4c5179e6be93f..abb8c2ec6b80a2 100644 --- a/docs/workflow/testing/libraries/testing-android.md +++ b/docs/workflow/testing/libraries/testing-android.md @@ -23,9 +23,9 @@ Android SDK and NDK can be automatically installed via the following script: set -e NDK_VER=r23c -SDK_VER=6200805_latest -SDK_API_LEVEL=29 -SDK_BUILD_TOOLS=29.0.3 +SDK_VER=9123335_latest +SDK_API_LEVEL=33 +SDK_BUILD_TOOLS=33.0.1 if [[ "$OSTYPE" == "darwin"* ]]; then HOST_OS=darwin @@ -63,7 +63,7 @@ Android Studio offers a convenient UI: Before running a build you might want to set the Android SDK and NDK environment variables: ``` export ANDROID_SDK_ROOT= -export ANDROID_NDK_ROOT= +export ANDROID_NDK_ROOT= ``` Now we're ready to build everything for Android: diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f3527efef6b90c..569c7743ca674c 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,8 +1,8 @@ - + https://github.com/dotnet/icu - d89651b995bbb9565a3674ddf77b187cb6f9f26d + bdaa3a0b2a204574ef4ab63bae2c4b5c6d607849 https://github.com/dotnet/msquic @@ -48,13 +48,13 @@ https://github.com/dotnet/command-line-api 5618b2d243ccdeb5c7e50a298b33b13036b4351b - + https://github.com/dotnet/emsdk - 33b038b591f6e19c32a390333d41292cbd86d93c + ca510604d1667f32b4fa4653e3d3b64be8136bdc - + https://github.com/dotnet/emsdk - 33b038b591f6e19c32a390333d41292cbd86d93c + ca510604d1667f32b4fa4653e3d3b64be8136bdc @@ -130,57 +130,57 @@ https://github.com/dotnet/arcade 02d5538bac1bacfd6fb310d92ab8914de08f3358 - + https://github.com/dotnet/runtime-assets - ddb9487d79d1ba17a4fd872ffafe5f1cd7b406d6 + 8a02fa4c0dca32e1402c1926eec810b8e45948a5 - + https://github.com/dotnet/runtime-assets - ddb9487d79d1ba17a4fd872ffafe5f1cd7b406d6 + 8a02fa4c0dca32e1402c1926eec810b8e45948a5 - + https://github.com/dotnet/runtime-assets - ddb9487d79d1ba17a4fd872ffafe5f1cd7b406d6 + 8a02fa4c0dca32e1402c1926eec810b8e45948a5 - + https://github.com/dotnet/runtime-assets - ddb9487d79d1ba17a4fd872ffafe5f1cd7b406d6 + 8a02fa4c0dca32e1402c1926eec810b8e45948a5 - + https://github.com/dotnet/runtime-assets - ddb9487d79d1ba17a4fd872ffafe5f1cd7b406d6 + 8a02fa4c0dca32e1402c1926eec810b8e45948a5 - + https://github.com/dotnet/runtime-assets - ddb9487d79d1ba17a4fd872ffafe5f1cd7b406d6 + 8a02fa4c0dca32e1402c1926eec810b8e45948a5 - + https://github.com/dotnet/runtime-assets - ddb9487d79d1ba17a4fd872ffafe5f1cd7b406d6 + 8a02fa4c0dca32e1402c1926eec810b8e45948a5 - + https://github.com/dotnet/runtime-assets - ddb9487d79d1ba17a4fd872ffafe5f1cd7b406d6 + 8a02fa4c0dca32e1402c1926eec810b8e45948a5 - + https://github.com/dotnet/runtime-assets - ddb9487d79d1ba17a4fd872ffafe5f1cd7b406d6 + 8a02fa4c0dca32e1402c1926eec810b8e45948a5 - + https://github.com/dotnet/runtime-assets - ddb9487d79d1ba17a4fd872ffafe5f1cd7b406d6 + 8a02fa4c0dca32e1402c1926eec810b8e45948a5 - + https://github.com/dotnet/runtime-assets - ddb9487d79d1ba17a4fd872ffafe5f1cd7b406d6 + 8a02fa4c0dca32e1402c1926eec810b8e45948a5 - + https://github.com/dotnet/runtime-assets - ddb9487d79d1ba17a4fd872ffafe5f1cd7b406d6 + 8a02fa4c0dca32e1402c1926eec810b8e45948a5 - + https://github.com/dotnet/runtime-assets - ddb9487d79d1ba17a4fd872ffafe5f1cd7b406d6 + 8a02fa4c0dca32e1402c1926eec810b8e45948a5 https://github.com/dotnet/llvm-project @@ -278,13 +278,13 @@ https://dev.azure.com/dnceng/internal/_git/dotnet-optimization 5e0b0da43f660de5798186f4fd3bc900fc90576c - + https://github.com/dotnet/hotreload-utils - 6d7ba7d63a9c246a6477bda4d19c3315fd91868f + 74472cc407fe757b9479fd78cdb64d7d6e67935f - + https://github.com/dotnet/runtime-assets - ddb9487d79d1ba17a4fd872ffafe5f1cd7b406d6 + 8a02fa4c0dca32e1402c1926eec810b8e45948a5 https://github.com/dotnet/roslyn-analyzers diff --git a/eng/Versions.props b/eng/Versions.props index 0d9e28a90a17b0..625e7058506c9e 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -22,8 +22,8 @@ false false $(AssemblyVersion) - 7.0.15 - 7.0.15 + 7.0.16 + 7.0.16 @@ -118,20 +118,20 @@ 4.5.0 7.0.0-rc.1.22414.6 - 7.0.0-beta.23566.2 - 7.0.0-beta.23566.2 - 7.0.0-beta.23566.2 - 7.0.0-beta.23566.2 - 7.0.0-beta.23566.2 - 7.0.0-beta.23566.2 - 7.0.0-beta.23566.2 - 7.0.0-beta.23566.2 - 7.0.0-beta.23566.2 - 7.0.0-beta.23566.2 - 7.0.0-beta.23566.2 - 7.0.0-beta.23566.2 - 7.0.0-beta.23566.2 - 7.0.0-beta.23566.2 + 7.0.0-beta.24060.3 + 7.0.0-beta.24060.3 + 7.0.0-beta.24060.3 + 7.0.0-beta.24060.3 + 7.0.0-beta.24060.3 + 7.0.0-beta.24060.3 + 7.0.0-beta.24060.3 + 7.0.0-beta.24060.3 + 7.0.0-beta.24060.3 + 7.0.0-beta.24060.3 + 7.0.0-beta.24060.3 + 7.0.0-beta.24060.3 + 7.0.0-beta.24060.3 + 7.0.0-beta.24060.3 1.0.0-prerelease.22415.6 1.0.0-prerelease.22415.6 @@ -157,7 +157,7 @@ 7.0.0-prerelease.24054.3 7.0.0-prerelease.24054.3 7.0.0-prerelease.24054.3 - 7.0.0-alpha.0.23566.1 + 7.0.0-alpha.0.24060.2 2.4.2 1.0.0 2.4.5 @@ -177,7 +177,7 @@ 7.0.100-1.23401.1 $(MicrosoftNETILLinkTasksVersion) - 7.0.0-rtm.23565.2 + 7.0.0-rtm.24060.3 2.2.3 7.0.0-alpha.1.22459.1 diff --git a/eng/native/ijw/IJW.cmake b/eng/native/ijw/IJW.cmake index 81b69f0d965d2c..453dfb26b1ac8f 100644 --- a/eng/native/ijw/IJW.cmake +++ b/eng/native/ijw/IJW.cmake @@ -46,7 +46,7 @@ if (CLR_CMAKE_HOST_WIN32) # 4365 - signed/unsigned mismatch # 4679 - Could not import member. This is an issue with IJW and static abstract methods in interfaces. - add_compile_options(/wd4365 /wd4679) + add_compile_options(/wd4365 /wd4679 /wd5271) # IJW add_compile_options(/clr:netcore) diff --git a/eng/pipelines/common/platform-matrix.yml b/eng/pipelines/common/platform-matrix.yml index 2ec41d7682c7e2..0c0969520b0dc8 100644 --- a/eng/pipelines/common/platform-matrix.yml +++ b/eng/pipelines/common/platform-matrix.yml @@ -580,6 +580,29 @@ jobs: helixQueueGroup: ${{ parameters.helixQueueGroup }} ${{ insert }}: ${{ parameters.jobParameters }} +- ${{ if containsValue(parameters.platforms, 'Android_arm64_perf_specific') }}: + - template: xplat-setup.yml + parameters: + jobTemplate: ${{ parameters.jobTemplate }} + helixQueuesTemplate: ${{ parameters.helixQueuesTemplate }} + variables: ${{ parameters.variables }} + osGroup: Android + archType: arm64 + targetRid: android-arm64 + platform: Android_arm64 + shouldContinueOnError: ${{ parameters.shouldContinueOnError }} + container: + image: cbl-mariner-2.0-cross-android-amd64 + registry: mcr + jobParameters: + runtimeFlavor: mono + stagedBuild: ${{ parameters.stagedBuild }} + buildConfig: ${{ parameters.buildConfig }} + ${{ if eq(parameters.passPlatforms, true) }}: + platforms: ${{ parameters.platforms }} + helixQueueGroup: ${{ parameters.helixQueueGroup }} + ${{ insert }}: ${{ parameters.jobParameters }} + # Mac Catalyst x64 - ${{ if containsValue(parameters.platforms, 'MacCatalyst_x64') }}: diff --git a/eng/pipelines/common/restore-internal-tools.yml b/eng/pipelines/common/restore-internal-tools.yml index eead4b67c30f64..fdec41da53da55 100644 --- a/eng/pipelines/common/restore-internal-tools.yml +++ b/eng/pipelines/common/restore-internal-tools.yml @@ -1,5 +1,5 @@ steps: - - task: NuGetAuthenticate@0 + - task: NuGetAuthenticate@1 inputs: nuGetServiceConnections: 'devdiv/dotnet-core-internal-tooling' forceReinstallCredentialProvider: true diff --git a/eng/pipelines/coreclr/ci.yml b/eng/pipelines/coreclr/ci.yml index ae8e4e42180420..3b9ad9519b1d6f 100644 --- a/eng/pipelines/coreclr/ci.yml +++ b/eng/pipelines/coreclr/ci.yml @@ -153,13 +153,3 @@ jobs: crossgen2: true displayNameArgs: R2R_CG2 liveLibrariesBuildConfig: Release - -# -# Formatting -# -- template: /eng/pipelines/common/platform-matrix.yml - parameters: - jobTemplate: /eng/pipelines/coreclr/templates/format-job.yml - platforms: - - Linux_x64 - - windows_x64 diff --git a/eng/pipelines/coreclr/perf-non-wasm-jobs.yml b/eng/pipelines/coreclr/perf-non-wasm-jobs.yml index 9a60000c79d08a..cbb49599d95684 100644 --- a/eng/pipelines/coreclr/perf-non-wasm-jobs.yml +++ b/eng/pipelines/coreclr/perf-non-wasm-jobs.yml @@ -65,7 +65,7 @@ jobs: buildConfig: release runtimeFlavor: mono platforms: - - Android_arm64 + - Android_arm64_perf_specific jobParameters: buildArgs: -s mono+libs+host+packs -c $(_BuildConfig) nameSuffix: AndroidMono @@ -314,4 +314,4 @@ jobs: projectFile: crossgen_perf.proj runKind: crossgen_scenarios runJobTemplate: /eng/pipelines/coreclr/templates/run-scenarios-job.yml - logicalmachine: 'perftiger_crossgen' \ No newline at end of file + logicalmachine: 'perftiger_crossgen' diff --git a/eng/pipelines/coreclr/templates/helix-queues-setup.yml b/eng/pipelines/coreclr/templates/helix-queues-setup.yml index f61ee6d3efe4a4..ce7aaf1f9ad308 100644 --- a/eng/pipelines/coreclr/templates/helix-queues-setup.yml +++ b/eng/pipelines/coreclr/templates/helix-queues-setup.yml @@ -31,7 +31,7 @@ jobs: # Android arm64 - ${{ if in(parameters.platform, 'Android_arm64') }}: - - Windows.10.Amd64.Android.Open + - Windows.11.Amd64.Android.Open # Android x64 - ${{ if in(parameters.platform, 'Android_x64') }}: diff --git a/eng/pipelines/installer/jobs/base-job.yml b/eng/pipelines/installer/jobs/base-job.yml index 0155c095669c53..b1fa3c3026b5b6 100644 --- a/eng/pipelines/installer/jobs/base-job.yml +++ b/eng/pipelines/installer/jobs/base-job.yml @@ -345,7 +345,7 @@ jobs: displayName: Clean up old artifacts owned by root - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - task: NuGetAuthenticate@0 + - task: NuGetAuthenticate@1 - ${{ if eq(parameters.osGroup, 'windows') }}: # NuGet's http cache lasts 30 minutes. If we're on a static machine, this may interfere with diff --git a/eng/pipelines/libraries/helix-queues-setup.yml b/eng/pipelines/libraries/helix-queues-setup.yml index 554d44135ba3be..68597d14960f5f 100644 --- a/eng/pipelines/libraries/helix-queues-setup.yml +++ b/eng/pipelines/libraries/helix-queues-setup.yml @@ -102,7 +102,7 @@ jobs: - ${{ if in(parameters.platform, 'Android_x86', 'Android_x64', 'Linux_bionic_x64') }}: - Ubuntu.1804.Amd64.Android.29.Open - ${{ if in(parameters.platform, 'Android_arm', 'Android_arm64', 'Linux_bionic_arm64') }}: - - Windows.10.Amd64.Android.Open + - Windows.11.Amd64.Android.Open # iOS Simulator/Mac Catalyst arm64 - ${{ if in(parameters.platform, 'MacCatalyst_arm64', 'iOSSimulator_arm64') }}: diff --git a/eng/pipelines/official/jobs/prepare-signed-artifacts.yml b/eng/pipelines/official/jobs/prepare-signed-artifacts.yml index 016b799e0099fc..213d56b3cf69e1 100644 --- a/eng/pipelines/official/jobs/prepare-signed-artifacts.yml +++ b/eng/pipelines/official/jobs/prepare-signed-artifacts.yml @@ -26,7 +26,7 @@ jobs: fetchDepth: 20 - ${{ if eq(parameters.isOfficialBuild, true) }}: - - task: NuGetAuthenticate@0 + - task: NuGetAuthenticate@1 - task: MicroBuildSigningPlugin@2 displayName: Install MicroBuild plugin for Signing diff --git a/eng/testing/performance/performance-setup.ps1 b/eng/testing/performance/performance-setup.ps1 index bd67e15fecdc9a..693591d73a2df3 100644 --- a/eng/testing/performance/performance-setup.ps1 +++ b/eng/testing/performance/performance-setup.ps1 @@ -50,7 +50,7 @@ if ($Internal) { "perftiger_crossgen" { $Queue = "Windows.10.Amd64.19H1.Tiger.Perf" } "perfowl" { $Queue = "Windows.10.Amd64.20H2.Owl.Perf" } "perfsurf" { $Queue = "Windows.10.Arm64.Perf.Surf" } - "perfpixel4a" { $Queue = "Windows.10.Amd64.Pixel.Perf" } + "perfpixel4a" { $Queue = "Windows.11.Amd64.Pixel.Perf" } "perfampere" { $Queue = "Windows.Server.Arm64.Perf" } Default { $Queue = "Windows.10.Amd64.19H1.Tiger.Perf" } } diff --git a/eng/testing/performance/performance-setup.sh b/eng/testing/performance/performance-setup.sh index 09e3a5980eec5f..a5703929ec15d4 100755 --- a/eng/testing/performance/performance-setup.sh +++ b/eng/testing/performance/performance-setup.sh @@ -245,7 +245,7 @@ if [[ "$internal" == true ]]; then extra_benchmark_dotnet_arguments= if [[ "$logical_machine" == "perfiphone12mini" ]]; then - queue=OSX.1015.Amd64.Iphone.Perf + queue=OSX.13.Amd64.Iphone.Perf elif [[ "$logical_machine" == "perfampere" ]]; then queue=Ubuntu.2004.Arm64.Perf elif [[ "$architecture" == "arm64" ]]; then diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OCSP.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OCSP.cs index 59736b39f47e82..8b07660a3fb657 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OCSP.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.OCSP.cs @@ -29,27 +29,30 @@ private static unsafe partial int CryptoNative_X509DecodeOcspToExpiration( int len, SafeOcspRequestHandle req, IntPtr subject, - IntPtr issuer, + IntPtr* issuers, + int issuersLen, ref long expiration); internal static unsafe bool X509DecodeOcspToExpiration( ReadOnlySpan buf, SafeOcspRequestHandle request, IntPtr x509Subject, - IntPtr x509Issuer, + ReadOnlySpan x509Issuers, out DateTimeOffset expiration) { long timeT = 0; int ret; fixed (byte* pBuf = buf) + fixed (IntPtr* pIssuers = x509Issuers) { ret = CryptoNative_X509DecodeOcspToExpiration( pBuf, buf.Length, request, x509Subject, - x509Issuer, + pIssuers, + x509Issuers.Length, ref timeT); } diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs index c8e7dd3649024c..2e668f6c84638e 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs @@ -67,6 +67,7 @@ internal enum ContextAttribute SECPKG_ATTR_ISSUER_LIST_EX = 0x59, // returns SecPkgContext_IssuerListInfoEx SECPKG_ATTR_CLIENT_CERT_POLICY = 0x60, // sets SecPkgCred_ClientCertCtlPolicy SECPKG_ATTR_CONNECTION_INFO = 0x5A, // returns SecPkgContext_ConnectionInfo + SECPKG_ATTR_SESSION_INFO = 0x5D, // sets SecPkgContext_SessionInfo SECPKG_ATTR_CIPHER_INFO = 0x64, // returns SecPkgContext_CipherInfo SECPKG_ATTR_REMOTE_CERT_CHAIN = 0x67, // returns PCCERT_CONTEXT SECPKG_ATTR_UI_INFO = 0x68, // sets SEcPkgContext_UiInfo @@ -249,7 +250,7 @@ public enum Flags SCH_CRED_IGNORE_REVOCATION_OFFLINE = 0x1000, SCH_CRED_CACHE_ONLY_URL_RETRIEVAL_ON_CREATE = 0x2000, SCH_SEND_ROOT_CERT = 0x40000, - SCH_SEND_AUX_RECORD = 0x00200000, + SCH_SEND_AUX_RECORD = 0x00200000, SCH_USE_STRONG_CRYPTO = 0x00400000, SCH_USE_PRESHAREDKEY_ONLY = 0x800000, SCH_ALLOW_NULL_ENCRYPTION = 0x02000000, @@ -334,6 +335,21 @@ internal unsafe struct SecPkgCred_ClientCertPolicy public char* pwszSslCtlIdentifier; } + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct SecPkgContext_SessionInfo + { + public uint dwFlags; + public uint cbSessionId; + public fixed byte rgbSessionId[32]; + + [Flags] + public enum Flags + { + Zero = 0, + SSL_SESSION_RECONNECT = 0x01, + }; + } + [LibraryImport(Interop.Libraries.SspiCli, SetLastError = true)] internal static partial int EncryptMessage( ref CredHandle contextHandle, diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIWrapper.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIWrapper.cs index 81fdba8901bb3a..b13e217b7374e2 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIWrapper.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/SSPIWrapper.cs @@ -298,6 +298,9 @@ private static bool QueryCertContextAttribute(ISSPIInterface secModule, SafeDele public static bool QueryContextAttributes_SECPKG_ATTR_REMOTE_CERT_CONTEXT(ISSPIInterface secModule, SafeDeleteContext securityContext, out SafeFreeCertContext? certContext) => QueryCertContextAttribute(secModule, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_REMOTE_CERT_CONTEXT, out certContext); + public static bool QueryContextAttributes_SECPKG_ATTR_LOCAL_CERT_CONTEXT(ISSPIInterface secModule, SafeDeleteContext securityContext, out SafeFreeCertContext? certContext) + => QueryCertContextAttribute(secModule, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_LOCAL_CERT_CONTEXT, out certContext); + public static bool QueryContextAttributes_SECPKG_ATTR_REMOTE_CERT_CHAIN(ISSPIInterface secModule, SafeDeleteContext securityContext, out SafeFreeCertContext? certContext) => QueryCertContextAttribute(secModule, securityContext, Interop.SspiCli.ContextAttribute.SECPKG_ATTR_REMOTE_CERT_CHAIN, out certContext); diff --git a/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs b/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs index 12603f7df6ae9a..880189a741625c 100644 --- a/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs +++ b/src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Globalization; using System.Runtime.InteropServices; +using System.Security.Cryptography.X509Certificates; using System.Security.Authentication.ExtendedProtection; using Microsoft.Win32.SafeHandles; @@ -310,10 +311,15 @@ public static unsafe int AcquireCredentialsHandle( internal sealed class SafeFreeCredential_SECURITY : SafeFreeCredentials { +#pragma warning disable 0649 + // This is used only by SslStream but it is included elsewhere + public X509Certificate? LocalCertificate; +#pragma warning restore 0649 public SafeFreeCredential_SECURITY() : base() { } protected override bool ReleaseHandle() { + LocalCertificate?.Dispose(); return Interop.SspiCli.FreeCredentialsHandle(ref _handle) == 0; } } diff --git a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs index 90de3cdee7b934..2962e25c6cd599 100644 --- a/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs +++ b/src/libraries/Common/tests/System/Net/Http/HttpClientHandlerTest.ServerCertificates.cs @@ -148,7 +148,8 @@ public async Task UseCallback_ValidCertificate_ExpectedValuesDuringCallback(Conf { bool callbackCalled = false; handler.CheckCertificateRevocationList = checkRevocation; - handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) => { + handler.ServerCertificateCustomValidationCallback = (request, cert, chain, errors) => + { callbackCalled = true; Assert.NotNull(request); @@ -227,6 +228,7 @@ public async Task NoCallback_BadCertificate_ThrowsException(string url) } [OuterLoop("Uses external servers")] + [ActiveIssue("https://github.com/dotnet/runtime/issues/77726")] [ConditionalFact(nameof(ClientSupportsDHECipherSuites))] public async Task NoCallback_RevokedCertificate_NoRevocationChecking_Succeeds() { diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs index e72d42e87d2174..55a044d62a695c 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/RSA/EncryptDecrypt.cs @@ -338,19 +338,10 @@ private void RsaCryptRoundtrip(RSAEncryptionPadding paddingMode, bool expectSucc Assert.Equal(TestData.HelloBytes, output); } - [ConditionalFact] + [ConditionalFact(nameof(PlatformSupportsEmptyRSAEncryption))] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)] public void RoundtripEmptyArray() { - if (OperatingSystem.IsIOS() && !OperatingSystem.IsIOSVersionAtLeast(13, 6)) - { - throw new SkipTestException("iOS prior to 13.6 does not reliably support RSA encryption of empty data."); - } - if (OperatingSystem.IsTvOS() && !OperatingSystem.IsTvOSVersionAtLeast(14, 0)) - { - throw new SkipTestException("tvOS prior to 14.0 does not reliably support RSA encryption of empty data."); - } - using (RSA rsa = RSAFactory.Create(TestData.RSA2048Params)) { void RoundtripEmpty(RSAEncryptionPadding paddingMode) @@ -701,6 +692,26 @@ public void NotSupportedValueMethods() } } + [ConditionalTheory] + [InlineData(new byte[] { 1, 2, 3, 4 })] + [InlineData(new byte[0])] + public void Decrypt_Pkcs1_ErrorsForInvalidPadding(byte[] data) + { + if (data.Length == 0 && !PlatformSupportsEmptyRSAEncryption) + { + throw new SkipTestException("Platform does not support RSA encryption of empty data."); + } + + using (RSA rsa = RSAFactory.Create(TestData.RSA2048Params)) + { + byte[] encrypted = Encrypt(rsa, data, RSAEncryptionPadding.Pkcs1); + encrypted[1] ^= 0xFF; + + // PKCS#1, the data, and the key are all deterministic so this should always throw an exception. + Assert.ThrowsAny(() => Decrypt(rsa, encrypted, RSAEncryptionPadding.Pkcs1)); + } + } + public static IEnumerable OaepPaddingModes { get @@ -715,5 +726,23 @@ public static IEnumerable OaepPaddingModes } } } + + public static bool PlatformSupportsEmptyRSAEncryption + { + get + { + if (OperatingSystem.IsIOS() && !OperatingSystem.IsIOSVersionAtLeast(13, 6)) + { + return false; + } + + if (OperatingSystem.IsTvOS() && !OperatingSystem.IsTvOSVersionAtLeast(14, 0)) + { + return false; + } + + return true; + } + } } } diff --git a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/RevocationResponder.cs b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/RevocationResponder.cs index b08655e6f1d10b..afed3b929aaeb4 100644 --- a/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/RevocationResponder.cs +++ b/src/libraries/Common/tests/System/Security/Cryptography/X509Certificates/RevocationResponder.cs @@ -16,6 +16,9 @@ internal sealed class RevocationResponder : IDisposable private static readonly bool s_traceEnabled = Environment.GetEnvironmentVariable("TRACE_REVOCATION_RESPONSE") != null; + private static readonly byte[] s_invalidResponse = + "The server is down for maintenence."u8.ToArray(); + private readonly HttpListener _listener; private readonly Dictionary _aiaPaths = @@ -29,7 +32,7 @@ private readonly Dictionary _crlPaths public string UriPrefix { get; } - public bool RespondEmpty { get; set; } + public RespondKind RespondKind { get; set; } public AiaResponseKind AiaResponseKind { get; set; } public TimeSpan ResponseDelay { get; set; } @@ -183,7 +186,12 @@ private void HandleRequest(HttpListenerContext context, ref bool responded) Thread.Sleep(ResponseDelay); } - byte[] certData = RespondEmpty ? Array.Empty() : GetCertDataForAiaResponseKind(AiaResponseKind, authority); + byte[] certData = RespondKind switch + { + RespondKind.Empty => Array.Empty(), + RespondKind.Invalid => s_invalidResponse, + _ => GetCertDataForAiaResponseKind(AiaResponseKind, authority), + }; responded = true; context.Response.StatusCode = 200; @@ -201,7 +209,12 @@ private void HandleRequest(HttpListenerContext context, ref bool responded) Thread.Sleep(ResponseDelay); } - byte[] crl = RespondEmpty ? Array.Empty() : authority.GetCrl(); + byte[] crl = RespondKind switch + { + RespondKind.Empty => Array.Empty(), + RespondKind.Invalid => s_invalidResponse, + _ => authority.GetCrl(), + }; responded = true; context.Response.StatusCode = 200; @@ -236,7 +249,12 @@ private void HandleRequest(HttpListenerContext context, ref bool responded) return; } - byte[] ocspResponse = RespondEmpty ? Array.Empty() : authority.BuildOcspResponse(certId, nonce); + byte[] ocspResponse = RespondKind switch + { + RespondKind.Empty => Array.Empty(), + RespondKind.Invalid => s_invalidResponse, + _ => authority.BuildOcspResponse(certId, nonce), + }; if (DelayedActions.HasFlag(DelayedActionsFlag.Ocsp)) { @@ -468,4 +486,11 @@ public enum AiaResponseKind Cert = 0, Pkcs12 = 1, } + + public enum RespondKind + { + Normal = 0, + Empty = 1, + Invalid = 2, + } } diff --git a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs index 881a72f78fe4a6..b6c0ca663a54cf 100644 --- a/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs +++ b/src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs @@ -301,6 +301,7 @@ public void ProcessStart_UseShellExecute_OnWindows_DoesNotThrow(bool isFolder) [ActiveIssue("https://github.com/dotnet/runtime/issues/34685", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [InlineData(true), InlineData(false)] [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "Not supported on iOS and tvOS.")] + [SkipOnPlatform(TestPlatforms.Android, "Android doesn't allow executing custom shell scripts")] public void ProcessStart_UseShellExecute_Executes(bool filenameAsUrl) { string filename = WriteScriptFile(TestDirectory, GetTestFileName(), returnValue: 42); @@ -373,6 +374,7 @@ public void ProcessStart_UseShellExecute_ExecuteOrder() nameof(PlatformDetection.IsNotAppSandbox))] [ActiveIssue("https://github.com/dotnet/runtime/issues/34685", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "Not supported on iOS and tvOS.")] + [SkipOnPlatform(TestPlatforms.Android, "Android doesn't allow executing custom shell scripts")] public void ProcessStart_UseShellExecute_WorkingDirectory() { // Create a directory that will ProcessStartInfo.WorkingDirectory @@ -2621,7 +2623,7 @@ public void NonElevatedUser_QueryProcessNameOfSystemProcess() { // returns the username of the owner of the process or null if the username can't be queried. // for services.exe, this will be null. - string? servicesUser = Helpers.GetProcessUserName(p); + string? servicesUser = Helpers.GetProcessUserName(p); // this isn't really verifying that services.exe is owned by SYSTEM, but we are sure it is not owned by the current user. if (servicesUser != currentProcessUser) diff --git a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.Stream.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.Stream.Tests.cs index 3cec81f62a501f..3c33adeb028cbe 100644 --- a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.Stream.Tests.cs +++ b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectory.Stream.Tests.cs @@ -207,6 +207,7 @@ public void PaxNameCollision_DedupInExtendedAttributes() [Theory] [MemberData(nameof(GetExactRootDirMatchCases))] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "The temporary directory on Apple mobile platforms exceeds the path length limit.")] public void ExtractToDirectory_ExactRootDirMatch_RegularFile_And_Directory_Throws(TarEntryFormat format, TarEntryType entryType, string fileName) { ExtractToDirectory_ExactRootDirMatch_RegularFile_And_Directory_Throws_Internal(format, entryType, fileName, inverted: false); @@ -214,6 +215,7 @@ public void ExtractToDirectory_ExactRootDirMatch_RegularFile_And_Directory_Throw } [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "The temporary directory on Apple mobile platforms exceeds the path length limit.")] public void ExtractToDirectory_ExactRootDirMatch_Directory_Relative_Throws() { string entryFolderName = "folder"; @@ -240,6 +242,7 @@ public void ExtractToDirectory_ExactRootDirMatch_Directory_Relative_Throws() [InlineData(TarEntryFormat.Ustar)] [InlineData(TarEntryFormat.Pax)] [InlineData(TarEntryFormat.Gnu)] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "The temporary directory on Apple mobile platforms exceeds the path length limit.")] public void ExtractToDirectory_ExactRootDirMatch_HardLinks_Throws(TarEntryFormat format) { ExtractToDirectory_ExactRootDirMatch_Links_Throws(format, TarEntryType.HardLink, inverted: false); diff --git a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectoryAsync.Stream.Tests.cs b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectoryAsync.Stream.Tests.cs index a765f4f911eb77..2ec99926dcc182 100644 --- a/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectoryAsync.Stream.Tests.cs +++ b/src/libraries/System.Formats.Tar/tests/TarFile/TarFile.ExtractToDirectoryAsync.Stream.Tests.cs @@ -272,6 +272,7 @@ public async Task PaxNameCollision_DedupInExtendedAttributesAsync() [Theory] [MemberData(nameof(GetExactRootDirMatchCases))] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "The temporary directory on Apple mobile platforms exceeds the path length limit.")] public async Task ExtractToDirectory_ExactRootDirMatch_RegularFile_And_Directory_Throws_Async(TarEntryFormat format, TarEntryType entryType, string fileName) { await ExtractToDirectory_ExactRootDirMatch_RegularFile_And_Directory_Throws_Internal_Async(format, entryType, fileName, inverted: false); @@ -279,6 +280,7 @@ public async Task ExtractToDirectory_ExactRootDirMatch_RegularFile_And_Directory } [Fact] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "The temporary directory on Apple mobile platforms exceeds the path length limit.")] public async Task ExtractToDirectory_ExactRootDirMatch_Directory_Relative_Throws_Async() { string entryFolderName = "folder"; @@ -305,6 +307,7 @@ public async Task ExtractToDirectory_ExactRootDirMatch_Directory_Relative_Throws [InlineData(TarEntryFormat.Ustar)] [InlineData(TarEntryFormat.Pax)] [InlineData(TarEntryFormat.Gnu)] + [SkipOnPlatform(TestPlatforms.iOS | TestPlatforms.tvOS, "The temporary directory on Apple mobile platforms exceeds the path length limit.")] public async Task ExtractToDirectory_ExactRootDirMatch_HardLinks_Throws_Async(TarEntryFormat format) { await ExtractToDirectory_ExactRootDirMatch_Links_Throws_Async(format, TarEntryType.HardLink, inverted: false); diff --git a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Android.cs b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Android.cs index 43716cd04367b7..96962f9240be9c 100644 --- a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Android.cs @@ -18,7 +18,7 @@ internal static SslPolicyErrors VerifyCertificateProperties( string? hostName) { if (remoteCertificate == null) - return SslPolicyErrors.RemoteCertificateNotAvailable; + return SslPolicyErrors.RemoteCertificateNotAvailable; SslPolicyErrors errors = chain.Build(remoteCertificate) ? SslPolicyErrors.None @@ -91,6 +91,10 @@ internal static SslPolicyErrors VerifyCertificateProperties( return cert; } + // This is only called when we selected local client certificate. + // Currently this is only when Java crypto asked for it. + internal static bool IsLocalCertificateUsed(SafeFreeCredentials? _1, SafeDeleteContext? _2) => true; + // // Used only by client SSL code, never returns null. // diff --git a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.OSX.cs b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.OSX.cs index f9d3afe570af62..3bd0c7142c3fc6 100644 --- a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.OSX.cs +++ b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.OSX.cs @@ -102,6 +102,10 @@ internal static SslPolicyErrors VerifyCertificateProperties( return result; } + // This is only called when we selected local client certificate. + // Currently this is only when Apple crypto asked for it. + internal static bool IsLocalCertificateUsed(SafeFreeCredentials? _1, SafeDeleteContext? _2) => true; + // // Used only by client SSL code, never returns null. // diff --git a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs index 498a095410376a..90b9275e9af36f 100644 --- a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs +++ b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs @@ -101,6 +101,10 @@ internal static SslPolicyErrors VerifyCertificateProperties( return result; } + // This is only called when we selected local client certificate. + // Currently this is only when OpenSSL needs it because peer asked. + internal static bool IsLocalCertificateUsed(SafeFreeCredentials? _1, SafeDeleteContext? _2) => true; + // // Used only by client SSL code, never returns null. // diff --git a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs index 1ead906ec3585c..ecdfef02429399 100644 --- a/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/CertificateValidationPal.Windows.cs @@ -8,6 +8,7 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Security.Principal; +using static Interop.SspiCli; namespace System.Net { @@ -89,6 +90,44 @@ internal static SslPolicyErrors VerifyCertificateProperties( return result; } + // Check that local certificate was used by schannel. + internal static bool IsLocalCertificateUsed(SafeFreeCredentials? credentialsHandle, SafeDeleteContext securityContext) + { + SecPkgContext_SessionInfo info = default; + + // fails on Server 2008 and older. We will fall-back to probing LOCAL_CERT_CONTEXT in that case. + if (SSPIWrapper.QueryBlittableContextAttributes( + GlobalSSPI.SSPISecureChannel, + securityContext, + Interop.SspiCli.ContextAttribute.SECPKG_ATTR_SESSION_INFO, + ref info) && + ((SecPkgContext_SessionInfo.Flags)info.dwFlags).HasFlag(SecPkgContext_SessionInfo.Flags.SSL_SESSION_RECONNECT)) + { + // This is TLS Resumed session. Windows can fail to query the local cert bellow. + // Instead, we will determine the usage form used credentials. + SafeFreeCredential_SECURITY creds = (SafeFreeCredential_SECURITY)credentialsHandle!; + return creds.LocalCertificate != null; + } + + SafeFreeCertContext? localContext = null; + try + { + if (SSPIWrapper.QueryContextAttributes_SECPKG_ATTR_LOCAL_CERT_CONTEXT(GlobalSSPI.SSPISecureChannel, securityContext, out localContext) && + localContext != null) + { + return !localContext.IsInvalid; + } + } + finally + { + localContext?.Dispose(); + } + + // Some older Windows do not support that. This is only called when client certificate was provided + // so assume it was for a reason. + return true; + } + // // Used only by client SSL code, never returns null. // diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs index 1d4dfa9ac6b0b5..45f0bbe408f24f 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStream.Protocol.cs @@ -30,8 +30,6 @@ public partial class SslStream private int _trailerSize = 16; private int _maxDataSize = 16354; - private bool _refreshCredentialNeeded = true; - private static readonly Oid s_serverAuthOid = new Oid("1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.1"); private static readonly Oid s_clientAuthOid = new Oid("1.3.6.1.5.5.7.3.2", "1.3.6.1.5.5.7.3.2"); @@ -56,7 +54,12 @@ internal X509Certificate? LocalClientCertificate { get { - return _selectedClientCertificate; + if (_selectedClientCertificate != null && CertificateValidationPal.IsLocalCertificateUsed(_credentialsHandle, _securityContext!)) + { + return _selectedClientCertificate; + } + + return null; } } @@ -104,11 +107,6 @@ internal bool RemoteCertRequired } } - internal void SetRefreshCredentialNeeded() - { - _refreshCredentialNeeded = true; - } - internal void CloseContext() { if (!_remoteCertificateExposed) @@ -510,7 +508,7 @@ This will not restart a session but helps minimizing the number of handles we cr --*/ - private bool AcquireClientCredentials(ref byte[]? thumbPrint) + private bool AcquireClientCredentials(ref byte[]? thumbPrint, bool newCredentialsRequested = false) { // Acquire possible Client Certificate information and set it on the handle. @@ -518,7 +516,6 @@ private bool AcquireClientCredentials(ref byte[]? thumbPrint) bool cachedCred = false; // this is a return result from this method. X509Certificate2? selectedCert = SelectClientCertificate(out sessionRestartAttempt); - try { // Try to locate cached creds first. @@ -576,7 +573,7 @@ private bool AcquireClientCredentials(ref byte[]? thumbPrint) _sslAuthenticationOptions.CertificateContext = SslStreamCertificateContext.Create(selectedCert!); } - _credentialsHandle = AcquireCredentialsHandle(_sslAuthenticationOptions); + _credentialsHandle = AcquireCredentialsHandle(_sslAuthenticationOptions, newCredentialsRequested); thumbPrint = guessedThumbPrint; // Delay until here in case something above threw. } } @@ -687,9 +684,9 @@ private bool AcquireServerCredentials(ref byte[]? thumbPrint) return cachedCred; } - private static SafeFreeCredentials? AcquireCredentialsHandle(SslAuthenticationOptions sslAuthenticationOptions) + private static SafeFreeCredentials? AcquireCredentialsHandle(SslAuthenticationOptions sslAuthenticationOptions, bool newCredentialsRequested = false) { - SafeFreeCredentials? cred = SslStreamPal.AcquireCredentialsHandle(sslAuthenticationOptions); + SafeFreeCredentials? cred = SslStreamPal.AcquireCredentialsHandle(sslAuthenticationOptions, newCredentialsRequested); if (sslAuthenticationOptions.CertificateContext != null && cred != null) { @@ -749,7 +746,6 @@ internal ProtocolToken NextMessage(ReadOnlySpan incomingBuffer) if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "NextMessage() returned SecurityStatusPal.CredentialsNeeded"); - SetRefreshCredentialNeeded(); status = GenerateToken(incomingBuffer, ref nextmsg); } @@ -788,6 +784,11 @@ private SecurityStatusPal GenerateToken(ReadOnlySpan inputBuffer, ref byte bool sendTrustList = false; byte[]? thumbPrint = null; + // We need to try get credentials at the beginning. + // _credentialsHandle may be always null on some platforms but + // _securityContext will be allocated on first call. + bool refreshCredentialNeeded = _securityContext == null; + // // Looping through ASC or ISC with potentially cached credential that could have been // already disposed from a different thread before ISC or ASC dir increment a cred ref count. @@ -797,7 +798,7 @@ private SecurityStatusPal GenerateToken(ReadOnlySpan inputBuffer, ref byte do { thumbPrint = null; - if (_refreshCredentialNeeded) + if (refreshCredentialNeeded) { cachedCreds = _sslAuthenticationOptions.IsServer ? AcquireServerCredentials(ref thumbPrint) @@ -826,15 +827,31 @@ private SecurityStatusPal GenerateToken(ReadOnlySpan inputBuffer, ref byte _sslAuthenticationOptions, SelectClientCertificate ); + + if (status.ErrorCode == SecurityStatusPalErrorCode.CredentialsNeeded) + { + refreshCredentialNeeded = true; + cachedCreds = AcquireClientCredentials(ref thumbPrint, newCredentialsRequested: true); + + if (NetEventSource.Log.IsEnabled()) + NetEventSource.Info(this, "InitializeSecurityContext() returned 'CredentialsNeeded'."); + + status = SslStreamPal.InitializeSecurityContext( + ref _credentialsHandle!, + ref _securityContext, + _sslAuthenticationOptions.TargetHost, + inputBuffer, + ref result, + _sslAuthenticationOptions, + SelectClientCertificate); + } } } while (cachedCreds && _credentialsHandle == null); } finally { - if (_refreshCredentialNeeded) + if (refreshCredentialNeeded) { - _refreshCredentialNeeded = false; - // // Assuming the ISC or ASC has referenced the credential, // we want to call dispose so to decrement the effective ref count. @@ -974,7 +991,6 @@ internal bool VerifyRemoteCertificate(RemoteCertificateValidationCallback? remot } _remoteCertificate = certificate; - if (_remoteCertificate == null) { if (NetEventSource.Log.IsEnabled() && RemoteCertRequired) NetEventSource.Error(this, $"Remote certificate required, but no remote certificate received"); diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.Linux.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.Linux.cs index 20574597af706a..f47f9b5976189e 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.Linux.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamCertificateContext.Linux.cs @@ -25,14 +25,31 @@ public partial class SslStreamCertificateContext private byte[]? _ocspResponse; private DateTimeOffset _ocspExpiration; private DateTimeOffset _nextDownload; + // Private copy of the intermediate certificates, in case the user decides to dispose the + // instances reachable through IntermediateCertificates property. + private X509Certificate2[] _privateIntermediateCertificates; + private X509Certificate2? _rootCertificate; private Task? _pendingDownload; private List? _ocspUrls; - private X509Certificate2? _ca; private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] intermediates, SslCertificateTrust? trust) { Certificate = target; IntermediateCertificates = intermediates; + if (intermediates.Length > 0) + { + _privateIntermediateCertificates = new X509Certificate2[intermediates.Length]; + + for (int i = 0; i < intermediates.Length; i++) + { + _privateIntermediateCertificates[i] = new X509Certificate2(intermediates[i]); + } + } + else + { + _privateIntermediateCertificates = Array.Empty(); + } + Trust = trust; SslContexts = new ConcurrentDictionary(); @@ -54,7 +71,7 @@ private SslStreamCertificateContext(X509Certificate2 target, X509Certificate2[] } } - if (KeyHandle== null) + if (KeyHandle == null) { throw new NotSupportedException(SR.net_ssl_io_no_server_cert); } @@ -75,15 +92,8 @@ partial void SetNoOcspFetch(bool noOcspFetch) partial void AddRootCertificate(X509Certificate2? rootCertificate, ref bool transferredOwnership) { - if (IntermediateCertificates.Length == 0) - { - _ca = rootCertificate; - transferredOwnership = true; - } - else - { - _ca = IntermediateCertificates[0]; - } + _rootCertificate = rootCertificate; + transferredOwnership = rootCertificate != null; if (!_staplingForbidden) { @@ -148,7 +158,7 @@ partial void AddRootCertificate(X509Certificate2? rootCertificate, ref bool tran return new ValueTask(pending); } - if (_ocspUrls is null && _ca is not null) + if (_ocspUrls is null && _rootCertificate is not null) { foreach (X509Extension ext in Certificate.Extensions) { @@ -191,7 +201,9 @@ partial void AddRootCertificate(X509Certificate2? rootCertificate, ref bool tran private async Task FetchOcspAsync() { - X509Certificate2? caCert = _ca; + Debug.Assert(_rootCertificate != null); + X509Certificate2? caCert = _privateIntermediateCertificates.Length > 0 ? _privateIntermediateCertificates[0] : _rootCertificate; + Debug.Assert(_ocspUrls is not null); Debug.Assert(_ocspUrls.Count > 0); Debug.Assert(caCert is not null); @@ -210,6 +222,13 @@ partial void AddRootCertificate(X509Certificate2? rootCertificate, ref bool tran return null; } + IntPtr[] issuerHandles = ArrayPool.Shared.Rent(_privateIntermediateCertificates.Length + 1); + for (int i = 0; i < _privateIntermediateCertificates.Length; i++) + { + issuerHandles[i] = _privateIntermediateCertificates[i].Handle; + } + issuerHandles[_privateIntermediateCertificates.Length] = _rootCertificate.Handle; + using (SafeOcspRequestHandle ocspRequest = Interop.Crypto.X509BuildOcspRequest(subject, issuer)) { byte[] rentedBytes = ArrayPool.Shared.Rent(Interop.Crypto.GetOcspRequestDerSize(ocspRequest)); @@ -226,8 +245,9 @@ partial void AddRootCertificate(X509Certificate2? rootCertificate, ref bool tran if (ret is not null) { - if (!Interop.Crypto.X509DecodeOcspToExpiration(ret, ocspRequest, subject, issuer, out DateTimeOffset expiration)) + if (!Interop.Crypto.X509DecodeOcspToExpiration(ret, ocspRequest, subject, issuerHandles.AsSpan(0, _privateIntermediateCertificates.Length + 1), out DateTimeOffset expiration)) { + ret = null; continue; } @@ -245,15 +265,27 @@ partial void AddRootCertificate(X509Certificate2? rootCertificate, ref bool tran _ocspResponse = ret; _ocspExpiration = expiration; _nextDownload = nextCheckA < nextCheckB ? nextCheckA : nextCheckB; - _pendingDownload = null; break; } } + issuerHandles.AsSpan().Clear(); + ArrayPool.Shared.Return(issuerHandles); ArrayPool.Shared.Return(rentedBytes); ArrayPool.Shared.Return(rentedChars.Array!); GC.KeepAlive(Certificate); + GC.KeepAlive(_privateIntermediateCertificates); + GC.KeepAlive(_rootCertificate); GC.KeepAlive(caCert); + + _pendingDownload = null; + if (ret == null) + { + // All download attempts failed, don't try again for 5 seconds. + // This backoff will be applied only if the OCSP staple is not expired. + // If it is expired, we will force-refresh it during next GetOcspResponseAsync call. + _nextDownload = DateTimeOffset.UtcNow.AddSeconds(5); + } return ret; } } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs index f01dd68e294b2d..184cbd2a817733 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Android.cs @@ -55,7 +55,7 @@ public static SecurityStatusPal Renegotiate( throw new PlatformNotSupportedException(); } - public static SafeFreeCredentials? AcquireCredentialsHandle(SslAuthenticationOptions sslAuthenticationOptions) + public static SafeFreeCredentials? AcquireCredentialsHandle(SslAuthenticationOptions _1, bool _2) { return null; } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs index 255b30d7f2c2fe..d8fc15ca7a5462 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.OSX.cs @@ -62,7 +62,7 @@ public static SecurityStatusPal Renegotiate( throw new PlatformNotSupportedException(); } - public static SafeFreeCredentials? AcquireCredentialsHandle(SslAuthenticationOptions sslAuthenticationOptions) + public static SafeFreeCredentials? AcquireCredentialsHandle(SslAuthenticationOptions _1, bool _2) { return null; } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs index 1e1a0df55889e9..e4188015d4167b 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Unix.cs @@ -46,7 +46,7 @@ public static SecurityStatusPal InitializeSecurityContext( return HandshakeInternal(ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions, clientCertificateSelectionCallback); } - public static SafeFreeCredentials? AcquireCredentialsHandle(SslAuthenticationOptions sslAuthenticationOptions) + public static SafeFreeCredentials? AcquireCredentialsHandle(SslAuthenticationOptions _1, bool _2) { return null; } diff --git a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs index 1321cc0754ed07..2c692931458005 100644 --- a/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs +++ b/src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs @@ -136,7 +136,7 @@ public static SecurityStatusPal Renegotiate( return status; } - public static SafeFreeCredentials AcquireCredentialsHandle(SslAuthenticationOptions sslAuthenticationOptions) + public static SafeFreeCredentials AcquireCredentialsHandle(SslAuthenticationOptions sslAuthenticationOptions, bool newCredentialsRequested) { try { @@ -156,6 +156,16 @@ public static SafeFreeCredentials AcquireCredentialsHandle(SslAuthenticationOpti AttachCertificateStore(cred, certificateContext.Trust._store!); } + // Windows can fail to get local credentials in case of TLS Resume. + // We will store associated certificate in credentials and use it in case + // of TLS resume. It will be disposed when the credentials are. + if (newCredentialsRequested && sslAuthenticationOptions.CertificateContext != null) + { + SafeFreeCredential_SECURITY handle = (SafeFreeCredential_SECURITY)cred; + // We need to create copy to avoid Disposal issue. + handle.LocalCertificate = new X509Certificate2(sslAuthenticationOptions.CertificateContext.Certificate); + } + return cred; } catch (Win32Exception e) diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs index 494bcf86fc226b..a5bc4f1ddd7ffb 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationClientServer.cs @@ -165,11 +165,16 @@ public async Task CertificateValidationClientServer_EndToEnd_Ok(bool useClientSe clientCerts.Add(_clientCertificate); } - Task clientAuthentication = sslClientStream.AuthenticateAsClientAsync( - serverName, - clientCerts, - SslProtocolSupport.DefaultSslProtocols, - false); + // Connect to GUID to prevent TLS resume + var options = new SslClientAuthenticationOptions() + { + TargetHost = Guid.NewGuid().ToString("N"), + ClientCertificates = clientCerts, + EnabledSslProtocols = SslProtocolSupport.DefaultSslProtocols, + CertificateChainPolicy = new X509ChainPolicy(), + }; + options.CertificateChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreInvalidName; + Task clientAuthentication = sslClientStream.AuthenticateAsClientAsync(options, default); Task serverAuthentication = sslServerStream.AuthenticateAsServerAsync( _serverCertificate, @@ -258,7 +263,6 @@ private bool ClientSideRemoteServerCertificateValidation(object sender, X509Cert Assert.Equal(expectedSslPolicyErrors, sslPolicyErrors); Assert.Equal(_serverCertificate, certificate); - return true; } diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs index 7b536e6e60738c..1ff37886924d77 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/CertificateValidationRemoteServer.cs @@ -97,6 +97,7 @@ public async Task DefaultConnect_EndToEnd_Ok(string host) [InlineData(false)] [SkipOnPlatform(TestPlatforms.Android, "The invalid certificate is rejected by Android and the .NET validation code isn't reached")] [ActiveIssue("https://github.com/dotnet/runtime/issues/70981", TestPlatforms.OSX)] + [ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)] public Task ConnectWithRevocation_WithCallback(bool checkRevocation) { X509RevocationMode mode = checkRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck; diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCertificateContextTests.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCertificateContextTests.cs new file mode 100644 index 00000000000000..a3fd09c41ef680 --- /dev/null +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamCertificateContextTests.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. + +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.Security.Cryptography.X509Certificates.Tests.Common; +using System.Threading.Tasks; +using Xunit; + +namespace System.Net.Security.Tests +{ + public static class SslStreamCertificateContextTests + { + [Fact] + [OuterLoop("Subject to resource contention and load.")] + [PlatformSpecific(TestPlatforms.Linux)] + public static async Task Create_OcspDoesNotReturnOrCacheInvalidStapleData() + { + string serverName = $"{nameof(Create_OcspDoesNotReturnOrCacheInvalidStapleData)}.example"; + + CertificateAuthority.BuildPrivatePki( + PkiOptions.EndEntityRevocationViaOcsp | PkiOptions.CrlEverywhere, + out RevocationResponder responder, + out CertificateAuthority rootAuthority, + out CertificateAuthority[] intermediateAuthorities, + out X509Certificate2 serverCert, + intermediateAuthorityCount: 1, + subjectName: serverName, + keySize: 2048, + extensions: TestHelper.BuildTlsServerCertExtensions(serverName)); + + using (responder) + using (rootAuthority) + using (CertificateAuthority intermediateAuthority = intermediateAuthorities[0]) + using (serverCert) + using (X509Certificate2 rootCert = rootAuthority.CloneIssuerCert()) + using (X509Certificate2 issuerCert = intermediateAuthority.CloneIssuerCert()) + { + responder.RespondKind = RespondKind.Invalid; + + SslStreamCertificateContext context = SslStreamCertificateContext.Create( + serverCert, + additionalCertificates: new X509Certificate2Collection { issuerCert }, + offline: false); + + MethodInfo fetchOcspAsyncMethod = typeof(SslStreamCertificateContext).GetMethod( + "DownloadOcspAsync", + BindingFlags.Instance | BindingFlags.NonPublic); + FieldInfo ocspResponseField = typeof(SslStreamCertificateContext).GetField( + "_ocspResponse", + BindingFlags.Instance | BindingFlags.NonPublic); + + Assert.NotNull(fetchOcspAsyncMethod); + Assert.NotNull(ocspResponseField); + + byte[] ocspFetch = await (ValueTask)fetchOcspAsyncMethod.Invoke(context, Array.Empty()); + Assert.Null(ocspFetch); + + byte[] ocspResponseValue = (byte[])ocspResponseField.GetValue(context); + Assert.Null(ocspResponseValue); + } + } + } +} diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamMutualAuthenticationTest.cs b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamMutualAuthenticationTest.cs index fc34a29fdc7ba8..90c0a6cb049014 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamMutualAuthenticationTest.cs +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamMutualAuthenticationTest.cs @@ -8,6 +8,7 @@ using System.Security.Cryptography.X509Certificates; using Xunit; +using System.Runtime.InteropServices; namespace System.Net.Security.Tests { @@ -32,6 +33,38 @@ public void Dispose() _clientCertificate.Dispose(); } + public enum ClientCertSource + { + ClientCertificate, + SelectionCallback, + } + + public static TheoryData CertSourceData() + { + TheoryData data = new(); + + foreach (var source in Enum.GetValues()) + { + data.Add(source); + } + + return data; + } + + + public static TheoryData BoolAndCertSourceData() + { + TheoryData data = new(); + + foreach (var source in Enum.GetValues()) + { + data.Add(true, source); + data.Add(false, source); + } + + return data; + } + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows7))] [InlineData(false, false)] [InlineData(false, true)] @@ -83,9 +116,112 @@ public async Task SslStream_RequireClientCert_IsMutuallyAuthenticated_ReturnsTru } } + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows7))] + [ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/65563", TestPlatforms.Android)] + public async Task SslStream_CachedCredentials_IsMutuallyAuthenticatedCorrect( + SslProtocols protocol) + { + var clientOptions = new SslClientAuthenticationOptions + { + ClientCertificates = new X509CertificateCollection() { _clientCertificate }, + EnabledSslProtocols = protocol, + RemoteCertificateValidationCallback = delegate { return true; }, + TargetHost = Guid.NewGuid().ToString("N") + }; + + for (int i = 0; i < 5; i++) + { + (SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams(); + using (client) + using (server) + { + bool expectMutualAuthentication = (i % 2) == 0; + + var serverOptions = new SslServerAuthenticationOptions + { + ClientCertificateRequired = expectMutualAuthentication, + ServerCertificate = expectMutualAuthentication ? _serverCertificate : _selfSignedCertificate, + RemoteCertificateValidationCallback = delegate { return true; }, + EnabledSslProtocols = protocol + }; + + await TestConfiguration.WhenAllOrAnyFailedWithTimeout( + client.AuthenticateAsClientAsync(clientOptions), + server.AuthenticateAsServerAsync(serverOptions)); + + // mutual authentication should only be set if server required client cert + Assert.Equal(expectMutualAuthentication, server.IsMutuallyAuthenticated); + Assert.Equal(expectMutualAuthentication, client.IsMutuallyAuthenticated); + }; + } + } + + [ConditionalTheory(typeof(TestConfiguration), nameof(TestConfiguration.SupportsRenegotiation))] + [MemberData(nameof(CertSourceData))] + [PlatformSpecific(TestPlatforms.Windows | TestPlatforms.Linux)] + public async Task SslStream_NegotiateClientCertificate_IsMutuallyAuthenticatedCorrect(ClientCertSource certSource) + { + SslStreamCertificateContext context = SslStreamCertificateContext.Create(_serverCertificate, null); + var clientOptions = new SslClientAuthenticationOptions + { + TargetHost = Guid.NewGuid().ToString("N") + }; + + for (int round = 0; round < 3; round++) + { + (Stream stream1, Stream stream2) = TestHelper.GetConnectedStreams(); + using (var client = new SslStream(stream1, false, AllowAnyCertificate)) + using (var server = new SslStream(stream2, false, AllowAnyCertificate)) + { + + switch (certSource) + { + case ClientCertSource.ClientCertificate: + clientOptions.ClientCertificates = new X509CertificateCollection() { _clientCertificate }; + break; + case ClientCertSource.SelectionCallback: + clientOptions.LocalCertificateSelectionCallback = ClientCertSelectionCallback; + break; + } + + Task t2 = client.AuthenticateAsClientAsync(clientOptions); + Task t1 = server.AuthenticateAsServerAsync(new SslServerAuthenticationOptions + { + ServerCertificateContext = context, + ClientCertificateRequired = false, + EnabledSslProtocols = SslProtocols.Tls12, + + }); + + await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2); + + if (round >= 0 && server.RemoteCertificate != null) + { + // TLS resumed + Assert.True(client.IsMutuallyAuthenticated, "client.IsMutuallyAuthenticated"); + Assert.True(server.IsMutuallyAuthenticated, "server.IsMutuallyAuthenticated"); + continue; + } + + Assert.False(client.IsMutuallyAuthenticated, "client.IsMutuallyAuthenticated"); + Assert.False(server.IsMutuallyAuthenticated, "server.IsMutuallyAuthenticated"); + + var t = client.ReadAsync(new byte[1]); + await server.NegotiateClientCertificateAsync(); + Assert.NotNull(server.RemoteCertificate); + await server.WriteAsync(new byte[1]); + await t; + + Assert.NotNull(server.RemoteCertificate); + Assert.True(client.IsMutuallyAuthenticated, "client.IsMutuallyAuthenticated"); + Assert.True(server.IsMutuallyAuthenticated, "server.IsMutuallyAuthenticated"); + } + } + } + + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindows7))] [ClassData(typeof(SslProtocolSupport.SupportedSslProtocolsTestData))] - [PlatformSpecific(TestPlatforms.Linux)] // https://github.com/dotnet/runtime/issues/65563 - [Theory] public async Task SslStream_ResumedSessionsClientCollection_IsMutuallyAuthenticatedCorrect( SslProtocols protocol) { @@ -128,7 +264,7 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( } else { - Assert.Null(server.RemoteCertificate); + Assert.Null(server.RemoteCertificate); } }; } @@ -183,7 +319,7 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( } else { - Assert.Null(server.RemoteCertificate); + Assert.Null(server.RemoteCertificate); } }; } @@ -221,7 +357,7 @@ public async Task SslStream_ResumedSessionsCallbackMaybeSet_IsMutuallyAuthentica if (expectMutualAuthentication) { - clientOptions.LocalCertificateSelectionCallback = (s, t, l, r, a) => _clientCertificate; + clientOptions.LocalCertificateSelectionCallback = (s, t, l, r, a) => _clientCertificate; } else { @@ -242,7 +378,7 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( } else { - Assert.Null(server.RemoteCertificate); + Assert.Null(server.RemoteCertificate); } }; } diff --git a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj index 7c3d432f34cb46..b313134997530b 100644 --- a/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj +++ b/src/libraries/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj @@ -31,6 +31,7 @@ + diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs index c2e70c7f150a86..1a339b57c65a33 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/AiaTests.cs @@ -33,7 +33,7 @@ public static void EmptyAiaResponseIsIgnored() using (endEntity) using (X509Certificate2 intermediate2Cert = intermediate2.CloneIssuerCert()) { - responder.RespondEmpty = true; + responder.RespondKind = RespondKind.Empty; RetryHelper.Execute(() => { using (ChainHolder holder = new ChainHolder()) diff --git a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h index efc0d4706027bb..7d5ec84cc7d54e 100644 --- a/src/native/libs/System.Security.Cryptography.Native/opensslshim.h +++ b/src/native/libs/System.Security.Cryptography.Native/opensslshim.h @@ -276,8 +276,10 @@ const EVP_CIPHER* EVP_chacha20_poly1305(void); REQUIRED_FUNCTION(ERR_peek_error) \ REQUIRED_FUNCTION(ERR_peek_error_line) \ REQUIRED_FUNCTION(ERR_peek_last_error) \ + REQUIRED_FUNCTION(ERR_pop_to_mark) \ FALLBACK_FUNCTION(ERR_put_error) \ REQUIRED_FUNCTION(ERR_reason_error_string) \ + REQUIRED_FUNCTION(ERR_set_mark) \ LIGHTUP_FUNCTION(ERR_set_debug) \ LIGHTUP_FUNCTION(ERR_set_error) \ REQUIRED_FUNCTION(EVP_aes_128_cbc) \ @@ -332,6 +334,7 @@ const EVP_CIPHER* EVP_chacha20_poly1305(void); REQUIRED_FUNCTION(EVP_PKCS82PKEY) \ REQUIRED_FUNCTION(EVP_PKEY2PKCS8) \ REQUIRED_FUNCTION(EVP_PKEY_CTX_ctrl) \ + REQUIRED_FUNCTION(EVP_PKEY_CTX_ctrl_str) \ REQUIRED_FUNCTION(EVP_PKEY_CTX_free) \ REQUIRED_FUNCTION(EVP_PKEY_CTX_get0_pkey) \ REQUIRED_FUNCTION(EVP_PKEY_CTX_new) \ @@ -758,8 +761,10 @@ FOR_ALL_OPENSSL_FUNCTIONS #define ERR_peek_error_line ERR_peek_error_line_ptr #define ERR_peek_last_error ERR_peek_last_error_ptr #define ERR_put_error ERR_put_error_ptr +#define ERR_pop_to_mark ERR_pop_to_mark_ptr #define ERR_reason_error_string ERR_reason_error_string_ptr #define ERR_set_debug ERR_set_debug_ptr +#define ERR_set_mark ERR_set_mark_ptr #define ERR_set_error ERR_set_error_ptr #define EVP_aes_128_cbc EVP_aes_128_cbc_ptr #define EVP_aes_128_cfb8 EVP_aes_128_cfb8_ptr @@ -813,6 +818,7 @@ FOR_ALL_OPENSSL_FUNCTIONS #define EVP_PKCS82PKEY EVP_PKCS82PKEY_ptr #define EVP_PKEY2PKCS8 EVP_PKEY2PKCS8_ptr #define EVP_PKEY_CTX_ctrl EVP_PKEY_CTX_ctrl_ptr +#define EVP_PKEY_CTX_ctrl_str EVP_PKEY_CTX_ctrl_str_ptr #define EVP_PKEY_CTX_free EVP_PKEY_CTX_free_ptr #define EVP_PKEY_CTX_get0_pkey EVP_PKEY_CTX_get0_pkey_ptr #define EVP_PKEY_CTX_new EVP_PKEY_CTX_new_ptr diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c b/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c index c9ccdf33e3afe0..043bf9f9d1edab 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c +++ b/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey_rsa.c @@ -67,6 +67,19 @@ static bool ConfigureEncryption(EVP_PKEY_CTX* ctx, RsaPaddingMode padding, const { return false; } + + // OpenSSL 3.2 introduced a change where PKCS#1 RSA decryption does not fail for invalid padding. + // If the padding is invalid, the decryption operation returns random data. + // See https://github.com/openssl/openssl/pull/13817 for background. + // Some Linux distributions backported this change to previous versions of OpenSSL. + // Here we do a best-effort to set a flag to revert the behavior to failing if the padding is invalid. + ERR_set_mark(); + + EVP_PKEY_CTX_ctrl_str(ctx, "rsa_pkcs1_implicit_rejection", "0"); + + // Undo any changes to the error queue that may have occured while configuring implicit rejection if the + // current version does not support implicit rejection. + ERR_pop_to_mark(); } else { diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c index 431c2b8ec06722..6372802c2c1832 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c +++ b/src/native/libs/System.Security.Cryptography.Native/pal_ssl.c @@ -577,7 +577,7 @@ X509* CryptoNative_SslGetPeerCertificate(SSL* ssl) long len = SSL_get_tlsext_status_ocsp_resp(ssl, &data); X509* cert = SSL_get1_peer_certificate(ssl); - if (len > 0 && cert != NULL) + if (len > 0 && cert != NULL && !X509_get_ex_data(cert, g_x509_ocsp_index)) { OCSP_RESPONSE* ocspResp = d2i_OCSP_RESPONSE(NULL, &data, len); diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_x509.c b/src/native/libs/System.Security.Cryptography.Native/pal_x509.c index 3681d1f3cb301c..11e3910a98e19a 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_x509.c +++ b/src/native/libs/System.Security.Cryptography.Native/pal_x509.c @@ -1280,11 +1280,11 @@ CryptoNative_X509ChainVerifyOcsp(X509_STORE_CTX* storeCtx, OCSP_REQUEST* req, OC return X509ChainVerifyOcsp(storeCtx, subject, issuer, req, resp, cachePath); } -int32_t CryptoNative_X509DecodeOcspToExpiration(const uint8_t* buf, int32_t len, OCSP_REQUEST* req, X509* subject, X509* issuer, int64_t* expiration) +int32_t CryptoNative_X509DecodeOcspToExpiration(const uint8_t* buf, int32_t len, OCSP_REQUEST* req, X509* subject, X509** issuers, int issuersLen, int64_t* expiration) { ERR_clear_error(); - if (buf == NULL || len == 0) + if (buf == NULL || len == 0 || issuersLen == 0) { return 0; } @@ -1307,7 +1307,16 @@ int32_t CryptoNative_X509DecodeOcspToExpiration(const uint8_t* buf, int32_t len, if (bag != NULL) { - if (X509_STORE_add_cert(store, issuer) && sk_X509_push(bag, issuer)) + int i; + for (i = 0; i < issuersLen; i++) + { + if (!X509_STORE_add_cert(store, issuers[i]) || !sk_X509_push(bag, issuers[i])) + { + break; + } + } + + if (i == issuersLen) { ctx = X509_STORE_CTX_new(); } @@ -1321,7 +1330,7 @@ int32_t CryptoNative_X509DecodeOcspToExpiration(const uint8_t* buf, int32_t len, { int canCache = 0; time_t expiration_t = 0; - X509VerifyStatusCode code = CheckOcspGetExpiry(req, resp, subject, issuer, ctx, &canCache, &expiration_t); + X509VerifyStatusCode code = CheckOcspGetExpiry(req, resp, subject, issuers[0], ctx, &canCache, &expiration_t); if (sizeof(time_t) == sizeof(int64_t)) { diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_x509.h b/src/native/libs/System.Security.Cryptography.Native/pal_x509.h index bcee51f069dfbd..c51d5ee4b02dcb 100644 --- a/src/native/libs/System.Security.Cryptography.Native/pal_x509.h +++ b/src/native/libs/System.Security.Cryptography.Native/pal_x509.h @@ -407,4 +407,4 @@ PALEXPORT int32_t CryptoNative_X509ChainVerifyOcsp(X509_STORE_CTX* storeCtx, Decode len bytes of buf into an OCSP response, process it against the OCSP request, and return if the bytes were valid. If the bytes were valid, and the OCSP response had a nextUpdate value, assign it to expiration. */ -PALEXPORT int32_t CryptoNative_X509DecodeOcspToExpiration(const uint8_t* buf, int32_t len, OCSP_REQUEST* req, X509* subject, X509* issuer, int64_t* expiration); +PALEXPORT int32_t CryptoNative_X509DecodeOcspToExpiration(const uint8_t* buf, int32_t len, OCSP_REQUEST* req, X509* subject, X509** issuers, int issuersLen, int64_t* expiration); diff --git a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs index 0f409c1745f7ca..dfcb4e3948cfc6 100644 --- a/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs +++ b/src/tasks/AndroidAppBuilder/AndroidAppBuilder.cs @@ -73,6 +73,8 @@ public class AndroidAppBuilderTask : Task public string? MinApiLevel { get; set; } + public string? TargetApiLevel { get; set; } + public string? BuildApiLevel { get; set; } public string? BuildToolsVersion { get; set; } @@ -108,6 +110,7 @@ public override bool Execute() apkBuilder.AndroidSdk = AndroidSdk; apkBuilder.AndroidNdk = AndroidNdk; apkBuilder.MinApiLevel = MinApiLevel; + apkBuilder.TargetApiLevel = TargetApiLevel; apkBuilder.BuildApiLevel = BuildApiLevel; apkBuilder.BuildToolsVersion = BuildToolsVersion; apkBuilder.StripDebugSymbols = StripDebugSymbols; diff --git a/src/tasks/AndroidAppBuilder/ApkBuilder.cs b/src/tasks/AndroidAppBuilder/ApkBuilder.cs index 4cfc8d0d1cac9b..6c022ad9d972db 100644 --- a/src/tasks/AndroidAppBuilder/ApkBuilder.cs +++ b/src/tasks/AndroidAppBuilder/ApkBuilder.cs @@ -13,12 +13,14 @@ public class ApkBuilder { private const string DefaultMinApiLevel = "21"; + private const string DefaultTargetApiLevel = "31"; public string? ProjectName { get; set; } public string? AppDir { get; set; } public string? AndroidNdk { get; set; } public string? AndroidSdk { get; set; } public string? MinApiLevel { get; set; } + public string? TargetApiLevel { get; set; } public string? BuildApiLevel { get; set; } public string? BuildToolsVersion { get; set; } public string OutputDir { get; set; } = ""!; @@ -125,14 +127,24 @@ public ApkBuilder(TaskLoggingHelper logger) if (string.IsNullOrEmpty(MinApiLevel)) MinApiLevel = DefaultMinApiLevel; - // make sure BuildApiLevel >= MinApiLevel + if (string.IsNullOrEmpty(TargetApiLevel)) + TargetApiLevel = DefaultTargetApiLevel; + + // make sure BuildApiLevel >= MinApiLevel and BuildApiLevel >= TargetApiLevel // only if these api levels are not "preview" (not integers) - if (int.TryParse(BuildApiLevel, out int intApi) && - int.TryParse(MinApiLevel, out int intMinApi) && - intApi < intMinApi) + if (int.TryParse(BuildApiLevel, out int intApi)) { - throw new ArgumentException($"BuildApiLevel={BuildApiLevel} <= MinApiLevel={MinApiLevel}. " + - "Make sure you've downloaded some recent build-tools in Android SDK"); + if (int.TryParse(MinApiLevel, out int intMinApi) && intApi < intMinApi) + { + throw new ArgumentException($"BuildApiLevel={BuildApiLevel} < MinApiLevel={MinApiLevel}. " + + "Make sure you've downloaded some recent build-tools in Android SDK"); + } + + if (int.TryParse(TargetApiLevel, out int intTargetApi) && intApi < intTargetApi) + { + throw new ArgumentException($"BuildApiLevel={BuildApiLevel} < TargetApiLevel={TargetApiLevel}. " + + "Make sure you've downloaded some recent build-tools in Android SDK"); + } } string buildToolsFolder = Path.Combine(AndroidSdk, "build-tools", BuildToolsVersion); @@ -422,7 +434,8 @@ public ApkBuilder(TaskLoggingHelper logger) Utils.GetEmbeddedResource("AndroidManifest.xml") .Replace("%PackageName%", packageId) .Replace("%NetworkSecurityConfig%", networkSecurityConfigAttribute) - .Replace("%MinSdkLevel%", MinApiLevel)); + .Replace("%MinSdkLevel%", MinApiLevel) + .Replace("%TargetSdkVersion%", TargetApiLevel)); string javaCompilerArgs = $"-d obj -classpath src -bootclasspath {androidJar} -source 1.8 -target 1.8 "; Utils.RunProcess(logger, javac, javaCompilerArgs + javaActivityPath, workingDir: OutputDir); diff --git a/src/tasks/AndroidAppBuilder/Templates/AndroidManifest.xml b/src/tasks/AndroidAppBuilder/Templates/AndroidManifest.xml index befd2e446a650e..e820327e6d2536 100644 --- a/src/tasks/AndroidAppBuilder/Templates/AndroidManifest.xml +++ b/src/tasks/AndroidAppBuilder/Templates/AndroidManifest.xml @@ -3,7 +3,7 @@ package="%PackageName%" a:versionCode="1" a:versionName="1.0"> - +