mirror of
https://github.com/refraction-networking/utls.git
synced 2025-04-03 03:57:36 +03:00
To disable TLS 1.3, simply remove VersionTLS13 from supportedVersions, as tested by TestEscapeRoute, and amend documentation. To make it opt-in, revert the change to (*Config).supportedVersions from this CL. I did not have the heart to implement the early data skipping feature when I realized that it did not offer a choice between two abstraction-breaking options, but demanded them both (look for handshake type in case of HelloRetryRequest, trial decryption otherwise). It's a lot of complexity for an apparently small gain, but if anyone has strong opinions about it let me know. Note that in TLS 1.3 alerts are encrypted, so the close_notify peeking to return (n > 0, io.EOF) from Read doesn't work. If we are lucky, those servers that unexpectedly close connections after serving a single request will have stopped (maybe thanks to H/2) before they got updated to TLS 1.3. Relatedly, session tickets are now provisioned on the client first Read instead of at Handshake time, because they are, well, post-handshake messages. If this proves to be a problem we might try to peek at them. Doubled the tests that cover logic that's different in TLS 1.3. The benchmarks for TLS 1.2 compared to be0f3c286b5 (before TLS 1.3 and its refactors, after CL 142817 changed them to use real connections) show little movement. name old time/op new time/op delta HandshakeServer/RSA-8 795µs ± 1% 798µs ± 1% ~ (p=0.057 n=10+18) HandshakeServer/ECDHE-P256-RSA-8 903µs ± 0% 909µs ± 1% +0.68% (p=0.000 n=8+17) HandshakeServer/ECDHE-P256-ECDSA-P256-8 198µs ± 0% 204µs ± 1% +3.24% (p=0.000 n=9+18) HandshakeServer/ECDHE-X25519-ECDSA-P256-8 202µs ± 3% 208µs ± 1% +2.98% (p=0.000 n=9+20) HandshakeServer/ECDHE-P521-ECDSA-P521-8 15.5ms ± 1% 15.9ms ± 2% +2.49% (p=0.000 n=10+20) Throughput/MaxPacket/1MB-8 5.81ms ±23% 6.14ms ±44% ~ (p=0.605 n=8+18) Throughput/MaxPacket/2MB-8 8.91ms ±22% 8.74ms ±33% ~ (p=0.498 n=9+19) Throughput/MaxPacket/4MB-8 12.8ms ± 3% 14.0ms ±10% +9.74% (p=0.000 n=10+17) Throughput/MaxPacket/8MB-8 25.1ms ± 7% 24.6ms ±16% ~ (p=0.129 n=9+19) Throughput/MaxPacket/16MB-8 46.3ms ± 4% 45.9ms ±12% ~ (p=0.340 n=9+20) Throughput/MaxPacket/32MB-8 88.5ms ± 4% 86.0ms ± 4% -2.82% (p=0.004 n=10+20) Throughput/MaxPacket/64MB-8 173ms ± 2% 167ms ± 7% -3.42% (p=0.001 n=10+19) Throughput/DynamicPacket/1MB-8 5.88ms ± 4% 6.59ms ±64% ~ (p=0.232 n=9+18) Throughput/DynamicPacket/2MB-8 9.08ms ±12% 8.73ms ±21% ~ (p=0.408 n=10+18) Throughput/DynamicPacket/4MB-8 14.2ms ± 5% 14.0ms ±11% ~ (p=0.188 n=9+19) Throughput/DynamicPacket/8MB-8 25.1ms ± 6% 24.0ms ± 7% -4.39% (p=0.000 n=10+18) Throughput/DynamicPacket/16MB-8 45.6ms ± 3% 43.3ms ± 1% -5.22% (p=0.000 n=10+8) Throughput/DynamicPacket/32MB-8 88.4ms ± 3% 84.8ms ± 2% -4.06% (p=0.000 n=10+10) Throughput/DynamicPacket/64MB-8 175ms ± 3% 167ms ± 2% -4.63% (p=0.000 n=10+10) Latency/MaxPacket/200kbps-8 694ms ± 0% 694ms ± 0% -0.02% (p=0.000 n=9+9) Latency/MaxPacket/500kbps-8 279ms ± 0% 279ms ± 0% -0.09% (p=0.000 n=10+10) Latency/MaxPacket/1000kbps-8 140ms ± 0% 140ms ± 0% -0.15% (p=0.000 n=10+9) Latency/MaxPacket/2000kbps-8 71.1ms ± 0% 71.0ms ± 0% -0.09% (p=0.001 n=8+9) Latency/MaxPacket/5000kbps-8 30.5ms ± 6% 30.1ms ± 6% ~ (p=0.905 n=10+9) Latency/DynamicPacket/200kbps-8 134ms ± 0% 134ms ± 0% ~ (p=0.796 n=9+9) Latency/DynamicPacket/500kbps-8 54.8ms ± 0% 54.7ms ± 0% -0.18% (p=0.000 n=8+10) Latency/DynamicPacket/1000kbps-8 28.5ms ± 0% 29.1ms ± 8% ~ (p=0.173 n=8+10) Latency/DynamicPacket/2000kbps-8 15.3ms ± 6% 15.9ms ±10% ~ (p=0.905 n=9+10) Latency/DynamicPacket/5000kbps-8 9.14ms ±21% 9.65ms ±82% ~ (p=0.529 n=10+10) name old speed new speed delta Throughput/MaxPacket/1MB-8 175MB/s ±13% 167MB/s ±64% ~ (p=0.646 n=7+20) Throughput/MaxPacket/2MB-8 241MB/s ±25% 241MB/s ±40% ~ (p=0.660 n=9+20) Throughput/MaxPacket/4MB-8 328MB/s ± 3% 300MB/s ± 9% -8.70% (p=0.000 n=10+17) Throughput/MaxPacket/8MB-8 335MB/s ± 7% 340MB/s ±17% ~ (p=0.212 n=9+20) Throughput/MaxPacket/16MB-8 363MB/s ± 4% 367MB/s ±11% ~ (p=0.340 n=9+20) Throughput/MaxPacket/32MB-8 379MB/s ± 4% 390MB/s ± 4% +2.93% (p=0.004 n=10+20) Throughput/MaxPacket/64MB-8 388MB/s ± 2% 401MB/s ± 7% +3.25% (p=0.004 n=10+20) Throughput/DynamicPacket/1MB-8 178MB/s ± 4% 157MB/s ±73% ~ (p=0.127 n=9+20) Throughput/DynamicPacket/2MB-8 232MB/s ±11% 243MB/s ±18% ~ (p=0.415 n=10+18) Throughput/DynamicPacket/4MB-8 296MB/s ± 5% 299MB/s ±15% ~ (p=0.295 n=9+20) Throughput/DynamicPacket/8MB-8 334MB/s ± 6% 350MB/s ± 7% +4.58% (p=0.000 n=10+18) Throughput/DynamicPacket/16MB-8 368MB/s ± 3% 388MB/s ± 1% +5.48% (p=0.000 n=10+8) Throughput/DynamicPacket/32MB-8 380MB/s ± 3% 396MB/s ± 2% +4.20% (p=0.000 n=10+10) Throughput/DynamicPacket/64MB-8 384MB/s ± 3% 403MB/s ± 2% +4.83% (p=0.000 n=10+10) Comparing TLS 1.2 and TLS 1.3 at tip shows a slight (~5-10%) slowdown of handshakes, which might be worth looking at next cycle, but the latency improvements are expected to overshadow that. name old time/op new time/op delta HandshakeServer/ECDHE-P256-RSA-8 909µs ± 1% 963µs ± 0% +5.87% (p=0.000 n=17+18) HandshakeServer/ECDHE-P256-ECDSA-P256-8 204µs ± 1% 225µs ± 2% +10.20% (p=0.000 n=18+20) HandshakeServer/ECDHE-X25519-ECDSA-P256-8 208µs ± 1% 230µs ± 2% +10.35% (p=0.000 n=20+18) HandshakeServer/ECDHE-P521-ECDSA-P521-8 15.9ms ± 2% 15.9ms ± 1% ~ (p=0.444 n=20+19) Throughput/MaxPacket/1MB-8 6.14ms ±44% 7.07ms ±46% ~ (p=0.057 n=18+19) Throughput/MaxPacket/2MB-8 8.74ms ±33% 8.61ms ± 9% ~ (p=0.552 n=19+17) Throughput/MaxPacket/4MB-8 14.0ms ±10% 14.1ms ±12% ~ (p=0.707 n=17+20) Throughput/MaxPacket/8MB-8 24.6ms ±16% 25.6ms ±14% ~ (p=0.107 n=19+20) Throughput/MaxPacket/16MB-8 45.9ms ±12% 44.7ms ± 6% ~ (p=0.607 n=20+19) Throughput/MaxPacket/32MB-8 86.0ms ± 4% 87.9ms ± 8% ~ (p=0.113 n=20+19) Throughput/MaxPacket/64MB-8 167ms ± 7% 169ms ± 2% +1.26% (p=0.011 n=19+19) Throughput/DynamicPacket/1MB-8 6.59ms ±64% 6.79ms ±43% ~ (p=0.480 n=18+19) Throughput/DynamicPacket/2MB-8 8.73ms ±21% 9.58ms ±13% +9.71% (p=0.006 n=18+20) Throughput/DynamicPacket/4MB-8 14.0ms ±11% 13.9ms ±10% ~ (p=0.687 n=19+20) Throughput/DynamicPacket/8MB-8 24.0ms ± 7% 24.6ms ± 8% +2.36% (p=0.045 n=18+17) Throughput/DynamicPacket/16MB-8 43.3ms ± 1% 44.3ms ± 2% +2.48% (p=0.001 n=8+9) Throughput/DynamicPacket/32MB-8 84.8ms ± 2% 86.7ms ± 2% +2.27% (p=0.000 n=10+10) Throughput/DynamicPacket/64MB-8 167ms ± 2% 170ms ± 3% +1.89% (p=0.005 n=10+10) Latency/MaxPacket/200kbps-8 694ms ± 0% 699ms ± 0% +0.65% (p=0.000 n=9+10) Latency/MaxPacket/500kbps-8 279ms ± 0% 280ms ± 0% +0.68% (p=0.000 n=10+10) Latency/MaxPacket/1000kbps-8 140ms ± 0% 141ms ± 0% +0.59% (p=0.000 n=9+9) Latency/MaxPacket/2000kbps-8 71.0ms ± 0% 71.3ms ± 0% +0.42% (p=0.000 n=9+9) Latency/MaxPacket/5000kbps-8 30.1ms ± 6% 30.7ms ±10% +1.93% (p=0.019 n=9+9) Latency/DynamicPacket/200kbps-8 134ms ± 0% 138ms ± 0% +3.22% (p=0.000 n=9+10) Latency/DynamicPacket/500kbps-8 54.7ms ± 0% 56.3ms ± 0% +3.03% (p=0.000 n=10+8) Latency/DynamicPacket/1000kbps-8 29.1ms ± 8% 29.1ms ± 0% ~ (p=0.173 n=10+8) Latency/DynamicPacket/2000kbps-8 15.9ms ±10% 16.4ms ±36% ~ (p=0.633 n=10+8) Latency/DynamicPacket/5000kbps-8 9.65ms ±82% 8.32ms ± 8% ~ (p=0.573 n=10+8) name old speed new speed delta Throughput/MaxPacket/1MB-8 167MB/s ±64% 155MB/s ±55% ~ (p=0.224 n=20+19) Throughput/MaxPacket/2MB-8 241MB/s ±40% 244MB/s ± 9% ~ (p=0.407 n=20+17) Throughput/MaxPacket/4MB-8 300MB/s ± 9% 298MB/s ±11% ~ (p=0.707 n=17+20) Throughput/MaxPacket/8MB-8 340MB/s ±17% 330MB/s ±13% ~ (p=0.201 n=20+20) Throughput/MaxPacket/16MB-8 367MB/s ±11% 375MB/s ± 5% ~ (p=0.607 n=20+19) Throughput/MaxPacket/32MB-8 390MB/s ± 4% 382MB/s ± 8% ~ (p=0.113 n=20+19) Throughput/MaxPacket/64MB-8 401MB/s ± 7% 397MB/s ± 2% -0.96% (p=0.030 n=20+19) Throughput/DynamicPacket/1MB-8 157MB/s ±73% 156MB/s ±39% ~ (p=0.738 n=20+20) Throughput/DynamicPacket/2MB-8 243MB/s ±18% 220MB/s ±14% -9.65% (p=0.006 n=18+20) Throughput/DynamicPacket/4MB-8 299MB/s ±15% 303MB/s ± 9% ~ (p=0.512 n=20+20) Throughput/DynamicPacket/8MB-8 350MB/s ± 7% 342MB/s ± 8% -2.27% (p=0.045 n=18+17) Throughput/DynamicPacket/16MB-8 388MB/s ± 1% 378MB/s ± 2% -2.41% (p=0.001 n=8+9) Throughput/DynamicPacket/32MB-8 396MB/s ± 2% 387MB/s ± 2% -2.21% (p=0.000 n=10+10) Throughput/DynamicPacket/64MB-8 403MB/s ± 2% 396MB/s ± 3% -1.84% (p=0.005 n=10+10) Fixes #9671 Change-Id: Ieb57c5140eb2c083b8be0d42b240cd2eeec0dcf6 Reviewed-on: https://go-review.googlesource.com/c/147638 Run-TryBot: Filippo Valsorda <filippo@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Adam Langley <agl@golang.org>
856 lines
25 KiB
Go
856 lines
25 KiB
Go
// Copyright 2018 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package tls
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto"
|
|
"crypto/hmac"
|
|
"crypto/rsa"
|
|
"errors"
|
|
"fmt"
|
|
"hash"
|
|
"io"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
// maxClientPSKIdentities is the number of client PSK identities the server will
|
|
// attempt to validate. It will ignore the rest not to let cheap ClientHello
|
|
// messages cause too much work in session ticket decryption attempts.
|
|
const maxClientPSKIdentities = 5
|
|
|
|
type serverHandshakeStateTLS13 struct {
|
|
c *Conn
|
|
clientHello *clientHelloMsg
|
|
hello *serverHelloMsg
|
|
sentDummyCCS bool
|
|
usingPSK bool
|
|
suite *cipherSuiteTLS13
|
|
cert *Certificate
|
|
sigAlg SignatureScheme
|
|
earlySecret []byte
|
|
sharedKey []byte
|
|
handshakeSecret []byte
|
|
masterSecret []byte
|
|
trafficSecret []byte // client_application_traffic_secret_0
|
|
transcript hash.Hash
|
|
clientFinished []byte
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) handshake() error {
|
|
c := hs.c
|
|
|
|
// For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2.
|
|
if err := hs.processClientHello(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.checkForResumption(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.pickCertificate(); err != nil {
|
|
return err
|
|
}
|
|
c.buffering = true
|
|
if err := hs.sendServerParameters(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.sendServerCertificate(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.sendServerFinished(); err != nil {
|
|
return err
|
|
}
|
|
// Note that at this point we could start sending application data without
|
|
// waiting for the client's second flight, but the application might not
|
|
// expect the lack of replay protection of the ClientHello parameters.
|
|
if _, err := c.flush(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.readClientCertificate(); err != nil {
|
|
return err
|
|
}
|
|
if err := hs.readClientFinished(); err != nil {
|
|
return err
|
|
}
|
|
|
|
atomic.StoreUint32(&c.handshakeStatus, 1)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) processClientHello() error {
|
|
c := hs.c
|
|
|
|
hs.hello = new(serverHelloMsg)
|
|
|
|
// TLS 1.3 froze the ServerHello.legacy_version field, and uses
|
|
// supported_versions instead. See RFC 8446, sections 4.1.3 and 4.2.1.
|
|
hs.hello.vers = VersionTLS12
|
|
hs.hello.supportedVersion = c.vers
|
|
|
|
if len(hs.clientHello.supportedVersions) == 0 {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: client used the legacy version field to negotiate TLS 1.3")
|
|
}
|
|
|
|
// Abort if the client is doing a fallback and landing lower than what we
|
|
// support. See RFC 7507, which however does not specify the interaction
|
|
// with supported_versions. The only difference is that with
|
|
// supported_versions a client has a chance to attempt a [TLS 1.2, TLS 1.4]
|
|
// handshake in case TLS 1.3 is broken but 1.2 is not. Alas, in that case,
|
|
// it will have to drop the TLS_FALLBACK_SCSV protection if it falls back to
|
|
// TLS 1.2, because a TLS 1.3 server would abort here. The situation before
|
|
// supported_versions was not better because there was just no way to do a
|
|
// TLS 1.4 handshake without risking the server selecting TLS 1.3.
|
|
for _, id := range hs.clientHello.cipherSuites {
|
|
if id == TLS_FALLBACK_SCSV {
|
|
// Use c.vers instead of max(supported_versions) because an attacker
|
|
// could defeat this by adding an arbitrary high version otherwise.
|
|
if c.vers < c.config.maxSupportedVersion(false) {
|
|
c.sendAlert(alertInappropriateFallback)
|
|
return errors.New("tls: client using inappropriate protocol fallback")
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(hs.clientHello.compressionMethods) != 1 ||
|
|
hs.clientHello.compressionMethods[0] != compressionNone {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: TLS 1.3 client supports illegal compression methods")
|
|
}
|
|
|
|
hs.hello.random = make([]byte, 32)
|
|
if _, err := io.ReadFull(c.config.rand(), hs.hello.random); err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
|
|
if len(hs.clientHello.secureRenegotiation) != 0 {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: initial handshake had non-empty renegotiation extension")
|
|
}
|
|
|
|
if hs.clientHello.earlyData {
|
|
// See RFC 8446, Section 4.2.10 for the complicated behavior required
|
|
// here. The scenario is that a different server at our address offered
|
|
// to accept early data in the past, which we can't handle. For now, all
|
|
// 0-RTT enabled session tickets need to expire before a Go server can
|
|
// replace a server or join a pool. That's the same requirement that
|
|
// applies to mixing or replacing with any TLS 1.2 server.
|
|
c.sendAlert(alertUnsupportedExtension)
|
|
return errors.New("tls: client sent unexpected early data")
|
|
}
|
|
|
|
hs.hello.sessionId = hs.clientHello.sessionId
|
|
hs.hello.compressionMethod = compressionNone
|
|
|
|
var preferenceList, supportedList []uint16
|
|
if c.config.PreferServerCipherSuites {
|
|
preferenceList = defaultCipherSuitesTLS13()
|
|
supportedList = hs.clientHello.cipherSuites
|
|
} else {
|
|
preferenceList = hs.clientHello.cipherSuites
|
|
supportedList = defaultCipherSuitesTLS13()
|
|
}
|
|
for _, suiteID := range preferenceList {
|
|
hs.suite = mutualCipherSuiteTLS13(supportedList, suiteID)
|
|
if hs.suite != nil {
|
|
break
|
|
}
|
|
}
|
|
if hs.suite == nil {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: no cipher suite supported by both client and server")
|
|
}
|
|
c.cipherSuite = hs.suite.id
|
|
hs.hello.cipherSuite = hs.suite.id
|
|
hs.transcript = hs.suite.hash.New()
|
|
|
|
// Pick the ECDHE group in server preference order, but give priority to
|
|
// groups with a key share, to avoid a HelloRetryRequest round-trip.
|
|
var selectedGroup CurveID
|
|
var clientKeyShare *keyShare
|
|
GroupSelection:
|
|
for _, preferredGroup := range c.config.curvePreferences() {
|
|
for _, ks := range hs.clientHello.keyShares {
|
|
if ks.group == preferredGroup {
|
|
selectedGroup = ks.group
|
|
clientKeyShare = &ks
|
|
break GroupSelection
|
|
}
|
|
}
|
|
if selectedGroup != 0 {
|
|
continue
|
|
}
|
|
for _, group := range hs.clientHello.supportedCurves {
|
|
if group == preferredGroup {
|
|
selectedGroup = group
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if selectedGroup == 0 {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: no ECDHE curve supported by both client and server")
|
|
}
|
|
if clientKeyShare == nil {
|
|
if err := hs.doHelloRetryRequest(selectedGroup); err != nil {
|
|
return err
|
|
}
|
|
clientKeyShare = &hs.clientHello.keyShares[0]
|
|
}
|
|
|
|
if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && !ok {
|
|
c.sendAlert(alertInternalError)
|
|
return errors.New("tls: CurvePreferences includes unsupported curve")
|
|
}
|
|
params, err := generateECDHEParameters(c.config.rand(), selectedGroup)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
hs.hello.serverShare = keyShare{group: selectedGroup, data: params.PublicKey()}
|
|
hs.sharedKey = params.SharedKey(clientKeyShare.data)
|
|
if hs.sharedKey == nil {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid client key share")
|
|
}
|
|
|
|
c.serverName = hs.clientHello.serverName
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) checkForResumption() error {
|
|
c := hs.c
|
|
|
|
if c.config.SessionTicketsDisabled {
|
|
return nil
|
|
}
|
|
|
|
modeOK := false
|
|
for _, mode := range hs.clientHello.pskModes {
|
|
if mode == pskModeDHE {
|
|
modeOK = true
|
|
break
|
|
}
|
|
}
|
|
if !modeOK {
|
|
return nil
|
|
}
|
|
|
|
if len(hs.clientHello.pskIdentities) != len(hs.clientHello.pskBinders) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid or missing PSK binders")
|
|
}
|
|
if len(hs.clientHello.pskIdentities) == 0 {
|
|
return nil
|
|
}
|
|
|
|
for i, identity := range hs.clientHello.pskIdentities {
|
|
if i >= maxClientPSKIdentities {
|
|
break
|
|
}
|
|
|
|
plaintext, _ := c.decryptTicket(identity.label)
|
|
if plaintext == nil {
|
|
continue
|
|
}
|
|
sessionState := new(sessionStateTLS13)
|
|
if ok := sessionState.unmarshal(plaintext); !ok {
|
|
continue
|
|
}
|
|
|
|
createdAt := time.Unix(int64(sessionState.createdAt), 0)
|
|
if c.config.time().Sub(createdAt) > maxSessionTicketLifetime {
|
|
continue
|
|
}
|
|
|
|
// We don't check the obfuscated ticket age because it's affected by
|
|
// clock skew and it's only a freshness signal useful for shrinking the
|
|
// window for replay attacks, which don't affect us as we don't do 0-RTT.
|
|
|
|
pskSuite := cipherSuiteTLS13ByID(sessionState.cipherSuite)
|
|
if pskSuite == nil || pskSuite.hash != hs.suite.hash {
|
|
continue
|
|
}
|
|
|
|
// PSK connections don't re-establish client certificates, but carry
|
|
// them over in the session ticket. Ensure the presence of client certs
|
|
// in the ticket is consistent with the configured requirements.
|
|
sessionHasClientCerts := len(sessionState.certificate.Certificate) != 0
|
|
needClientCerts := requiresClientCert(c.config.ClientAuth)
|
|
if needClientCerts && !sessionHasClientCerts {
|
|
continue
|
|
}
|
|
if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
|
|
continue
|
|
}
|
|
|
|
psk := hs.suite.expandLabel(sessionState.resumptionSecret, "resumption",
|
|
nil, hs.suite.hash.Size())
|
|
hs.earlySecret = hs.suite.extract(psk, nil)
|
|
binderKey := hs.suite.deriveSecret(hs.earlySecret, resumptionBinderLabel, nil)
|
|
// Clone the transcript in case a HelloRetryRequest was recorded.
|
|
transcript := cloneHash(hs.transcript, hs.suite.hash)
|
|
if transcript == nil {
|
|
c.sendAlert(alertInternalError)
|
|
return errors.New("tls: internal error: failed to clone hash")
|
|
}
|
|
transcript.Write(hs.clientHello.marshalWithoutBinders())
|
|
pskBinder := hs.suite.finishedHash(binderKey, transcript)
|
|
if !hmac.Equal(hs.clientHello.pskBinders[i], pskBinder) {
|
|
c.sendAlert(alertDecryptError)
|
|
return errors.New("tls: invalid PSK binder")
|
|
}
|
|
|
|
if err := c.processCertsFromClient(sessionState.certificate); err != nil {
|
|
return err
|
|
}
|
|
|
|
hs.hello.selectedIdentityPresent = true
|
|
hs.hello.selectedIdentity = uint16(i)
|
|
hs.usingPSK = true
|
|
c.didResume = true
|
|
return nil
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// cloneHash uses the encoding.BinaryMarshaler and encoding.BinaryUnmarshaler
|
|
// interfaces implemented by standard library hashes to clone the state of in
|
|
// to a new instance of h. It returns nil if the operation fails.
|
|
func cloneHash(in hash.Hash, h crypto.Hash) hash.Hash {
|
|
// Recreate the interface to avoid importing encoding.
|
|
type binaryMarshaler interface {
|
|
MarshalBinary() (data []byte, err error)
|
|
UnmarshalBinary(data []byte) error
|
|
}
|
|
marshaler, ok := in.(binaryMarshaler)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
state, err := marshaler.MarshalBinary()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
out := h.New()
|
|
unmarshaler, ok := out.(binaryMarshaler)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
if err := unmarshaler.UnmarshalBinary(state); err != nil {
|
|
return nil
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) pickCertificate() error {
|
|
c := hs.c
|
|
|
|
// Only one of PSK and certificates are used at a time.
|
|
if hs.usingPSK {
|
|
return nil
|
|
}
|
|
|
|
// This implements a very simplistic certificate selection strategy for now:
|
|
// getCertificate delegates to the application Config.GetCertificate, or
|
|
// selects based on the server_name only. If the selected certificate's
|
|
// public key does not match the client signature_algorithms, the handshake
|
|
// is aborted. No attention is given to signature_algorithms_cert, and it is
|
|
// not passed to the application Config.GetCertificate. This will need to
|
|
// improve according to RFC 8446, sections 4.4.2.2 and 4.2.3.
|
|
certificate, err := c.config.getCertificate(clientHelloInfo(c, hs.clientHello))
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
supportedAlgs := signatureSchemesForCertificate(certificate)
|
|
if supportedAlgs == nil {
|
|
c.sendAlert(alertInternalError)
|
|
return fmt.Errorf("tls: unsupported certificate key (%T)", certificate.PrivateKey)
|
|
}
|
|
// Pick signature scheme in client preference order, as the server
|
|
// preference order is not configurable.
|
|
for _, preferredAlg := range hs.clientHello.supportedSignatureAlgorithms {
|
|
if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) {
|
|
hs.sigAlg = preferredAlg
|
|
break
|
|
}
|
|
}
|
|
if hs.sigAlg == 0 {
|
|
c.sendAlert(alertHandshakeFailure)
|
|
return errors.New("tls: client doesn't support selected certificate")
|
|
}
|
|
hs.cert = certificate
|
|
|
|
return nil
|
|
}
|
|
|
|
// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility
|
|
// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4.
|
|
func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
|
|
if hs.sentDummyCCS {
|
|
return nil
|
|
}
|
|
hs.sentDummyCCS = true
|
|
|
|
_, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
|
|
return err
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error {
|
|
c := hs.c
|
|
|
|
// The first ClientHello gets double-hashed into the transcript upon a
|
|
// HelloRetryRequest. See RFC 8446, Section 4.4.1.
|
|
hs.transcript.Write(hs.clientHello.marshal())
|
|
chHash := hs.transcript.Sum(nil)
|
|
hs.transcript.Reset()
|
|
hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
|
hs.transcript.Write(chHash)
|
|
|
|
helloRetryRequest := &serverHelloMsg{
|
|
vers: hs.hello.vers,
|
|
random: helloRetryRequestRandom,
|
|
sessionId: hs.hello.sessionId,
|
|
cipherSuite: hs.hello.cipherSuite,
|
|
compressionMethod: hs.hello.compressionMethod,
|
|
supportedVersion: hs.hello.supportedVersion,
|
|
selectedGroup: selectedGroup,
|
|
}
|
|
|
|
hs.transcript.Write(helloRetryRequest.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
return err
|
|
}
|
|
|
|
msg, err := c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
clientHello, ok := msg.(*clientHelloMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(clientHello, msg)
|
|
}
|
|
|
|
if len(clientHello.keyShares) != 1 || clientHello.keyShares[0].group != selectedGroup {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: client sent invalid key share in second ClientHello")
|
|
}
|
|
|
|
if clientHello.earlyData {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: client indicated early data in second ClientHello")
|
|
}
|
|
|
|
if illegalClientHelloChange(clientHello, hs.clientHello) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: client illegally modified second ClientHello")
|
|
}
|
|
|
|
hs.clientHello = clientHello
|
|
return nil
|
|
}
|
|
|
|
// illegalClientHelloChange returns whether the two ClientHello messages are
|
|
// different, with the exception of the changes allowed before and after a
|
|
// HelloRetryRequest. See RFC 8446, Section 4.1.2.
|
|
func illegalClientHelloChange(ch, ch1 *clientHelloMsg) bool {
|
|
if len(ch.supportedVersions) != len(ch1.supportedVersions) ||
|
|
len(ch.cipherSuites) != len(ch1.cipherSuites) ||
|
|
len(ch.supportedCurves) != len(ch1.supportedCurves) ||
|
|
len(ch.supportedSignatureAlgorithms) != len(ch1.supportedSignatureAlgorithms) ||
|
|
len(ch.supportedSignatureAlgorithmsCert) != len(ch1.supportedSignatureAlgorithmsCert) ||
|
|
len(ch.alpnProtocols) != len(ch1.alpnProtocols) {
|
|
return true
|
|
}
|
|
for i := range ch.supportedVersions {
|
|
if ch.supportedVersions[i] != ch1.supportedVersions[i] {
|
|
return true
|
|
}
|
|
}
|
|
for i := range ch.cipherSuites {
|
|
if ch.cipherSuites[i] != ch1.cipherSuites[i] {
|
|
return true
|
|
}
|
|
}
|
|
for i := range ch.supportedCurves {
|
|
if ch.supportedCurves[i] != ch1.supportedCurves[i] {
|
|
return true
|
|
}
|
|
}
|
|
for i := range ch.supportedSignatureAlgorithms {
|
|
if ch.supportedSignatureAlgorithms[i] != ch1.supportedSignatureAlgorithms[i] {
|
|
return true
|
|
}
|
|
}
|
|
for i := range ch.supportedSignatureAlgorithmsCert {
|
|
if ch.supportedSignatureAlgorithmsCert[i] != ch1.supportedSignatureAlgorithmsCert[i] {
|
|
return true
|
|
}
|
|
}
|
|
for i := range ch.alpnProtocols {
|
|
if ch.alpnProtocols[i] != ch1.alpnProtocols[i] {
|
|
return true
|
|
}
|
|
}
|
|
return ch.vers != ch1.vers ||
|
|
!bytes.Equal(ch.random, ch1.random) ||
|
|
!bytes.Equal(ch.sessionId, ch1.sessionId) ||
|
|
!bytes.Equal(ch.compressionMethods, ch1.compressionMethods) ||
|
|
ch.nextProtoNeg != ch1.nextProtoNeg ||
|
|
ch.serverName != ch1.serverName ||
|
|
ch.ocspStapling != ch1.ocspStapling ||
|
|
!bytes.Equal(ch.supportedPoints, ch1.supportedPoints) ||
|
|
ch.ticketSupported != ch1.ticketSupported ||
|
|
!bytes.Equal(ch.sessionTicket, ch1.sessionTicket) ||
|
|
ch.secureRenegotiationSupported != ch1.secureRenegotiationSupported ||
|
|
!bytes.Equal(ch.secureRenegotiation, ch1.secureRenegotiation) ||
|
|
ch.scts != ch1.scts ||
|
|
!bytes.Equal(ch.cookie, ch1.cookie) ||
|
|
!bytes.Equal(ch.pskModes, ch1.pskModes)
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
|
|
c := hs.c
|
|
|
|
hs.transcript.Write(hs.clientHello.marshal())
|
|
hs.transcript.Write(hs.hello.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
|
return err
|
|
}
|
|
|
|
earlySecret := hs.earlySecret
|
|
if earlySecret == nil {
|
|
earlySecret = hs.suite.extract(nil, nil)
|
|
}
|
|
hs.handshakeSecret = hs.suite.extract(hs.sharedKey,
|
|
hs.suite.deriveSecret(earlySecret, "derived", nil))
|
|
|
|
clientSecret := hs.suite.deriveSecret(hs.handshakeSecret,
|
|
clientHandshakeTrafficLabel, hs.transcript)
|
|
c.in.setTrafficSecret(hs.suite, clientSecret)
|
|
serverSecret := hs.suite.deriveSecret(hs.handshakeSecret,
|
|
serverHandshakeTrafficLabel, hs.transcript)
|
|
c.out.setTrafficSecret(hs.suite, serverSecret)
|
|
|
|
err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.clientHello.random, clientSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.clientHello.random, serverSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
|
|
encryptedExtensions := new(encryptedExtensionsMsg)
|
|
|
|
if len(hs.clientHello.alpnProtocols) > 0 {
|
|
if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback {
|
|
encryptedExtensions.alpnProtocol = selectedProto
|
|
c.clientProtocol = selectedProto
|
|
}
|
|
}
|
|
|
|
hs.transcript.Write(encryptedExtensions.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, encryptedExtensions.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) requestClientCert() bool {
|
|
return hs.c.config.ClientAuth >= RequestClientCert && !hs.usingPSK
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) sendServerCertificate() error {
|
|
c := hs.c
|
|
|
|
// Only one of PSK and certificates are used at a time.
|
|
if hs.usingPSK {
|
|
return nil
|
|
}
|
|
|
|
if hs.requestClientCert() {
|
|
// Request a client certificate
|
|
certReq := new(certificateRequestMsgTLS13)
|
|
certReq.ocspStapling = true
|
|
certReq.scts = true
|
|
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms
|
|
if c.config.ClientCAs != nil {
|
|
certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
|
|
}
|
|
|
|
hs.transcript.Write(certReq.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
certMsg := new(certificateMsgTLS13)
|
|
|
|
certMsg.certificate = *hs.cert
|
|
certMsg.scts = hs.clientHello.scts && len(hs.cert.SignedCertificateTimestamps) > 0
|
|
certMsg.ocspStapling = hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0
|
|
|
|
hs.transcript.Write(certMsg.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
certVerifyMsg := new(certificateVerifyMsg)
|
|
certVerifyMsg.hasSignatureAlgorithm = true
|
|
certVerifyMsg.signatureAlgorithm = hs.sigAlg
|
|
|
|
sigType := signatureFromSignatureScheme(hs.sigAlg)
|
|
sigHash, err := hashFromSignatureScheme(hs.sigAlg)
|
|
if sigType == 0 || err != nil {
|
|
// getCertificate returned a certificate incompatible with the
|
|
// ClientHello supported signature algorithms.
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
h := sigHash.New()
|
|
writeSignedMessage(h, serverSignatureContext, hs.transcript)
|
|
|
|
signOpts := crypto.SignerOpts(sigHash)
|
|
if sigType == signatureRSAPSS {
|
|
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
|
|
}
|
|
sig, err := hs.cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), h.Sum(nil), signOpts)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return errors.New("tls: failed to sign handshake: " + err.Error())
|
|
}
|
|
certVerifyMsg.signature = sig
|
|
|
|
hs.transcript.Write(certVerifyMsg.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) sendServerFinished() error {
|
|
c := hs.c
|
|
|
|
finished := &finishedMsg{
|
|
verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
|
|
}
|
|
|
|
hs.transcript.Write(finished.marshal())
|
|
if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Derive secrets that take context through the server Finished.
|
|
|
|
hs.masterSecret = hs.suite.extract(nil,
|
|
hs.suite.deriveSecret(hs.handshakeSecret, "derived", nil))
|
|
|
|
hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
|
|
clientApplicationTrafficLabel, hs.transcript)
|
|
serverSecret := hs.suite.deriveSecret(hs.masterSecret,
|
|
serverApplicationTrafficLabel, hs.transcript)
|
|
c.out.setTrafficSecret(hs.suite, serverSecret)
|
|
|
|
err := c.config.writeKeyLog(keyLogLabelClientTraffic, hs.clientHello.random, hs.trafficSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.clientHello.random, serverSecret)
|
|
if err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
|
|
c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript)
|
|
|
|
// If we did not request client certificates, at this point we can
|
|
// precompute the client finished and roll the transcript forward to send
|
|
// session tickets in our first flight.
|
|
if !hs.requestClientCert() {
|
|
if err := hs.sendSessionTickets(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) shouldSendSessionTickets() bool {
|
|
if hs.c.config.SessionTicketsDisabled {
|
|
return false
|
|
}
|
|
|
|
// Don't send tickets the client wouldn't use. See RFC 8446, Section 4.2.9.
|
|
for _, pskMode := range hs.clientHello.pskModes {
|
|
if pskMode == pskModeDHE {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) sendSessionTickets() error {
|
|
c := hs.c
|
|
|
|
hs.clientFinished = hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
|
|
finishedMsg := &finishedMsg{
|
|
verifyData: hs.clientFinished,
|
|
}
|
|
hs.transcript.Write(finishedMsg.marshal())
|
|
|
|
if !hs.shouldSendSessionTickets() {
|
|
return nil
|
|
}
|
|
|
|
resumptionSecret := hs.suite.deriveSecret(hs.masterSecret,
|
|
resumptionLabel, hs.transcript)
|
|
|
|
m := new(newSessionTicketMsgTLS13)
|
|
|
|
var certsFromClient [][]byte
|
|
for _, cert := range c.peerCertificates {
|
|
certsFromClient = append(certsFromClient, cert.Raw)
|
|
}
|
|
state := sessionStateTLS13{
|
|
cipherSuite: hs.suite.id,
|
|
createdAt: uint64(c.config.time().Unix()),
|
|
resumptionSecret: resumptionSecret,
|
|
certificate: Certificate{
|
|
Certificate: certsFromClient,
|
|
OCSPStaple: c.ocspResponse,
|
|
SignedCertificateTimestamps: c.scts,
|
|
},
|
|
}
|
|
var err error
|
|
m.label, err = c.encryptTicket(state.marshal())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.lifetime = uint32(maxSessionTicketLifetime / time.Second)
|
|
|
|
if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) readClientCertificate() error {
|
|
c := hs.c
|
|
|
|
if !hs.requestClientCert() {
|
|
return nil
|
|
}
|
|
|
|
// If we requested a client certificate, then the client must send a
|
|
// certificate message. If it's empty, no CertificateVerify is sent.
|
|
|
|
msg, err := c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
certMsg, ok := msg.(*certificateMsgTLS13)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(certMsg, msg)
|
|
}
|
|
hs.transcript.Write(certMsg.marshal())
|
|
|
|
if err := c.processCertsFromClient(certMsg.certificate); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(certMsg.certificate.Certificate) != 0 {
|
|
msg, err = c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
certVerify, ok := msg.(*certificateVerifyMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(certVerify, msg)
|
|
}
|
|
|
|
// See RFC 8446, Section 4.4.3.
|
|
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms) {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid certificate signature algorithm")
|
|
}
|
|
sigType := signatureFromSignatureScheme(certVerify.signatureAlgorithm)
|
|
sigHash, err := hashFromSignatureScheme(certVerify.signatureAlgorithm)
|
|
if sigType == 0 || err != nil {
|
|
c.sendAlert(alertInternalError)
|
|
return err
|
|
}
|
|
if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
|
|
c.sendAlert(alertIllegalParameter)
|
|
return errors.New("tls: invalid certificate signature algorithm")
|
|
}
|
|
h := sigHash.New()
|
|
writeSignedMessage(h, clientSignatureContext, hs.transcript)
|
|
if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
|
|
sigHash, h.Sum(nil), certVerify.signature); err != nil {
|
|
c.sendAlert(alertDecryptError)
|
|
return errors.New("tls: invalid certificate signature")
|
|
}
|
|
|
|
hs.transcript.Write(certVerify.marshal())
|
|
}
|
|
|
|
// If we waited until the client certificates to send session tickets, we
|
|
// are ready to do it now.
|
|
if err := hs.sendSessionTickets(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (hs *serverHandshakeStateTLS13) readClientFinished() error {
|
|
c := hs.c
|
|
|
|
msg, err := c.readHandshake()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
finished, ok := msg.(*finishedMsg)
|
|
if !ok {
|
|
c.sendAlert(alertUnexpectedMessage)
|
|
return unexpectedMessageError(finished, msg)
|
|
}
|
|
|
|
if !hmac.Equal(hs.clientFinished, finished.verifyData) {
|
|
c.sendAlert(alertDecryptError)
|
|
return errors.New("tls: invalid client finished hash")
|
|
}
|
|
|
|
c.in.setTrafficSecret(hs.suite, hs.trafficSecret)
|
|
|
|
return nil
|
|
}
|