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>;
32 // SessionStateType type;
33 // uint16 cipher_suite;
35 // opaque secret<1..2^8-1>;
36 // opaque extra<0..2^24-1>;
37 // uint8 early_data = { 0, 1 };
38 // CertificateEntry certificate_list<0..2^24-1>;
39 // select (SessionState.early_data) {
41 // case 1: opaque alpn<1..2^8-1>;
43 // select (SessionState.type) {
44 // case server: /* empty */;
45 // case client: struct {
46 // CertificateChain verified_chains<0..2^24-1>; /* excluding leaf */
47 // select (SessionState.version) {
48 // case VersionTLS10..VersionTLS12: /* empty */;
49 // case VersionTLS13: struct {
59 // Extra is ignored by crypto/tls, but is encoded by [SessionState.Bytes]
60 // and parsed by [ParseSessionState].
62 // This allows [Config.UnwrapSession]/[Config.WrapSession] and
63 // [ClientSessionCache] implementations to store and retrieve additional
66 // If Extra is already set, the implementation must preserve the previous
67 // value across a round-trip, for example by appending and stripping a
68 // fixed-length suffix.
71 // EarlyData indicates whether the ticket can be used for 0-RTT in a QUIC
72 // connection. The application may set this to false if it is true to
73 // decline to offer 0-RTT even if supported.
79 // createdAt is the generation time of the secret on the sever (which for
80 // TLS 1.0–1.2 might be earlier than the current session) and the time at
81 // which the ticket was received on the client.
82 createdAt uint64 // seconds since UNIX epoch
83 secret []byte // master secret for TLS 1.2, or the PSK for TLS 1.3
84 peerCertificates []*x509.Certificate
85 activeCertHandles []*activeCert
88 alpnProtocol string // only set if EarlyData is true
90 // Client-side fields.
91 verifiedChains [][]*x509.Certificate
93 // Client-side TLS 1.3-only fields.
94 useBy uint64 // seconds since UNIX epoch
98 // Bytes encodes the session, including any private fields, so that it can be
99 // parsed by [ParseSessionState]. The encoding contains secret values critical
100 // to the security of future and possibly past sessions.
102 // The specific encoding should be considered opaque and may change incompatibly
103 // between Go versions.
104 func (s *SessionState) Bytes() ([]byte, error) {
105 var b cryptobyte.Builder
106 b.AddUint16(s.version)
108 b.AddUint8(2) // client
110 b.AddUint8(1) // server
112 b.AddUint16(s.cipherSuite)
113 addUint64(&b, s.createdAt)
114 b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
117 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
125 marshalCertificate(&b, s.certificate())
127 b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
128 b.AddBytes([]byte(s.alpnProtocol))
132 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
133 for _, chain := range s.verifiedChains {
134 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
135 // We elide the first certificate because it's always the leaf.
137 b.SetError(errors.New("tls: internal error: empty verified chain"))
140 for _, cert := range chain[1:] {
141 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
148 if s.version >= VersionTLS13 {
149 addUint64(&b, s.useBy)
150 b.AddUint32(s.ageAdd)
156 func (s *SessionState) certificate() Certificate {
158 Certificate: certificatesToBytesSlice(s.peerCertificates),
159 OCSPStaple: s.ocspResponse,
160 SignedCertificateTimestamps: s.scts,
164 func certificatesToBytesSlice(certs []*x509.Certificate) [][]byte {
165 s := make([][]byte, 0, len(certs))
166 for _, c := range certs {
172 // ParseSessionState parses a [SessionState] encoded by [SessionState.Bytes].
173 func ParseSessionState(data []byte) (*SessionState, error) {
174 ss := &SessionState{}
175 s := cryptobyte.String(data)
176 var typ, earlyData uint8
178 if !s.ReadUint16(&ss.version) ||
179 !s.ReadUint8(&typ) ||
180 (typ != 1 && typ != 2) ||
181 !s.ReadUint16(&ss.cipherSuite) ||
182 !readUint64(&s, &ss.createdAt) ||
183 !readUint8LengthPrefixed(&s, &ss.secret) ||
184 !readUint24LengthPrefixed(&s, &ss.Extra) ||
185 !s.ReadUint8(&earlyData) ||
186 len(ss.secret) == 0 ||
187 !unmarshalCertificate(&s, &cert) {
188 return nil, errors.New("tls: invalid session encoding")
196 return nil, errors.New("tls: invalid session encoding")
198 for _, cert := range cert.Certificate {
199 c, err := globalCertCache.newCert(cert)
203 ss.activeCertHandles = append(ss.activeCertHandles, c)
204 ss.peerCertificates = append(ss.peerCertificates, c.cert)
206 ss.ocspResponse = cert.OCSPStaple
207 ss.scts = cert.SignedCertificateTimestamps
210 if !readUint8LengthPrefixed(&s, &alpn) {
211 return nil, errors.New("tls: invalid session encoding")
213 ss.alpnProtocol = string(alpn)
215 if isClient := typ == 2; !isClient {
217 return nil, errors.New("tls: invalid session encoding")
222 if len(ss.peerCertificates) == 0 {
223 return nil, errors.New("tls: no server certificates in client session")
225 var chainList cryptobyte.String
226 if !s.ReadUint24LengthPrefixed(&chainList) {
227 return nil, errors.New("tls: invalid session encoding")
229 for !chainList.Empty() {
230 var certList cryptobyte.String
231 if !chainList.ReadUint24LengthPrefixed(&certList) {
232 return nil, errors.New("tls: invalid session encoding")
234 var chain []*x509.Certificate
235 chain = append(chain, ss.peerCertificates[0])
236 for !certList.Empty() {
238 if !readUint24LengthPrefixed(&certList, &cert) {
239 return nil, errors.New("tls: invalid session encoding")
241 c, err := globalCertCache.newCert(cert)
245 ss.activeCertHandles = append(ss.activeCertHandles, c)
246 chain = append(chain, c.cert)
248 ss.verifiedChains = append(ss.verifiedChains, chain)
250 if ss.version < VersionTLS13 {
252 return nil, errors.New("tls: invalid session encoding")
256 if !s.ReadUint64(&ss.useBy) || !s.ReadUint32(&ss.ageAdd) || !s.Empty() {
257 return nil, errors.New("tls: invalid session encoding")
262 // sessionState returns a partially filled-out [SessionState] with information
263 // from the current connection.
264 func (c *Conn) sessionState() (*SessionState, error) {
265 var verifiedChains [][]*x509.Certificate
267 verifiedChains = c.verifiedChains
268 if len(c.peerCertificates) == 0 {
269 return nil, errors.New("tls: internal error: empty peer certificates")
272 return &SessionState{
274 cipherSuite: c.cipherSuite,
275 createdAt: uint64(c.config.time().Unix()),
276 alpnProtocol: c.clientProtocol,
277 peerCertificates: c.peerCertificates,
278 activeCertHandles: c.activeCertHandles,
279 ocspResponse: c.ocspResponse,
281 isClient: c.isClient,
282 verifiedChains: verifiedChains,
286 // EncryptTicket encrypts a ticket with the Config's configured (or default)
287 // session ticket keys. It can be used as a [Config.WrapSession] implementation.
288 func (c *Config) EncryptTicket(cs ConnectionState, ss *SessionState) ([]byte, error) {
289 ticketKeys := c.ticketKeys(nil)
290 stateBytes, err := ss.Bytes()
294 return c.encryptTicket(stateBytes, ticketKeys)
297 var _ = &Config{WrapSession: (&Config{}).EncryptTicket}
299 func (c *Config) encryptTicket(state []byte, ticketKeys []ticketKey) ([]byte, error) {
300 if len(ticketKeys) == 0 {
301 return nil, errors.New("tls: internal error: session ticket keys unavailable")
304 encrypted := make([]byte, aes.BlockSize+len(state)+sha256.Size)
305 iv := encrypted[:aes.BlockSize]
306 ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size]
307 authenticated := encrypted[:len(encrypted)-sha256.Size]
308 macBytes := encrypted[len(encrypted)-sha256.Size:]
310 if _, err := io.ReadFull(c.rand(), iv); err != nil {
314 block, err := aes.NewCipher(key.aesKey[:])
316 return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
318 cipher.NewCTR(block, iv).XORKeyStream(ciphertext, state)
320 mac := hmac.New(sha256.New, key.hmacKey[:])
321 mac.Write(authenticated)
322 mac.Sum(macBytes[:0])
324 return encrypted, nil
327 // DecryptTicket decrypts a ticket encrypted by [Config.EncryptTicket]. It can
328 // be used as a [Config.UnwrapSession] implementation.
330 // If the ticket can't be decrypted or parsed, DecryptTicket returns (nil, nil).
331 func (c *Config) DecryptTicket(identity []byte, cs ConnectionState) (*SessionState, error) {
332 ticketKeys := c.ticketKeys(nil)
333 stateBytes := c.decryptTicket(identity, ticketKeys)
334 if stateBytes == nil {
337 s, err := ParseSessionState(stateBytes)
339 return nil, nil // drop unparsable tickets on the floor
344 var _ = &Config{UnwrapSession: (&Config{}).DecryptTicket}
346 func (c *Config) decryptTicket(encrypted []byte, ticketKeys []ticketKey) []byte {
347 if len(encrypted) < aes.BlockSize+sha256.Size {
351 iv := encrypted[:aes.BlockSize]
352 ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size]
353 authenticated := encrypted[:len(encrypted)-sha256.Size]
354 macBytes := encrypted[len(encrypted)-sha256.Size:]
356 for _, key := range ticketKeys {
357 mac := hmac.New(sha256.New, key.hmacKey[:])
358 mac.Write(authenticated)
359 expected := mac.Sum(nil)
361 if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
365 block, err := aes.NewCipher(key.aesKey[:])
369 plaintext := make([]byte, len(ciphertext))
370 cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
378 // ClientSessionState contains the state needed by a client to
379 // resume a previous TLS session.
380 type ClientSessionState struct {
382 session *SessionState
385 // ResumptionState returns the session ticket sent by the server (also known as
386 // the session's identity) and the state necessary to resume this session.
388 // It can be called by [ClientSessionCache.Put] to serialize (with
389 // [SessionState.Bytes]) and store the session.
390 func (cs *ClientSessionState) ResumptionState() (ticket []byte, state *SessionState, err error) {
391 return cs.ticket, cs.session, nil
394 // NewResumptionState returns a state value that can be returned by
395 // [ClientSessionCache.Get] to resume a previous session.
397 // state needs to be returned by [ParseSessionState], and the ticket and session
398 // state must have been returned by [ClientSessionState.ResumptionState].
399 func NewResumptionState(ticket []byte, state *SessionState) (*ClientSessionState, error) {
400 return &ClientSessionState{
401 ticket: ticket, session: state,