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.
17 "golang.org/x/crypto/cryptobyte"
20 // A SessionState is a resumable session.
21 type SessionState struct {
22 // Encoded as a SessionState (in the language of RFC 8446, Section 3).
24 // enum { server(1), client(2) } SessionStateType;
26 // opaque Certificate<1..2^24-1>;
28 // Certificate CertificateChain<0..2^24-1>;
30 // opaque Extra<0..2^24-1>;
34 // SessionStateType type;
35 // uint16 cipher_suite;
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) {
45 // case 1: opaque alpn<1..2^8-1>;
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 {
62 // Extra is ignored by crypto/tls, but is encoded by [SessionState.Bytes]
63 // and parsed by [ParseSessionState].
65 // This allows [Config.UnwrapSession]/[Config.WrapSession] and
66 // [ClientSessionCache] implementations to store and retrieve additional
67 // data alongside this session.
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 a id and version prefix).
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.
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
89 peerCertificates []*x509.Certificate
90 activeCertHandles []*activeCert
93 verifiedChains [][]*x509.Certificate
94 alpnProtocol string // only set if EarlyData is true
96 // Client-side TLS 1.3-only fields.
97 useBy uint64 // seconds since UNIX epoch
101 // Bytes encodes the session, including any private fields, so that it can be
102 // parsed by [ParseSessionState]. The encoding contains secret values critical
103 // to the security of future and possibly past sessions.
105 // The specific encoding should be considered opaque and may change incompatibly
106 // between Go versions.
107 func (s *SessionState) Bytes() ([]byte, error) {
108 var b cryptobyte.Builder
109 b.AddUint16(s.version)
111 b.AddUint8(2) // client
113 b.AddUint8(1) // server
115 b.AddUint16(s.cipherSuite)
116 addUint64(&b, s.createdAt)
117 b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
120 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
121 for _, extra := range s.Extra {
122 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
127 if s.extMasterSecret {
137 marshalCertificate(&b, Certificate{
138 Certificate: certificatesToBytesSlice(s.peerCertificates),
139 OCSPStaple: s.ocspResponse,
140 SignedCertificateTimestamps: s.scts,
142 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
143 for _, chain := range s.verifiedChains {
144 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
145 // We elide the first certificate because it's always the leaf.
147 b.SetError(errors.New("tls: internal error: empty verified chain"))
150 for _, cert := range chain[1:] {
151 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
159 b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
160 b.AddBytes([]byte(s.alpnProtocol))
164 if s.version >= VersionTLS13 {
165 addUint64(&b, s.useBy)
166 b.AddUint32(s.ageAdd)
172 func certificatesToBytesSlice(certs []*x509.Certificate) [][]byte {
173 s := make([][]byte, 0, len(certs))
174 for _, c := range certs {
180 // ParseSessionState parses a [SessionState] encoded by [SessionState.Bytes].
181 func ParseSessionState(data []byte) (*SessionState, error) {
182 ss := &SessionState{}
183 s := cryptobyte.String(data)
184 var typ, extMasterSecret, earlyData uint8
186 var extra cryptobyte.String
187 if !s.ReadUint16(&ss.version) ||
188 !s.ReadUint8(&typ) ||
189 (typ != 1 && typ != 2) ||
190 !s.ReadUint16(&ss.cipherSuite) ||
191 !readUint64(&s, &ss.createdAt) ||
192 !readUint8LengthPrefixed(&s, &ss.secret) ||
193 !s.ReadUint24LengthPrefixed(&extra) ||
194 !s.ReadUint8(&extMasterSecret) ||
195 !s.ReadUint8(&earlyData) ||
196 len(ss.secret) == 0 ||
197 !unmarshalCertificate(&s, &cert) {
198 return nil, errors.New("tls: invalid session encoding")
202 if !readUint24LengthPrefixed(&extra, &e) {
203 return nil, errors.New("tls: invalid session encoding")
205 ss.Extra = append(ss.Extra, e)
207 switch extMasterSecret {
209 ss.extMasterSecret = false
211 ss.extMasterSecret = true
213 return nil, errors.New("tls: invalid session encoding")
221 return nil, errors.New("tls: invalid session encoding")
223 for _, cert := range cert.Certificate {
224 c, err := globalCertCache.newCert(cert)
228 ss.activeCertHandles = append(ss.activeCertHandles, c)
229 ss.peerCertificates = append(ss.peerCertificates, c.cert)
231 ss.ocspResponse = cert.OCSPStaple
232 ss.scts = cert.SignedCertificateTimestamps
233 var chainList cryptobyte.String
234 if !s.ReadUint24LengthPrefixed(&chainList) {
235 return nil, errors.New("tls: invalid session encoding")
237 for !chainList.Empty() {
238 var certList cryptobyte.String
239 if !chainList.ReadUint24LengthPrefixed(&certList) {
240 return nil, errors.New("tls: invalid session encoding")
242 var chain []*x509.Certificate
243 if len(ss.peerCertificates) == 0 {
244 return nil, errors.New("tls: invalid session encoding")
246 chain = append(chain, ss.peerCertificates[0])
247 for !certList.Empty() {
249 if !readUint24LengthPrefixed(&certList, &cert) {
250 return nil, errors.New("tls: invalid session encoding")
252 c, err := globalCertCache.newCert(cert)
256 ss.activeCertHandles = append(ss.activeCertHandles, c)
257 chain = append(chain, c.cert)
259 ss.verifiedChains = append(ss.verifiedChains, chain)
263 if !readUint8LengthPrefixed(&s, &alpn) {
264 return nil, errors.New("tls: invalid session encoding")
266 ss.alpnProtocol = string(alpn)
268 if isClient := typ == 2; !isClient {
270 return nil, errors.New("tls: invalid session encoding")
275 if len(ss.peerCertificates) == 0 {
276 return nil, errors.New("tls: no server certificates in client session")
278 if ss.version < VersionTLS13 {
280 return nil, errors.New("tls: invalid session encoding")
284 if !s.ReadUint64(&ss.useBy) || !s.ReadUint32(&ss.ageAdd) || !s.Empty() {
285 return nil, errors.New("tls: invalid session encoding")
290 // sessionState returns a partially filled-out [SessionState] with information
291 // from the current connection.
292 func (c *Conn) sessionState() (*SessionState, error) {
293 return &SessionState{
295 cipherSuite: c.cipherSuite,
296 createdAt: uint64(c.config.time().Unix()),
297 alpnProtocol: c.clientProtocol,
298 peerCertificates: c.peerCertificates,
299 activeCertHandles: c.activeCertHandles,
300 ocspResponse: c.ocspResponse,
302 isClient: c.isClient,
303 extMasterSecret: c.extMasterSecret,
304 verifiedChains: c.verifiedChains,
308 // EncryptTicket encrypts a ticket with the Config's configured (or default)
309 // session ticket keys. It can be used as a [Config.WrapSession] implementation.
310 func (c *Config) EncryptTicket(cs ConnectionState, ss *SessionState) ([]byte, error) {
311 ticketKeys := c.ticketKeys(nil)
312 stateBytes, err := ss.Bytes()
316 return c.encryptTicket(stateBytes, ticketKeys)
319 func (c *Config) encryptTicket(state []byte, ticketKeys []ticketKey) ([]byte, error) {
320 if len(ticketKeys) == 0 {
321 return nil, errors.New("tls: internal error: session ticket keys unavailable")
324 encrypted := make([]byte, aes.BlockSize+len(state)+sha256.Size)
325 iv := encrypted[:aes.BlockSize]
326 ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size]
327 authenticated := encrypted[:len(encrypted)-sha256.Size]
328 macBytes := encrypted[len(encrypted)-sha256.Size:]
330 if _, err := io.ReadFull(c.rand(), iv); err != nil {
334 block, err := aes.NewCipher(key.aesKey[:])
336 return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
338 cipher.NewCTR(block, iv).XORKeyStream(ciphertext, state)
340 mac := hmac.New(sha256.New, key.hmacKey[:])
341 mac.Write(authenticated)
342 mac.Sum(macBytes[:0])
344 return encrypted, nil
347 // DecryptTicket decrypts a ticket encrypted by [Config.EncryptTicket]. It can
348 // be used as a [Config.UnwrapSession] implementation.
350 // If the ticket can't be decrypted or parsed, DecryptTicket returns (nil, nil).
351 func (c *Config) DecryptTicket(identity []byte, cs ConnectionState) (*SessionState, error) {
352 ticketKeys := c.ticketKeys(nil)
353 stateBytes := c.decryptTicket(identity, ticketKeys)
354 if stateBytes == nil {
357 s, err := ParseSessionState(stateBytes)
359 return nil, nil // drop unparsable tickets on the floor
364 func (c *Config) decryptTicket(encrypted []byte, ticketKeys []ticketKey) []byte {
365 if len(encrypted) < aes.BlockSize+sha256.Size {
369 iv := encrypted[:aes.BlockSize]
370 ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size]
371 authenticated := encrypted[:len(encrypted)-sha256.Size]
372 macBytes := encrypted[len(encrypted)-sha256.Size:]
374 for _, key := range ticketKeys {
375 mac := hmac.New(sha256.New, key.hmacKey[:])
376 mac.Write(authenticated)
377 expected := mac.Sum(nil)
379 if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
383 block, err := aes.NewCipher(key.aesKey[:])
387 plaintext := make([]byte, len(ciphertext))
388 cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
396 // ClientSessionState contains the state needed by a client to
397 // resume a previous TLS session.
398 type ClientSessionState struct {
400 session *SessionState
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.
406 // It can be called by [ClientSessionCache.Put] to serialize (with
407 // [SessionState.Bytes]) and store the session.
408 func (cs *ClientSessionState) ResumptionState() (ticket []byte, state *SessionState, err error) {
409 return cs.ticket, cs.session, nil
412 // NewResumptionState returns a state value that can be returned by
413 // [ClientSessionCache.Get] to resume a previous session.
415 // state needs to be returned by [ParseSessionState], and the ticket and session
416 // state must have been returned by [ClientSessionState.ResumptionState].
417 func NewResumptionState(ticket []byte, state *SessionState) (*ClientSessionState, error) {
418 return &ClientSessionState{
419 ticket: ticket, session: state,