github.com/sagernet/quic-go@v0.43.1-beta.1/internal/qtls_ech/go121.go (about)

     1  package qtls
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  
     7  	"github.com/sagernet/quic-go/internal/protocol"
     8  	"github.com/sagernet/cloudflare-tls"
     9  )
    10  
    11  type (
    12  	QUICConn                 = tls.QUICConn
    13  	QUICConfig               = tls.QUICConfig
    14  	QUICEvent                = tls.QUICEvent
    15  	QUICEventKind            = tls.QUICEventKind
    16  	QUICEncryptionLevel      = tls.QUICEncryptionLevel
    17  	QUICSessionTicketOptions = tls.QUICSessionTicketOptions
    18  	AlertError               = tls.AlertError
    19  )
    20  
    21  const (
    22  	QUICEncryptionLevelInitial     = tls.QUICEncryptionLevelInitial
    23  	QUICEncryptionLevelEarly       = tls.QUICEncryptionLevelEarly
    24  	QUICEncryptionLevelHandshake   = tls.QUICEncryptionLevelHandshake
    25  	QUICEncryptionLevelApplication = tls.QUICEncryptionLevelApplication
    26  )
    27  
    28  const (
    29  	QUICNoEvent                     = tls.QUICNoEvent
    30  	QUICSetReadSecret               = tls.QUICSetReadSecret
    31  	QUICSetWriteSecret              = tls.QUICSetWriteSecret
    32  	QUICWriteData                   = tls.QUICWriteData
    33  	QUICTransportParameters         = tls.QUICTransportParameters
    34  	QUICTransportParametersRequired = tls.QUICTransportParametersRequired
    35  	QUICRejectedEarlyData           = tls.QUICRejectedEarlyData
    36  	QUICHandshakeDone               = tls.QUICHandshakeDone
    37  )
    38  
    39  func QUICServer(config *QUICConfig) *QUICConn { return tls.QUICServer(config) }
    40  func QUICClient(config *QUICConfig) *QUICConn { return tls.QUICClient(config) }
    41  
    42  func SetupConfigForServer(qconf *QUICConfig, _ bool, getData func() []byte, handleSessionTicket func([]byte, bool) bool) {
    43  	conf := qconf.TLSConfig
    44  
    45  	// Workaround for https://github.com/golang/go/issues/60506.
    46  	// This initializes the session tickets _before_ cloning the config.
    47  	_, _ = conf.DecryptTicket(nil, tls.ConnectionState{})
    48  
    49  	conf = conf.Clone()
    50  	conf.MinVersion = tls.VersionTLS13
    51  	qconf.TLSConfig = conf
    52  
    53  	// add callbacks to save transport parameters into the session ticket
    54  	origWrapSession := conf.WrapSession
    55  	conf.WrapSession = func(cs tls.ConnectionState, state *tls.SessionState) ([]byte, error) {
    56  		// Add QUIC session ticket
    57  		state.Extra = append(state.Extra, addExtraPrefix(getData()))
    58  
    59  		if origWrapSession != nil {
    60  			return origWrapSession(cs, state)
    61  		}
    62  		b, err := conf.EncryptTicket(cs, state)
    63  		return b, err
    64  	}
    65  	origUnwrapSession := conf.UnwrapSession
    66  	// UnwrapSession might be called multiple times, as the client can use multiple session tickets.
    67  	// However, using 0-RTT is only possible with the first session ticket.
    68  	// crypto/tls guarantees that this callback is called in the same order as the session ticket in the ClientHello.
    69  	var unwrapCount int
    70  	conf.UnwrapSession = func(identity []byte, connState tls.ConnectionState) (*tls.SessionState, error) {
    71  		unwrapCount++
    72  		var state *tls.SessionState
    73  		var err error
    74  		if origUnwrapSession != nil {
    75  			state, err = origUnwrapSession(identity, connState)
    76  		} else {
    77  			state, err = conf.DecryptTicket(identity, connState)
    78  		}
    79  		if err != nil || state == nil {
    80  			return nil, err
    81  		}
    82  
    83  		extra := findExtraData(state.Extra)
    84  		if extra != nil {
    85  			state.EarlyData = handleSessionTicket(extra, state.EarlyData && unwrapCount == 1)
    86  		} else {
    87  			state.EarlyData = false
    88  		}
    89  
    90  		return state, nil
    91  	}
    92  }
    93  
    94  func SetupConfigForClient(
    95  	qconf *QUICConfig,
    96  	getData func(earlyData bool) []byte,
    97  	setData func(data []byte, earlyData bool) (allowEarlyData bool),
    98  ) {
    99  	conf := qconf.TLSConfig
   100  	if conf.ClientSessionCache != nil {
   101  		origCache := conf.ClientSessionCache
   102  		conf.ClientSessionCache = &clientSessionCache{
   103  			wrapped: origCache,
   104  			getData: getData,
   105  			setData: setData,
   106  		}
   107  	}
   108  }
   109  
   110  func ToTLSEncryptionLevel(e protocol.EncryptionLevel) tls.QUICEncryptionLevel {
   111  	switch e {
   112  	case protocol.EncryptionInitial:
   113  		return tls.QUICEncryptionLevelInitial
   114  	case protocol.EncryptionHandshake:
   115  		return tls.QUICEncryptionLevelHandshake
   116  	case protocol.Encryption1RTT:
   117  		return tls.QUICEncryptionLevelApplication
   118  	case protocol.Encryption0RTT:
   119  		return tls.QUICEncryptionLevelEarly
   120  	default:
   121  		panic(fmt.Sprintf("unexpected encryption level: %s", e))
   122  	}
   123  }
   124  
   125  func FromTLSEncryptionLevel(e tls.QUICEncryptionLevel) protocol.EncryptionLevel {
   126  	switch e {
   127  	case tls.QUICEncryptionLevelInitial:
   128  		return protocol.EncryptionInitial
   129  	case tls.QUICEncryptionLevelHandshake:
   130  		return protocol.EncryptionHandshake
   131  	case tls.QUICEncryptionLevelApplication:
   132  		return protocol.Encryption1RTT
   133  	case tls.QUICEncryptionLevelEarly:
   134  		return protocol.Encryption0RTT
   135  	default:
   136  		panic(fmt.Sprintf("unexpect encryption level: %s", e))
   137  	}
   138  }
   139  
   140  const extraPrefix = "quic-go1"
   141  
   142  func addExtraPrefix(b []byte) []byte {
   143  	return append([]byte(extraPrefix), b...)
   144  }
   145  
   146  func findExtraData(extras [][]byte) []byte {
   147  	prefix := []byte(extraPrefix)
   148  	for _, extra := range extras {
   149  		if len(extra) < len(prefix) || !bytes.Equal(prefix, extra[:len(prefix)]) {
   150  			continue
   151  		}
   152  		return extra[len(prefix):]
   153  	}
   154  	return nil
   155  }
   156  
   157  func SendSessionTicket(c *QUICConn, allow0RTT bool) error {
   158  	return c.SendSessionTicket(tls.QUICSessionTicketOptions{
   159  		EarlyData: allow0RTT,
   160  	})
   161  }