xref: /aosp_15_r20/external/boringssl/src/ssl/test/runner/ticket.go (revision 8fb009dc861624b67b6cdb62ea21f0f22d0c584b)
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 runner
6
7import (
8	"crypto/aes"
9	"crypto/cipher"
10	"crypto/hmac"
11	"crypto/sha256"
12	"crypto/subtle"
13	"errors"
14	"io"
15	"time"
16
17	"golang.org/x/crypto/cryptobyte"
18)
19
20// sessionState contains the information that is serialized into a session
21// ticket in order to later resume a connection.
22type sessionState struct {
23	vers                        uint16
24	cipherSuite                 uint16
25	secret                      []byte
26	handshakeHash               []byte
27	certificates                [][]byte
28	extendedMasterSecret        bool
29	earlyALPN                   []byte
30	ticketCreationTime          time.Time
31	ticketExpiration            time.Time
32	ticketFlags                 uint32
33	ticketAgeAdd                uint32
34	hasApplicationSettings      bool
35	localApplicationSettings    []byte
36	peerApplicationSettings     []byte
37	hasApplicationSettingsOld   bool
38	localApplicationSettingsOld []byte
39	peerApplicationSettingsOld  []byte
40}
41
42func (s *sessionState) marshal() []byte {
43	msg := cryptobyte.NewBuilder(nil)
44	msg.AddUint16(s.vers)
45	msg.AddUint16(s.cipherSuite)
46	addUint16LengthPrefixedBytes(msg, s.secret)
47	addUint16LengthPrefixedBytes(msg, s.handshakeHash)
48	msg.AddUint16(uint16(len(s.certificates)))
49	for _, cert := range s.certificates {
50		addUint24LengthPrefixedBytes(msg, cert)
51	}
52
53	if s.extendedMasterSecret {
54		msg.AddUint8(1)
55	} else {
56		msg.AddUint8(0)
57	}
58
59	if s.vers >= VersionTLS13 {
60		msg.AddUint64(uint64(s.ticketCreationTime.UnixNano()))
61		msg.AddUint64(uint64(s.ticketExpiration.UnixNano()))
62		msg.AddUint32(s.ticketFlags)
63		msg.AddUint32(s.ticketAgeAdd)
64	}
65
66	addUint16LengthPrefixedBytes(msg, s.earlyALPN)
67
68	if s.hasApplicationSettings {
69		msg.AddUint8(1)
70		addUint16LengthPrefixedBytes(msg, s.localApplicationSettings)
71		addUint16LengthPrefixedBytes(msg, s.peerApplicationSettings)
72	} else {
73		msg.AddUint8(0)
74	}
75
76	if s.hasApplicationSettingsOld {
77		msg.AddUint8(1)
78		addUint16LengthPrefixedBytes(msg, s.localApplicationSettingsOld)
79		addUint16LengthPrefixedBytes(msg, s.peerApplicationSettingsOld)
80	} else {
81		msg.AddUint8(0)
82	}
83
84	return msg.BytesOrPanic()
85}
86
87func readBool(reader *cryptobyte.String, out *bool) bool {
88	var value uint8
89	if !reader.ReadUint8(&value) {
90		return false
91	}
92	if value == 0 {
93		*out = false
94		return true
95	}
96	if value == 1 {
97		*out = true
98		return true
99	}
100	return false
101}
102
103func (s *sessionState) unmarshal(data []byte) bool {
104	reader := cryptobyte.String(data)
105	var numCerts uint16
106	if !reader.ReadUint16(&s.vers) ||
107		!reader.ReadUint16(&s.cipherSuite) ||
108		!readUint16LengthPrefixedBytes(&reader, &s.secret) ||
109		!readUint16LengthPrefixedBytes(&reader, &s.handshakeHash) ||
110		!reader.ReadUint16(&numCerts) {
111		return false
112	}
113
114	s.certificates = make([][]byte, int(numCerts))
115	for i := range s.certificates {
116		if !readUint24LengthPrefixedBytes(&reader, &s.certificates[i]) {
117			return false
118		}
119	}
120
121	if !readBool(&reader, &s.extendedMasterSecret) {
122		return false
123	}
124
125	if s.vers >= VersionTLS13 {
126		var ticketCreationTime, ticketExpiration uint64
127		if !reader.ReadUint64(&ticketCreationTime) ||
128			!reader.ReadUint64(&ticketExpiration) ||
129			!reader.ReadUint32(&s.ticketFlags) ||
130			!reader.ReadUint32(&s.ticketAgeAdd) {
131			return false
132		}
133		s.ticketCreationTime = time.Unix(0, int64(ticketCreationTime))
134		s.ticketExpiration = time.Unix(0, int64(ticketExpiration))
135	}
136
137	if !readUint16LengthPrefixedBytes(&reader, &s.earlyALPN) ||
138		!readBool(&reader, &s.hasApplicationSettings) {
139		return false
140	}
141
142	if s.hasApplicationSettings {
143		if !readUint16LengthPrefixedBytes(&reader, &s.localApplicationSettings) ||
144			!readUint16LengthPrefixedBytes(&reader, &s.peerApplicationSettings) {
145			return false
146		}
147	}
148
149	if !readBool(&reader, &s.hasApplicationSettingsOld) {
150		return false
151	}
152
153	if s.hasApplicationSettingsOld {
154		if !readUint16LengthPrefixedBytes(&reader, &s.localApplicationSettingsOld) ||
155			!readUint16LengthPrefixedBytes(&reader, &s.peerApplicationSettingsOld) {
156			return false
157		}
158	}
159
160	if len(reader) > 0 {
161		return false
162	}
163
164	return true
165}
166
167func (c *Conn) encryptTicket(state *sessionState) ([]byte, error) {
168	key := c.config.SessionTicketKey[:]
169	if c.config.Bugs.EncryptSessionTicketKey != nil {
170		key = c.config.Bugs.EncryptSessionTicketKey[:]
171	}
172
173	serialized := state.marshal()
174	encrypted := make([]byte, aes.BlockSize+len(serialized)+sha256.Size)
175	iv := encrypted[:aes.BlockSize]
176	macBytes := encrypted[len(encrypted)-sha256.Size:]
177
178	if _, err := io.ReadFull(c.config.rand(), iv); err != nil {
179		return nil, err
180	}
181	block, err := aes.NewCipher(key[:16])
182	if err != nil {
183		return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
184	}
185	cipher.NewCTR(block, iv).XORKeyStream(encrypted[aes.BlockSize:], serialized)
186
187	mac := hmac.New(sha256.New, key[16:32])
188	mac.Write(encrypted[:len(encrypted)-sha256.Size])
189	mac.Sum(macBytes[:0])
190
191	return encrypted, nil
192}
193
194func (c *Conn) decryptTicket(encrypted []byte) (*sessionState, bool) {
195	if len(encrypted) < aes.BlockSize+sha256.Size {
196		return nil, false
197	}
198
199	iv := encrypted[:aes.BlockSize]
200	macBytes := encrypted[len(encrypted)-sha256.Size:]
201
202	mac := hmac.New(sha256.New, c.config.SessionTicketKey[16:32])
203	mac.Write(encrypted[:len(encrypted)-sha256.Size])
204	expected := mac.Sum(nil)
205
206	if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
207		return nil, false
208	}
209
210	block, err := aes.NewCipher(c.config.SessionTicketKey[:16])
211	if err != nil {
212		return nil, false
213	}
214	ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size]
215	plaintext := make([]byte, len(ciphertext))
216	cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
217
218	state := new(sessionState)
219	ok := state.unmarshal(plaintext)
220	return state, ok
221}
222