package libp2ptls

import (
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	

	ic 
	
	
)

const certValidityPeriod = 100 * 365 * 24 * time.Hour // ~100 years
const certificatePrefix = "libp2p-tls-handshake:"
const alpn string = "libp2p"

var extensionID = getPrefixedExtensionID([]int{1, 1})
var extensionCritical bool // so we can mark the extension critical in tests

type signedKey struct {
	PubKey    []byte
	Signature []byte
}

// Identity is used to secure connections
type Identity struct {
	config tls.Config
}

// IdentityConfig is used to configure an Identity
type IdentityConfig struct {
	CertTemplate *x509.Certificate
	KeyLogWriter io.Writer
}

// IdentityOption transforms an IdentityConfig to apply optional settings.
type IdentityOption func(r *IdentityConfig)

// WithCertTemplate specifies the template to use when generating a new certificate.
func ( *x509.Certificate) IdentityOption {
	return func( *IdentityConfig) {
		.CertTemplate = 
	}
}

// WithKeyLogWriter optionally specifies a destination for TLS master secrets
// in NSS key log format that can be used to allow external programs
// such as Wireshark to decrypt TLS connections.
// See https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format.
// Use of KeyLogWriter compromises security and should only be
// used for debugging.
func ( io.Writer) IdentityOption {
	return func( *IdentityConfig) {
		.KeyLogWriter = 
	}
}

// NewIdentity creates a new identity
func ( ic.PrivKey,  ...IdentityOption) (*Identity, error) {
	 := IdentityConfig{}
	for ,  := range  {
		(&)
	}

	var  error
	if .CertTemplate == nil {
		.CertTemplate,  = certTemplate()
		if  != nil {
			return nil, 
		}
	}

	,  := keyToCertificate(, .CertTemplate)
	if  != nil {
		return nil, 
	}
	return &Identity{
		config: tls.Config{
			MinVersion:         tls.VersionTLS13,
			InsecureSkipVerify: true, // This is not insecure here. We will verify the cert chain ourselves.
			ClientAuth:         tls.RequireAnyClientCert,
			Certificates:       []tls.Certificate{*},
			VerifyPeerCertificate: func( [][]byte,  [][]*x509.Certificate) error {
				panic("tls config not specialized for peer")
			},
			NextProtos:             []string{alpn},
			SessionTicketsDisabled: true,
			KeyLogWriter:           .KeyLogWriter,
		},
	}, nil
}

// ConfigForPeer creates a new single-use tls.Config that verifies the peer's
// certificate chain and returns the peer's public key via the channel. If the
// peer ID is empty, the returned config will accept any peer.
//
// It should be used to create a new tls.Config before securing either an
// incoming or outgoing connection.
func ( *Identity) ( peer.ID) (*tls.Config, <-chan ic.PubKey) {
	 := make(chan ic.PubKey, 1)
	// We need to check the peer ID in the VerifyPeerCertificate callback.
	// The tls.Config it is also used for listening, and we might also have concurrent dials.
	// Clone it so we can check for the specific peer ID we're dialing here.
	 := .config.Clone()
	// We're using InsecureSkipVerify, so the verifiedChains parameter will always be empty.
	// We need to parse the certificates ourselves from the raw certs.
	.VerifyPeerCertificate = func( [][]byte,  [][]*x509.Certificate) ( error) {
		defer func() {
			if  := recover();  != nil {
				fmt.Fprintf(os.Stderr, "panic when processing peer certificate in TLS handshake: %s\n%s\n", , debug.Stack())
				 = fmt.Errorf("panic when processing peer certificate in TLS handshake: %s", )

			}
		}()

		defer close()

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

		,  := PubKeyFromCertChain()
		if  != nil {
			return 
		}
		if  != "" && !.MatchesPublicKey() {
			,  := peer.IDFromPublicKey()
			if  != nil {
				 = peer.ID(fmt.Sprintf("(not determined: %s)", .Error()))
			}
			return sec.ErrPeerIDMismatch{Expected: , Actual: }
		}
		 <- 
		return nil
	}
	return , 
}

// PubKeyFromCertChain verifies the certificate chain and extract the remote's public key.
func ( []*x509.Certificate) (ic.PubKey, error) {
	if len() != 1 {
		return nil, errors.New("expected one certificates in the chain")
	}
	 := [0]
	 := x509.NewCertPool()
	.AddCert()
	var  bool
	var  pkix.Extension
	// find the libp2p key extension, skipping all unknown extensions
	for ,  := range .Extensions {
		if extensionIDEqual(.Id, extensionID) {
			 = 
			 = true
			for ,  := range .UnhandledCriticalExtensions {
				if .Equal(.Id) {
					// delete the extension from UnhandledCriticalExtensions
					.UnhandledCriticalExtensions = append(.UnhandledCriticalExtensions[:], .UnhandledCriticalExtensions[+1:]...)
					break
				}
			}
			break
		}
	}
	if ! {
		return nil, errors.New("expected certificate to contain the key extension")
	}
	if ,  := .Verify(x509.VerifyOptions{Roots: });  != nil {
		// If we return an x509 error here, it will be sent on the wire.
		// Wrap the error to avoid that.
		return nil, fmt.Errorf("certificate verification failed: %s", )
	}

	var  signedKey
	if ,  := asn1.Unmarshal(.Value, &);  != nil {
		return nil, fmt.Errorf("unmarshalling signed certificate failed: %s", )
	}
	,  := ic.UnmarshalPublicKey(.PubKey)
	if  != nil {
		return nil, fmt.Errorf("unmarshalling public key failed: %s", )
	}
	,  := x509.MarshalPKIXPublicKey(.PublicKey)
	if  != nil {
		return nil, 
	}
	,  := .Verify(append([]byte(certificatePrefix), ...), .Signature)
	if  != nil {
		return nil, fmt.Errorf("signature verification failed: %s", )
	}
	if ! {
		return nil, errors.New("signature invalid")
	}
	return , nil
}

// GenerateSignedExtension uses the provided private key to sign the public key, and returns the
// signature within a pkix.Extension.
// This extension is included in a certificate to cryptographically tie it to the libp2p private key.
func ( ic.PrivKey,  crypto.PublicKey) (pkix.Extension, error) {
	,  := ic.MarshalPublicKey(.GetPublic())
	if  != nil {
		return pkix.Extension{}, 
	}
	,  := x509.MarshalPKIXPublicKey()
	if  != nil {
		return pkix.Extension{}, 
	}
	,  := .Sign(append([]byte(certificatePrefix), ...))
	if  != nil {
		return pkix.Extension{}, 
	}
	,  := asn1.Marshal(signedKey{
		PubKey:    ,
		Signature: ,
	})
	if  != nil {
		return pkix.Extension{}, 
	}

	return pkix.Extension{Id: extensionID, Critical: extensionCritical, Value: }, nil
}

// keyToCertificate generates a new ECDSA private key and corresponding x509 certificate.
// The certificate includes an extension that cryptographically ties it to the provided libp2p
// private key to authenticate TLS connections.
func keyToCertificate( ic.PrivKey,  *x509.Certificate) (*tls.Certificate, error) {
	,  := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if  != nil {
		return nil, 
	}

	// after calling CreateCertificate, these will end up in Certificate.Extensions
	,  := GenerateSignedExtension(, .Public())
	if  != nil {
		return nil, 
	}
	.ExtraExtensions = append(.ExtraExtensions, )

	,  := x509.CreateCertificate(rand.Reader, , , .Public(), )
	if  != nil {
		return nil, 
	}
	return &tls.Certificate{
		Certificate: [][]byte{},
		PrivateKey:  ,
	}, nil
}

// certTemplate returns the template for generating an Identity's TLS certificates.
func certTemplate() (*x509.Certificate, error) {
	 := big.NewInt(1 << 62)
	,  := rand.Int(rand.Reader, )
	if  != nil {
		return nil, 
	}

	,  := rand.Int(rand.Reader, )
	if  != nil {
		return nil, 
	}

	return &x509.Certificate{
		SerialNumber: ,
		NotBefore:    time.Now().Add(-time.Hour),
		NotAfter:     time.Now().Add(certValidityPeriod),
		// According to RFC 3280, the issuer field must be set,
		// see https://datatracker.ietf.org/doc/html/rfc3280#section-4.1.2.4.
		Subject: pkix.Name{SerialNumber: .String()},
	}, nil
}