ipfs, libp2p, secio howto

景成和
2023-12-01

SecureOutbound, Inbound is similar

Swarm.DialAddr -> Transport.Dial -> Tpt.Upgrader.UpgradeOutbound

call stack:

github.com/libp2p/go-libp2p-secio.(*secureSession).runHandshake at protocol.go:119
github.com/libp2p/go-libp2p-secio.newSecureSession at protocol.go:95
github.com/libp2p/go-libp2p-secio.(*Transport).SecureOutbound at transport.go:40
github.com/libp2p/go-conn-security-multistream.(*SSMuxer).SecureOutbound at ssms.go:56
github.com/libp2p/go-libp2p-transport-upgrader.(*Upgrader).setupSecurity at upgrader.go:108
github.com/libp2p/go-libp2p-transport-upgrader.(*Upgrader).upgrade at upgrader.go:86
github.com/libp2p/go-libp2p-transport-upgrader.(*Upgrader).UpgradeOutbound at upgrader.go:57
github.com/libp2p/go-tcp-transport.(*TcpTransport).Dial at tcp.go:107
github.com/libp2p/go-libp2p-swarm.(*Swarm).dialAddr at swarm_dial.go:462
github.com/libp2p/go-libp2p-swarm.(*Swarm).dialAddr-fm at swarm_dial.go:450
github.com/libp2p/go-libp2p-swarm.(*dialLimiter).executeDial at limiter.go:218
runtime.goexit at asm_amd64.s:1357
 - Async stack trace
github.com/libp2p/go-libp2p-swarm.(*dialLimiter).addCheckFdLimit at limiter.go:168

After dialing, try to upgrade…

func (t *TcpTransport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (transport.CapableConn, error) {
	conn, err := t.maDial(ctx, raddr)
	if err != nil {
		return nil, err
	}
	// Set linger to 0 so we never get stuck in the TIME-WAIT state. When
	// linger is 0, connections are _reset_ instead of closed with a FIN.
	// This means we can immediately reuse the 5-tuple and reconnect.
	tryLinger(conn, 0)
	return t.Upgrader.UpgradeOutbound(ctx, t, conn, p)
}

In upgrade, upgrader might Protect and then setupSecurity/setupMuxer

func (u *Upgrader) upgrade(ctx context.Context, t transport.Transport, maconn manet.Conn, p peer.ID) (transport.CapableConn, error) {
	if u.Filters != nil && u.Filters.AddrBlocked(maconn.RemoteMultiaddr()) {
		log.Debugf("blocked connection from %s", maconn.RemoteMultiaddr())
		maconn.Close()
		return nil, fmt.Errorf("blocked connection from %s", maconn.RemoteMultiaddr())
	}

	var conn net.Conn = maconn
	if u.Protector != nil {
		pconn, err := u.Protector.Protect(conn)
		if err != nil {
			conn.Close()
			return nil, fmt.Errorf("failed to setup private network protector: %s", err)
		}
		conn = pconn
	} else if pnet.ForcePrivateNetwork {
		log.Error("tried to dial with no Private Network Protector but usage" +
			" of Private Networks is forced by the enviroment")
		return nil, pnet.ErrNotInPrivateNetwork
	}
	sconn, err := u.setupSecurity(ctx, conn, p)
	if err != nil {
		conn.Close()
		return nil, fmt.Errorf("failed to negotiate security protocol: %s", err)
	}
	smconn, err := u.setupMuxer(ctx, sconn, p)
	if err != nil {
		sconn.Close()
		return nil, fmt.Errorf("failed to negotiate security stream multiplexer: %s", err)
	}
	return &transportConn{
		MuxedConn:      smconn,
		ConnMultiaddrs: maconn,
		ConnSecurity:   sconn,
		transport:      t,
	}, nil
}

In the end, newSecureSession then runHandshake to negotiate security parameters:

func (s *secureSession) runHandshake(ctx context.Context) error {
	defer log.EventBegin(ctx, "secureHandshake", s).Done()

	result := make(chan error, 1)
	go func() {
		// do *not* close the channel (will look like a success).
		result <- s.runHandshakeSync()
	}()

	var err error
	select {
	case <-ctx.Done():
		err = ctx.Err()

		// State unknown. We *have* to close this.
		s.insecure.Close()
		// Wait for the handshake to return.
		<-result
	case err = <-result:
	}
	return err
}

Supported algorithms. AES means AES-CTR

// SupportedExchanges is the list of supported ECDH curves
var SupportedExchanges = DefaultSupportedExchanges

const DefaultSupportedExchanges = "P-256,P-384,P-521"

// SupportedCiphers is the list of supported Ciphers
var SupportedCiphers = DefaultSupportedCiphers

const DefaultSupportedCiphers = "AES-256,AES-128"

// SupportedHashes is the list of supported Hashes
var SupportedHashes = DefaultSupportedHashes

const DefaultSupportedHashes = "SHA256,SHA512"

SendMsg

mux.Session.send -> secureSession.Write -> insecureConn.Write

func (w *etmWriter) WriteMsg(b []byte) error {
	w.Lock()
	defer w.Unlock()

	// encrypt.
	buf := pool.Get(4 + len(b) + w.mac.Size())
	defer pool.Put(buf)
	data := buf[4 : 4+len(b)]
	w.str.XORKeyStream(data, b)

	// log.Debugf("ENC plaintext (%d): %s %v", len(b), b, b)
	// log.Debugf("ENC ciphertext (%d): %s %v", len(data), data, data)

	// then, mac.
	if _, err := w.mac.Write(data); err != nil {
		return err
	}

	// Sum appends.
	data = w.mac.Sum(data)
	w.mac.Reset()
	binary.BigEndian.PutUint32(buf[:4], uint32(len(data)))

	_, err := w.w.Write(buf) // Write through insecure conn
	return err
}

Callstack

github.com/libp2p/go-libp2p-secio.(*etmWriter).WriteMsg at rw.go:42
github.com/libp2p/go-libp2p-secio.(*etmWriter).Write at rw.go:34
<autogenerated>:2
<autogenerated>:2
github.com/libp2p/go-yamux.(*Session).sendLoop at session.go:472
github.com/libp2p/go-yamux.(*Session).send at session.go:384
runtime.goexit at asm_amd64.s:1357
 - Async stack trace
github.com/libp2p/go-yamux.newSession at session.go:130

ReadMsg

func (r *etmReader) ReadMsg() ([]byte, error) {
	r.Lock()
	defer r.Unlock()

	msg, err := r.msg.ReadMsg()
	if err != nil {
		return nil, err
	}

	n, err := r.macCheckThenDecrypt(msg)
	if err != nil {
		r.msg.ReleaseMsg(msg)
		return nil, err
	}
	return msg[:n], nil
}

 类似资料:

相关阅读

相关文章

相关问答