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