diff --git a/common.go b/common.go index 73b6dad5..7061df99 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 } @@ -914,6 +916,10 @@ type Config struct { // autoSessionTicketKeys is like sessionTicketKeys but is owned by the // auto-rotation logic. See Config.ticketKeys. autoSessionTicketKeys []ticketKey + + // 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 @@ -1020,6 +1026,7 @@ func (c *Config) Clone() *Config { autoSessionTicketKeys: c.autoSessionTicketKeys, PreferSkipResumptionOnNilExtension: c.PreferSkipResumptionOnNilExtension, // [UTLS] + GetOscur0KeyShare: c.GetOscur0KeyShare, // [uTLS] } } 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, } } diff --git a/handshake_server_tls13.go b/handshake_server_tls13.go index 510db6f6..8162d185 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 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()