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