github.com/sagernet/quic-go@v0.43.1-beta.1/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/sagernet/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( 97 qconf *QUICConfig, 98 getData func(earlyData bool) []byte, 99 setData func(data []byte, earlyData bool) (allowEarlyData bool), 100 ) { 101 conf := qconf.TLSConfig 102 if conf.ClientSessionCache != nil { 103 origCache := conf.ClientSessionCache 104 conf.ClientSessionCache = &clientSessionCache{ 105 wrapped: origCache, 106 getData: getData, 107 setData: setData, 108 } 109 } 110 } 111 112 func ToTLSEncryptionLevel(e protocol.EncryptionLevel) tls.QUICEncryptionLevel { 113 switch e { 114 case protocol.EncryptionInitial: 115 return tls.QUICEncryptionLevelInitial 116 case protocol.EncryptionHandshake: 117 return tls.QUICEncryptionLevelHandshake 118 case protocol.Encryption1RTT: 119 return tls.QUICEncryptionLevelApplication 120 case protocol.Encryption0RTT: 121 return tls.QUICEncryptionLevelEarly 122 default: 123 panic(fmt.Sprintf("unexpected encryption level: %s", e)) 124 } 125 } 126 127 func FromTLSEncryptionLevel(e tls.QUICEncryptionLevel) protocol.EncryptionLevel { 128 switch e { 129 case tls.QUICEncryptionLevelInitial: 130 return protocol.EncryptionInitial 131 case tls.QUICEncryptionLevelHandshake: 132 return protocol.EncryptionHandshake 133 case tls.QUICEncryptionLevelApplication: 134 return protocol.Encryption1RTT 135 case tls.QUICEncryptionLevelEarly: 136 return protocol.Encryption0RTT 137 default: 138 panic(fmt.Sprintf("unexpect encryption level: %s", e)) 139 } 140 } 141 142 const extraPrefix = "quic-go1" 143 144 func addExtraPrefix(b []byte) []byte { 145 return append([]byte(extraPrefix), b...) 146 } 147 148 func findExtraData(extras [][]byte) []byte { 149 prefix := []byte(extraPrefix) 150 for _, extra := range extras { 151 if len(extra) < len(prefix) || !bytes.Equal(prefix, extra[:len(prefix)]) { 152 continue 153 } 154 return extra[len(prefix):] 155 } 156 return nil 157 } 158 159 func SendSessionTicket(c *QUICConn, allow0RTT bool) error { 160 return c.SendSessionTicket(tls.QUICSessionTicketOptions{ 161 EarlyData: allow0RTT, 162 }) 163 }