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

package dtls

import (
	
	
	
	
	
	
	
	
	
	

	
	
)

type ecdsaSignature struct {
	R, S *big.Int
}

func valueKeyMessage(, ,  []byte,  elliptic.Curve) []byte {
	 := make([]byte, 4)
	[0] = 3 // named curve
	binary.BigEndian.PutUint16([1:], uint16())
	[3] = byte(len())

	 := []byte{}
	 = append(, ...)
	 = append(, ...)
	 = append(, ...)
	 = append(, ...)

	return 
}

// If the client provided a "signature_algorithms" extension, then all
// certificates provided by the server MUST be signed by a
// hash/signature algorithm pair that appears in that extension
//
// https://tools.ietf.org/html/rfc5246#section-7.4.2
func generateKeySignature(
	, ,  []byte,
	 elliptic.Curve,
	 crypto.Signer,
	 hash.Algorithm,
) ([]byte, error) {
	 := valueKeyMessage(, , , )
	switch .Public().(type) {
	case ed25519.PublicKey:
		// https://crypto.stackexchange.com/a/55483
		return .Sign(rand.Reader, , crypto.Hash(0))
	case *ecdsa.PublicKey:
		 := .Digest()

		return .Sign(rand.Reader, , .CryptoHash())
	case *rsa.PublicKey:
		 := .Digest()

		return .Sign(rand.Reader, , .CryptoHash())
	}

	return nil, errKeySignatureGenerateUnimplemented
}

//nolint:dupl,cyclop
func verifyKeySignature(
	,  []byte,
	 hash.Algorithm,
	 [][]byte,
) error {
	if len() == 0 {
		return errLengthMismatch
	}
	,  := x509.ParseCertificate([0])
	if  != nil {
		return 
	}

	switch pubKey := .PublicKey.(type) {
	case ed25519.PublicKey:
		if  := ed25519.Verify(, , ); ! {
			return errKeySignatureMismatch
		}

		return nil
	case *ecdsa.PublicKey:
		 := &ecdsaSignature{}
		if ,  := asn1.Unmarshal(, );  != nil {
			return 
		}
		if .R.Sign() <= 0 || .S.Sign() <= 0 {
			return errInvalidECDSASignature
		}
		 := .Digest()
		if !ecdsa.Verify(, , .R, .S) {
			return errKeySignatureMismatch
		}

		return nil
	case *rsa.PublicKey:
		 := .Digest()
		if rsa.VerifyPKCS1v15(, .CryptoHash(), , ) != nil {
			return errKeySignatureMismatch
		}

		return nil
	}

	return errKeySignatureVerifyUnimplemented
}

// If the server has sent a CertificateRequest message, the client MUST send the Certificate
// message.  The ClientKeyExchange message is now sent, and the content
// of that message will depend on the public key algorithm selected
// between the ClientHello and the ServerHello.  If the client has sent
// a certificate with signing ability, a digitally-signed
// CertificateVerify message is sent to explicitly verify possession of
// the private key in the certificate.
// https://tools.ietf.org/html/rfc5246#section-7.3
func generateCertificateVerify(
	 []byte,
	 crypto.Signer,
	 hash.Algorithm,
) ([]byte, error) {
	if ,  := .Public().(ed25519.PublicKey);  {
		// https://pkg.go.dev/crypto/ed25519#PrivateKey.Sign
		// Sign signs the given message with priv. Ed25519 performs two passes over
		// messages to be signed and therefore cannot handle pre-hashed messages.
		return .Sign(rand.Reader, , crypto.Hash(0))
	}

	 := .Digest()

	switch .Public().(type) {
	case *ecdsa.PublicKey:
		return .Sign(rand.Reader, , .CryptoHash())
	case *rsa.PublicKey:
		return .Sign(rand.Reader, , .CryptoHash())
	}

	return nil, errInvalidSignatureAlgorithm
}

//nolint:dupl,cyclop
func verifyCertificateVerify(
	 []byte,
	 hash.Algorithm,
	 []byte,
	 [][]byte,
) error {
	if len() == 0 {
		return errLengthMismatch
	}
	,  := x509.ParseCertificate([0])
	if  != nil {
		return 
	}

	switch pubKey := .PublicKey.(type) {
	case ed25519.PublicKey:
		if  := ed25519.Verify(, , ); ! {
			return errKeySignatureMismatch
		}

		return nil
	case *ecdsa.PublicKey:
		 := &ecdsaSignature{}
		if ,  := asn1.Unmarshal(, );  != nil {
			return 
		}
		if .R.Sign() <= 0 || .S.Sign() <= 0 {
			return errInvalidECDSASignature
		}
		 := .Digest()
		if !ecdsa.Verify(, , .R, .S) {
			return errKeySignatureMismatch
		}

		return nil
	case *rsa.PublicKey:
		 := .Digest()
		if rsa.VerifyPKCS1v15(, .CryptoHash(), , ) != nil {
			return errKeySignatureMismatch
		}

		return nil
	}

	return errKeySignatureVerifyUnimplemented
}

func loadCerts( [][]byte) ([]*x509.Certificate, error) {
	if len() == 0 {
		return nil, errLengthMismatch
	}

	 := make([]*x509.Certificate, 0, len())
	for ,  := range  {
		,  := x509.ParseCertificate()
		if  != nil {
			return nil, 
		}
		 = append(, )
	}

	return , nil
}

func verifyClientCert( [][]byte,  *x509.CertPool) ( [][]*x509.Certificate,  error) {
	,  := loadCerts()
	if  != nil {
		return nil, 
	}
	 := x509.NewCertPool()
	for ,  := range [1:] {
		.AddCert()
	}
	 := x509.VerifyOptions{
		Roots:         ,
		CurrentTime:   time.Now(),
		Intermediates: ,
		KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
	}

	return [0].Verify()
}

func verifyServerCert(
	 [][]byte,
	 *x509.CertPool,
	 string,
) ( [][]*x509.Certificate,  error) {
	,  := loadCerts()
	if  != nil {
		return nil, 
	}
	 := x509.NewCertPool()
	for ,  := range [1:] {
		.AddCert()
	}
	 := x509.VerifyOptions{
		Roots:         ,
		CurrentTime:   time.Now(),
		DNSName:       ,
		Intermediates: ,
	}

	return [0].Verify()
}