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