package libp2ptls

import (
	
	
	
	
	
	
	

	
	ci 
	
	
	
	
	tptu 

	manet 
)

// ID is the protocol ID (used when negotiating with multistream)
const ID = "/tls/1.0.0"

// Transport constructs secure communication sessions for a peer.
type Transport struct {
	identity *Identity

	localPeer  peer.ID
	privKey    ci.PrivKey
	muxers     []protocol.ID
	protocolID protocol.ID
}

var _ sec.SecureTransport = &Transport{}

// New creates a TLS encrypted transport
func ( protocol.ID,  ci.PrivKey,  []tptu.StreamMuxer) (*Transport, error) {
	,  := peer.IDFromPrivateKey()
	if  != nil {
		return nil, 
	}
	 := make([]protocol.ID, 0, len())
	for ,  := range  {
		 = append(, .ID)
	}
	 := &Transport{
		protocolID: ,
		localPeer:  ,
		privKey:    ,
		muxers:     ,
	}

	,  := NewIdentity()
	if  != nil {
		return nil, 
	}
	.identity = 
	return , nil
}

// SecureInbound runs the TLS handshake as a server.
// If p is empty, connections from any peer are accepted.
func ( *Transport) ( context.Context,  net.Conn,  peer.ID) (sec.SecureConn, error) {
	,  := .identity.ConfigForPeer()
	 := make([]string, 0, len(.muxers))
	for ,  := range .muxers {
		 = append(, string())
	}
	// TLS' ALPN selection lets the server select the protocol, preferring the server's preferences.
	// We want to prefer the client's preference though.
	 := .GetConfigForClient
	.GetConfigForClient = func( *tls.ClientHelloInfo) (*tls.Config, error) {
	:
		for ,  := range .SupportedProtos {
			for ,  := range  {
				if  ==  {
					// Match found. Select this muxer, as it's the client's preference.
					// There's no need to add the "libp2p" entry here.
					.NextProtos = []string{}
					break 
				}
			}
		}
		if  != nil {
			return ()
		}
		return , nil
	}
	.NextProtos = append(, .NextProtos...)
	,  := .handshake(, tls.Server(, ), )
	if  != nil {
		,  := manet.FromNetAddr(.RemoteAddr())
		if  == nil {
			canonicallog.LogPeerStatus(100, , , "handshake_failure", "tls", "err", .Error())
		}
		.Close()
	}
	return , 
}

// SecureOutbound runs the TLS handshake as a client.
// Note that SecureOutbound will not return an error if the server doesn't
// accept the certificate. This is due to the fact that in TLS 1.3, the client
// sends its certificate and the ClientFinished in the same flight, and can send
// application data immediately afterwards.
// If the handshake fails, the server will close the connection. The client will
// notice this after 1 RTT when calling Read.
func ( *Transport) ( context.Context,  net.Conn,  peer.ID) (sec.SecureConn, error) {
	,  := .identity.ConfigForPeer()
	 := make([]string, 0, len(.muxers))
	for ,  := range .muxers {
		 = append(, (string)())
	}
	// Prepend the preferred muxers list to TLS config.
	.NextProtos = append(, .NextProtos...)
	,  := .handshake(, tls.Client(, ), )
	if  != nil {
		.Close()
	}
	return , 
}

func ( *Transport) ( context.Context,  *tls.Conn,  <-chan ci.PubKey) ( sec.SecureConn,  error) {
	defer func() {
		if  := recover();  != nil {
			fmt.Fprintf(os.Stderr, "panic in TLS handshake: %s\n%s\n", , debug.Stack())
			 = fmt.Errorf("panic in TLS handshake: %s", )

		}
	}()

	// handshaking...
	if  := .HandshakeContext();  != nil {
		return nil, 
	}

	// Should be ready by this point, don't block.
	var  ci.PubKey
	select {
	case  = <-:
	default:
	}
	if  == nil {
		return nil, errors.New("go-libp2p tls BUG: expected remote pub key to be set")
	}

	return .setupConn(, )
}

func ( *Transport) ( *tls.Conn,  ci.PubKey) (sec.SecureConn, error) {
	,  := peer.IDFromPublicKey()
	if  != nil {
		return nil, 
	}

	 := .ConnectionState().NegotiatedProtocol
	// The special ALPN extension value "libp2p" is used by libp2p versions
	// that don't support early muxer negotiation. If we see this sepcial
	// value selected, that means we are handshaking with a version that does
	// not support early muxer negotiation. In this case return empty nextProto
	// to indicate no muxer is selected.
	if  == "libp2p" {
		 = ""
	}

	return &conn{
		Conn:         ,
		localPeer:    .localPeer,
		remotePeer:   ,
		remotePubKey: ,
		connectionState: network.ConnectionState{
			StreamMultiplexer:         protocol.ID(),
			UsedEarlyMuxerNegotiation:  != "",
		},
	}, nil
}

func ( *Transport) () protocol.ID {
	return .protocolID
}