package noise

import (
	
	
	
	
	
	
	

	
	
	
	

	
	pool 
	
)

// payloadSigPrefix is prepended to our Noise static key before signing with
// our libp2p identity key.
const payloadSigPrefix = "noise-libp2p-static-key:"

// All noise session share a fixed cipher suite
var cipherSuite = noise.NewCipherSuite(noise.DH25519, noise.CipherChaChaPoly, noise.HashSHA256)

// runHandshake exchanges handshake messages with the remote peer to establish
// a noise-libp2p session. It blocks until the handshake completes or fails.
func ( *secureSession) ( context.Context) ( error) {
	defer func() {
		if  := recover();  != nil {
			fmt.Fprintf(os.Stderr, "caught panic: %s\n%s\n", , debug.Stack())
			 = fmt.Errorf("panic in Noise handshake: %s", )
		}
	}()

	,  := noise.DH25519.GenerateKeypair(rand.Reader)
	if  != nil {
		return fmt.Errorf("error generating static keypair: %w", )
	}

	 := noise.Config{
		CipherSuite:   cipherSuite,
		Pattern:       noise.HandshakeXX,
		Initiator:     .initiator,
		StaticKeypair: ,
		Prologue:      .prologue,
	}

	,  := noise.NewHandshakeState()
	if  != nil {
		return fmt.Errorf("error initializing handshake state: %w", )
	}

	// set a deadline to complete the handshake, if one has been supplied.
	// clear it after we're done.
	if ,  := .Deadline();  {
		if  := .SetDeadline();  == nil {
			// schedule the deadline removal once we're done handshaking.
			defer .SetDeadline(time.Time{})
		}
	}

	// We can re-use this buffer for all handshake messages.
	 := pool.Get(2 << 10)
	defer pool.Put()

	if .initiator {
		// stage 0 //
		// Handshake Msg Len = len(DH ephemeral key)
		if  := .sendHandshakeMessage(, nil, );  != nil {
			return fmt.Errorf("error sending handshake message: %w", )
		}

		// stage 1 //
		,  := .readHandshakeMessage()
		if  != nil {
			return fmt.Errorf("error reading handshake message: %w", )
		}
		,  := .handleRemoteHandshakePayload(, .PeerStatic())
		if  != nil {
			return 
		}
		if .initiatorEarlyDataHandler != nil {
			if  := .initiatorEarlyDataHandler.Received(, .insecureConn, );  != nil {
				return 
			}
		}

		// stage 2 //
		// Handshake Msg Len = len(DHT static key) +  MAC(static key is encrypted) + len(Payload) + MAC(payload is encrypted)
		var  *pb.NoiseExtensions
		if .initiatorEarlyDataHandler != nil {
			 = .initiatorEarlyDataHandler.Send(, .insecureConn, .remoteID)
		}
		,  := .generateHandshakePayload(, )
		if  != nil {
			return 
		}
		if  := .sendHandshakeMessage(, , );  != nil {
			return fmt.Errorf("error sending handshake message: %w", )
		}
		return nil
	} else {
		// stage 0 //
		if ,  := .readHandshakeMessage();  != nil {
			return fmt.Errorf("error reading handshake message: %w", )
		}

		// stage 1 //
		// Handshake Msg Len = len(DH ephemeral key) + len(DHT static key) +  MAC(static key is encrypted) + len(Payload) +
		// MAC(payload is encrypted)
		var  *pb.NoiseExtensions
		if .responderEarlyDataHandler != nil {
			 = .responderEarlyDataHandler.Send(, .insecureConn, .remoteID)
		}
		,  := .generateHandshakePayload(, )
		if  != nil {
			return 
		}
		if  := .sendHandshakeMessage(, , );  != nil {
			return fmt.Errorf("error sending handshake message: %w", )
		}

		// stage 2 //
		,  := .readHandshakeMessage()
		if  != nil {
			return fmt.Errorf("error reading handshake message: %w", )
		}
		,  := .handleRemoteHandshakePayload(, .PeerStatic())
		if  != nil {
			return 
		}
		if .responderEarlyDataHandler != nil {
			if  := .responderEarlyDataHandler.Received(, .insecureConn, );  != nil {
				return 
			}
		}
		return nil
	}
}

// setCipherStates sets the initial cipher states that will be used to protect
// traffic after the handshake.
//
// It is called when the final handshake message is processed by
// either sendHandshakeMessage or readHandshakeMessage.
func ( *secureSession) (,  *noise.CipherState) {
	if .initiator {
		.enc = 
		.dec = 
	} else {
		.enc = 
		.dec = 
	}
}

// sendHandshakeMessage sends the next handshake message in the sequence.
//
// If payload is non-empty, it will be included in the handshake message.
// If this is the final message in the sequence, calls setCipherStates
// to initialize cipher states.
func ( *secureSession) ( *noise.HandshakeState,  []byte,  []byte) error {
	// the first two bytes will be the length of the noise handshake message.
	, , ,  := .WriteMessage([:LengthPrefixLength], )
	if  != nil {
		return 
	}

	// bz will also include the length prefix as we passed a slice of LengthPrefixLength length
	// to hs.Write().
	binary.BigEndian.PutUint16(, uint16(len()-LengthPrefixLength))

	_,  = .writeMsgInsecure()
	if  != nil {
		return 
	}

	if  != nil &&  != nil {
		.setCipherStates(, )
	}
	return nil
}

// readHandshakeMessage reads a message from the insecure conn and tries to
// process it as the expected next message in the handshake sequence.
//
// If the message contains a payload, it will be decrypted and returned.
//
// If this is the final message in the sequence, it calls setCipherStates
// to initialize cipher states.
func ( *secureSession) ( *noise.HandshakeState) ([]byte, error) {
	,  := .readNextInsecureMsgLen()
	if  != nil {
		return nil, 
	}

	 := pool.Get()
	defer pool.Put()

	if  := .readNextMsgInsecure();  != nil {
		return nil, 
	}

	, , ,  := .ReadMessage(nil, )
	if  != nil {
		return nil, 
	}
	if  != nil &&  != nil {
		.setCipherStates(, )
	}
	return , nil
}

// generateHandshakePayload creates a libp2p handshake payload with a
// signature of our static noise key.
func ( *secureSession) ( noise.DHKey,  *pb.NoiseExtensions) ([]byte, error) {
	// obtain the public key from the handshake session, so we can sign it with
	// our libp2p secret key.
	,  := crypto.MarshalPublicKey(.LocalPublicKey())
	if  != nil {
		return nil, fmt.Errorf("error serializing libp2p identity key: %w", )
	}

	// prepare payload to sign; perform signature.
	 := append([]byte(payloadSigPrefix), .Public...)
	,  := .localKey.Sign()
	if  != nil {
		return nil, fmt.Errorf("error sigining handshake payload: %w", )
	}

	// create payload
	,  := proto.Marshal(&pb.NoiseHandshakePayload{
		IdentityKey: ,
		IdentitySig: ,
		Extensions:  ,
	})
	if  != nil {
		return nil, fmt.Errorf("error marshaling handshake payload: %w", )
	}
	return , nil
}

// handleRemoteHandshakePayload unmarshals the handshake payload object sent
// by the remote peer and validates the signature against the peer's static Noise key.
// It returns the data attached to the payload.
func ( *secureSession) ( []byte,  []byte) (*pb.NoiseExtensions, error) {
	// unmarshal payload
	 := new(pb.NoiseHandshakePayload)
	 := proto.Unmarshal(, )
	if  != nil {
		return nil, fmt.Errorf("error unmarshaling remote handshake payload: %w", )
	}

	// unpack remote peer's public libp2p key
	,  := crypto.UnmarshalPublicKey(.GetIdentityKey())
	if  != nil {
		return nil, 
	}
	,  := peer.IDFromPublicKey()
	if  != nil {
		return nil, 
	}

	// check the peer ID if enabled
	if .checkPeerID && .remoteID !=  {
		return nil, sec.ErrPeerIDMismatch{Expected: .remoteID, Actual: }
	}

	// verify payload is signed by asserted remote libp2p key.
	 := .GetIdentitySig()
	 := append([]byte(payloadSigPrefix), ...)
	,  := .Verify(, )
	if  != nil {
		return nil, fmt.Errorf("error verifying signature: %w", )
	} else if ! {
		return nil, fmt.Errorf("handshake signature invalid")
	}

	// set remote peer key and id
	.remoteID = 
	.remoteKey = 
	return .Extensions, nil
}