From 06b3378fd778dc71672e1172f3ab5e9a635cf95c Mon Sep 17 00:00:00 2001
From: Simon Zolin <s.zolin@adguard.com>
Date: Mon, 23 Mar 2020 10:23:34 +0300
Subject: [PATCH] Merge: + DNS, Web: use only secure TLSv1.2 ciphers Close
 #1384

Squashed commit of the following:

commit cd90abcce573a8e930446ba153565e553e6b81d5
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Fri Mar 20 19:17:53 2020 +0300

    minor

commit a1914c5f41425e82cdedc9716bce84470afab65b
Merge: 72c53673 c8285c41
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Fri Mar 20 19:17:21 2020 +0300

    Merge remote-tracking branch 'origin/master' into 1384-tls12-ciphers

commit 72c536737e0502bb397562ade47aedb9f2ae4494
Author: Simon Zolin <s.zolin@adguard.com>
Date:   Wed Mar 4 18:16:24 2020 +0300

    + DNS, Web: use only secure TLSv1.2 ciphers
---
 dnsforward/dnsforward.go |  2 ++
 home/dns.go              |  1 +
 home/home.go             |  2 ++
 home/web.go              |  1 +
 util/tls.go              | 50 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 56 insertions(+)

diff --git a/dnsforward/dnsforward.go b/dnsforward/dnsforward.go
index 1024a6b4..59b3e516 100644
--- a/dnsforward/dnsforward.go
+++ b/dnsforward/dnsforward.go
@@ -186,6 +186,7 @@ type ServerConfig struct {
 	TLSAllowUnencryptedDOH bool
 
 	TLSv12Roots *x509.CertPool // list of root CAs for TLSv1.2
+	TLSCiphers  []uint16       // list of TLS ciphers to use
 
 	// Called when the configuration is changed by HTTP request
 	ConfigModified func()
@@ -348,6 +349,7 @@ func (s *Server) Prepare(config *ServerConfig) error {
 		}
 	}
 	upstream.RootCAs = s.conf.TLSv12Roots
+	upstream.CipherSuites = s.conf.TLSCiphers
 
 	if len(proxyConfig.Upstreams) == 0 {
 		log.Fatal("len(proxyConfig.Upstreams) == 0")
diff --git a/home/dns.go b/home/dns.go
index e19079ce..f1a5a501 100644
--- a/home/dns.go
+++ b/home/dns.go
@@ -172,6 +172,7 @@ func generateServerConfig() dnsforward.ServerConfig {
 		}
 	}
 	newconfig.TLSv12Roots = Context.tlsRoots
+	newconfig.TLSCiphers = Context.tlsCiphers
 	newconfig.TLSAllowUnencryptedDOH = tlsConf.AllowUnencryptedDOH
 
 	newconfig.FilterHandler = applyAdditionalFiltering
diff --git a/home/home.go b/home/home.go
index 69447bc6..8689d4ea 100644
--- a/home/home.go
+++ b/home/home.go
@@ -80,6 +80,7 @@ type homeContext struct {
 	disableUpdate    bool   // If set, don't check for updates
 	controlLock      sync.Mutex
 	tlsRoots         *x509.CertPool // list of root CAs for TLSv1.2
+	tlsCiphers       []uint16       // list of TLS ciphers to use
 	transport        *http.Transport
 	client           *http.Client
 	appSignalChannel chan os.Signal // Channel for receiving OS signals by the console app
@@ -174,6 +175,7 @@ func run(args options) {
 	initConfig()
 
 	Context.tlsRoots = util.LoadSystemRootCAs()
+	Context.tlsCiphers = util.InitTLSCiphers()
 	Context.transport = &http.Transport{
 		DialContext: customDialContext,
 		Proxy:       getHTTPProxy,
diff --git a/home/web.go b/home/web.go
index 28db3948..a6c515e0 100644
--- a/home/web.go
+++ b/home/web.go
@@ -176,6 +176,7 @@ func (w *Web) httpServerLoop() {
 				Certificates: []tls.Certificate{w.httpsServer.cert},
 				MinVersion:   tls.VersionTLS12,
 				RootCAs:      Context.tlsRoots,
+				CipherSuites: Context.tlsCiphers,
 			},
 		}
 
diff --git a/util/tls.go b/util/tls.go
index a4d125f9..48b8b35a 100644
--- a/util/tls.go
+++ b/util/tls.go
@@ -1,12 +1,14 @@
 package util
 
 import (
+	"crypto/tls"
 	"crypto/x509"
 	"io/ioutil"
 	"os"
 	"runtime"
 
 	"github.com/AdguardTeam/golibs/log"
+	"golang.org/x/sys/cpu"
 )
 
 // LoadSystemRootCAs - load root CAs from the system
@@ -45,3 +47,51 @@ func LoadSystemRootCAs() *x509.CertPool {
 	}
 	return nil
 }
+
+// InitTLSCiphers - the same as initDefaultCipherSuites() from src/crypto/tls/common.go
+//  but with the difference that we don't use so many other default ciphers.
+func InitTLSCiphers() []uint16 {
+	var ciphers []uint16
+
+	// Check the cpu flags for each platform that has optimized GCM implementations.
+	// Worst case, these variables will just all be false.
+	var (
+		hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
+		hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
+		// Keep in sync with crypto/aes/cipher_s390x.go.
+		hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR && (cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
+
+		hasGCMAsm = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X
+	)
+
+	if hasGCMAsm {
+		// If AES-GCM hardware is provided then prioritise AES-GCM
+		// cipher suites.
+		ciphers = []uint16{
+			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+			tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+			tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
+		}
+	} else {
+		// Without AES-GCM hardware, we put the ChaCha20-Poly1305
+		// cipher suites first.
+		ciphers = []uint16{
+			tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
+			tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
+			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+		}
+	}
+
+	otherCiphers := []uint16{
+		tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+		tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+	}
+	ciphers = append(ciphers, otherCiphers...)
+	return ciphers
+}