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