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