]> Cypherpunks.ru repositories - gostls13.git/blob - src/crypto/tls/ticket.go
4aceebf2a161666ff550b149c59713e67b798ece
[gostls13.git] / src / crypto / tls / ticket.go
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         //   struct {
31         //       uint16 version;
32         //       SessionStateType type;
33         //       uint16 cipher_suite;
34         //       uint64 created_at;
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) {
40         //           case 0: Empty;
41         //           case 1: opaque alpn<1..2^8-1>;
42         //       };
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 {
50         //                       uint64 use_by;
51         //                       uint32 age_add;
52         //                   };
53         //               };
54         //           };
55         //       };
56         //   } SessionState;
57         //
58
59         // Extra is ignored by crypto/tls, but is encoded by [SessionState.Bytes]
60         // and parsed by [ParseSessionState].
61         //
62         // This allows [Config.UnwrapSession]/[Config.WrapSession] and
63         // [ClientSessionCache] implementations to store and retrieve additional
64         // data.
65         //
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.
69         Extra []byte
70
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.
74         EarlyData bool
75
76         version     uint16
77         isClient    bool
78         cipherSuite uint16
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
86         ocspResponse      []byte
87         scts              [][]byte
88         alpnProtocol      string // only set if EarlyData is true
89
90         // Client-side fields.
91         verifiedChains [][]*x509.Certificate
92
93         // Client-side TLS 1.3-only fields.
94         useBy  uint64 // seconds since UNIX epoch
95         ageAdd uint32
96 }
97
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.
101 //
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)
107         if s.isClient {
108                 b.AddUint8(2) // client
109         } else {
110                 b.AddUint8(1) // server
111         }
112         b.AddUint16(s.cipherSuite)
113         addUint64(&b, s.createdAt)
114         b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
115                 b.AddBytes(s.secret)
116         })
117         b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
118                 b.AddBytes(s.Extra)
119         })
120         if s.EarlyData {
121                 b.AddUint8(1)
122         } else {
123                 b.AddUint8(0)
124         }
125         marshalCertificate(&b, s.certificate())
126         if s.EarlyData {
127                 b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
128                         b.AddBytes([]byte(s.alpnProtocol))
129                 })
130         }
131         if s.isClient {
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.
136                                         if len(chain) == 0 {
137                                                 b.SetError(errors.New("tls: internal error: empty verified chain"))
138                                                 return
139                                         }
140                                         for _, cert := range chain[1:] {
141                                                 b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
142                                                         b.AddBytes(cert.Raw)
143                                                 })
144                                         }
145                                 })
146                         }
147                 })
148                 if s.version >= VersionTLS13 {
149                         addUint64(&b, s.useBy)
150                         b.AddUint32(s.ageAdd)
151                 }
152         }
153         return b.Bytes()
154 }
155
156 func (s *SessionState) certificate() Certificate {
157         return Certificate{
158                 Certificate:                 certificatesToBytesSlice(s.peerCertificates),
159                 OCSPStaple:                  s.ocspResponse,
160                 SignedCertificateTimestamps: s.scts,
161         }
162 }
163
164 func certificatesToBytesSlice(certs []*x509.Certificate) [][]byte {
165         s := make([][]byte, 0, len(certs))
166         for _, c := range certs {
167                 s = append(s, c.Raw)
168         }
169         return s
170 }
171
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
177         var cert Certificate
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")
189         }
190         switch earlyData {
191         case 0:
192                 ss.EarlyData = false
193         case 1:
194                 ss.EarlyData = true
195         default:
196                 return nil, errors.New("tls: invalid session encoding")
197         }
198         for _, cert := range cert.Certificate {
199                 c, err := globalCertCache.newCert(cert)
200                 if err != nil {
201                         return nil, err
202                 }
203                 ss.activeCertHandles = append(ss.activeCertHandles, c)
204                 ss.peerCertificates = append(ss.peerCertificates, c.cert)
205         }
206         ss.ocspResponse = cert.OCSPStaple
207         ss.scts = cert.SignedCertificateTimestamps
208         if ss.EarlyData {
209                 var alpn []byte
210                 if !readUint8LengthPrefixed(&s, &alpn) {
211                         return nil, errors.New("tls: invalid session encoding")
212                 }
213                 ss.alpnProtocol = string(alpn)
214         }
215         if isClient := typ == 2; !isClient {
216                 if !s.Empty() {
217                         return nil, errors.New("tls: invalid session encoding")
218                 }
219                 return ss, nil
220         }
221         ss.isClient = true
222         if len(ss.peerCertificates) == 0 {
223                 return nil, errors.New("tls: no server certificates in client session")
224         }
225         var chainList cryptobyte.String
226         if !s.ReadUint24LengthPrefixed(&chainList) {
227                 return nil, errors.New("tls: invalid session encoding")
228         }
229         for !chainList.Empty() {
230                 var certList cryptobyte.String
231                 if !chainList.ReadUint24LengthPrefixed(&certList) {
232                         return nil, errors.New("tls: invalid session encoding")
233                 }
234                 var chain []*x509.Certificate
235                 chain = append(chain, ss.peerCertificates[0])
236                 for !certList.Empty() {
237                         var cert []byte
238                         if !readUint24LengthPrefixed(&certList, &cert) {
239                                 return nil, errors.New("tls: invalid session encoding")
240                         }
241                         c, err := globalCertCache.newCert(cert)
242                         if err != nil {
243                                 return nil, err
244                         }
245                         ss.activeCertHandles = append(ss.activeCertHandles, c)
246                         chain = append(chain, c.cert)
247                 }
248                 ss.verifiedChains = append(ss.verifiedChains, chain)
249         }
250         if ss.version < VersionTLS13 {
251                 if !s.Empty() {
252                         return nil, errors.New("tls: invalid session encoding")
253                 }
254                 return ss, nil
255         }
256         if !s.ReadUint64(&ss.useBy) || !s.ReadUint32(&ss.ageAdd) || !s.Empty() {
257                 return nil, errors.New("tls: invalid session encoding")
258         }
259         return ss, nil
260 }
261
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
266         if c.isClient {
267                 verifiedChains = c.verifiedChains
268                 if len(c.peerCertificates) == 0 {
269                         return nil, errors.New("tls: internal error: empty peer certificates")
270                 }
271         }
272         return &SessionState{
273                 version:           c.vers,
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,
280                 scts:              c.scts,
281                 isClient:          c.isClient,
282                 verifiedChains:    verifiedChains,
283         }, nil
284 }
285
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()
291         if err != nil {
292                 return nil, err
293         }
294         return c.encryptTicket(stateBytes, ticketKeys)
295 }
296
297 var _ = &Config{WrapSession: (&Config{}).EncryptTicket}
298
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")
302         }
303
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:]
309
310         if _, err := io.ReadFull(c.rand(), iv); err != nil {
311                 return nil, err
312         }
313         key := ticketKeys[0]
314         block, err := aes.NewCipher(key.aesKey[:])
315         if err != nil {
316                 return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
317         }
318         cipher.NewCTR(block, iv).XORKeyStream(ciphertext, state)
319
320         mac := hmac.New(sha256.New, key.hmacKey[:])
321         mac.Write(authenticated)
322         mac.Sum(macBytes[:0])
323
324         return encrypted, nil
325 }
326
327 // DecryptTicket decrypts a ticket encrypted by [Config.EncryptTicket]. It can
328 // be used as a [Config.UnwrapSession] implementation.
329 //
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 {
335                 return nil, nil
336         }
337         s, err := ParseSessionState(stateBytes)
338         if err != nil {
339                 return nil, nil // drop unparsable tickets on the floor
340         }
341         return s, nil
342 }
343
344 var _ = &Config{UnwrapSession: (&Config{}).DecryptTicket}
345
346 func (c *Config) decryptTicket(encrypted []byte, ticketKeys []ticketKey) []byte {
347         if len(encrypted) < aes.BlockSize+sha256.Size {
348                 return nil
349         }
350
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:]
355
356         for _, key := range ticketKeys {
357                 mac := hmac.New(sha256.New, key.hmacKey[:])
358                 mac.Write(authenticated)
359                 expected := mac.Sum(nil)
360
361                 if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
362                         continue
363                 }
364
365                 block, err := aes.NewCipher(key.aesKey[:])
366                 if err != nil {
367                         return nil
368                 }
369                 plaintext := make([]byte, len(ciphertext))
370                 cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
371
372                 return plaintext
373         }
374
375         return nil
376 }
377
378 // ClientSessionState contains the state needed by a client to
379 // resume a previous TLS session.
380 type ClientSessionState struct {
381         ticket  []byte
382         session *SessionState
383 }
384
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.
387 //
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
392 }
393
394 // NewResumptionState returns a state value that can be returned by
395 // [ClientSessionCache.Get] to resume a previous session.
396 //
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,
402         }, nil
403 }