golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.73k stars 17.63k forks source link

crypto/tls: Client Hello is always sent in 2 TCP frames if GODEBUG=tlskyber=1 #70047

Open mmatczuk opened 2 days ago

mmatczuk commented 2 days ago

Go version

go1.23

Output of go env in your module/workspace:

GO111MODULE='on'
GOARCH='arm64'
GOBIN=''
GOCACHE='/Users/michal/Library/Caches/go-build'
GOENV='/Users/michal/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMODCACHE='/Users/michal/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/michal/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/opt/homebrew/opt/go/libexec'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/opt/homebrew/opt/go/libexec/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.23.1'
GODEBUG=''
GOTELEMETRY='local'
GOTELEMETRYDIR='/Users/michal/Library/Application Support/go/telemetry'
GCCGO='gccgo'
GOARM64='v8.0'
AR='ar'
CC='cc'
CXX='c++'
CGO_ENABLED='1'
GOMOD='/dev/null'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/7q/nyynjpwj5p19npby48ykjpx00000gn/T/go-build3894506068=/tmp/go-build -gno-record-gcc-switches -fno-common'

What did you do?

I'm openning TLS connection with very basic TLS config attached below

    return &tls.Config{
        MinVersion: tls.VersionTLS13,
    }

What did you see happen?

I see that the Client Hello is constructed from two frames even if I do not intend to send any ECH data.

Frame 6494: 113 bytes on wire (904 bits), 113 bytes captured (904 bits) on interface en0, id 0
Ethernet II 
Internet Protocol Version 4 
Transmission Control Protocol, Src Port: 59002, Dst Port: 443, Seq: 1449, Ack: 1, Len: 47
[2 Reassembled TCP Segments (1495 bytes): #6493(1448), #6494(47)]
    [Frame: 6493, payload: 0-1447 (1448 bytes)]
    [Frame: 6494, payload: 1448-1494 (47 bytes)]
    [Segment count: 2]
    [Reassembled TCP length: 1495]
    [Reassembled TCP Data [truncated]: 16030105d2010005ce03038f7550981f43ec10487301e7bef64e55896c65323cb911be2c9b6287652635212073b17b871f9f6acf58da2436a4ae96ffa1721bdc3b293ecd89a2659f8ed9ce8c00061301130213030100057f0000002c002a0000276d616b69343]
Transport Layer Security
    TLSv1.3 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 1490
        Handshake Protocol: Client Hello

What did you expect to see?

Running the same code with Go 1.22 sends Client Hello in a single frame

Frame 3168: 340 bytes on wire (2720 bits), 340 bytes captured (2720 bits) on interface en0, id 0
Ethernet II
Internet Protocol Version 4
Transmission Control Protocol, Src Port: 58849, Dst Port: 443, Seq: 1, Ack: 1, Len: 274
Transport Layer Security
    TLSv1.3 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.0 (0x0301)
        Length: 269
        Handshake Protocol: Client Hello
gabyhelp commented 2 days ago

Related Issues and Documentation

(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)

mmatczuk commented 2 days ago

This makes PaloAlto reject connection due to ssl_partial_client_hello_incomplete as described here https://knowledgebase.paloaltonetworks.com/KCSArticleDetail?id=kA14u000000kI0RCAU

mateusz834 commented 2 days ago

I would say that this is caused by X25519Kyber768Draft00. See #67061.

CC @golang/security @FiloSottile

mmatczuk commented 2 days ago

I can confirm that setting GODEBUG=tlskyber=0 sends Client Hello in a single frame.

rolandshoemaker commented 2 days ago

There is not really much we can do about this. PQ schemes are, generally, large, and using them is likely to cause a client hello to span more than one TCP packet. TLS makes no guarantee that the hello will be a single packet, so this seems like solely a PaloAlto issue. As noted, you can avoid this by disabling the Kyber KEX.

This is a semi-common problem: https://tldr.fail/.