package handshake

import (
	
	
	
	tls 
	

	
	
	
	
	
	
)

var keyUpdateInterval atomic.Uint64

func init() {
	keyUpdateInterval.Store(protocol.KeyUpdateInterval)
}

func ( uint64) ( func()) {
	 := keyUpdateInterval.Swap()
	return func() { keyUpdateInterval.Store() }
}

// FirstKeyUpdateInterval is the maximum number of packets we send or receive before initiating the first key update.
// It's a package-level variable to allow modifying it for testing purposes.
var FirstKeyUpdateInterval uint64 = 100

type updatableAEAD struct {
	suite cipherSuite

	keyPhase           protocol.KeyPhase
	largestAcked       protocol.PacketNumber
	firstPacketNumber  protocol.PacketNumber
	handshakeConfirmed bool

	invalidPacketLimit uint64
	invalidPacketCount uint64

	// Time when the keys should be dropped. Keys are dropped on the next call to Open().
	prevRcvAEADExpiry monotime.Time
	prevRcvAEAD       cipher.AEAD

	firstRcvdWithCurrentKey protocol.PacketNumber
	firstSentWithCurrentKey protocol.PacketNumber
	// highestRcvdPN is the highest packet number successfully unprotected, kept
	// per draft-ietf-quic-multipath PathID: each path has its own packet-number
	// space, so a high-PN packet on one path must not skew the truncated
	// packet-number reconstruction of a low-PN packet on another. With multipath
	// off the map holds only the PathIDZero entry and behaves like the former
	// single global value.
	highestRcvdPN         map[protocol.PathID]protocol.PacketNumber
	numRcvdWithCurrentKey uint64
	numSentWithCurrentKey uint64
	rcvAEAD               cipher.AEAD
	sendAEAD              cipher.AEAD
	// caches cipher.AEAD.Overhead(). This speeds up calls to Overhead().
	aeadOverhead int

	nextRcvAEAD           cipher.AEAD
	nextSendAEAD          cipher.AEAD
	nextRcvTrafficSecret  []byte
	nextSendTrafficSecret []byte

	headerDecrypter headerProtector
	headerEncrypter headerProtector

	rttStats *utils.RTTStats

	qlogger qlogwriter.Recorder
	logger  utils.Logger
	version protocol.Version

	// use a single slice to avoid allocations
	nonceBuf []byte
}

var (
	_ ShortHeaderOpener = &updatableAEAD{}
	_ ShortHeaderSealer = &updatableAEAD{}
)

func newUpdatableAEAD( *utils.RTTStats,  qlogwriter.Recorder,  utils.Logger,  protocol.Version) *updatableAEAD {
	return &updatableAEAD{
		firstPacketNumber:       protocol.InvalidPacketNumber,
		largestAcked:            protocol.InvalidPacketNumber,
		firstRcvdWithCurrentKey: protocol.InvalidPacketNumber,
		firstSentWithCurrentKey: protocol.InvalidPacketNumber,
		highestRcvdPN:           map[protocol.PathID]protocol.PacketNumber{},
		rttStats:                ,
		qlogger:                 ,
		logger:                  ,
		version:                 ,
	}
}

func ( *updatableAEAD) () {
	if .prevRcvAEAD != nil {
		.logger.Debugf("Dropping key phase %d ahead of scheduled time. Drop time was: %s", .keyPhase-1, .prevRcvAEADExpiry)
		if .qlogger != nil {
			.qlogger.RecordEvent(qlog.KeyDiscarded{
				KeyType:  qlog.KeyTypeClient1RTT,
				KeyPhase: .keyPhase - 1,
			})
			.qlogger.RecordEvent(qlog.KeyDiscarded{
				KeyType:  qlog.KeyTypeServer1RTT,
				KeyPhase: .keyPhase - 1,
			})
		}
		.prevRcvAEADExpiry = 0
	}

	.keyPhase++
	.firstRcvdWithCurrentKey = protocol.InvalidPacketNumber
	.firstSentWithCurrentKey = protocol.InvalidPacketNumber
	.numRcvdWithCurrentKey = 0
	.numSentWithCurrentKey = 0
	.prevRcvAEAD = .rcvAEAD
	.rcvAEAD = .nextRcvAEAD
	.sendAEAD = .nextSendAEAD

	.nextRcvTrafficSecret = .getNextTrafficSecret(.suite.Hash, .nextRcvTrafficSecret)
	.nextSendTrafficSecret = .getNextTrafficSecret(.suite.Hash, .nextSendTrafficSecret)
	.nextRcvAEAD = createAEAD(.suite, .nextRcvTrafficSecret, .version)
	.nextSendAEAD = createAEAD(.suite, .nextSendTrafficSecret, .version)
}

func ( *updatableAEAD) ( monotime.Time) {
	 := 3 * .rttStats.PTO(true)
	.logger.Debugf("Starting key drop timer to drop key phase %d (in %s)", .keyPhase-1, )
	.prevRcvAEADExpiry = .Add()
}

func ( *updatableAEAD) ( crypto.Hash,  []byte) []byte {
	return hkdfExpandLabel(, , []byte{}, "quic ku", .Size())
}

// SetReadKey sets the read key.
// For the client, this function is called before SetWriteKey.
// For the server, this function is called after SetWriteKey.
func ( *updatableAEAD) ( cipherSuite,  []byte) {
	.rcvAEAD = createAEAD(, , .version)
	.headerDecrypter = newHeaderProtector(, , false, .version)
	if .suite.ID == 0 { // suite is not set yet
		.setAEADParameters(.rcvAEAD, )
	}

	.nextRcvTrafficSecret = .getNextTrafficSecret(.Hash, )
	.nextRcvAEAD = createAEAD(, .nextRcvTrafficSecret, .version)
}

// SetWriteKey sets the write key.
// For the client, this function is called after SetReadKey.
// For the server, this function is called before SetReadKey.
func ( *updatableAEAD) ( cipherSuite,  []byte) {
	.sendAEAD = createAEAD(, , .version)
	.headerEncrypter = newHeaderProtector(, , false, .version)
	if .suite.ID == 0 { // suite is not set yet
		.setAEADParameters(.sendAEAD, )
	}

	.nextSendTrafficSecret = .getNextTrafficSecret(.Hash, )
	.nextSendAEAD = createAEAD(, .nextSendTrafficSecret, .version)
}

func ( *updatableAEAD) ( cipher.AEAD,  cipherSuite) {
	// 12 bytes holds the draft-ietf-quic-multipath ยง2.4 path-and-packet-number
	// (path id + packet number); putPathNonce writes the trailing 8 bytes for
	// PathIDZero and all 12 for a non-zero path. aead.NonceSize() is 8 (the
	// xorNonceAEAD packet-number size), so this is strictly larger.
	.nonceBuf = make([]byte, aeadNonceLength)
	.aeadOverhead = .Overhead()
	.suite = 
	switch .ID {
	case tls.TLS_AES_128_GCM_SHA256, tls.TLS_AES_256_GCM_SHA384:
		.invalidPacketLimit = protocol.InvalidPacketLimitAES
	case tls.TLS_CHACHA20_POLY1305_SHA256:
		.invalidPacketLimit = protocol.InvalidPacketLimitChaCha
	default:
		panic(fmt.Sprintf("unknown cipher suite %d", .ID))
	}
}

func ( *updatableAEAD) ( protocol.PathID,  protocol.PacketNumber,  protocol.PacketNumberLen) protocol.PacketNumber {
	// The zero value (no packet yet seen on pid) is packet number 0, matching the
	// former single global highestRcvdPN field, so PathIDZero decode is unchanged.
	return protocol.DecodePacketNumber(, .highestRcvdPN[], )
}

func ( *updatableAEAD) (,  []byte,  monotime.Time,  protocol.PathID,  protocol.PacketNumber,  protocol.KeyPhaseBit,  []byte) ([]byte, error) {
	,  := .open(, , , , , , )
	if  == ErrDecryptionFailed {
		.invalidPacketCount++
		if .invalidPacketCount >= .invalidPacketLimit {
			return nil, &qerr.TransportError{ErrorCode: qerr.AEADLimitReached}
		}
	}
	if  == nil {
		.highestRcvdPN[] = max(.highestRcvdPN[], )
	}
	return , 
}

func ( *updatableAEAD) (,  []byte,  monotime.Time,  protocol.PathID,  protocol.PacketNumber,  protocol.KeyPhaseBit,  []byte) ([]byte, error) {
	if .prevRcvAEAD != nil && !.prevRcvAEADExpiry.IsZero() && .After(.prevRcvAEADExpiry) {
		.prevRcvAEAD = nil
		.logger.Debugf("Dropping key phase %d", .keyPhase-1)
		.prevRcvAEADExpiry = 0
		if .qlogger != nil {
			.qlogger.RecordEvent(qlog.KeyDiscarded{
				KeyType:  qlog.KeyTypeClient1RTT,
				KeyPhase: .keyPhase - 1,
			})
			.qlogger.RecordEvent(qlog.KeyDiscarded{
				KeyType:  qlog.KeyTypeServer1RTT,
				KeyPhase: .keyPhase - 1,
			})
		}
	}
	 := putPathNonce(.nonceBuf, , )
	if  != .keyPhase.Bit() {
		if .keyPhase > 0 && .firstRcvdWithCurrentKey == protocol.InvalidPacketNumber ||  < .firstRcvdWithCurrentKey {
			if .prevRcvAEAD == nil {
				return nil, ErrKeysDropped
			}
			// we updated the key, but the peer hasn't updated yet
			,  := .prevRcvAEAD.Open(, , , )
			if  != nil {
				 = ErrDecryptionFailed
			}
			return , 
		}
		// try opening the packet with the next key phase
		,  := .nextRcvAEAD.Open(, , , )
		if  != nil {
			return nil, ErrDecryptionFailed
		}
		// Opening succeeded. Check if the peer was allowed to update.
		if .keyPhase > 0 && .firstSentWithCurrentKey == protocol.InvalidPacketNumber {
			return nil, &qerr.TransportError{
				ErrorCode:    qerr.KeyUpdateError,
				ErrorMessage: "keys updated too quickly",
			}
		}
		.rollKeys()
		.logger.Debugf("Peer updated keys to %d", .keyPhase)
		// The peer initiated this key update. It's safe to drop the keys for the previous generation now.
		// Start a timer to drop the previous key generation.
		.startKeyDropTimer()
		if .qlogger != nil {
			.qlogger.RecordEvent(qlog.KeyUpdated{
				Trigger:  qlog.KeyUpdateRemote,
				KeyType:  qlog.KeyTypeClient1RTT,
				KeyPhase: .keyPhase,
			})
			.qlogger.RecordEvent(qlog.KeyUpdated{
				Trigger:  qlog.KeyUpdateRemote,
				KeyType:  qlog.KeyTypeServer1RTT,
				KeyPhase: .keyPhase,
			})
		}
		.firstRcvdWithCurrentKey = 
		return , 
	}
	// The AEAD we're using here will be the qtls.aeadAESGCM13.
	// It uses the nonce provided here and XOR it with the IV.
	,  := .rcvAEAD.Open(, , , )
	if  != nil {
		return , ErrDecryptionFailed
	}
	.numRcvdWithCurrentKey++
	if .firstRcvdWithCurrentKey == protocol.InvalidPacketNumber {
		// We initiated the key updated, and now we received the first packet protected with the new key phase.
		// Therefore, we are certain that the peer rolled its keys as well. Start a timer to drop the old keys.
		if .keyPhase > 0 {
			.logger.Debugf("Peer confirmed key update to phase %d", .keyPhase)
			.startKeyDropTimer()
		}
		.firstRcvdWithCurrentKey = 
	}
	return , 
}

func ( *updatableAEAD) (,  []byte,  protocol.PathID,  protocol.PacketNumber,  []byte) []byte {
	if .firstSentWithCurrentKey == protocol.InvalidPacketNumber {
		.firstSentWithCurrentKey = 
	}
	if .firstPacketNumber == protocol.InvalidPacketNumber {
		.firstPacketNumber = 
	}
	.numSentWithCurrentKey++
	// The AEAD we're using here will be the qtls.aeadAESGCM13.
	// It uses the nonce provided here and XOR it with the IV.
	return .sendAEAD.Seal(, putPathNonce(.nonceBuf, , ), , )
}

func ( *updatableAEAD) ( protocol.PacketNumber) error {
	if .firstSentWithCurrentKey != protocol.InvalidPacketNumber &&
		 >= .firstSentWithCurrentKey && .numRcvdWithCurrentKey == 0 {
		return &qerr.TransportError{
			ErrorCode:    qerr.KeyUpdateError,
			ErrorMessage: fmt.Sprintf("received ACK for key phase %d, but peer didn't update keys", .keyPhase),
		}
	}
	.largestAcked = 
	return nil
}

func ( *updatableAEAD) () {
	.handshakeConfirmed = true
}

func ( *updatableAEAD) () bool {
	if !.handshakeConfirmed {
		return false
	}
	// the first key update is allowed as soon as the handshake is confirmed
	return .keyPhase == 0 ||
		// subsequent key updates as soon as a packet sent with that key phase has been acknowledged
		(.firstSentWithCurrentKey != protocol.InvalidPacketNumber &&
			.largestAcked != protocol.InvalidPacketNumber &&
			.largestAcked >= .firstSentWithCurrentKey)
}

func ( *updatableAEAD) () bool {
	if !.updateAllowed() {
		return false
	}
	// Initiate the first key update shortly after the handshake, in order to exercise the key update mechanism.
	if .keyPhase == 0 {
		if .numRcvdWithCurrentKey >= FirstKeyUpdateInterval || .numSentWithCurrentKey >= FirstKeyUpdateInterval {
			return true
		}
	}
	if .numRcvdWithCurrentKey >= keyUpdateInterval.Load() {
		.logger.Debugf("Received %d packets with current key phase. Initiating key update to the next key phase: %d", .numRcvdWithCurrentKey, .keyPhase+1)
		return true
	}
	if .numSentWithCurrentKey >= keyUpdateInterval.Load() {
		.logger.Debugf("Sent %d packets with current key phase. Initiating key update to the next key phase: %d", .numSentWithCurrentKey, .keyPhase+1)
		return true
	}
	return false
}

func ( *updatableAEAD) () protocol.KeyPhaseBit {
	if .shouldInitiateKeyUpdate() {
		.rollKeys()
		if .qlogger != nil {
			.qlogger.RecordEvent(qlog.KeyUpdated{
				Trigger:  qlog.KeyUpdateLocal,
				KeyType:  qlog.KeyTypeClient1RTT,
				KeyPhase: .keyPhase,
			})
			.qlogger.RecordEvent(qlog.KeyUpdated{
				Trigger:  qlog.KeyUpdateLocal,
				KeyType:  qlog.KeyTypeServer1RTT,
				KeyPhase: .keyPhase,
			})
		}
	}
	return .keyPhase.Bit()
}

func ( *updatableAEAD) () int {
	return .aeadOverhead
}

func ( *updatableAEAD) ( []byte,  *byte,  []byte) {
	.headerEncrypter.EncryptHeader(, , )
}

func ( *updatableAEAD) ( []byte,  *byte,  []byte) {
	.headerDecrypter.DecryptHeader(, , )
}

func ( *updatableAEAD) () protocol.PacketNumber {
	return .firstPacketNumber
}