private static QUIC_ALLOWED_CIPHER_SUITE_FLAGS CipherSuitePolicyToFlags(CipherSuitesPolicy cipherSuitesPolicy) { QUIC_ALLOWED_CIPHER_SUITE_FLAGS flags = QUIC_ALLOWED_CIPHER_SUITE_FLAGS.NONE; foreach (TlsCipherSuite cipher in cipherSuitesPolicy.AllowedCipherSuites) { switch (cipher) { case TlsCipherSuite.TLS_AES_128_GCM_SHA256: flags |= QUIC_ALLOWED_CIPHER_SUITE_FLAGS.AES_128_GCM_SHA256; break; case TlsCipherSuite.TLS_AES_256_GCM_SHA384: flags |= QUIC_ALLOWED_CIPHER_SUITE_FLAGS.AES_256_GCM_SHA384; break; case TlsCipherSuite.TLS_CHACHA20_POLY1305_SHA256: flags |= QUIC_ALLOWED_CIPHER_SUITE_FLAGS.CHACHA20_POLY1305_SHA256; break; case TlsCipherSuite.TLS_AES_128_CCM_SHA256: // not supported by MsQuic (yet?), but QUIC RFC allows it so we ignore it. default: // ignore break; } } if (flags == QUIC_ALLOWED_CIPHER_SUITE_FLAGS.NONE) { throw new ArgumentException(SR.net_quic_empty_cipher_suite, nameof(SslClientAuthenticationOptions.CipherSuitesPolicy)); } return(flags); }
public void NoSupportedCiphers_ThrowsArgumentException(TlsCipherSuite[] ciphers) { CipherSuitesPolicy policy = new CipherSuitesPolicy(ciphers); var listenerOptions = new QuicListenerOptions() { ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), ApplicationProtocols = new List <SslApplicationProtocol>() { ApplicationProtocol }, ConnectionOptionsCallback = (_, _, _) => { var serverOptions = CreateQuicServerOptions(); serverOptions.ServerAuthenticationOptions.CipherSuitesPolicy = policy; return(ValueTask.FromResult(serverOptions)); } }; Assert.ThrowsAsync <ArgumentException>(async() => await CreateQuicListener(listenerOptions)); var clientOptions = CreateQuicClientOptions(new IPEndPoint(IPAddress.Loopback, 5000)); clientOptions.ClientAuthenticationOptions.CipherSuitesPolicy = policy; Assert.ThrowsAsync <ArgumentException>(async() => await CreateQuicConnection(clientOptions)); }
private async Task TestConnection(CipherSuitesPolicy serverPolicy, CipherSuitesPolicy clientPolicy) { var listenerOptions = new QuicListenerOptions() { ListenEndPoint = new IPEndPoint(IPAddress.Loopback, 0), ApplicationProtocols = new List <SslApplicationProtocol>() { ApplicationProtocol }, ConnectionOptionsCallback = (_, _, _) => { var serverOptions = CreateQuicServerOptions(); serverOptions.ServerAuthenticationOptions.CipherSuitesPolicy = serverPolicy; return(ValueTask.FromResult(serverOptions)); } }; await using QuicListener listener = await CreateQuicListener(listenerOptions); var clientOptions = CreateQuicClientOptions(listener.LocalEndPoint); clientOptions.ClientAuthenticationOptions.CipherSuitesPolicy = clientPolicy; await using QuicConnection clientConnection = await CreateQuicConnection(clientOptions); await clientConnection.CloseAsync(0); }
public void NoSupportedCiphers_ThrowsArgumentException(TlsCipherSuite[] ciphers) { CipherSuitesPolicy policy = new CipherSuitesPolicy(ciphers); var listenerOptions = CreateQuicListenerOptions(); listenerOptions.ServerAuthenticationOptions.CipherSuitesPolicy = policy; Assert.ThrowsAsync <ArgumentException>(async() => await CreateQuicListener(listenerOptions)); var clientOptions = CreateQuicClientOptions(); clientOptions.ClientAuthenticationOptions.CipherSuitesPolicy = policy; clientOptions.RemoteEndPoint = new IPEndPoint(IPAddress.Loopback, 5000); Assert.ThrowsAsync <ArgumentException>(async() => await CreateQuicConnection(clientOptions)); }
public void CipherSuitesPolicy_NothingAllowed_Fails() { CipherSuitesPolicy csp = BuildPolicy(); var sp = new ConnectionParams(); sp.CipherSuitesPolicy = csp; var cp = new ConnectionParams(); cp.CipherSuitesPolicy = csp; NegotiatedParams ret = ConnectAndGetNegotiatedParams(sp, cp); ret.Failed(); }
private async Task TestConnection(CipherSuitesPolicy serverPolicy, CipherSuitesPolicy clientPolicy) { var listenerOptions = CreateQuicListenerOptions(); listenerOptions.ServerAuthenticationOptions.CipherSuitesPolicy = serverPolicy; using QuicListener listener = await CreateQuicListener(listenerOptions); var clientOptions = CreateQuicClientOptions(); clientOptions.ClientAuthenticationOptions.CipherSuitesPolicy = clientPolicy; clientOptions.RemoteEndPoint = listener.ListenEndPoint; using QuicConnection clientConnection = await CreateQuicConnection(clientOptions); await clientConnection.ConnectAsync(); await clientConnection.CloseAsync(0); }
public void CipherSuitesPolicy_AllowedCipherSuitesIncludesSubsetOfInput_Success() { TlsCipherSuite[] allCipherSuites = (TlsCipherSuite[])Enum.GetValues(typeof(TlsCipherSuite)); var r = new Random(123); int[] numOfCipherSuites = new int[] { 0, 1, 2, 5, 10, 15, 30 }; foreach (int n in numOfCipherSuites) { HashSet <TlsCipherSuite> cipherSuites = PickRandomValues(allCipherSuites, n, r); var csp = new CipherSuitesPolicy(cipherSuites); Assert.NotNull(csp.AllowedCipherSuites); Assert.InRange(csp.AllowedCipherSuites.Count(), 0, n); foreach (var cs in csp.AllowedCipherSuites) { Assert.True(cipherSuites.Contains(cs)); } } }
private static void AllowOneOnOneSide(IEnumerable <TlsCipherSuite> cipherSuites, Predicate <TlsCipherSuite> mustSucceed, Action <TlsCipherSuite> cipherSuitePicked = null) { foreach (TlsCipherSuite cs in cipherSuites) { CipherSuitesPolicy csp = BuildPolicy(cs); var paramsA = new ConnectionParams() { CipherSuitesPolicy = csp, }; var paramsB = new ConnectionParams(); int score = 0; // 1 for success 0 for fail. Sum should be even for (int i = 0; i < 2; i++) { NegotiatedParams ret = i == 0 ? ConnectAndGetNegotiatedParams(paramsA, paramsB) : ConnectAndGetNegotiatedParams(paramsB, paramsA); score += ret.HasSucceeded ? 1 : 0; if (mustSucceed(cs) || ret.HasSucceeded) { // we do not always guarantee success but if it succeeds it // must use the picked cipher suite ret.Succeeded(); ret.CheckCipherSuite(cs); if (cipherSuitePicked != null && i == 0) { cipherSuitePicked(cs); } } } // we should either get 2 successes or 2 failures Assert.True(score % 2 == 0); } }
public HttpsConnectionMiddleware(RavenServer server, KestrelServerOptions options) { _server = server; CipherSuitesPolicy = server.Configuration.Security.TlsCipherSuites != null && server.Configuration.Security.TlsCipherSuites.Length > 0 ? new CipherSuitesPolicy(server.Configuration.Security.TlsCipherSuites) : null; options.ConfigureHttpsDefaults(o => { o.SslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12; o.CheckCertificateRevocation = false; o.ClientCertificateMode = ClientCertificateMode.AllowCertificate; o.ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => { if (certificate == null) { return(true); // we handle the error from not having certificate higher in the stack } var certificate2 = ConvertToX509Certificate2(certificate); if (certificate2 == null) { return(false); // we require to be able to convert it in all cases } // Here we are explicitly ignoring trust chain issues for client certificates // this is because we don't actually require trust, we just use the certificate // as a way to authenticate. The admin is going to tell us which specific certs // we can trust anyway, so we can ignore such errors. return(sslPolicyErrors == SslPolicyErrors.RemoteCertificateChainErrors || sslPolicyErrors == SslPolicyErrors.None); }; o.ServerCertificateSelector = (_, __) => _serverCertificate; if (CipherSuitesPolicy != null) { o.OnAuthenticate = (_, sslServerAuthenticationOptions) => sslServerAuthenticationOptions.CipherSuitesPolicy = CipherSuitesPolicy; } }); }
public Task SupportedCipher_Success() { CipherSuitesPolicy policy = new CipherSuitesPolicy(new[] { TlsCipherSuite.TLS_AES_128_GCM_SHA256 }); return(TestConnection(policy, policy)); }
public void CloneSslOptionsClonesAllProperties() { var propertyNames = typeof(SslServerAuthenticationOptions).GetProperties().Select(property => property.Name).ToList(); CipherSuitesPolicy cipherSuitesPolicy = null; if (!OperatingSystem.IsWindows()) { try { // The CipherSuitesPolicy ctor throws a PlatformNotSupportedException on Windows. cipherSuitesPolicy = new CipherSuitesPolicy(new[] { TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 }); } catch (PlatformNotSupportedException) { // The CipherSuitesPolicy ctor throws a PlatformNotSupportedException on Ubuntu 16.04. // I don't know exactly which other distros/versions throw PNEs, but it isn't super relevant to this test, // so let's just swallow this exception. } } // Set options properties to non-default values to verify they're copied. var options = new SslServerAuthenticationOptions { // Defaults to true AllowRenegotiation = false, // Defaults to null ApplicationProtocols = new List <SslApplicationProtocol> { SslApplicationProtocol.Http2 }, // Defaults to X509RevocationMode.NoCheck CertificateRevocationCheckMode = X509RevocationMode.Offline, // Defaults to null CipherSuitesPolicy = cipherSuitesPolicy, // Defaults to false ClientCertificateRequired = true, // Defaults to SslProtocols.None #pragma warning disable SYSLIB0039 // TLS 1.0 and 1.1 are obsolete EnabledSslProtocols = SslProtocols.Tls13 | SslProtocols.Tls11, #pragma warning restore SYSLIB0039 #pragma warning disable SYSLIB0040 // EncryptionPolicy.NoEncryption is obsolete // Defaults to EncryptionPolicy.RequireEncryption EncryptionPolicy = EncryptionPolicy.NoEncryption, #pragma warning restore SYSLIB0040 // Defaults to null RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true, // Defaults to null ServerCertificate = new X509Certificate2(Array.Empty <byte>()), // Defaults to null ServerCertificateContext = SslStreamCertificateContext.Create(_x509Certificate2, additionalCertificates: null, offline: true), // Defaults to null ServerCertificateSelectionCallback = (sender, serverName) => null, // Defaults to null CertificateChainPolicy = new X509ChainPolicy(), }; var clonedOptions = SniOptionsSelector.CloneSslOptions(options); Assert.NotSame(options, clonedOptions); Assert.Equal(options.AllowRenegotiation, clonedOptions.AllowRenegotiation); Assert.True(propertyNames.Remove(nameof(options.AllowRenegotiation))); // Ensure the List<SslApplicationProtocol> is also cloned since it could be modified by a user callback. Assert.NotSame(options.ApplicationProtocols, clonedOptions.ApplicationProtocols); Assert.Equal(Assert.Single(options.ApplicationProtocols), Assert.Single(clonedOptions.ApplicationProtocols)); Assert.True(propertyNames.Remove(nameof(options.ApplicationProtocols))); Assert.Equal(options.CertificateRevocationCheckMode, clonedOptions.CertificateRevocationCheckMode); Assert.True(propertyNames.Remove(nameof(options.CertificateRevocationCheckMode))); Assert.Same(options.CipherSuitesPolicy, clonedOptions.CipherSuitesPolicy); Assert.True(propertyNames.Remove(nameof(options.CipherSuitesPolicy))); Assert.Equal(options.ClientCertificateRequired, clonedOptions.ClientCertificateRequired); Assert.True(propertyNames.Remove(nameof(options.ClientCertificateRequired))); Assert.Equal(options.EnabledSslProtocols, clonedOptions.EnabledSslProtocols); Assert.True(propertyNames.Remove(nameof(options.EnabledSslProtocols))); Assert.Equal(options.EncryptionPolicy, clonedOptions.EncryptionPolicy); Assert.True(propertyNames.Remove(nameof(options.EncryptionPolicy))); Assert.Same(options.RemoteCertificateValidationCallback, clonedOptions.RemoteCertificateValidationCallback); Assert.True(propertyNames.Remove(nameof(options.RemoteCertificateValidationCallback))); // Technically the ServerCertificate could be reset/reimported, but I'm hoping this is uncommon. Trying to clone the certificate and/or context seems risky. Assert.Same(options.ServerCertificate, clonedOptions.ServerCertificate); Assert.True(propertyNames.Remove(nameof(options.ServerCertificate))); Assert.Same(options.ServerCertificateContext, clonedOptions.ServerCertificateContext); Assert.True(propertyNames.Remove(nameof(options.ServerCertificateContext))); Assert.Same(options.ServerCertificateSelectionCallback, clonedOptions.ServerCertificateSelectionCallback); Assert.True(propertyNames.Remove(nameof(options.ServerCertificateSelectionCallback))); Assert.Same(options.CertificateChainPolicy, clonedOptions.CertificateChainPolicy); Assert.True(propertyNames.Remove(nameof(options.CertificateChainPolicy))); // Ensure we've checked every property. When new properties get added, we'll have to update this test along with the CloneSslOptions implementation. Assert.Empty(propertyNames); }