From 63aeec73c56472de05a46d416744b313c7f26eb9 Mon Sep 17 00:00:00 2001 From: Mingye Chen Date: Thu, 3 Apr 2025 16:01:57 -0600 Subject: [PATCH 1/5] feat: add quic changes from stdlib --- u_quic.go | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/u_quic.go b/u_quic.go index e985c820..27b4c2ed 100644 --- a/u_quic.go +++ b/u_quic.go @@ -25,13 +25,14 @@ type UQUICConn struct { // // The config's MinVersion must be at least TLS 1.3. func UQUICClient(config *QUICConfig, clientHelloID ClientHelloID) *UQUICConn { - return newUQUICConn(UClient(nil, config.TLSConfig, clientHelloID)) + return newUQUICConn(UClient(nil, config.TLSConfig, clientHelloID), config) } -func newUQUICConn(uconn *UConn) *UQUICConn { +func newUQUICConn(uconn *UConn, config *QUICConfig) *UQUICConn { uconn.quic = &quicState{ - signalc: make(chan struct{}), - blockedc: make(chan struct{}), + signalc: make(chan struct{}), + blockedc: make(chan struct{}), + enableSessionEvents: config.EnableSessionEvents, } uconn.quic.events = uconn.quic.eventArr[:0] return &UQUICConn{ @@ -49,7 +50,7 @@ func (q *UQUICConn) Start(ctx context.Context) error { } q.conn.quic.started = true if q.conn.config.MinVersion < VersionTLS13 { - return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.13")) + return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.3")) } go q.conn.HandshakeContext(ctx) if _, ok := <-q.conn.quic.blockedc; !ok { @@ -71,6 +72,11 @@ func (q *UQUICConn) NextEvent() QUICEvent { // to catch callers erroniously retaining it. qs.events[last].Data[0] = 0 } + if qs.nextEvent >= len(qs.events) && qs.waitingForDrain { + qs.waitingForDrain = false + <-qs.signalc + <-qs.blockedc + } if qs.nextEvent >= len(qs.events) { qs.events = qs.events[:0] qs.nextEvent = 0 @@ -151,6 +157,24 @@ func (q *UQUICConn) SendSessionTicket(opts QUICSessionTicketOptions) error { return quicError(c.sendSessionTicket(opts.EarlyData, opts.Extra)) } +// StoreSession stores a session previously received in a QUICStoreSession event +// in the ClientSessionCache. +// The application may process additional events or modify the SessionState +// before storing the session. +func (q *UQUICConn) StoreSession(session *SessionState) error { + c := q.conn + if !c.isClient { + return quicError(errors.New("tls: StoreSessionTicket called on the server")) + } + cacheKey := c.clientSessionCacheKey() + if cacheKey == "" { + return nil + } + cs := &ClientSessionState{session: session} + c.config.ClientSessionCache.Put(cacheKey, cs) + return nil +} + // ConnectionState returns basic TLS details about the connection. func (q *UQUICConn) ConnectionState() ConnectionState { return q.conn.ConnectionState() From 54708de6a9ace91c63aa6c5cb232f87d86325395 Mon Sep 17 00:00:00 2001 From: Mingye Chen Date: Fri, 23 May 2025 15:48:26 -0400 Subject: [PATCH 2/5] feat: add API for getting pq keyshare --- common.go | 4 ++++ handshake_server_tls13.go | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/common.go b/common.go index fea76bd3..1bed813f 100644 --- a/common.go +++ b/common.go @@ -930,6 +930,10 @@ type Config struct { // // If GREASE ECH extension is present, this field will be ignored. ECHConfigs []ECHConfig // [uTLS] + + // GetOscur0KeyShare gets the first keyshare, terminate the connection if + // non-nil err is returned. For use with Oscur0 only. + GetOscur0KeyShare func(*KeyShare) error // [uTLS] } // EncryptedClientHelloKey holds a private key that is associated diff --git a/handshake_server_tls13.go b/handshake_server_tls13.go index b05f1421..016bb75b 100644 --- a/handshake_server_tls13.go +++ b/handshake_server_tls13.go @@ -254,6 +254,11 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error { return errors.New("tls: invalid X25519MLKEM768 client key share") } ecdhData = ecdhData[mlkem.EncapsulationKeySize768:] + if hs.c.config.GetOscur0KeyShare != nil { + if err := hs.c.config.GetOscur0KeyShare(&KeyShare{Group: clientKeyShare.group, Data: clientKeyShare.data}); err != nil { + return err + } + } } if _, ok := curveForCurveID(ecdhGroup); !ok { c.sendAlert(alertInternalError) From c5e6a36d2372dce57ba8bcb6d2374a06272a4357 Mon Sep 17 00:00:00 2001 From: Mingye Chen Date: Fri, 23 May 2025 16:04:25 -0400 Subject: [PATCH 3/5] fix: use API before checking preferred group --- handshake_server_tls13.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/handshake_server_tls13.go b/handshake_server_tls13.go index 016bb75b..7d216e3e 100644 --- a/handshake_server_tls13.go +++ b/handshake_server_tls13.go @@ -231,6 +231,11 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error { var clientKeyShare *keyShare for _, ks := range hs.clientHello.keyShares { + if ks.group == X25519MLKEM768 && hs.c.config.GetOscur0KeyShare != nil { + if err := hs.c.config.GetOscur0KeyShare(&KeyShare{Group: ks.group, Data: ks.data}); err != nil { + return err + } + } if ks.group == selectedGroup { clientKeyShare = &ks break @@ -254,11 +259,6 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error { return errors.New("tls: invalid X25519MLKEM768 client key share") } ecdhData = ecdhData[mlkem.EncapsulationKeySize768:] - if hs.c.config.GetOscur0KeyShare != nil { - if err := hs.c.config.GetOscur0KeyShare(&KeyShare{Group: clientKeyShare.group, Data: clientKeyShare.data}); err != nil { - return err - } - } } if _, ok := curveForCurveID(ecdhGroup); !ok { c.sendAlert(alertInternalError) From e3fa4364dbc99d809e735f6a3ecc3a86014f1078 Mon Sep 17 00:00:00 2001 From: Mingye Chen Date: Fri, 23 May 2025 16:08:32 -0400 Subject: [PATCH 4/5] fix: clone GetOscur0Keyshare in config --- common.go | 1 + 1 file changed, 1 insertion(+) diff --git a/common.go b/common.go index 1bed813f..c8fdfe29 100644 --- a/common.go +++ b/common.go @@ -1041,6 +1041,7 @@ func (c *Config) Clone() *Config { PreferSkipResumptionOnNilExtension: c.PreferSkipResumptionOnNilExtension, // [UTLS] ECHConfigs: c.ECHConfigs, // [uTLS] + GetOscur0KeyShare: c.GetOscur0KeyShare, // [uTLS] } } From d00f903419872d403209e7247a6fb894e9ae6bfd Mon Sep 17 00:00:00 2001 From: Saamocha Date: Wed, 29 Oct 2025 14:52:26 -0400 Subject: [PATCH 5/5] feat: expose ech data for oscur0 --- common.go | 2 ++ handshake_server.go | 23 ++++++++++++----------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/common.go b/common.go index 73b6dad5..a2129391 100644 --- a/common.go +++ b/common.go @@ -476,6 +476,8 @@ type ClientHelloInfo struct { // for use with SupportsCertificate. config *Config + EncryptedClientHello []byte // [uTLS] raw outer ECH payload + // ctx is the context of the handshake that is in progress. ctx context.Context } diff --git a/handshake_server.go b/handshake_server.go index e38ebaeb..cfdf3143 100644 --- a/handshake_server.go +++ b/handshake_server.go @@ -994,16 +994,17 @@ func clientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg) } return &ClientHelloInfo{ - CipherSuites: clientHello.cipherSuites, - ServerName: clientHello.serverName, - SupportedCurves: clientHello.supportedCurves, - SupportedPoints: clientHello.supportedPoints, - SignatureSchemes: clientHello.supportedSignatureAlgorithms, - SupportedProtos: clientHello.alpnProtocols, - SupportedVersions: supportedVersions, - Extensions: clientHello.extensions, - Conn: c.conn, - config: c.config, - ctx: ctx, + CipherSuites: clientHello.cipherSuites, + ServerName: clientHello.serverName, + SupportedCurves: clientHello.supportedCurves, + SupportedPoints: clientHello.supportedPoints, + SignatureSchemes: clientHello.supportedSignatureAlgorithms, + SupportedProtos: clientHello.alpnProtocols, + SupportedVersions: supportedVersions, + Extensions: clientHello.extensions, + EncryptedClientHello: append([]byte(nil), clientHello.encryptedClientHello...), // [uTLS] expose ECH + Conn: c.conn, + config: c.config, + ctx: ctx, } }