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

	
)

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

	// usedOldKey is true if the ticket from which this session came from
	// was encrypted with an older key and thus should be refreshed.
	usedOldKey bool
}

func ( *sessionState) () []byte {
	var  cryptobyte.Builder
	.AddUint16(.vers)
	.AddUint16(.cipherSuite)
	addUint64(&, .createdAt)
	.AddUint16LengthPrefixed(func( *cryptobyte.Builder) {
		.AddBytes(.masterSecret)
	})
	.AddUint24LengthPrefixed(func( *cryptobyte.Builder) {
		for ,  := range .certificates {
			.AddUint24LengthPrefixed(func( *cryptobyte.Builder) {
				.AddBytes()
			})
		}
	})
	return .BytesOrPanic()
}

func ( *sessionState) ( []byte) bool {
	* = sessionState{usedOldKey: .usedOldKey}
	 := cryptobyte.String()
	if  := .ReadUint16(&.vers) &&
		.ReadUint16(&.cipherSuite) &&
		readUint64(&, &.createdAt) &&
		readUint16LengthPrefixed(&, &.masterSecret) &&
		len(.masterSecret) != 0; ! {
		return false
	}
	var  cryptobyte.String
	if !.ReadUint24LengthPrefixed(&) {
		return false
	}
	for !.Empty() {
		var  []byte
		if !readUint24LengthPrefixed(&, &) {
			return false
		}
		.certificates = append(.certificates, )
	}
	return .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 ( *sessionStateTLS13) () []byte {
	var  cryptobyte.Builder
	.AddUint16(VersionTLS13)
	.AddUint8(0) // revision
	.AddUint16(.cipherSuite)
	addUint64(&, .createdAt)
	.AddUint8LengthPrefixed(func( *cryptobyte.Builder) {
		.AddBytes(.resumptionSecret)
	})
	marshalCertificate(&, .certificate)
	return .BytesOrPanic()
}

func ( *sessionStateTLS13) ( []byte) bool {
	* = sessionStateTLS13{}
	 := cryptobyte.String()
	var  uint16
	var  uint8
	return .ReadUint16(&) &&
		 == VersionTLS13 &&
		.ReadUint8(&) &&
		 == 0 &&
		.ReadUint16(&.cipherSuite) &&
		readUint64(&, &.createdAt) &&
		readUint8LengthPrefixed(&, &.resumptionSecret) &&
		len(.resumptionSecret) != 0 &&
		unmarshalCertificate(&, &.certificate) &&
		.Empty()
}

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

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

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

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

	return , nil
}

func ( *Conn) ( []byte) ( []byte,  bool) {
	if len() < ticketKeyNameLen+aes.BlockSize+sha256.Size {
		return nil, false
	}

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

	 := -1
	for ,  := range .ticketKeys {
		if bytes.Equal(, .keyName[:]) {
			 = 
			break
		}
	}
	if  == -1 {
		return nil, false
	}
	 := &.ticketKeys[]

	 := hmac.New(sha256.New, .hmacKey[:])
	.Write([:len()-sha256.Size])
	 := .Sum(nil)

	if subtle.ConstantTimeCompare(, ) != 1 {
		return nil, false
	}

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

	return ,  > 0
}