]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/crypto/tls/ticket.go
crypto/tls: add SessionState and use it on the server side
[gostls13.git] / src / crypto / tls / ticket.go
index 494f83b033e4212133ed67ca0ef5a8cfe360ae49..dfa0d430c0042e1ecd4ede4143f87b12fab4f2ce 100644 (file)
@@ -16,99 +16,51 @@ import (
        "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>;
+// A SessionState is a resumable session.
+type SessionState struct {
+       version uint16 // uint16 version;
+       // uint8 revision = 1;
+       cipherSuite uint16
+       createdAt   uint64
+       secret      []byte      // opaque master_secret<1..2^8-1>;
+       certificate Certificate // CertificateEntry certificate_list<0..2^24-1>;
 }
 
-func (m *sessionState) marshal() ([]byte, error) {
+// Bytes encodes the session, including any private fields, so that it can be
+// parsed by [ParseSessionState]. The encoding contains secret values.
+//
+// The specific encoding should be considered opaque and may change incompatibly
+// between Go versions.
+func (m *SessionState) Bytes() ([]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.version)
+       b.AddUint8(1) // revision
        b.AddUint16(m.cipherSuite)
        addUint64(&b, m.createdAt)
        b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
-               b.AddBytes(m.resumptionSecret)
+               b.AddBytes(m.secret)
        })
        marshalCertificate(&b, m.certificate)
        return b.Bytes()
 }
 
-func (m *sessionStateTLS13) unmarshal(data []byte) bool {
-       *m = sessionStateTLS13{}
+// ParseSessionState parses a [SessionState] encoded by [SessionState.Bytes].
+func ParseSessionState(data []byte) (*SessionState, error) {
+       ss := &SessionState{}
        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()
+       if !s.ReadUint16(&ss.version) ||
+               !s.ReadUint8(&revision) ||
+               revision != 1 ||
+               !s.ReadUint16(&ss.cipherSuite) ||
+               !readUint64(&s, &ss.createdAt) ||
+               !readUint8LengthPrefixed(&s, &ss.secret) ||
+               len(ss.secret) == 0 ||
+               !unmarshalCertificate(&s, &ss.certificate) ||
+               !s.Empty() {
+               return nil, errors.New("tls: invalid session encoding")
+       }
+       return ss, nil
 }
 
 func (c *Conn) encryptTicket(state []byte) ([]byte, error) {