Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
Expand All @@ -15,6 +16,7 @@
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
Expand All @@ -28,6 +30,7 @@ internal static partial class OpenSsl
private const int DefaultTlsCacheSizeClient = 500; // since we keep only one TLS Session per hostname, 500 should be enough to cover most scenarios
private const int DefaultTlsCacheSizeServer = -1; // use implementation default
private const SslProtocols FakeAlpnSslProtocol = (SslProtocols)1; // used to distinguish server sessions with ALPN
private static readonly Lazy<string[]> s_defaultSigAlgs = new(GetDefaultSignatureAlgorithms);

private sealed class SafeSslContextCache : SafeHandleCache<SslContextCacheKey, SafeSslContextHandle> { }

Expand Down Expand Up @@ -414,6 +417,11 @@ internal static SafeSslHandle AllocateSslHandle(SslAuthenticationOptions sslAuth
sslHandle.SslContextHandle = sslCtxHandle;
}

if (!sslAuthenticationOptions.AllowRsaPssPadding || !sslAuthenticationOptions.AllowRsaPkcs1Padding)
{
ConfigureSignatureAlgorithms(sslHandle, sslAuthenticationOptions.AllowRsaPssPadding, sslAuthenticationOptions.AllowRsaPkcs1Padding);
}

if (sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0)
{
if (sslAuthenticationOptions.IsServer)
Expand Down Expand Up @@ -516,6 +524,141 @@ internal static SafeSslHandle AllocateSslHandle(SslAuthenticationOptions sslAuth
return sslHandle;
}

internal static string[] GetDefaultSignatureAlgorithms()
{
ushort[] rawAlgs = Interop.Ssl.GetDefaultSignatureAlgorithms();

// The mapping below is taken from STRINT_PAIR signature_tls13_scheme_list and other
// data structures in OpenSSL source code (apps/lib/s_cb.c file).
static string ConvertAlg(ushort rawAlg) => rawAlg switch
{
0x0201 => "rsa_pkcs1_sha1",
0x0203 => "ecdsa_sha1",
0x0401 => "rsa_pkcs1_sha256",
0x0403 => "ecdsa_secp256r1_sha256",
0x0501 => "rsa_pkcs1_sha384",
0x0503 => "ecdsa_secp384r1_sha384",
0x0601 => "rsa_pkcs1_sha512",
0x0603 => "ecdsa_secp521r1_sha512",
0x0804 => "rsa_pss_rsae_sha256",
0x0805 => "rsa_pss_rsae_sha384",
0x0806 => "rsa_pss_rsae_sha512",
0x0807 => "ed25519",
0x0808 => "ed448",
0x0809 => "rsa_pss_pss_sha256",
0x080a => "rsa_pss_pss_sha384",
0x080b => "rsa_pss_pss_sha512",
0x081a => "ecdsa_brainpoolP256r1_sha256",
0x081b => "ecdsa_brainpoolP384r1_sha384",
0x081c => "ecdsa_brainpoolP512r1_sha512",
0x0904 => "mldsa44",
0x0905 => "mldsa65",
0x0906 => "mldsa87",
_ =>
Tls12HashName((byte)(rawAlg >> 8)) is string hashName &&
Tls12SignatureName((byte)rawAlg) is string sigName
? $"{sigName}+{hashName}"
: $"0x{rawAlg:x4}" // this will cause the setter to fail, but at least we get a string representation in the log.
};

static string? Tls12HashName(byte raw) => raw switch
{
0x00 => "none",
0x01 => "MD5",
0x02 => "SHA1",
0x03 => "SHA224",
0x04 => "SHA256",
0x05 => "SHA384",
0x06 => "SHA512",
_ => null
};

static string? Tls12SignatureName(byte raw) => raw switch
{
0x00 => "anonymous",
0x01 => "RSA",
0x02 => "DSA",
0x03 => "ECDSA",
_ => null
};

string[] result = Array.ConvertAll(rawAlgs, ConvertAlg);
if (NetEventSource.Log.IsEnabled())
{
NetEventSource.Info(null, $"Default signature algorithms: {string.Join(":", result)}");
}

return result;
}

internal static unsafe void ConfigureSignatureAlgorithms(SafeSslHandle sslHandle, bool enablePss, bool enablePkcs1)
{
byte[] buffer = ArrayPool<byte>.Shared.Rent(512);
try
{
int index = 0;

foreach (string alg in s_defaultSigAlgs.Value)
{
// includes both rsa_pss_pss_* and rsa_pss_rsae_*
if (alg.StartsWith("rsa_pss_", StringComparison.Ordinal) && !enablePss)
{
continue;
}

if (alg.StartsWith("rsa_pkcs1_", StringComparison.Ordinal) && !enablePkcs1)
{
continue;
}

// Ensure we have enough space for the algorithm name, separator and null terminator.
EnsureSize(ref buffer, index + alg.Length + 2);

if (index > 0)
{
buffer[index++] = (byte)':';
}

index += Encoding.UTF8.GetBytes(alg, buffer.AsSpan(index));
}
buffer[index] = 0; // null terminator

int ret;
fixed (byte* pBuffer = buffer)
{
ret = Interop.Ssl.SslSetSigalgs(sslHandle, pBuffer);
if (ret != 1)
{
throw CreateSslException(SR.Format(SR.net_ssl_set_sigalgs_failed, "server"));
}

ret = Interop.Ssl.SslSetClientSigalgs(sslHandle, pBuffer);
if (ret != 1)
{
throw CreateSslException(SR.Format(SR.net_ssl_set_sigalgs_failed, "client"));
}
}
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}

static void EnsureSize(ref byte[] buffer, int size)
{
if (buffer.Length < size)
{
// there are a few dozen algorithms total in existence, so we don't expect the buffer to grow too large.
Debug.Assert(size < 10 * 1024, "The buffer should not grow too large.");

byte[] oldBuffer = buffer;
buffer = ArrayPool<byte>.Shared.Rent(buffer.Length * 2);
oldBuffer.AsSpan().CopyTo(buffer);
ArrayPool<byte>.Shared.Return(oldBuffer);
}
}
}

internal static SecurityStatusPal SslRenegotiate(SafeSslHandle sslContext, out byte[]? outputBuffer)
{
int ret = Interop.Ssl.SslRenegotiate(sslContext, out Ssl.SslErrorCode errorCode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,24 @@ internal static unsafe ReadOnlySpan<byte> SslGetAlpnSelected(SafeSslHandle ssl)
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslRead", SetLastError = true)]
internal static partial int SslRead(SafeSslHandle ssl, ref byte buf, int num, out SslErrorCode error);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_GetDefaultSignatureAlgorithms")]
private static unsafe partial int GetDefaultSignatureAlgorithms(Span<ushort> algorithms, ref int algorithmCount);

internal static ushort[] GetDefaultSignatureAlgorithms()
{
// 256 algorithms should be more than enough for any use case.
Span<ushort> algorithms = stackalloc ushort[256];
int algorithmCount = algorithms.Length;
int res = GetDefaultSignatureAlgorithms(algorithms, ref algorithmCount);

if (res != 0 || algorithmCount > algorithms.Length)
{
throw Interop.OpenSsl.CreateSslException(SR.net_ssl_get_default_sigalgs_failed);
}

return algorithms.Slice(0, algorithmCount).ToArray();
}

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslRenegotiate")]
internal static partial int SslRenegotiate(SafeSslHandle ssl, out SslErrorCode error);

Expand Down Expand Up @@ -186,6 +204,12 @@ internal static SafeSharedX509StackHandle SslGetPeerCertChain(SafeSslHandle ssl)
[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslSetPostHandshakeAuth")]
internal static partial void SslSetPostHandshakeAuth(SafeSslHandle ssl, int value);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslSetSigalgs")]
internal static unsafe partial int SslSetSigalgs(SafeSslHandle ssl, byte* str);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_SslSetClientSigalgs")]
internal static unsafe partial int SslSetClientSigalgs(SafeSslHandle ssl, byte* str);

[LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_Tls13Supported")]
private static partial int Tls13SupportedImpl();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,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,
Expand All @@ -259,7 +259,7 @@ public enum Flags
internal unsafe struct TLS_PARAMETERS
{
public int cAlpnIds; // Valid for server applications only. Must be zero otherwise. Number of ALPN IDs in rgstrAlpnIds; set to 0 if applies to all.
public IntPtr rgstrAlpnIds; // Valid for server applications only. Must be NULL otherwise. Array of ALPN IDs that the following settings apply to; set to NULL if applies to all.
public UNICODE_STRING* rgstrAlpnIds; // Valid for server applications only. Must be NULL otherwise. Array of ALPN IDs that the following settings apply to; set to NULL if applies to all.
public uint grbitDisabledProtocols; // List protocols you DO NOT want negotiated.
public int cDisabledCrypto; // Number of CRYPTO_SETTINGS structures; set to 0 if there are none.
public CRYPTO_SETTINGS* pDisabledCrypto; // Array of CRYPTO_SETTINGS structures; set to NULL if there are none;
Expand All @@ -278,7 +278,7 @@ public enum Flags
internal unsafe struct CRYPTO_SETTINGS
{
public TlsAlgorithmUsage eAlgorithmUsage; // How this algorithm is being used.
public UNICODE_STRING* strCngAlgId; // CNG algorithm identifier.
public UNICODE_STRING strCngAlgId; // CNG algorithm identifier.
public int cChainingModes; // Set to 0 if CNG algorithm does not have a chaining mode.
public UNICODE_STRING* rgstrChainingModes; // Set to NULL if CNG algorithm does not have a chaining mode.
public int dwMinBitLength; // Minimum bit length for the specified CNG algorithm. Set to 0 if not defined or CNG algorithm implies bit length.
Expand Down Expand Up @@ -374,7 +374,7 @@ internal static unsafe partial int VerifySignature(
ref CredHandle contextHandle,
in SecBufferDesc input,
uint sequenceNumber,
uint *qualityOfProtection);
uint* qualityOfProtection);

[LibraryImport(Interop.Libraries.SspiCli, SetLastError = true)]
internal static partial int QuerySecurityContextToken(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,18 @@ public static SslClientAuthenticationOptions ShallowClone(this SslClientAuthenti
EncryptionPolicy = options.EncryptionPolicy,
LocalCertificateSelectionCallback = options.LocalCertificateSelectionCallback,
RemoteCertificateValidationCallback = options.RemoteCertificateValidationCallback,
TargetHost = options.TargetHost
TargetHost = options.TargetHost,
#pragma warning disable CA1416 // Ignore SupportedOSPlatform checks, the value will be validated at runtime inside SslStream
AllowRsaPssPadding = options.AllowRsaPssPadding,
AllowRsaPkcs1Padding = options.AllowRsaPkcs1Padding
#pragma warning restore CA1416
};

#if DEBUG
// Try to detect if a property gets added that we're not copying correctly.
// The property count is guard for new properties that also needs to be added above.
PropertyInfo[] properties = typeof(SslClientAuthenticationOptions).GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)!;
Debug.Assert(properties.Length == 13);
Debug.Assert(properties.Length == 15);
foreach (PropertyInfo pi in properties)
{
object? origValue = pi.GetValue(options);
Expand Down
Loading
Loading