th5/_dev/tris-testclient/client.go

304 lines
11 KiB
Go
Raw Normal View History

package main
import (
"crypto/tls"
"crypto/x509"
"errors"
"flag"
"fmt"
"io"
"log"
"os"
"strings"
)
var tlsVersionToName = map[uint16]string{
2018-09-16 08:36:45 +01:00
tls.VersionTLS10: "1.0",
tls.VersionTLS11: "1.1",
tls.VersionTLS12: "1.2",
tls.VersionTLS13: "1.3",
}
var cipherSuiteIdToName = map[uint16]string{
tls.TLS_RSA_WITH_AES_128_CBC_SHA: "TLS_RSA_WITH_AES_128_CBC_SHA",
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
tls.TLS_AES_128_GCM_SHA256: "TLS_AES_128_GCM_SHA256",
tls.TLS_AES_256_GCM_SHA384: "TLS_AES_256_GCM_SHA384",
tls.TLS_CHACHA20_POLY1305_SHA256: "TLS_CHACHA20_POLY1305_SHA256",
}
var namedGroupsToName = map[uint16]string{
uint16(tls.HybridSIDHp503Curve25519): "X25519-SIDHp503",
uint16(tls.HybridSIKEp503Curve25519): "X25519-SIKEp503",
uint16(tls.X25519): "X25519",
uint16(tls.CurveP256): "P-256",
uint16(tls.CurveP384): "P-384",
uint16(tls.CurveP521): "P-521",
}
func getIDByName(m map[uint16]string, name string) (uint16, error) {
for key, value := range m {
if value == name {
return key, nil
}
}
return 0, errors.New("Unknown value")
}
2018-08-15 17:50:01 +01:00
var failed uint
type Client struct {
2018-08-15 17:50:01 +01:00
TLS tls.Config
addr string
}
2018-08-15 17:50:01 +01:00
func NewClient() *Client {
var c Client
c.TLS.InsecureSkipVerify = true
return &c
}
func (c *Client) clone() *Client {
var clone Client
clone.TLS = *c.TLS.Clone()
clone.addr = c.addr
return &clone
}
func (c *Client) setMinMaxTLS(ver uint16) {
c.TLS.MinVersion = ver
c.TLS.MaxVersion = ver
}
func (c *Client) run() {
fmt.Printf("TLS %s with %s\n", tlsVersionToName[c.TLS.MinVersion], cipherSuiteIdToName[c.TLS.CipherSuites[0]])
con, err := tls.Dial("tcp", c.addr, &c.TLS)
if err != nil {
fmt.Printf("handshake failed: %v\n\n", err)
2018-08-15 17:50:01 +01:00
failed++
return
}
defer con.Close()
_, err = con.Write([]byte("GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"))
if err != nil {
fmt.Printf("Write failed: %v\n\n", err)
2018-08-15 17:50:01 +01:00
failed++
return
}
buf := make([]byte, 1024)
n, err := con.Read(buf)
// A non-zero read with EOF is acceptable and occurs when a close_notify
// is received right after reading data (observed with NSS selfserv).
if !(n > 0 && err == io.EOF) && err != nil {
fmt.Printf("Read failed: %v\n\n", err)
2018-08-15 17:50:01 +01:00
failed++
return
}
fmt.Printf("[TLS: %s] Read %d bytes\n", tlsVersionToName[con.ConnectionState().Version], n)
fmt.Println("OK\n")
}
2018-08-15 17:50:01 +01:00
func result() {
if failed > 0 {
log.Fatalf("Failed handshakes: %d\n", failed)
} else {
fmt.Println("All handshakes passed")
}
}
// Usage client args host:port
func main() {
var keylog_file, tls_version, named_groups, named_ciphers string
var enable_rsa, enable_ecdsa, client_auth bool
flag.StringVar(&keylog_file, "keylogfile", "", "Secrets will be logged here")
flag.BoolVar(&enable_rsa, "rsa", true, "Whether to enable RSA cipher suites")
flag.BoolVar(&enable_ecdsa, "ecdsa", true, "Whether to enable ECDSA cipher suites")
flag.BoolVar(&client_auth, "cliauth", false, "Whether to enable client authentication")
flag.StringVar(&tls_version, "tls_version", "1.3", "TLS version to use")
flag.StringVar(&named_groups, "groups", "X25519:P-256:P-384:P-521", "NamedGroups IDs to use")
flag.StringVar(&named_ciphers, "ciphers", "TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384", "Named cipher IDs to use")
flag.Parse()
if flag.NArg() != 1 {
flag.Usage()
os.Exit(1)
}
2018-08-15 17:50:01 +01:00
client := NewClient()
client.addr = flag.Arg(0)
if !strings.Contains(client.addr, ":") {
client.addr += ":443"
}
if keylog_file == "" {
keylog_file = os.Getenv("SSLKEYLOGFILE")
}
if keylog_file != "" {
keylog_writer, err := os.OpenFile(keylog_file, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
if err != nil {
log.Fatalf("Cannot open keylog file: %v", err)
}
2018-08-15 17:50:01 +01:00
client.TLS.KeyLogWriter = keylog_writer
log.Println("Enabled keylog")
}
if client_auth {
var err error
2018-08-15 17:50:01 +01:00
client_cert, err := tls.X509KeyPair([]byte(client_crt), []byte(client_key))
if err != nil {
panic("Can't load client certificate")
}
2018-08-15 17:50:01 +01:00
client.TLS.Certificates = []tls.Certificate{client_cert}
client.TLS.RootCAs = x509.NewCertPool()
if !client.TLS.RootCAs.AppendCertsFromPEM([]byte(client_ca)) {
panic("Can't load client CA cert")
}
}
if enable_rsa {
// Sanity check: TLS 1.2 with the mandatory cipher suite from RFC 5246
2018-08-15 17:50:01 +01:00
c := client.clone()
c.TLS.CipherSuites = []uint16{tls.TLS_RSA_WITH_AES_128_CBC_SHA}
c.setMinMaxTLS(tls.VersionTLS12)
c.run()
}
if enable_ecdsa {
// Sane cipher suite for TLS 1.2 with an ECDSA cert (as used by boringssl)
2018-08-15 17:50:01 +01:00
c := client.clone()
c.TLS.CipherSuites = []uint16{tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256}
c.setMinMaxTLS(tls.VersionTLS12)
c.run()
}
// Set requested DH groups
client.TLS.CurvePreferences = []tls.CurveID{}
for _, ng := range strings.Split(named_groups, ":") {
id, err := getIDByName(namedGroupsToName, ng)
if err != nil {
panic("Wrong TLS version provided")
}
client.TLS.CurvePreferences = append(client.TLS.CurvePreferences, tls.CurveID(id))
}
2018-08-15 17:50:01 +01:00
// Perform TLS handshake with each each requested CipherSuite
tlsID, err := getIDByName(tlsVersionToName, tls_version)
if err != nil {
panic("Unknown TLS version")
}
for _, cn := range strings.Split(named_ciphers, ":") {
id, err := getIDByName(cipherSuiteIdToName, cn)
if err != nil {
panic("Wrong cipher name provided")
}
client.setMinMaxTLS(tlsID)
client.TLS.CipherSuites = []uint16{id}
client.run()
}
// TODO test other kex methods besides X25519, like MTI secp256r1
// TODO limit supported groups?
2018-08-15 17:50:01 +01:00
result()
}
const (
client_ca = `-----BEGIN CERTIFICATE-----
MIIF6zCCA9OgAwIBAgIUC4U4HlbkVMrKKTFK0mNrMFDpRskwDQYJKoZIhvcNAQEL
BQAwfTELMAkGA1UEBhMCRlIxDTALBgNVBAgMBFBBQ0ExFzAVBgNVBAcMDkNhZ25l
cyBzdXIgTWVyMSIwIAYDVQQLDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9uMSIw
IAYDVQQDDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9uMB4XDTE5MDIyMjAwNDIz
OVoXDTQ2MDcwOTAwNDIzOVowfTELMAkGA1UEBhMCRlIxDTALBgNVBAgMBFBBQ0Ex
FzAVBgNVBAcMDkNhZ25lcyBzdXIgTWVyMSIwIAYDVQQLDBlDZXJ0IFRlc3Rpbmcg
T3JnYW5pemF0aW9uMSIwIAYDVQQDDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9u
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0z+DMLb7YIMFFNZpn+ve
NdT7GL9DPyV9ZWSHpUuDyme6Og6Mp5IpCLlKjNXtizX5aQ9xQ746slt70fivSV/r
tiEtayZkcwS7zHtc5f+U/S0hR1q5Zh3DaLQH9diSeuNFQN5pg7zQT5csJFlxf6EB
j/ioSBC+J1E8A2FAh0qDq+TvPPyZEEjcJy0oBuNHUnkC3rwjt24DAUI26rN/Qk9P
a6KR9bBOdHFFul3DEP/uPqWV9TvV5tJhP3J2RbfS79WljFy/lFIwvJvfQHYEjMt4
/gq8yTSUgJ8zmgJQ1sgOKH1FzJd4EdAMquSYbElkc35jX8gggUNOUcwsIfJBnu41
SC51JQruNT256zse76o8Dx3lSHiz5c6luZyJnZWWt6xWtfGEGMnckpn6cVvcbbgq
eWqmttgE2QTpgYoYUVcX/XFtsmZVTu05r8MZoqje5rgW9nEvvW+3M+eT5h0M9eGQ
bIT3D3tdXB2XWCjUWqxpZscFwyumGu7vdykBKLhMVR3nEpFfORnH+534vwi49fjz
WnN6fXAZZLPnGtEdWXNgs9JtgI5UheAQbcA3FT+M3maa88V2JrETLps405NYp6hJ
6msbS/AmV/eSilRmbGVj9TfKHb/BVHNYwVQ0Bu/QN2YQNQ9olOpIxXgKr6Y4tKZt
wTOMiCxZrnDQneQOTnW0NAMCAwEAAaNjMGEwHQYDVR0OBBYEFNLiS6YezH2bWiZ3
TNbkQzMoBBB2MB8GA1UdIwQYMBaAFNLiS6YezH2bWiZ3TNbkQzMoBBB2MA8GA1Ud
EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQAQ
cWIFOusSLKizqcqMsbMIY/Jzy0Tq5jzeOAQEQBztu7eJb208SG2EJtD6ylBQCF8t
FCxUbvWNrly1MJoMSXdn3uMz3kLKNQa6RENckwA1UuYZpdhvTUtmPun9QqFPJdqm
oi0paOVut9q3dplDy6MUknGN4tNWp2ZDfyvom3mUMfYGEO/FCWTy8eFd6cHRE9bw
tHkcX5r7GpDHH5vKXOF/deMp1Xgep5ZTasL13YwPiYgctst91pEfdcztjHW0mQNT
ZH/TUQDgs2UCjcvyeOlgoZixWOpkf1Qyje15k9qMb89/5hdarxvAQbG2BezQtzyk
bbCu1MQa2DBdAKbhQxas/DPSvSkA/y8v+hiovTWtPKErPnQqZqVy59KUTBWj8ZAj
5dkDVjBvUcsJ/6zHv0X9puEnIDZ8pK+Xn9LbcbPE7Nf1ikDyOqHmLmhGfWlEGvoD
3Q8f8zUySZ40mfqtVhc7OYqA66Q9quNQ4VBESVNiEJ/LuWHRXe74KqFdggsQqtS6
UQQgw5lFnKHZ9pk2VlKzgpkmd5fLMOhcHWQbsah9TFOuW5vEhWGHNhGCyGouWTzD
mkwlPS8arj/ymUn6t/oiwSOA6GbjQLnTXvoAjdBxnukQlNY6TUDk+lSQw0qfZGIA
xZywUgRbLZH8TFUnuEQps35XnWrY8rrXVj9+9h0B4g==
-----END CERTIFICATE-----`
client_crt = `-----BEGIN CERTIFICATE-----
MIIFSjCCAzKgAwIBAgIUCKk2npLYEX2Z3Ceu1CwSKK50j04wDQYJKoZIhvcNAQEL
BQAwfTELMAkGA1UEBhMCRlIxDTALBgNVBAgMBFBBQ0ExFzAVBgNVBAcMDkNhZ25l
cyBzdXIgTWVyMSIwIAYDVQQLDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9uMSIw
IAYDVQQDDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9uMB4XDTE5MDIyMjAwNDMz
MloXDTQ2MDcwOTAwNDMzMlowfTELMAkGA1UEBhMCRlIxDTALBgNVBAgMBFBBQ0Ex
FzAVBgNVBAcMDkNhZ25lcyBzdXIgTWVyMSIwIAYDVQQLDBlDZXJ0IFRlc3Rpbmcg
T3JnYW5pemF0aW9uMSIwIAYDVQQDDBlDZXJ0IFRlc3RpbmcgT3JnYW5pemF0aW9u
MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEA0DQrWlCfijylutz28aTB
T9WDnvBWpJ535t/Clt4o2nv3Cp6JxvUVzYkdKuaLR295gyEBx9JSHiZxPiJoPVfp
wigmG0R9HvByAG5rhaQbQt99npoBaHMps1i12VxxFy1yaqZW6mrwrHMfV716rZ2M
AzWx7UfhutloBYeeluiziDWUSEuGeJG7kHdvUtGYlbRd/ElFWHOfAQ7Oc8UUjEHW
sorkqciqyAERV/H9hr5Rap/J/ERcFC8bNecS4t1Yh98WgIun/MbcBKQzo1LsWmOQ
dmaMBoG8g1mYRbNap8G/+aQbjfRi1zN0yaW1wtlLoBJmNgLjwYgaS/5Uey5NZMdb
NwIBA6OBwzCBwDAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIFoDAxBglghkgB
hvhCAQ0EJBYiQ2VydCBUZXN0aW5nIEludGVybWVkaWF0ZSAtIENsaWVudDAdBgNV
HQ4EFgQULlrGFPbxwz525ywQYPs72P7YnL4wHwYDVR0jBBgwFoAU0uJLph7MfZta
JndM1uRDMygEEHYwDgYDVR0PAQH/BAQDAgXgMB0GA1UdJQQWMBQGCCsGAQUFBwMC
BggrBgEFBQcDBDANBgkqhkiG9w0BAQsFAAOCAgEAwLc+Xg5Fyfbgu5iFs1y7E0Et
4E/5lF0A4iqDVX3e7/upoUIBFZFv2PlAqIlhQ49NgGlIfrBlwEijZJ9kgVmUcKDS
UrqBvKUn+99dTC8Zn/Py9ofLNcJy+qNJg4TpbpBxXaP1MXdZYXdYkGtyyPIGo31U
oHibNLQDCtKFMoEPCvFuCBtJgyT46l5KN7VQCA0ZDm84fVmIgEEOXWwz0mDIhGWm
hDhmqONznl0+aHirqJxsBaplBaFVV1N02ksR53sPPy/UfDsAD3Fpp8R1DAMEyy0o
kTqm8QINVL961YT1Y/oI+GlypjPq9cL0dEHdxwu6gyCHPMMGGGIDHmLoqJJuj/Kr
/T08jhtDv8D7e9m3wfSW/RqHKE31Yy21SXv/gpcHGunwzDoj/QUvRl/xTjJfx+S8
2NHxSU8QOdexhJumsNFJe8kH8cRJMCMB8/hfiBpI0QANkUBJ1aaa/p7vZuEKJm+/
85m3Yz+zn58/Bube06z6QzFeR8Edi+6hXk4/WoHltgXiNowD3d4xI48sPWEbe+QZ
6u60sEdpY2a+3Xwt9m9R2R+sGP3QyDFd9GVaUPt21TeeLdfS3kPqwO2k+UXB8nV3
Yh1Hvyx67u0tX3wBVe40CNaAu7iW+e4aXjksG2dxk71lNq5CHJCOtbRK4LUArjy5
cw4KAXWoaR8YIC3BWgg=
-----END CERTIFICATE-----`
client_key = `-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQNCtaUJ+KPKW6
3PbxpMFP1YOe8Faknnfm38KW3ijae/cKnonG9RXNiR0q5otHb3mDIQHH0lIeJnE+
Img9V+nCKCYbRH0e8HIAbmuFpBtC332emgFocymzWLXZXHEXLXJqplbqavCscx9X
vXqtnYwDNbHtR+G62WgFh56W6LOINZRIS4Z4kbuQd29S0ZiVtF38SUVYc58BDs5z
xRSMQdayiuSpyKrIARFX8f2GvlFqn8n8RFwULxs15xLi3ViH3xaAi6f8xtwEpDOj
UuxaY5B2ZowGgbyDWZhFs1qnwb/5pBuN9GLXM3TJpbXC2UugEmY2AuPBiBpL/lR7
Lk1kx1s3AgEDAoIBAQCKzXI8Nb+xfcPR6KShGIDf460UoDnDFE/vP9cPPsXm/U9c
abEvTg6JBhNx7weE9PuswKvajDa+xEt+wZrTj/EsGsQSLai/Svaq9EeubWeB6lO/
EVZFohvM5c6Q6EtkyPbxxDnxnKBy92o6flHJE7KsznaeL+vR5kVZBRRkmyJazS4s
Z5rbrN9AhSIfyHs9GCQGgsXT6HMsyoJYFastwQ2qj+9L2ypcM8TW+KGzGfJipoJb
l/N/8WHb4ZumA67lfWq4v5JTA5qAUKcfPszEBrUfQ34Tk+73Iiov9f7SXPYxWxVJ
g9PuzfewvJrp6CPv+/mKNt8PmBYkaXlnyjr9tCwLAoGBAPjAVZapQVuIqftcOZtf
Re9fAV9Vvv1FEO8bKJeIsPDlRkdg+TfTMgxhZU0I3P4XdEj7Fa87w4wkA6GkIrOO
W9/usPOYzSdTP5aVEsdGbT8yD2vTST7Aw/GESKTRJA/Fe1PIb5Nz3OijyTusvFE+
XSR3EXb1myX+2rFS0Wbiz2U5AoGBANZFWoeFzREnBcDG60RayjiTg71E1/T4zhvU
e/w+71FNbLZXBrNqgV20F73xOme/Mb13yr+YgXxIEQfFtR6hRxZ8u1jndEzw66Jf
YfHt7EGVceMV2pdP4md5ebebEj7qICfXPxF9IZicwZG3QMR5u0tvnx40iNMWhW0M
rY4FabPvAoGBAKXVjmRw1j0FxqeS0RI/g/TqAOo5Kf4uC0oSGw+wdfXuLtpApiU3
drLrmN4F6Klk+DCnY8on17LCrRZtbHe0PT/0dfe7M2+M1Q8ODITZniohX503hinV
1/ZYMG3gwrUuUjfa9Qz36JsX230d0uDUPhhPYPn5EhlUkcuMi5nsikN7AoGBAI7Y
5wUD3gtvWSsvR4LnMXsNAn4t5U37NBKNp/1/SjYznc7kryJHAOkiun6g0Zp/dn5P
3H+7AP2FYK/ZI2nA2g790jtE+DNLR8GU6/aenYEOS+y5PGTf7ET7pnpnYX9GwBqP
f2D+FmW91mEk1dhRJ4efv2l4WzdkWPNdyQlY8SKfAoGAEIWmowo7EpbR5Boxc2o3
Tl0JbGi1CNJAHtDjEJCd1OxKrMUrK07hOeEKF6y8K/WBuQhFvI2pu16oT4sMal9Z
mEiJdJAFErefPLQGomHLXfq9mDEY13Ug/xAd9aMyYcubIg5XjAqLMrB60HcrLr7Q
2hMCSDdVP2V/F3QVh8DEirE=
-----END PRIVATE KEY-----`
)