ticket.mx raw
1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package tls
6
7 import (
8 "crypto/aes"
9 "crypto/cipher"
10 "crypto/hmac"
11 "crypto/sha256"
12 "crypto/subtle"
13 "crypto/x509"
14 "errors"
15 "io"
16
17 "golang.org/x/crypto/cryptobyte"
18 )
19
20 // A SessionState is a resumable session.
21 type SessionState struct {
22 // Encoded as a SessionState (in the language of RFC 8446, Section 3).
23 //
24 // enum { server(1), client(2) } SessionStateType;
25 //
26 // opaque Certificate<1..2^24-1>;
27 //
28 // Certificate CertificateChain<0..2^24-1>;
29 //
30 // opaque Extra<0..2^24-1>;
31 //
32 // struct {
33 // uint16 version;
34 // SessionStateType type;
35 // uint16 cipher_suite;
36 // uint64 created_at;
37 // opaque secret<1..2^8-1>;
38 // Extra extra<0..2^24-1>;
39 // uint8 ext_master_secret = { 0, 1 };
40 // uint8 early_data = { 0, 1 };
41 // CertificateEntry certificate_list<0..2^24-1>;
42 // CertificateChain verified_chains<0..2^24-1>; /* excluding leaf */
43 // select (SessionState.early_data) {
44 // case 0: Empty;
45 // case 1: opaque alpn<1..2^8-1>;
46 // };
47 // select (SessionState.version) {
48 // case VersionTLS10..VersionTLS12: uint16 curve_id;
49 // case VersionTLS13: select (SessionState.type) {
50 // case server: Empty;
51 // case client: struct {
52 // uint64 use_by;
53 // uint32 age_add;
54 // };
55 // };
56 // };
57 // } SessionState;
58 //
59 // The format can be extended backwards-compatibly by adding new fields at
60 // the end. Otherwise, a new SessionStateType must be used, as different Go
61 // versions may share the same session ticket encryption key.
62
63 // Extra is ignored by crypto/tls, but is encoded by [SessionState.Bytes]
64 // and parsed by [ParseSessionState].
65 //
66 // This allows [Config.UnwrapSession]/[Config.WrapSession] and
67 // [ClientSessionCache] implementations to store and retrieve additional
68 // data alongside this session.
69 //
70 // To allow different layers in a protocol stack to share this field,
71 // applications must only append to it, not replace it, and must use entries
72 // that can be recognized even if out of order (for example, by starting
73 // with an id and version prefix).
74 Extra [][]byte
75
76 // EarlyData indicates whether the ticket can be used for 0-RTT in a QUIC
77 // connection. The application may set this to false if it is true to
78 // decline to offer 0-RTT even if supported.
79 EarlyData bool
80
81 version uint16
82 isClient bool
83 cipherSuite uint16
84 // createdAt is the generation time of the secret on the server (which for
85 // TLS 1.0–1.2 might be earlier than the current session) and the time at
86 // which the ticket was received on the client.
87 createdAt uint64 // seconds since UNIX epoch
88 secret []byte // master secret for TLS 1.2, or the PSK for TLS 1.3
89 extMasterSecret bool
90 peerCertificates []*x509.Certificate
91 ocspResponse []byte
92 scts [][]byte
93 verifiedChains [][]*x509.Certificate
94 alpnProtocol []byte // only set if EarlyData is true
95
96 // Client-side TLS 1.3-only fields.
97 useBy uint64 // seconds since UNIX epoch
98 ageAdd uint32
99 ticket []byte
100
101 // TLS 1.0–1.2 only fields.
102 curveID CurveID
103 }
104
105 // Bytes encodes the session, including any private fields, so that it can be
106 // parsed by [ParseSessionState]. The encoding contains secret values critical
107 // to the security of future and possibly past sessions.
108 //
109 // The specific encoding should be considered opaque and may change incompatibly
110 // between Go versions.
111 func (s *SessionState) Bytes() ([]byte, error) {
112 var b cryptobyte.Builder
113 b.AddUint16(s.version)
114 if s.isClient {
115 b.AddUint8(2) // client
116 } else {
117 b.AddUint8(1) // server
118 }
119 b.AddUint16(s.cipherSuite)
120 addUint64(&b, s.createdAt)
121 b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
122 b.AddBytes(s.secret)
123 })
124 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
125 for _, extra := range s.Extra {
126 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
127 b.AddBytes(extra)
128 })
129 }
130 })
131 if s.extMasterSecret {
132 b.AddUint8(1)
133 } else {
134 b.AddUint8(0)
135 }
136 if s.EarlyData {
137 b.AddUint8(1)
138 } else {
139 b.AddUint8(0)
140 }
141 marshalCertificate(&b, Certificate{
142 Certificate: certificatesToBytesSlice(s.peerCertificates),
143 OCSPStaple: s.ocspResponse,
144 SignedCertificateTimestamps: s.scts,
145 })
146 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
147 for _, chain := range s.verifiedChains {
148 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
149 // We elide the first certificate because it's always the leaf.
150 if len(chain) == 0 {
151 b.SetError(errors.New("tls: internal error: empty verified chain"))
152 return
153 }
154 for _, cert := range chain[1:] {
155 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
156 b.AddBytes(cert.Raw)
157 })
158 }
159 })
160 }
161 })
162 if s.EarlyData {
163 b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
164 b.AddBytes([]byte(s.alpnProtocol))
165 })
166 }
167 if s.version >= VersionTLS13 {
168 if s.isClient {
169 addUint64(&b, s.useBy)
170 b.AddUint32(s.ageAdd)
171 }
172 } else {
173 b.AddUint16(uint16(s.curveID))
174 }
175 return b.Bytes()
176 }
177
178 func certificatesToBytesSlice(certs []*x509.Certificate) [][]byte {
179 s := [][]byte{:0:len(certs)}
180 for _, c := range certs {
181 s = append(s, c.Raw)
182 }
183 return s
184 }
185
186 // ParseSessionState parses a [SessionState] encoded by [SessionState.Bytes].
187 func ParseSessionState(data []byte) (*SessionState, error) {
188 ss := &SessionState{}
189 s := cryptobyte.String(data)
190 var typ, extMasterSecret, earlyData uint8
191 var cert Certificate
192 var extra cryptobyte.String
193 if !s.ReadUint16(&ss.version) ||
194 !s.ReadUint8(&typ) ||
195 !s.ReadUint16(&ss.cipherSuite) ||
196 !readUint64(&s, &ss.createdAt) ||
197 !readUint8LengthPrefixed(&s, &ss.secret) ||
198 !s.ReadUint24LengthPrefixed(&extra) ||
199 !s.ReadUint8(&extMasterSecret) ||
200 !s.ReadUint8(&earlyData) ||
201 len(ss.secret) == 0 ||
202 !unmarshalCertificate(&s, &cert) {
203 return nil, errors.New("tls: invalid session encoding")
204 }
205 for !extra.Empty() {
206 var e []byte
207 if !readUint24LengthPrefixed(&extra, &e) {
208 return nil, errors.New("tls: invalid session encoding")
209 }
210 ss.Extra = append(ss.Extra, e)
211 }
212 switch typ {
213 case 1:
214 ss.isClient = false
215 case 2:
216 ss.isClient = true
217 default:
218 return nil, errors.New("tls: unknown session encoding")
219 }
220 switch extMasterSecret {
221 case 0:
222 ss.extMasterSecret = false
223 case 1:
224 ss.extMasterSecret = true
225 default:
226 return nil, errors.New("tls: invalid session encoding")
227 }
228 switch earlyData {
229 case 0:
230 ss.EarlyData = false
231 case 1:
232 ss.EarlyData = true
233 default:
234 return nil, errors.New("tls: invalid session encoding")
235 }
236 for _, cert := range cert.Certificate {
237 c, err := globalCertCache.newCert(cert)
238 if err != nil {
239 return nil, err
240 }
241 ss.peerCertificates = append(ss.peerCertificates, c)
242 }
243 if ss.isClient && len(ss.peerCertificates) == 0 {
244 return nil, errors.New("tls: no server certificates in client session")
245 }
246 ss.ocspResponse = cert.OCSPStaple
247 ss.scts = cert.SignedCertificateTimestamps
248 var chainList cryptobyte.String
249 if !s.ReadUint24LengthPrefixed(&chainList) {
250 return nil, errors.New("tls: invalid session encoding")
251 }
252 for !chainList.Empty() {
253 var certList cryptobyte.String
254 if !chainList.ReadUint24LengthPrefixed(&certList) {
255 return nil, errors.New("tls: invalid session encoding")
256 }
257 var chain []*x509.Certificate
258 if len(ss.peerCertificates) == 0 {
259 return nil, errors.New("tls: invalid session encoding")
260 }
261 chain = append(chain, ss.peerCertificates[0])
262 for !certList.Empty() {
263 var cert []byte
264 if !readUint24LengthPrefixed(&certList, &cert) {
265 return nil, errors.New("tls: invalid session encoding")
266 }
267 c, err := globalCertCache.newCert(cert)
268 if err != nil {
269 return nil, err
270 }
271 chain = append(chain, c)
272 }
273 ss.verifiedChains = append(ss.verifiedChains, chain)
274 }
275 if ss.EarlyData {
276 var alpn []byte
277 if !readUint8LengthPrefixed(&s, &alpn) {
278 return nil, errors.New("tls: invalid session encoding")
279 }
280 ss.alpnProtocol = []byte(alpn)
281 }
282 if ss.version >= VersionTLS13 {
283 if ss.isClient {
284 if !s.ReadUint64(&ss.useBy) || !s.ReadUint32(&ss.ageAdd) {
285 return nil, errors.New("tls: invalid session encoding")
286 }
287 }
288 } else {
289 if !s.ReadUint16((*uint16)(&ss.curveID)) {
290 return nil, errors.New("tls: invalid session encoding")
291 }
292 }
293 return ss, nil
294 }
295
296 // sessionState returns a partially filled-out [SessionState] with information
297 // from the current connection.
298 func (c *Conn) sessionState() *SessionState {
299 return &SessionState{
300 version: c.vers,
301 cipherSuite: c.cipherSuite,
302 createdAt: uint64(c.config.time().Unix()),
303 alpnProtocol: c.clientProtocol,
304 peerCertificates: c.peerCertificates,
305 ocspResponse: c.ocspResponse,
306 scts: c.scts,
307 isClient: c.isClient,
308 extMasterSecret: c.extMasterSecret,
309 verifiedChains: c.verifiedChains,
310 curveID: c.curveID,
311 }
312 }
313
314 // EncryptTicket encrypts a ticket with the [Config]'s configured (or default)
315 // session ticket keys. It can be used as a [Config.WrapSession] implementation.
316 func (c *Config) EncryptTicket(cs ConnectionState, ss *SessionState) ([]byte, error) {
317 ticketKeys := c.ticketKeys(nil)
318 stateBytes, err := ss.Bytes()
319 if err != nil {
320 return nil, err
321 }
322 return c.encryptTicket(stateBytes, ticketKeys)
323 }
324
325 func (c *Config) encryptTicket(state []byte, ticketKeys []ticketKey) ([]byte, error) {
326 if len(ticketKeys) == 0 {
327 return nil, errors.New("tls: internal error: session ticket keys unavailable")
328 }
329
330 encrypted := []byte{:aes.BlockSize+len(state)+sha256.Size}
331 iv := encrypted[:aes.BlockSize]
332 ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size]
333 authenticated := encrypted[:len(encrypted)-sha256.Size]
334 macBytes := encrypted[len(encrypted)-sha256.Size:]
335
336 if _, err := io.ReadFull(c.rand(), iv); err != nil {
337 return nil, err
338 }
339 key := ticketKeys[0]
340 block, err := aes.NewCipher(key.aesKey[:])
341 if err != nil {
342 return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
343 }
344 cipher.NewCTR(block, iv).XORKeyStream(ciphertext, state)
345
346 mac := hmac.New(sha256.New, key.hmacKey[:])
347 mac.Write(authenticated)
348 mac.Sum(macBytes[:0])
349
350 return encrypted, nil
351 }
352
353 // DecryptTicket decrypts a ticket encrypted by [Config.EncryptTicket]. It can
354 // be used as a [Config.UnwrapSession] implementation.
355 //
356 // If the ticket can't be decrypted or parsed, DecryptTicket returns (nil, nil).
357 func (c *Config) DecryptTicket(identity []byte, cs ConnectionState) (*SessionState, error) {
358 ticketKeys := c.ticketKeys(nil)
359 stateBytes := c.decryptTicket(identity, ticketKeys)
360 if stateBytes == nil {
361 return nil, nil
362 }
363 s, err := ParseSessionState(stateBytes)
364 if err != nil {
365 return nil, nil // drop unparsable tickets on the floor
366 }
367 return s, nil
368 }
369
370 func (c *Config) decryptTicket(encrypted []byte, ticketKeys []ticketKey) []byte {
371 if len(encrypted) < aes.BlockSize+sha256.Size {
372 return nil
373 }
374
375 iv := encrypted[:aes.BlockSize]
376 ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size]
377 authenticated := encrypted[:len(encrypted)-sha256.Size]
378 macBytes := encrypted[len(encrypted)-sha256.Size:]
379
380 for _, key := range ticketKeys {
381 mac := hmac.New(sha256.New, key.hmacKey[:])
382 mac.Write(authenticated)
383 expected := mac.Sum(nil)
384
385 if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
386 continue
387 }
388
389 block, err := aes.NewCipher(key.aesKey[:])
390 if err != nil {
391 return nil
392 }
393 plaintext := []byte{:len(ciphertext)}
394 cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
395
396 return plaintext
397 }
398
399 return nil
400 }
401
402 // ClientSessionState contains the state needed by a client to
403 // resume a previous TLS session.
404 type ClientSessionState struct {
405 session *SessionState
406 }
407
408 // ResumptionState returns the session ticket sent by the server (also known as
409 // the session's identity) and the state necessary to resume this session.
410 //
411 // It can be called by [ClientSessionCache.Put] to serialize (with
412 // [SessionState.Bytes]) and store the session.
413 func (cs *ClientSessionState) ResumptionState() (ticket []byte, state *SessionState, err error) {
414 if cs == nil || cs.session == nil {
415 return nil, nil, nil
416 }
417 return cs.session.ticket, cs.session, nil
418 }
419
420 // NewResumptionState returns a state value that can be returned by
421 // [ClientSessionCache.Get] to resume a previous session.
422 //
423 // state needs to be returned by [ParseSessionState], and the ticket and session
424 // state must have been returned by [ClientSessionState.ResumptionState].
425 func NewResumptionState(ticket []byte, state *SessionState) (*ClientSessionState, error) {
426 state.ticket = ticket
427 return &ClientSessionState{
428 session: state,
429 }, nil
430 }
431