|
@@ -27,17 +27,74 @@ var (
|
|
|
ErrIdentificationFailed = fmt.Errorf("failed to identify socket type")
|
|
|
)
|
|
|
|
|
|
-// NewCertificate generates and returns a new TLS certificate. If tlsRSABits
|
|
|
-// is greater than zero we generate an RSA certificate with the specified
|
|
|
-// number of bits. Otherwise we create a 384 bit ECDSA certificate.
|
|
|
-func NewCertificate(certFile, keyFile, tlsDefaultCommonName string, tlsRSABits int) (tls.Certificate, error) {
|
|
|
- var priv interface{}
|
|
|
- var err error
|
|
|
- if tlsRSABits > 0 {
|
|
|
- priv, err = rsa.GenerateKey(rand.Reader, tlsRSABits)
|
|
|
- } else {
|
|
|
- priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
|
|
+var (
|
|
|
+ // The list of cipher suites we will use / suggest for TLS connections.
|
|
|
+ // This is built based on the component slices below, depending on what
|
|
|
+ // the hardware prefers.
|
|
|
+ cipherSuites []uint16
|
|
|
+
|
|
|
+ // Suites that are good and fast on hardware with AES-NI. These are
|
|
|
+ // reordered from the Go default to put the 256 bit ciphers above the
|
|
|
+ // 128 bit ones - because that looks cooler, even though there is
|
|
|
+ // probably no relevant difference in strength yet.
|
|
|
+ gcmSuites = []uint16{
|
|
|
+ tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
|
+ tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
|
|
+ tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
|
+ tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
|
}
|
|
|
+
|
|
|
+ // Suites that are good and fast on hardware *without* AES-NI.
|
|
|
+ chaChaSuites = []uint16{
|
|
|
+ tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
|
|
+ tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
|
|
+ }
|
|
|
+
|
|
|
+ // The rest of the suites, minus DES stuff.
|
|
|
+ otherSuites = []uint16{
|
|
|
+ tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
|
|
+ tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
|
|
+ tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
|
|
+ tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
|
|
+ tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
|
|
+ tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
|
|
+ tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
|
|
|
+ tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
|
|
+ tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
|
|
|
+ tls.TLS_RSA_WITH_AES_128_CBC_SHA,
|
|
|
+ tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
|
|
+ }
|
|
|
+)
|
|
|
+
|
|
|
+func init() {
|
|
|
+ // Creates the list of ciper suites that SecureDefault uses.
|
|
|
+ cipherSuites = buildCipherSuites()
|
|
|
+}
|
|
|
+
|
|
|
+// SecureDefault returns a tls.Config with reasonable, secure defaults set.
|
|
|
+func SecureDefault() *tls.Config {
|
|
|
+ // paranoia
|
|
|
+ cs := make([]uint16, len(cipherSuites))
|
|
|
+ copy(cs, cipherSuites)
|
|
|
+
|
|
|
+ return &tls.Config{
|
|
|
+ // TLS 1.2 is the minimum we accept
|
|
|
+ MinVersion: tls.VersionTLS12,
|
|
|
+ // We want the longer curves at the front, because that's more
|
|
|
+ // secure (so the web tells me, don't ask me to explain the
|
|
|
+ // details).
|
|
|
+ CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
|
|
|
+ // The cipher suite lists built above.
|
|
|
+ CipherSuites: cs,
|
|
|
+ // We've put some thought into this choice and would like it to
|
|
|
+ // matter.
|
|
|
+ PreferServerCipherSuites: true,
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// NewCertificate generates and returns a new TLS certificate.
|
|
|
+func NewCertificate(certFile, keyFile, commonName string) (tls.Certificate, error) {
|
|
|
+ priv, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
|
|
if err != nil {
|
|
|
return tls.Certificate{}, fmt.Errorf("generate key: %s", err)
|
|
|
}
|
|
@@ -48,11 +105,11 @@ func NewCertificate(certFile, keyFile, tlsDefaultCommonName string, tlsRSABits i
|
|
|
template := x509.Certificate{
|
|
|
SerialNumber: new(big.Int).SetInt64(rand.Int63()),
|
|
|
Subject: pkix.Name{
|
|
|
- CommonName: tlsDefaultCommonName,
|
|
|
+ CommonName: commonName,
|
|
|
},
|
|
|
- NotBefore: notBefore,
|
|
|
- NotAfter: notAfter,
|
|
|
-
|
|
|
+ NotBefore: notBefore,
|
|
|
+ NotAfter: notAfter,
|
|
|
+ SignatureAlgorithm: x509.ECDSAWithSHA256,
|
|
|
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
|
|
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
|
|
BasicConstraintsValid: true,
|
|
@@ -185,3 +242,79 @@ func pemBlockForKey(priv interface{}) (*pem.Block, error) {
|
|
|
return nil, fmt.Errorf("unknown key type")
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+// buildCipherSuites returns a list of cipher suites with either AES-GCM or
|
|
|
+// ChaCha20 at the top. This takes advantage of the CPU detection that the
|
|
|
+// TLS package does to create an optimal cipher suite list for the current
|
|
|
+// hardware.
|
|
|
+func buildCipherSuites() []uint16 {
|
|
|
+ pref := preferredCipherSuite()
|
|
|
+ for _, suite := range gcmSuites {
|
|
|
+ if suite == pref {
|
|
|
+ // Go preferred an AES-GCM suite. Use those first.
|
|
|
+ return append(gcmSuites, append(chaChaSuites, otherSuites...)...)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // Use ChaCha20 at the top, then AES-GCM etc.
|
|
|
+ return append(chaChaSuites, append(gcmSuites, otherSuites...)...)
|
|
|
+}
|
|
|
+
|
|
|
+// preferredCipherSuite returns the cipher suite that is selected for a TLS
|
|
|
+// connection made with the Go defaults to ourselves. This is (currently,
|
|
|
+// probably) either a ChaCha20 suite or an AES-GCM suite, depending on what
|
|
|
+// the CPU detection has decided is fastest on this hardware.
|
|
|
+//
|
|
|
+// The function will return zero if something odd happens, and there's no
|
|
|
+// guarantee what cipher suite would be chosen anyway, so the return value
|
|
|
+// should be taken with a grain of salt.
|
|
|
+func preferredCipherSuite() uint16 {
|
|
|
+ // This is one of our certs from NewCertificate above, to avoid having
|
|
|
+ // to generate one at init time just for this function.
|
|
|
+ crtBs := []byte(`-----BEGIN CERTIFICATE-----
|
|
|
+MIIBXDCCAQOgAwIBAgIIQUODl2/bE4owCgYIKoZIzj0EAwIwFDESMBAGA1UEAxMJ
|
|
|
+c3luY3RoaW5nMB4XDTE4MTAxNDA2MjU0M1oXDTQ5MTIzMTIzNTk1OVowFDESMBAG
|
|
|
+A1UEAxMJc3luY3RoaW5nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMqP+1lL4
|
|
|
+0s/xtI3ygExzYc/GvLHr0qetpBrUVHaDwS/cR1yXDsYaJpJcUNtrf1XK49IlpWW1
|
|
|
+Ds8seQsSg7/9BaM/MD0wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUF
|
|
|
+BwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMAoGCCqGSM49BAMCA0cAMEQCIFxY
|
|
|
+MDBA92FKqZYSZjmfdIbT1OI6S9CnAFvL/pJZJwNuAiAV7osre2NiCHtXABOvsGrH
|
|
|
+vKWqDvXcHr6Tlo+LmTAdyg==
|
|
|
+-----END CERTIFICATE-----
|
|
|
+ `)
|
|
|
+ keyBs := []byte(`-----BEGIN EC PRIVATE KEY-----
|
|
|
+MHcCAQEEIHtPxVHlj6Bhi9RgSR2/lAtIQ7APM9wmpaJAcds6TD2CoAoGCCqGSM49
|
|
|
+AwEHoUQDQgAEMqP+1lL40s/xtI3ygExzYc/GvLHr0qetpBrUVHaDwS/cR1yXDsYa
|
|
|
+JpJcUNtrf1XK49IlpWW1Ds8seQsSg7/9BQ==
|
|
|
+-----END EC PRIVATE KEY-----
|
|
|
+ `)
|
|
|
+
|
|
|
+ cert, err := tls.X509KeyPair(crtBs, keyBs)
|
|
|
+ if err != nil {
|
|
|
+ return 0
|
|
|
+ }
|
|
|
+
|
|
|
+ serverCfg := &tls.Config{
|
|
|
+ MinVersion: tls.VersionTLS12,
|
|
|
+ PreferServerCipherSuites: true,
|
|
|
+ Certificates: []tls.Certificate{cert},
|
|
|
+ }
|
|
|
+
|
|
|
+ clientCfg := &tls.Config{
|
|
|
+ MinVersion: tls.VersionTLS12,
|
|
|
+ InsecureSkipVerify: true,
|
|
|
+ }
|
|
|
+
|
|
|
+ c0, c1 := net.Pipe()
|
|
|
+
|
|
|
+ c := tls.Client(c0, clientCfg)
|
|
|
+ go func() {
|
|
|
+ c.Handshake()
|
|
|
+ }()
|
|
|
+
|
|
|
+ s := tls.Server(c1, serverCfg)
|
|
|
+ if err := s.Handshake(); err != nil {
|
|
|
+ return 0
|
|
|
+ }
|
|
|
+
|
|
|
+ return c.ConnectionState().CipherSuite
|
|
|
+}
|