// 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 (
	
	
	
	
	
	
	
	

	
)

// A SessionState is a resumable session.
type SessionState struct {
	// Encoded as a SessionState (in the language of RFC 8446, Section 3).
	//
	//   enum { server(1), client(2) } SessionStateType;
	//
	//   opaque Certificate<1..2^24-1>;
	//
	//   Certificate CertificateChain<0..2^24-1>;
	//
	//   opaque Extra<0..2^24-1>;
	//
	//   struct {
	//       uint16 version;
	//       SessionStateType type;
	//       uint16 cipher_suite;
	//       uint64 created_at;
	//       opaque secret<1..2^8-1>;
	//       Extra extra<0..2^24-1>;
	//       uint8 ext_master_secret = { 0, 1 };
	//       uint8 early_data = { 0, 1 };
	//       CertificateEntry certificate_list<0..2^24-1>;
	//       CertificateChain verified_chains<0..2^24-1>; /* excluding leaf */
	//       select (SessionState.early_data) {
	//           case 0: Empty;
	//           case 1: opaque alpn<1..2^8-1>;
	//       };
	//       select (SessionState.type) {
	//           case server: Empty;
	//           case client: struct {
	//               select (SessionState.version) {
	//                   case VersionTLS10..VersionTLS12: Empty;
	//                   case VersionTLS13: struct {
	//                       uint64 use_by;
	//                       uint32 age_add;
	//                   };
	//               };
	//           };
	//       };
	//   } SessionState;
	//

	// Extra is ignored by crypto/tls, but is encoded by [SessionState.Bytes]
	// and parsed by [ParseSessionState].
	//
	// This allows [Config.UnwrapSession]/[Config.WrapSession] and
	// [ClientSessionCache] implementations to store and retrieve additional
	// data alongside this session.
	//
	// To allow different layers in a protocol stack to share this field,
	// applications must only append to it, not replace it, and must use entries
	// that can be recognized even if out of order (for example, by starting
	// with an id and version prefix).
	Extra [][]byte

	// EarlyData indicates whether the ticket can be used for 0-RTT in a QUIC
	// connection. The application may set this to false if it is true to
	// decline to offer 0-RTT even if supported.
	EarlyData bool

	version     uint16
	isClient    bool
	cipherSuite uint16
	// createdAt is the generation time of the secret on the sever (which for
	// TLS 1.0–1.2 might be earlier than the current session) and the time at
	// which the ticket was received on the client.
	createdAt         uint64 // seconds since UNIX epoch
	secret            []byte // master secret for TLS 1.2, or the PSK for TLS 1.3
	extMasterSecret   bool
	peerCertificates  []*x509.Certificate
	activeCertHandles []*activeCert
	ocspResponse      []byte
	scts              [][]byte
	verifiedChains    [][]*x509.Certificate
	alpnProtocol      string // only set if EarlyData is true

	// Client-side TLS 1.3-only fields.
	useBy  uint64 // seconds since UNIX epoch
	ageAdd uint32
}

// Bytes encodes the session, including any private fields, so that it can be
// parsed by [ParseSessionState]. The encoding contains secret values critical
// to the security of future and possibly past sessions.
//
// The specific encoding should be considered opaque and may change incompatibly
// between Go versions.
func ( *SessionState) () ([]byte, error) {
	var  cryptobyte.Builder
	.AddUint16(.version)
	if .isClient {
		.AddUint8(2) // client
	} else {
		.AddUint8(1) // server
	}
	.AddUint16(.cipherSuite)
	addUint64(&, .createdAt)
	.AddUint8LengthPrefixed(func( *cryptobyte.Builder) {
		.AddBytes(.secret)
	})
	.AddUint24LengthPrefixed(func( *cryptobyte.Builder) {
		for ,  := range .Extra {
			.AddUint24LengthPrefixed(func( *cryptobyte.Builder) {
				.AddBytes()
			})
		}
	})
	if .extMasterSecret {
		.AddUint8(1)
	} else {
		.AddUint8(0)
	}
	if .EarlyData {
		.AddUint8(1)
	} else {
		.AddUint8(0)
	}
	marshalCertificate(&, Certificate{
		Certificate:                 certificatesToBytesSlice(.peerCertificates),
		OCSPStaple:                  .ocspResponse,
		SignedCertificateTimestamps: .scts,
	})
	.AddUint24LengthPrefixed(func( *cryptobyte.Builder) {
		for ,  := range .verifiedChains {
			.AddUint24LengthPrefixed(func( *cryptobyte.Builder) {
				// We elide the first certificate because it's always the leaf.
				if len() == 0 {
					.SetError(errors.New("tls: internal error: empty verified chain"))
					return
				}
				for ,  := range [1:] {
					.AddUint24LengthPrefixed(func( *cryptobyte.Builder) {
						.AddBytes(.Raw)
					})
				}
			})
		}
	})
	if .EarlyData {
		.AddUint8LengthPrefixed(func( *cryptobyte.Builder) {
			.AddBytes([]byte(.alpnProtocol))
		})
	}
	if .isClient {
		if .version >= VersionTLS13 {
			addUint64(&, .useBy)
			.AddUint32(.ageAdd)
		}
	}
	return .Bytes()
}

func certificatesToBytesSlice( []*x509.Certificate) [][]byte {
	 := make([][]byte, 0, len())
	for ,  := range  {
		 = append(, .Raw)
	}
	return 
}

// ParseSessionState parses a [SessionState] encoded by [SessionState.Bytes].
func ( []byte) (*SessionState, error) {
	 := &SessionState{}
	 := cryptobyte.String()
	var , ,  uint8
	var  Certificate
	var  cryptobyte.String
	if !.ReadUint16(&.version) ||
		!.ReadUint8(&) ||
		( != 1 &&  != 2) ||
		!.ReadUint16(&.cipherSuite) ||
		!readUint64(&, &.createdAt) ||
		!readUint8LengthPrefixed(&, &.secret) ||
		!.ReadUint24LengthPrefixed(&) ||
		!.ReadUint8(&) ||
		!.ReadUint8(&) ||
		len(.secret) == 0 ||
		!unmarshalCertificate(&, &) {
		return nil, errors.New("tls: invalid session encoding")
	}
	for !.Empty() {
		var  []byte
		if !readUint24LengthPrefixed(&, &) {
			return nil, errors.New("tls: invalid session encoding")
		}
		.Extra = append(.Extra, )
	}
	switch  {
	case 0:
		.extMasterSecret = false
	case 1:
		.extMasterSecret = true
	default:
		return nil, errors.New("tls: invalid session encoding")
	}
	switch  {
	case 0:
		.EarlyData = false
	case 1:
		.EarlyData = true
	default:
		return nil, errors.New("tls: invalid session encoding")
	}
	for ,  := range .Certificate {
		,  := globalCertCache.newCert()
		if  != nil {
			return nil, 
		}
		.activeCertHandles = append(.activeCertHandles, )
		.peerCertificates = append(.peerCertificates, .cert)
	}
	.ocspResponse = .OCSPStaple
	.scts = .SignedCertificateTimestamps
	var  cryptobyte.String
	if !.ReadUint24LengthPrefixed(&) {
		return nil, errors.New("tls: invalid session encoding")
	}
	for !.Empty() {
		var  cryptobyte.String
		if !.ReadUint24LengthPrefixed(&) {
			return nil, errors.New("tls: invalid session encoding")
		}
		var  []*x509.Certificate
		if len(.peerCertificates) == 0 {
			return nil, errors.New("tls: invalid session encoding")
		}
		 = append(, .peerCertificates[0])
		for !.Empty() {
			var  []byte
			if !readUint24LengthPrefixed(&, &) {
				return nil, errors.New("tls: invalid session encoding")
			}
			,  := globalCertCache.newCert()
			if  != nil {
				return nil, 
			}
			.activeCertHandles = append(.activeCertHandles, )
			 = append(, .cert)
		}
		.verifiedChains = append(.verifiedChains, )
	}
	if .EarlyData {
		var  []byte
		if !readUint8LengthPrefixed(&, &) {
			return nil, errors.New("tls: invalid session encoding")
		}
		.alpnProtocol = string()
	}
	if  :=  == 2; ! {
		if !.Empty() {
			return nil, errors.New("tls: invalid session encoding")
		}
		return , nil
	}
	.isClient = true
	if len(.peerCertificates) == 0 {
		return nil, errors.New("tls: no server certificates in client session")
	}
	if .version < VersionTLS13 {
		if !.Empty() {
			return nil, errors.New("tls: invalid session encoding")
		}
		return , nil
	}
	if !.ReadUint64(&.useBy) || !.ReadUint32(&.ageAdd) || !.Empty() {
		return nil, errors.New("tls: invalid session encoding")
	}
	return , nil
}

// sessionState returns a partially filled-out [SessionState] with information
// from the current connection.
func ( *Conn) () (*SessionState, error) {
	return &SessionState{
		version:           .vers,
		cipherSuite:       .cipherSuite,
		createdAt:         uint64(.config.time().Unix()),
		alpnProtocol:      .clientProtocol,
		peerCertificates:  .peerCertificates,
		activeCertHandles: .activeCertHandles,
		ocspResponse:      .ocspResponse,
		scts:              .scts,
		isClient:          .isClient,
		extMasterSecret:   .extMasterSecret,
		verifiedChains:    .verifiedChains,
	}, nil
}

// EncryptTicket encrypts a ticket with the [Config]'s configured (or default)
// session ticket keys. It can be used as a [Config.WrapSession] implementation.
func ( *Config) ( ConnectionState,  *SessionState) ([]byte, error) {
	 := .ticketKeys(nil)
	,  := .Bytes()
	if  != nil {
		return nil, 
	}
	return .encryptTicket(, )
}

func ( *Config) ( []byte,  []ticketKey) ([]byte, error) {
	if len() == 0 {
		return nil, errors.New("tls: internal error: session ticket keys unavailable")
	}

	 := make([]byte, aes.BlockSize+len()+sha256.Size)
	 := [:aes.BlockSize]
	 := [aes.BlockSize : len()-sha256.Size]
	 := [:len()-sha256.Size]
	 := [len()-sha256.Size:]

	if ,  := io.ReadFull(.rand(), );  != nil {
		return nil, 
	}
	 := [0]
	,  := aes.NewCipher(.aesKey[:])
	if  != nil {
		return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + .Error())
	}
	cipher.NewCTR(, ).XORKeyStream(, )

	 := hmac.New(sha256.New, .hmacKey[:])
	.Write()
	.Sum([:0])

	return , nil
}

// DecryptTicket decrypts a ticket encrypted by [Config.EncryptTicket]. It can
// be used as a [Config.UnwrapSession] implementation.
//
// If the ticket can't be decrypted or parsed, DecryptTicket returns (nil, nil).
func ( *Config) ( []byte,  ConnectionState) (*SessionState, error) {
	 := .ticketKeys(nil)
	 := .decryptTicket(, )
	if  == nil {
		return nil, nil
	}
	,  := ParseSessionState()
	if  != nil {
		return nil, nil // drop unparsable tickets on the floor
	}
	return , nil
}

func ( *Config) ( []byte,  []ticketKey) []byte {
	if len() < aes.BlockSize+sha256.Size {
		return nil
	}

	 := [:aes.BlockSize]
	 := [aes.BlockSize : len()-sha256.Size]
	 := [:len()-sha256.Size]
	 := [len()-sha256.Size:]

	for ,  := range  {
		 := hmac.New(sha256.New, .hmacKey[:])
		.Write()
		 := .Sum(nil)

		if subtle.ConstantTimeCompare(, ) != 1 {
			continue
		}

		,  := aes.NewCipher(.aesKey[:])
		if  != nil {
			return nil
		}
		 := make([]byte, len())
		cipher.NewCTR(, ).XORKeyStream(, )

		return 
	}

	return nil
}

// ClientSessionState contains the state needed by a client to
// resume a previous TLS session.
type ClientSessionState struct {
	ticket  []byte
	session *SessionState
}

// ResumptionState returns the session ticket sent by the server (also known as
// the session's identity) and the state necessary to resume this session.
//
// It can be called by [ClientSessionCache.Put] to serialize (with
// [SessionState.Bytes]) and store the session.
func ( *ClientSessionState) () ( []byte,  *SessionState,  error) {
	return .ticket, .session, nil
}

// NewResumptionState returns a state value that can be returned by
// [ClientSessionCache.Get] to resume a previous session.
//
// state needs to be returned by [ParseSessionState], and the ticket and session
// state must have been returned by [ClientSessionState.ResumptionState].
func ( []byte,  *SessionState) (*ClientSessionState, error) {
	return &ClientSessionState{
		ticket: , session: ,
	}, nil
}