diff --git a/.travis.yml b/.travis.yml index 71d7b62..4e97d47 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ go: - 1.7 env: - - MODE=interop CLIENT=boring + - MODE=interop CLIENT=boring SERVER=boring - MODE=interop CLIENT=tstclnt - MODE=interop CLIENT=picotls ZRTT=1 - MODE=interop CLIENT=mint @@ -28,10 +28,13 @@ matrix: install: - if [ "$MODE" = "interop" ]; then ./_dev/tris-localserver/start.sh -d && docker ps -a; fi - if [ "$MODE" = "interop" ]; then ./_dev/interop.sh INSTALL $CLIENT $REVISION; fi + - if [ -n "$SERVER" -a "$CLIENT" != "$SERVER" ]; then ./_dev/interop.sh INSTALL $SERVER $REVISION; fi + - if [ -n "$SERVER" ]; then ./_dev/interop.sh INSTALL-CLIENT; fi script: - if [ "$MODE" = "interop" ]; then ./_dev/interop.sh RUN $CLIENT; fi - if [ "$MODE" = "interop" ] && [ "$ZRTT" = "1" ]; then ./_dev/interop.sh 0-RTT $CLIENT; fi + - if [ -n "$SERVER" ]; then ./_dev/interop.sh RUN-CLIENT $SERVER; fi - if [ "$MODE" = "gotest" ]; then ./_dev/go.sh test -race crypto/tls; fi - if [ "$MODE" = "bogo" ]; then ./_dev/bogo.sh; fi diff --git a/_dev/.gitignore b/_dev/.gitignore index 836893d..23c0b93 100644 --- a/_dev/.gitignore +++ b/_dev/.gitignore @@ -1,5 +1,6 @@ /GOROOT /go /tris-localserver/tris-localserver +/tris-testclient/tris-testclient /caddy/caddy /caddy/echo diff --git a/_dev/boring/Dockerfile b/_dev/boring/Dockerfile index ec8d298..ca67272 100644 --- a/_dev/boring/Dockerfile +++ b/_dev/boring/Dockerfile @@ -12,11 +12,7 @@ RUN apk add --update \ RUN git clone https://boringssl.googlesource.com/boringssl -# Cacheable layer of compilation, to make the next incremental -RUN cd boringssl && git checkout 9b885c5 RUN mkdir boringssl/build -RUN cd boringssl/build && cmake -GNinja .. -RUN cd boringssl && ninja -C build # Draft 14 # ARG REVISION=88536c3 @@ -38,7 +34,9 @@ RUN cd boringssl && ninja -C build # ARG REVISION=89917a5 # Draft 18 -ARG REVISION=9b885c5 +#ARG REVISION=9b885c5 +# Draft 18, but with "bssl server -loop -www" support and build fix +ARG REVISION=40b24c8154 RUN cd boringssl && git fetch RUN cd boringssl && git checkout $REVISION @@ -47,4 +45,5 @@ RUN cd boringssl && ninja -C build ADD httpreq.txt /httpreq.txt ADD run.sh /run.sh +ADD server.sh rsa.pem ecdsa.pem / ENTRYPOINT ["/run.sh"] diff --git a/_dev/boring/ecdsa.pem b/_dev/boring/ecdsa.pem new file mode 100644 index 0000000..3926a1d --- /dev/null +++ b/_dev/boring/ecdsa.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIIBbTCCAROgAwIBAgIQZCsHZcs5ZkzV+zC2E6j5RzAKBggqhkjOPQQDAjASMRAw +DgYDVQQKEwdBY21lIENvMB4XDTE2MDkyNDE3NTE1OFoXDTI2MDkyMjE3NTE1OFow +EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDTO +B3IyzjYfKCp2HWy+P3QHxhdBT4AUGYgwTiSEj5phumPIahFNcOSWptN0UzlZvJdN +MMjVmrFYK/FjF4abkNKjSzBJMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr +BgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDAKBggq +hkjOPQQDAgNIADBFAiEAp9W157PM1IadPBc33Cbj7vaFvp+rXs/hSuMCzP8pgV8C +IHCswo1qiC0ZjQmWsBlmz5Zbp9rOorIzBYmGRhRdNs3j +-----END CERTIFICATE----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIFdhO7IW5UIwpB1e2Vunm9QyKvUHWcVwGfLjhpOajuR7oAoGCCqGSM49 +AwEHoUQDQgAENM4HcjLONh8oKnYdbL4/dAfGF0FPgBQZiDBOJISPmmG6Y8hqEU1w +5Jam03RTOVm8l00wyNWasVgr8WMXhpuQ0g== +-----END EC PRIVATE KEY----- diff --git a/_dev/boring/rsa.pem b/_dev/boring/rsa.pem new file mode 100644 index 0000000..f5cc584 --- /dev/null +++ b/_dev/boring/rsa.pem @@ -0,0 +1,45 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA1DHcIM3SThFqy8nAkPQFX0E7ph8jqh8EATXryjKHGuVjR3Xh +OQ0BSPoJxyfdg/VEwevFrtmZAfz0WCbxvP2SVCmf7oobg4V2KPSo3nNt9vlBFUne +RtIyHRQ8YRnGSWaRHzJbX6ffltnG2aD+8qUfk161rdZgxBA9G0Ga47IkwQhT2Hqu +H3dW2Uu4W2WMyt6gX/tdyEAV57MOPcoceknr7Nb2kfiuDPR7h6wFrW3I6eoj8oX2 +SkIOuVNt1Z31BAUcPJDUjqopI0o9tolM/7X13M8dEY0OJQVr7FQYDF9JeSYeEMyb +wizjBaHDm48mSghP1o5UssQBbNNC83btXCjiLQIDAQABAoIBACzvGgRAUYaCnbDl +2kdXxUN0luMIuQ6vXrO67WF17bI+XRWm2riwDlObzzJDON9Wsua1vLjYD1SickOw +i4RP1grIfbuPt1/UhT8LAC+LFgA0rBmL+OvaWw5ZWKffQ2QLujN3AG5zKB/Tog43 +z4UmfldAuQxE11zta2M4M0qAUNQnQj1oiuI8RUdG0VvvLw8Htdi1ogH0CI5R669z +NjHt+JV+2gzKx6EX0s8mQL3yXGkC2xXItRbFclyCMJEhPS7QbBu+tru35N6WpzAq +BCl2Q7LQogvSA6MXuMOx6CyuExVfgmhbfeoheLE8gmXwl0Y37n/g6ZBZFAtpCjcs +UckPv0ECgYEA1orl7RwgIsZljMap6vWtMGoRIHKmT91DGpMmkh4suZe+yAk85maU +49Vd+8ZfIN41AH37yrsGOcPHgz5o5QufELpoub6DCsQ7u9F1vQp55cp+qyBWzAgz +b/xUuVnIyv3kLan3fpk7ZGCBXFBpLG0QXMFOHtda3Mlk5SmuoEYaYRkCgYEA/TLR +u4neKqyqwsqMuRJGC1iKFVmfCjZeNMtPNbTWpdqez/vvT8APnEpIumUGt8YROLGZ +8biUr5/ViOkmaP3wmQbO9m2/cE01lMTYv75w1cw2KVQe6kAHJkOx+JEx9xg53RJ/ +QlFtG5MQUy2599Gxp8BMGaXLH5yo4qwvNvY6CDUCgYEArxr7AwX7rKZlZ/sV4HHY +gzVu+R7aY0DibiRATO5X7rrNuhLgI+UCDNqvNLn6FqeGdvpcsmDneeozQwmDL77G +ey7KHyBBcF4tquQQxtRwHX+i1yUz8p+W7AX1WLrRSezjeenJ2QhUE1849hGjZeE2 +g546lq2Kub2enfPhVWsiSLECgYEA72T5QCPeVuLioUH5Q5Kvf1K7W+xcnr9A2xHP +Vqwgtre5qFQ/tFuXZuIlWXbjnyY6aiwhrZYjntm0f7pRgrt2nHj/fafOdVPK8Voc +xU4+SSbHntPWVw0qtVcUEjzVzRauvwMaJ43tZ0DpEnwNdO5i1oTObwF+x+jLFWZP +TdwIinECgYBzjZeCxxOMk5SlPpTsLUtgC+q3m1AavXhUVNEPP2gKMOIPTETPbhbG +LBxB2vVbJiS3J7itQy8gceT89O0vSEZnaTPXiM/Ws1QbkBJ8yW7KI7X4WuzN4Imq +/cLBRXLb8R328U27YyQFNGMjr2tX/+vx5FulJjSloWMRNuFWUngv7w== +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIC+jCCAeKgAwIBAgIRANBDimJ/ww2tz77qcYIhuZowDQYJKoZIhvcNAQELBQAw +EjEQMA4GA1UEChMHQWNtZSBDbzAeFw0xNjA5MjQxNzI5MTlaFw0yNjA5MjIxNzI5 +MTlaMBIxEDAOBgNVBAoTB0FjbWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQDUMdwgzdJOEWrLycCQ9AVfQTumHyOqHwQBNevKMoca5WNHdeE5DQFI ++gnHJ92D9UTB68Wu2ZkB/PRYJvG8/ZJUKZ/uihuDhXYo9Kjec232+UEVSd5G0jId +FDxhGcZJZpEfMltfp9+W2cbZoP7ypR+TXrWt1mDEED0bQZrjsiTBCFPYeq4fd1bZ +S7hbZYzK3qBf+13IQBXnsw49yhx6Sevs1vaR+K4M9HuHrAWtbcjp6iPyhfZKQg65 +U23VnfUEBRw8kNSOqikjSj22iUz/tfXczx0RjQ4lBWvsVBgMX0l5Jh4QzJvCLOMF +ocObjyZKCE/WjlSyxAFs00Lzdu1cKOItAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIF +oDATBgNVHSUEDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuC +CWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAygPV4enmvwSuMd1JarxOXpOK +Z4Nsk7EKlfCPgzxQUOkFdLIr5ZG1kUkQt/omzTmoIWjLAsoYzT0ZCPOrioczKsWj +MceFUIkT0w+eIl+8DzauPy34o8rjcApglF165UG3iphlpI+jdPzv5TBarUAbwsFb +ClMLEiNJQ0OMxAIaRtb2RehD4q3OWlpWf6joJ36PRBqL8T5+f2x6Tg3c64UR+QPX +98UcCQHHdEhm7y2z5Z2Wt0B48tZ+UAxDEoEwMghNyw7wUD79IRlXGYypBnXaMuLX +46aGxbsSQ7Rfg62Co3JG7vo+eJd0AoZHrtFUnfM8V70IFzMBZnSwRslHRJe56Q== +-----END CERTIFICATE----- diff --git a/_dev/boring/server.sh b/_dev/boring/server.sh new file mode 100755 index 0000000..39ff199 --- /dev/null +++ b/_dev/boring/server.sh @@ -0,0 +1,17 @@ +#!/bin/sh +PATH=/boringssl/build/tool:$PATH +set -x + +# RSA +bssl server \ + -key rsa.pem \ + -min-version tls1.2 -max-version tls1.3 \ + -accept 1443 -loop -www 2>&1 & + +# ECDSA +bssl server \ + -key ecdsa.pem \ + -min-version tls1.2 -max-version tls1.3 \ + -accept 2443 -loop -www 2>&1 & + +wait diff --git a/_dev/interop.sh b/_dev/interop.sh index 8512b6b..9478bf8 100755 --- a/_dev/interop.sh +++ b/_dev/interop.sh @@ -2,6 +2,7 @@ set -xeuo pipefail if [ "$1" = "INSTALL" ]; then + # INSTALL [] if [ -n "${3:-}" ]; then REVISION="--build-arg REVISION=$3" else @@ -10,6 +11,7 @@ if [ "$1" = "INSTALL" ]; then docker build $REVISION -t tls-tris:$2 _dev/$2 elif [ "$1" = "RUN" ]; then + # RUN IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' tris-localserver) docker run --rm tls-tris:$2 $IP:1443 | tee output.txt # RSA @@ -21,6 +23,7 @@ elif [ "$1" = "RUN" ]; then grep "Hello TLS 1.3" output.txt | grep "resumed" | grep -v "0-RTT" elif [ "$1" = "0-RTT" ]; then + # 0-RTT IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' tris-localserver) docker run --rm tls-tris:$2 $IP:3443 | tee output.txt # rejecting 0-RTT @@ -32,4 +35,24 @@ elif [ "$1" = "0-RTT" ]; then docker run --rm tls-tris:$2 $IP:5443 | tee output.txt # confirming 0-RTT grep "Hello TLS 1.3" output.txt | grep "resumed" | grep "0-RTT confirmed" +elif [ "$1" = "INSTALL-CLIENT" ]; then + cd "$(dirname "$0")/tris-testclient" + ./build.sh + +elif [ "$1" = "RUN-CLIENT" ]; then + # RUN-CLIENT + cd "$(dirname "$0")/tris-testclient" + + servername="$2-localserver" + docker run --rm --detach --name "$servername" \ + --entrypoint /server.sh \ + --expose 1443 --expose 2443 \ + tls-tris:$2 + IP=$(docker inspect -f '{{ .NetworkSettings.IPAddress }}' "$servername") + # Obtain information and stop server on exit + trap 'docker ps -a; docker logs "$servername"; docker kill "$servername"' EXIT + + docker run --rm tris-testclient -ecdsa=false $IP:1443 # RSA + docker run --rm tris-testclient -rsa=false $IP:2443 # ECDSA + # TODO maybe check server logs for expected output? fi diff --git a/_dev/tris-testclient/Dockerfile b/_dev/tris-testclient/Dockerfile new file mode 100644 index 0000000..c4ef2fb --- /dev/null +++ b/_dev/tris-testclient/Dockerfile @@ -0,0 +1,7 @@ +FROM scratch + +ENV TLSDEBUG error + +ADD tris-testclient / + +ENTRYPOINT ["/tris-testclient"] diff --git a/_dev/tris-testclient/build.sh b/_dev/tris-testclient/build.sh new file mode 100755 index 0000000..b6d2e6a --- /dev/null +++ b/_dev/tris-testclient/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh +set -e +cd "$(dirname "$0")" +GOOS=linux ../go.sh build -v -i . +docker build -t tris-testclient . diff --git a/_dev/tris-testclient/client.go b/_dev/tris-testclient/client.go new file mode 100644 index 0000000..05c44f8 --- /dev/null +++ b/_dev/tris-testclient/client.go @@ -0,0 +1,121 @@ +package main + +import ( + "crypto/tls" + "flag" + "fmt" + "io" + "log" + "os" + "strings" +) + +var tlsVersionToName = map[uint16]string{ + tls.VersionTLS10: "1.0", + tls.VersionTLS11: "1.1", + tls.VersionTLS12: "1.2", + tls.VersionTLS13: "1.3", + tls.VersionTLS13Draft18: "1.3 (draft 18)", +} + +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", +} + +type Client struct { + KeyLogWriter io.Writer + failed uint +} + +func (c *Client) run(addr string, version, cipherSuite uint16) { + fmt.Printf("TLS %s with %s\n", tlsVersionToName[version], cipherSuiteIdToName[cipherSuite]) + tls_config := &tls.Config{ + InsecureSkipVerify: true, + MinVersion: version, + MaxVersion: version, + CipherSuites: []uint16{cipherSuite}, + KeyLogWriter: c.KeyLogWriter, + } + con, err := tls.Dial("tcp", addr, tls_config) + if err != nil { + fmt.Printf("handshake failed: %v\n\n", err) + c.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) + c.failed++ + return + } + + buf := make([]byte, 1024) + n, err := con.Read(buf) + if err != nil { + fmt.Printf("Read failed: %v\n\n", err) + c.failed++ + return + } + fmt.Printf("Read %d bytes\n", n) + + fmt.Println("OK\n") +} + +func main() { + var keylog_file string + var enable_rsa, enable_ecdsa 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.Parse() + if flag.NArg() != 1 { + flag.Usage() + os.Exit(1) + } + addr := flag.Arg(0) + if !strings.Contains(addr, ":") { + addr += ":443" + } + + client := Client{} + 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) + } + client.KeyLogWriter = keylog_writer + log.Println("Enabled keylog") + } + + if enable_rsa { + // Sanity check: TLS 1.2 with the mandatory cipher suite from RFC 5246 + client.run(addr, tls.VersionTLS12, tls.TLS_RSA_WITH_AES_128_CBC_SHA) + } + if enable_ecdsa { + // Sane cipher suite for TLS 1.2 with an ECDSA cert (as used by boringssl) + client.run(addr, tls.VersionTLS12, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256) + } + + client.run(addr, tls.VersionTLS13, tls.TLS_CHACHA20_POLY1305_SHA256) + client.run(addr, tls.VersionTLS13, tls.TLS_AES_128_GCM_SHA256) + client.run(addr, tls.VersionTLS13, tls.TLS_AES_256_GCM_SHA384) + + // TODO test with client cert + // TODO test other kex methods besides X25519, like MTI secp256r1 + // TODO limit supported groups? + + if client.failed > 0 { + log.Fatalf("Failed handshakes: %d\n", client.failed) + } else { + fmt.Println("All handshakes passed") + } +}