2019-09-09 14:42:26 +01:00
|
|
|
// +build go1.12
|
|
|
|
|
2018-02-21 05:59:18 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"go/format"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/mozilla/tls-observatory/constants"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
pkg = flag.String("pkg", "rules", "package name to be added to the output file")
|
|
|
|
outputFile = flag.String("outputFile", "tls_config.go", "name of the output file")
|
|
|
|
)
|
|
|
|
|
|
|
|
// TLSConfURL url where Mozilla publishes the TLS ciphers recommendations
|
|
|
|
const TLSConfURL = "https://statics.tls.security.mozilla.org/server-side-tls-conf.json"
|
|
|
|
|
|
|
|
// ServerSideTLSJson contains all the available configurations and the version of the current document.
|
|
|
|
type ServerSideTLSJson struct {
|
|
|
|
Configurations map[string]Configuration `json:"configurations"`
|
|
|
|
Version float64 `json:"version"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Configuration represents configurations levels declared by the Mozilla server-side-tls
|
|
|
|
// see https://wiki.mozilla.org/Security/Server_Side_TLS
|
|
|
|
type Configuration struct {
|
2019-09-09 13:36:05 +01:00
|
|
|
OpenSSLCiphersuites []string `json:"openssl_ciphersuites"`
|
2019-09-09 14:04:54 +01:00
|
|
|
OpenSSLCiphers []string `json:"openssl_ciphers"`
|
2018-02-21 05:59:18 +00:00
|
|
|
TLSVersions []string `json:"tls_versions"`
|
|
|
|
TLSCurves []string `json:"tls_curves"`
|
|
|
|
CertificateTypes []string `json:"certificate_types"`
|
|
|
|
CertificateCurves []string `json:"certificate_curves"`
|
|
|
|
CertificateSignatures []string `json:"certificate_signatures"`
|
|
|
|
RsaKeySize float64 `json:"rsa_key_size"`
|
|
|
|
DHParamSize float64 `json:"dh_param_size"`
|
|
|
|
ECDHParamSize float64 `json:"ecdh_param_size"`
|
|
|
|
HstsMinAge float64 `json:"hsts_min_age"`
|
|
|
|
OldestClients []string `json:"oldest_clients"`
|
2019-09-09 13:36:05 +01:00
|
|
|
OCSPStample bool `json:"ocsp_staple"`
|
|
|
|
ServerPreferedOrder bool `json:"server_preferred_order"`
|
|
|
|
MaxCertLifespan float64 `json:"maximum_certificate_lifespan"`
|
2018-02-21 05:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type goCipherConfiguration struct {
|
|
|
|
Name string
|
|
|
|
Ciphers []string
|
|
|
|
MinVersion string
|
|
|
|
MaxVersion string
|
|
|
|
}
|
|
|
|
|
|
|
|
type goTLSConfiguration struct {
|
|
|
|
cipherConfigs []goCipherConfiguration
|
|
|
|
}
|
|
|
|
|
|
|
|
// getTLSConfFromURL retrieves the json containing the TLS configurations from the specified URL.
|
|
|
|
func getTLSConfFromURL(url string) (*ServerSideTLSJson, error) {
|
2018-09-04 07:55:03 +01:00
|
|
|
r, err := http.Get(url) // #nosec G107
|
2018-02-21 05:59:18 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer r.Body.Close()
|
|
|
|
|
|
|
|
var sstls ServerSideTLSJson
|
|
|
|
err = json.NewDecoder(r.Body).Decode(&sstls)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &sstls, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getGoCipherConfig(name string, sstls ServerSideTLSJson) (goCipherConfiguration, error) {
|
|
|
|
cipherConf := goCipherConfiguration{Name: strings.Title(name)}
|
|
|
|
conf, ok := sstls.Configurations[name]
|
|
|
|
if !ok {
|
|
|
|
return cipherConf, fmt.Errorf("TLS configuration '%s' not found", name)
|
|
|
|
}
|
|
|
|
|
2019-09-09 14:04:54 +01:00
|
|
|
// These ciphers are already defined in IANA format
|
|
|
|
cipherConf.Ciphers = append(cipherConf.Ciphers, conf.OpenSSLCiphersuites...)
|
|
|
|
|
|
|
|
for _, cipherName := range conf.OpenSSLCiphers {
|
2018-02-21 05:59:18 +00:00
|
|
|
cipherSuite, ok := constants.CipherSuites[cipherName]
|
|
|
|
if !ok {
|
2018-03-12 08:17:32 +00:00
|
|
|
log.Printf("'%s' cipher is not available in crypto/tls package\n", cipherName)
|
2018-02-21 05:59:18 +00:00
|
|
|
}
|
|
|
|
if len(cipherSuite.IANAName) > 0 {
|
|
|
|
cipherConf.Ciphers = append(cipherConf.Ciphers, cipherSuite.IANAName)
|
2020-09-02 09:41:26 +01:00
|
|
|
if len(cipherSuite.NSSName) > 0 && cipherSuite.NSSName != cipherSuite.IANAName {
|
|
|
|
cipherConf.Ciphers = append(cipherConf.Ciphers, cipherSuite.NSSName)
|
|
|
|
}
|
2018-02-21 05:59:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
versions := mapTLSVersions(conf.TLSVersions)
|
|
|
|
if len(versions) > 0 {
|
|
|
|
cipherConf.MinVersion = fmt.Sprintf("0x%04x", versions[0])
|
|
|
|
cipherConf.MaxVersion = fmt.Sprintf("0x%04x", versions[len(versions)-1])
|
|
|
|
} else {
|
|
|
|
return cipherConf, fmt.Errorf("No TLS versions found for configuration '%s'", name)
|
|
|
|
}
|
|
|
|
return cipherConf, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getGoTLSConf() (goTLSConfiguration, error) {
|
|
|
|
sstls, err := getTLSConfFromURL(TLSConfURL)
|
|
|
|
if err != nil || sstls == nil {
|
|
|
|
msg := fmt.Sprintf("Could not load the Server Side TLS configuration from Mozilla's website. Check the URL: %s. Error: %v\n",
|
|
|
|
TLSConfURL, err)
|
|
|
|
panic(msg)
|
|
|
|
}
|
|
|
|
|
|
|
|
tlsConfg := goTLSConfiguration{}
|
|
|
|
|
|
|
|
modern, err := getGoCipherConfig("modern", *sstls)
|
|
|
|
if err != nil {
|
|
|
|
return tlsConfg, err
|
|
|
|
}
|
|
|
|
tlsConfg.cipherConfigs = append(tlsConfg.cipherConfigs, modern)
|
|
|
|
|
|
|
|
intermediate, err := getGoCipherConfig("intermediate", *sstls)
|
|
|
|
if err != nil {
|
|
|
|
return tlsConfg, err
|
|
|
|
}
|
|
|
|
tlsConfg.cipherConfigs = append(tlsConfg.cipherConfigs, intermediate)
|
|
|
|
|
|
|
|
old, err := getGoCipherConfig("old", *sstls)
|
|
|
|
if err != nil {
|
|
|
|
return tlsConfg, err
|
|
|
|
}
|
|
|
|
tlsConfg.cipherConfigs = append(tlsConfg.cipherConfigs, old)
|
|
|
|
|
|
|
|
return tlsConfg, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func getCurrentDir() (string, error) {
|
|
|
|
dir := "."
|
|
|
|
if args := flag.Args(); len(args) == 1 {
|
|
|
|
dir = args[0]
|
|
|
|
} else if len(args) > 1 {
|
|
|
|
return "", errors.New("only one directory at a time")
|
|
|
|
}
|
|
|
|
dir, err := filepath.Abs(dir)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return dir, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
dir, err := getCurrentDir()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalln(err)
|
|
|
|
}
|
|
|
|
tlsConfig, err := getGoTLSConf()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalln(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
err = generatedHeaderTmpl.Execute(&buf, *pkg)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Failed to generate the header: %v", err)
|
|
|
|
}
|
|
|
|
for _, cipherConfig := range tlsConfig.cipherConfigs {
|
|
|
|
err := generatedRuleTmpl.Execute(&buf, cipherConfig)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Failed to generated the cipher config: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
src, err := format.Source(buf.Bytes())
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("warnings: Failed to format the code: %v", err)
|
|
|
|
src = buf.Bytes()
|
|
|
|
}
|
|
|
|
|
|
|
|
outputPath := filepath.Join(dir, *outputFile)
|
|
|
|
if err := ioutil.WriteFile(outputPath, src, 0644); err != nil {
|
|
|
|
log.Fatalf("Writing output: %s", err)
|
2020-02-28 11:48:18 +00:00
|
|
|
} // #nosec G306
|
2018-02-21 05:59:18 +00:00
|
|
|
}
|