Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions handshake_client_tls13.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,11 +389,6 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
// and utlsExtensionPadding are supposed to change
if hs.uconn != nil {
if hs.uconn.ClientHelloID != HelloGolang {
if len(hs.hello.pskIdentities) > 0 {
// TODO: wait for someone who cares about PSK to implement
return errors.New("uTLS does not support reprocessing of PSK key triggered by HelloRetryRequest")
}

keyShareExtFound := false
for _, ext := range hs.uconn.Extensions {
// new ks seems to be generated either way
Expand Down Expand Up @@ -438,6 +433,22 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
if err := hs.uconn.MarshalClientHelloNoECH(); err != nil {
return err
}

if len(hs.hello.pskIdentities) > 0 {
for _, ext := range hs.uconn.Extensions {
if psk, ok := ext.(PreSharedKeyExtension); ok {
if err := psk.UpdateOnHRR(chHash, hs, c.config.time()); err != nil {
// Graceful degradation: clear PSK and continue without resumption.
hs.uconn.HandshakeState.Hello.PskIdentities = nil
hs.uconn.HandshakeState.Hello.PskBinders = nil
} else {
psk.PatchBuiltHello(hs.uconn.HandshakeState.Hello)
}
break
}
}
}

hs.hello.original = hs.uconn.HandshakeState.Hello.Raw
}
}
Expand Down
53 changes: 52 additions & 1 deletion u_pre_shared_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"errors"
"io"
"time"

"golang.org/x/crypto/cryptobyte"
)
Expand Down Expand Up @@ -88,6 +89,12 @@ type PreSharedKeyExtension interface {
// Its purpose is to update the binders of PSK (Pre-Shared Key) identities.
PatchBuiltHello(hello *PubClientHelloMsg) error

// UpdateOnHRR is called when a HelloRetryRequest is received and PSK identities
// need to be reprocessed. It updates the session, cipher suite, obfuscated ticket
// age, and stores the previous ClientHello hash for correct transcript reconstruction
// in PatchBuiltHello.
UpdateOnHRR(prevClientHelloHash []byte, hs *clientHandshakeStateTLS13, timeNow time.Time) error

mustEmbedUnimplementedPreSharedKeyExtension() // this works like a type guard
}

Expand Down Expand Up @@ -127,6 +134,10 @@ func (*UnimplementedPreSharedKeyExtension) SetOmitEmptyPsk(val bool) {
panic("tls: SetOmitEmptyPsk is not implemented for the PreSharedKeyExtension")
}

func (*UnimplementedPreSharedKeyExtension) UpdateOnHRR([]byte, *clientHandshakeStateTLS13, time.Time) error {
panic("tls: UpdateOnHRR is not implemented for the PreSharedKeyExtension")
}

// UtlsPreSharedKeyExtension is an extension used to set the PSK extension in the
// ClientHello.
type UtlsPreSharedKeyExtension struct {
Expand All @@ -136,6 +147,10 @@ type UtlsPreSharedKeyExtension struct {
cachedLength *int
// Deprecated: Set OmitEmptyPsk in Config instead.
OmitEmptyPsk bool

// used only for HRR-based recalculation of binders
prevClientHelloHash []byte
serverHello *serverHelloMsg
}

func (e *UtlsPreSharedKeyExtension) IsInitialized() bool {
Expand Down Expand Up @@ -272,8 +287,17 @@ func (e *UtlsPreSharedKeyExtension) PatchBuiltHello(hello *PubClientHelloMsg) er
private.original = hello.Raw
private.pskBinders = e.Binders // set the placeholder to the private Hello

//--- mirror loadSession() begin ---//
//--- mirror loadSession() and processHelloRetryRequest() begin ---//
transcript := e.cipherSuite.hash.New()

if len(e.prevClientHelloHash) > 0 { // HRR will set this field
transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(e.prevClientHelloHash))})
transcript.Write(e.prevClientHelloHash)
if err := transcriptMsg(e.serverHello, transcript); err != nil {
return err
}
}

helloBytes, err := private.marshalWithoutBinders() // no marshal() will be actually called, as we have set the field `raw`
if err != nil {
return err
Expand Down Expand Up @@ -307,6 +331,29 @@ func (e *UtlsPreSharedKeyExtension) PatchBuiltHello(hello *PubClientHelloMsg) er
return io.EOF
}

func (e *UtlsPreSharedKeyExtension) UpdateOnHRR(prevClientHelloHash []byte, hs *clientHandshakeStateTLS13, timeNow time.Time) error {
if len(e.Identities) > 0 {
e.Session = hs.session
e.cipherSuite = cipherSuiteTLS13ByID(e.Session.cipherSuite)
if e.cipherSuite.hash != hs.suite.hash {
// disable PatchBuiltHello
e.Session = nil
e.cachedLength = new(int)
return errors.New("tls: cipher suite hash mismatch, PSK will not be used")
}

// update the obfuscated ticket age
ticketAge := timeNow.Sub(time.Unix(int64(hs.session.createdAt), 0))
e.Identities[0].ObfuscatedTicketAge = uint32(ticketAge/time.Millisecond) + hs.session.ageAdd

e.cachedLength = nil // clear the cached length so re-serialization recalculates

e.prevClientHelloHash = prevClientHelloHash
e.serverHello = hs.serverHello
}
return nil
}

func (e *UtlsPreSharedKeyExtension) Write(b []byte) (int, error) {
return len(b), nil // ignore the data
}
Expand Down Expand Up @@ -388,6 +435,10 @@ func (*FakePreSharedKeyExtension) PatchBuiltHello(*PubClientHelloMsg) error {
return nil // no need to patch the hello since we don't need to update binders
}

func (*FakePreSharedKeyExtension) UpdateOnHRR([]byte, *clientHandshakeStateTLS13, time.Time) error {
return nil // no need to update on HRR since we don't recalculate binders
}

func (e *FakePreSharedKeyExtension) Write(b []byte) (n int, err error) {
fullLen := len(b)
s := cryptobyte.String(b)
Expand Down