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