// Copyright 2012 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package tls import ( "crypto/aes" "crypto/cipher" "crypto/hmac" "crypto/sha256" "crypto/subtle" "errors" "io" "golang.org/x/crypto/cryptobyte" ) // sessionState contains the information that is serialized into a session // ticket in order to later resume a connection. type sessionState struct { vers uint16 cipherSuite uint16 createdAt uint64 masterSecret []byte // opaque master_secret<1..2^16-1>; // struct { opaque certificate<1..2^24-1> } Certificate; certificates [][]byte // Certificate certificate_list<0..2^24-1>; } func (m *sessionState) marshal() ([]byte, error) { var b cryptobyte.Builder b.AddUint16(m.vers) b.AddUint16(m.cipherSuite) addUint64(&b, m.createdAt) b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { b.AddBytes(m.masterSecret) }) b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { for _, cert := range m.certificates { b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) { b.AddBytes(cert) }) } }) return b.Bytes() } func (m *sessionState) unmarshal(data []byte) bool { *m = sessionState{} s := cryptobyte.String(data) if ok := s.ReadUint16(&m.vers) && s.ReadUint16(&m.cipherSuite) && readUint64(&s, &m.createdAt) && readUint16LengthPrefixed(&s, &m.masterSecret) && len(m.masterSecret) != 0; !ok { return false } var certList cryptobyte.String if !s.ReadUint24LengthPrefixed(&certList) { return false } for !certList.Empty() { var cert []byte if !readUint24LengthPrefixed(&certList, &cert) { return false } m.certificates = append(m.certificates, cert) } return s.Empty() } // sessionStateTLS13 is the content of a TLS 1.3 session ticket. Its first // version (revision = 0) doesn't carry any of the information needed for 0-RTT // validation and the nonce is always empty. type sessionStateTLS13 struct { // uint8 version = 0x0304; // uint8 revision = 0; cipherSuite uint16 createdAt uint64 resumptionSecret []byte // opaque resumption_master_secret<1..2^8-1>; certificate Certificate // CertificateEntry certificate_list<0..2^24-1>; } func (m *sessionStateTLS13) marshal() ([]byte, error) { var b cryptobyte.Builder b.AddUint16(VersionTLS13) b.AddUint8(0) // revision b.AddUint16(m.cipherSuite) addUint64(&b, m.createdAt) b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) { b.AddBytes(m.resumptionSecret) }) marshalCertificate(&b, m.certificate) return b.Bytes() } func (m *sessionStateTLS13) unmarshal(data []byte) bool { *m = sessionStateTLS13{} s := cryptobyte.String(data) var version uint16 var revision uint8 return s.ReadUint16(&version) && version == VersionTLS13 && s.ReadUint8(&revision) && revision == 0 && s.ReadUint16(&m.cipherSuite) && readUint64(&s, &m.createdAt) && readUint8LengthPrefixed(&s, &m.resumptionSecret) && len(m.resumptionSecret) != 0 && unmarshalCertificate(&s, &m.certificate) && s.Empty() } func (c *Conn) encryptTicket(state []byte) ([]byte, error) { if len(c.ticketKeys) == 0 { return nil, errors.New("tls: internal error: session ticket keys unavailable") } encrypted := make([]byte, aes.BlockSize+len(state)+sha256.Size) iv := encrypted[:aes.BlockSize] ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size] authenticated := encrypted[:len(encrypted)-sha256.Size] macBytes := encrypted[len(encrypted)-sha256.Size:] if _, err := io.ReadFull(c.config.rand(), iv); err != nil { return nil, err } key := c.ticketKeys[0] block, err := aes.NewCipher(key.aesKey[:]) if err != nil { return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error()) } cipher.NewCTR(block, iv).XORKeyStream(ciphertext, state) mac := hmac.New(sha256.New, key.hmacKey[:]) mac.Write(authenticated) mac.Sum(macBytes[:0]) return encrypted, nil } func (c *Conn) decryptTicket(encrypted []byte) []byte { if len(encrypted) < aes.BlockSize+sha256.Size { return nil } iv := encrypted[:aes.BlockSize] ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size] authenticated := encrypted[:len(encrypted)-sha256.Size] macBytes := encrypted[len(encrypted)-sha256.Size:] for _, key := range c.ticketKeys { mac := hmac.New(sha256.New, key.hmacKey[:]) mac.Write(authenticated) expected := mac.Sum(nil) if subtle.ConstantTimeCompare(macBytes, expected) != 1 { continue } block, err := aes.NewCipher(key.aesKey[:]) if err != nil { return nil } plaintext := make([]byte, len(ciphertext)) cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext) return plaintext } return nil }