github.com/MerlinKodo/quic-go@v0.39.2/internal/qtls/go121.go (about)

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