// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

package stun

import ( //nolint:gci
	  //nolint:gosec
	 //nolint:gosec
	
	
	

	
)

// separator for credentials.
const credentialsSep = ":"

// NewLongTermIntegrity returns new MessageIntegrity with key for long-term
// credentials. Password, username, and realm must be SASL-prepared.
func (, ,  string) MessageIntegrity {
	 := strings.Join([]string{, , }, credentialsSep)
	 := md5.New() //nolint:gosec
	fmt.Fprint(, )
	return MessageIntegrity(.Sum(nil))
}

// NewShortTermIntegrity returns new MessageIntegrity with key for short-term
// credentials. Password must be SASL-prepared.
func ( string) MessageIntegrity {
	return MessageIntegrity()
}

// MessageIntegrity represents MESSAGE-INTEGRITY attribute.
//
// AddTo and Check methods are using zero-allocation version of hmac, see
// newHMAC function and internal/hmac/pool.go.
//
// RFC 5389 Section 15.4
type MessageIntegrity []byte

func newHMAC(, ,  []byte) []byte {
	 := hmac.AcquireSHA1()
	writeOrPanic(, )
	defer hmac.PutSHA1()
	return .Sum()
}

func ( MessageIntegrity) () string {
	return fmt.Sprintf("KEY: 0x%x", []byte())
}

const messageIntegritySize = 20

// ErrFingerprintBeforeIntegrity means that FINGERPRINT attribute is already in
// message, so MESSAGE-INTEGRITY attribute cannot be added.
var ErrFingerprintBeforeIntegrity = errors.New("FINGERPRINT before MESSAGE-INTEGRITY attribute")

// AddTo adds MESSAGE-INTEGRITY attribute to message.
//
// CPU costly, see BenchmarkMessageIntegrity_AddTo.
func ( MessageIntegrity) ( *Message) error {
	for ,  := range .Attributes {
		// Message should not contain FINGERPRINT attribute
		// before MESSAGE-INTEGRITY.
		if .Type == AttrFingerprint {
			return ErrFingerprintBeforeIntegrity
		}
	}
	// The text used as input to HMAC is the STUN message,
	// including the header, up to and including the attribute preceding the
	// MESSAGE-INTEGRITY attribute.
	 := .Length
	// Adjusting m.Length to contain MESSAGE-INTEGRITY TLV.
	.Length += messageIntegritySize + attributeHeaderSize
	.WriteLength()                            // writing length to m.Raw
	 := newHMAC(, .Raw, .Raw[len(.Raw):]) // calculating HMAC for adjusted m.Raw
	.Length =                           // changing m.Length back

	// Copy hmac value to temporary variable to protect it from resetting
	// while processing m.Add call.
	 := make([]byte, sha1.Size)
	copy(, )

	.Add(AttrMessageIntegrity, )
	return nil
}

// ErrIntegrityMismatch means that computed HMAC differs from expected.
var ErrIntegrityMismatch = errors.New("integrity check failed")

// Check checks MESSAGE-INTEGRITY attribute.
//
// CPU costly, see BenchmarkMessageIntegrity_Check.
func ( MessageIntegrity) ( *Message) error {
	,  := .Get(AttrMessageIntegrity)
	if  != nil {
		return 
	}

	// Adjusting length in header to match m.Raw that was
	// used when computing HMAC.
	var (
		         = .Length
		 = false
		    int
	)
	for ,  := range .Attributes {
		if  {
			 += nearestPaddedValueLength(int(.Length))
			 += attributeHeaderSize
		}
		if .Type == AttrMessageIntegrity {
			 = true
		}
	}
	.Length -= uint32()
	.WriteLength()
	// startOfHMAC should be first byte of integrity attribute.
	 := messageHeaderSize + .Length - (attributeHeaderSize + messageIntegritySize)
	 := .Raw[:] // data before integrity attribute
	 := newHMAC(, , .Raw[len(.Raw):])
	.Length = 
	.WriteLength() // writing length back
	return checkHMAC(, )
}