Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/psk-hrr-support'
Browse files Browse the repository at this point in the history
  • Loading branch information
adotkhan committed Aug 7, 2024
2 parents 23de245 + dd2ffe0 commit 185b54f
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 19 deletions.
12 changes: 6 additions & 6 deletions examples/tls-resumption/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,8 @@ func runResumptionCheck(helloID tls.ClientHelloID, getCustomSpec func() *tls.Cli

func main() {
tls13Url := "www.microsoft.com:443"
tls12Url1 := "spocs.getpocket.com:443"
tls12Url2 := "marketplace.visualstudio.com:443"
tls13HRRUrl := "marketplace.visualstudio.com:443" // will send HRR for P384/P521
tls12Url := "tls-v1-2.badssl.com:1012"
runResumptionCheck(tls.HelloChrome_100, nil, noResumption, tls13Url, 3, false) // no-resumption + utls
func() {
defer func() {
Expand All @@ -189,9 +189,9 @@ func main() {
runResumptionCheck(tls.HelloChrome_100_PSK, nil, pskResumption, tls13Url, 1, false) // psk + utls
runResumptionCheck(tls.HelloGolang, nil, pskResumption, tls13Url, 1, false) // psk + crypto/tls

runResumptionCheck(tls.HelloChrome_100_PSK, nil, ticketResumption, tls12Url1, 10, false) // session ticket + utls
runResumptionCheck(tls.HelloGolang, nil, ticketResumption, tls12Url1, 10, false) // session ticket + crypto/tls
runResumptionCheck(tls.HelloChrome_100_PSK, nil, ticketResumption, tls12Url2, 10, false) // session ticket + utls
runResumptionCheck(tls.HelloGolang, nil, ticketResumption, tls12Url2, 10, false) // session ticket + crypto/tls
runResumptionCheck(tls.HelloChrome_100_PSK, nil, pskResumption, tls13HRRUrl, 20, false) // psk (HRR) + utls
runResumptionCheck(tls.HelloGolang, nil, pskResumption, tls13HRRUrl, 20, false) // psk (HRR) + crypto/tls

runResumptionCheck(tls.HelloChrome_100_PSK, nil, ticketResumption, tls12Url, 10, false) // session ticket + utls
runResumptionCheck(tls.HelloGolang, nil, ticketResumption, tls12Url, 10, false) // session ticket + crypto/tls
}
22 changes: 17 additions & 5 deletions handshake_client_tls13.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"errors"
"fmt"
"hash"
"log"
"time"

"github.com/cloudflare/circl/kem"
Expand Down Expand Up @@ -410,11 +411,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 @@ -459,6 +455,22 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
if err := hs.uconn.MarshalClientHello(); 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 {
hs.uconn.HandshakeState.Hello.PskIdentities = nil
hs.uconn.HandshakeState.Hello.PskBinders = nil
log.Printf("[Error] PreSharedKeyExtension.UpdateOnHRR failed: %v", err)
} else {
psk.PatchBuiltHello(hs.uconn.HandshakeState.Hello)
}
break
}
}
}

hs.hello.raw = hs.uconn.HandshakeState.Hello.Raw
}
}
Expand Down
1 change: 1 addition & 0 deletions u_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,7 @@ var (
// Chrome w/ PSK: Chrome start sending this ClientHello after doing TLS 1.3 handshake with the same server.
// Beta: PSK extension added. However, uTLS doesn't ship with full PSK support.
// Use at your own discretion.
HelloChrome_PSK_Auto = HelloChrome_114_Padding_PSK_Shuf
HelloChrome_100_PSK = ClientHelloID{helloChrome, "100_PSK", nil, nil}
HelloChrome_112_PSK_Shuf = ClientHelloID{helloChrome, "112_PSK", nil, nil}
HelloChrome_114_Padding_PSK_Shuf = ClientHelloID{helloChrome, "114_PSK", nil, nil}
Expand Down
88 changes: 81 additions & 7 deletions 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 @@ -68,6 +69,26 @@ type PreSharedKeyCommon struct {
// > - Implementations should gather and provide the final pre-shared key (PSK) related data.
//
// > - This data will be incorporated into both the clientHello and HandshakeState, ensuring that the PSK-related information is properly set and ready for the handshake process.
//
// HelloRetryRequest Phase (server selects a different curve supported but not selected by the client):
//
// - [UpdateOnHRR() called]:
//
// > - Implementations should update the extension's state accordingly and save the first Client Hello's hash.
//
// > - The binders should be recalculated based on the updated state LATER when PatchBuiltHello() and/or GetPreSharedKeyCommon() is called.
//
// - [PatchBuiltHello() called]:
//
// > - The client hello is already marshaled in the "hello.Raw" format.
//
// > - Implementations are expected to update the binders within the marshaled client hello.
//
// - [GetPreSharedKeyCommon() called]:
//
// > - Implementations should gather and provide the final pre-shared key (PSK) related data.
//
// > - This data will be incorporated into both the clientHello and HandshakeState, ensuring that the PSK-related information is properly set and ready for the handshake process.
type PreSharedKeyExtension interface {
// TLSExtension must be implemented by all PreSharedKeyExtension implementations.
TLSExtension
Expand All @@ -88,6 +109,11 @@ type PreSharedKeyExtension interface {
// Its purpose is to update the binders of PSK (Pre-Shared Key) identities.
PatchBuiltHello(hello *PubClientHelloMsg) error

// UpdateOnHRR is called when the server sends a HelloRetryRequest.
// Implementations should update the extension's state accordingly
// and recalculate the binders.
UpdateOnHRR(prevClientHelloHash []byte, hs *clientHandshakeStateTLS13, timeNow time.Time) error

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

Expand All @@ -99,7 +125,7 @@ func (*UnimplementedPreSharedKeyExtension) IsInitialized() bool {
panic("tls: IsInitialized is not implemented for the PreSharedKeyExtension")
}

func (*UnimplementedPreSharedKeyExtension) InitializeByUtls(session *SessionState, earlySecret []byte, binderKey []byte, identities []PskIdentity) {
func (*UnimplementedPreSharedKeyExtension) InitializeByUtls(*SessionState, []byte, []byte, []PskIdentity) {
panic("tls: Initialize is not implemented for the PreSharedKeyExtension")
}

Expand All @@ -119,14 +145,18 @@ func (*UnimplementedPreSharedKeyExtension) GetPreSharedKeyCommon() PreSharedKeyC
panic("tls: GetPreSharedKeyCommon is not implemented for the PreSharedKeyExtension")
}

func (*UnimplementedPreSharedKeyExtension) PatchBuiltHello(hello *PubClientHelloMsg) error {
func (*UnimplementedPreSharedKeyExtension) PatchBuiltHello(*PubClientHelloMsg) error {
panic("tls: ReadWithRawHello is not implemented for the PreSharedKeyExtension")
}

func (*UnimplementedPreSharedKeyExtension) SetOmitEmptyPsk(val bool) {
func (*UnimplementedPreSharedKeyExtension) SetOmitEmptyPsk(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 +166,10 @@ type UtlsPreSharedKeyExtension struct {
cachedLength *int
// Deprecated: Set OmitEmptyPsk in Config instead.
OmitEmptyPsk bool

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

func (e *UtlsPreSharedKeyExtension) IsInitialized() bool {
Expand Down Expand Up @@ -265,15 +299,25 @@ func (e *UtlsPreSharedKeyExtension) PatchBuiltHello(hello *PubClientHelloMsg) er
if e.Len() == 0 {
return nil
}

private := hello.getCachedPrivatePtr()
if private == nil {
private = hello.getPrivatePtr()
}
private.raw = hello.Raw
private.pskBinders = e.Binders // set the placeholder to the private Hello

//--- mirror loadSession() begin ---//
// derived from 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 All @@ -284,19 +328,43 @@ func (e *UtlsPreSharedKeyExtension) PatchBuiltHello(hello *PubClientHelloMsg) er
if err := private.updateBinders(pskBinders); err != nil {
return err
}
//--- mirror loadSession() end ---//
// derived end //
e.Binders = pskBinders

// no need to care about other PSK related fields, they will be handled separately

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.Binders = nil
e.cachedLength = nil // clear the cached length

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

func (e *UtlsPreSharedKeyExtension) Write(b []byte) (int, error) {
return len(b), nil // ignore the data
}

func (e *UtlsPreSharedKeyExtension) UnmarshalJSON(_ []byte) error {
func (e *UtlsPreSharedKeyExtension) UnmarshalJSON([]byte) error {
return nil // ignore the data
}

Expand All @@ -319,7 +387,7 @@ func (e *FakePreSharedKeyExtension) IsInitialized() bool {
return e.Identities != nil && e.Binders != nil
}

func (e *FakePreSharedKeyExtension) InitializeByUtls(session *SessionState, earlySecret []byte, binderKey []byte, identities []PskIdentity) {
func (e *FakePreSharedKeyExtension) InitializeByUtls(*SessionState, []byte, []byte, []PskIdentity) {
panic("InitializeByUtls failed: don't let utls initialize FakePreSharedKeyExtension; provide your own identities and binders or use UtlsPreSharedKeyExtension")
}

Expand Down Expand Up @@ -459,13 +527,19 @@ func (e *FakePreSharedKeyExtension) UnmarshalJSON(data []byte) error {
return nil
}

func (e *FakePreSharedKeyExtension) UpdateOnHRR([]byte, *clientHandshakeStateTLS13, time.Time) error {
return nil
}

// type guard
var (
_ PreSharedKeyExtension = (*UtlsPreSharedKeyExtension)(nil)
_ TLSExtensionJSON = (*UtlsPreSharedKeyExtension)(nil)
_ TLSExtensionWriter = (*UtlsPreSharedKeyExtension)(nil)
_ PreSharedKeyExtension = (*FakePreSharedKeyExtension)(nil)
_ TLSExtensionJSON = (*FakePreSharedKeyExtension)(nil)
_ TLSExtensionWriter = (*FakePreSharedKeyExtension)(nil)
_ PreSharedKeyExtension = (*UnimplementedPreSharedKeyExtension)(nil)
)

// type ExternalPreSharedKeyExtension struct{} // TODO: wait for whoever cares about external PSK to implement it
2 changes: 1 addition & 1 deletion u_session_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ func (s *sessionController) shouldUpdateBinders() bool {

func (s *sessionController) updateBinders() {
uAssert(s.shouldUpdateBinders(), "tls: updateBinders failed: shouldn't update binders")
s.pskExtension.PatchBuiltHello(s.uconnRef.HandshakeState.Hello)
s.pskExtension.PatchBuiltHello(s.uconnRef.HandshakeState.Hello) // bugrisk: retured error is ignored
}

func (s *sessionController) overrideExtension(extension Initializable, override func(), initializedState sessionControllerState) error {
Expand Down

0 comments on commit 185b54f

Please sign in to comment.