Skip to content

feat: optional TLS for P2P connections (#1420) - #3907

Open
iho wants to merge 3 commits into
mimblewimble:stagingfrom
iho:feat/p2p-tls
Open

feat: optional TLS for P2P connections (#1420)#3907
iho wants to merge 3 commits into
mimblewimble:stagingfrom
iho:feat/p2p-tls

Conversation

@iho

@iho iho commented Jul 10, 2026

Copy link
Copy Markdown

Summary

Implements optional TLS for peer-to-peer connections (#1420).

  • Encrypts P2P traffic with rustls (same stack as the HTTP API) for privacy against passive packet inspection
  • Opt-in via p2p_config.tls_enabled (default false) so the existing plaintext network stays compatible
  • Self-signed certificates are auto-generated under {data_dir}/p2p_tls/ when paths are not configured
  • Clients intentionally do not verify peer certificates — goal is privacy encryption, not PKI authentication
  • Optional PEM paths: tls_certificate_file / tls_certificate_key
  • Config comments updated in grin-server.toml template

Peer discovery: Capabilities::TLS + tls_required

  • New capability bit Capabilities::TLS (0b1_0000_0000), advertised when tls_enabled = true
  • Not part of default capabilities (TLS remains opt-in for network compatibility)
  • p2p_config.tls_required (default false): when set with tls_enabled, only attempt outbound connections to peers known to advertise TLS, and request PEER_LIST | TLS in peer-list queries
  • Seeds, preferred, and allow-listed addresses are still tried for bootstrap (capabilities unknown until first successful handshake)
  • Unknown peers (no stored capabilities) are skipped when tls_required is on

Design notes

  • New Stream abstraction wraps plain TcpStream or a mutex-shared rustls session (needed because reader/writer threads cannot try_clone a TLS session the way they do TCP)
  • Both peers must enable TLS to connect; mixed TLS/plaintext will fail the handshake
  • Not consensus-breaking; pure transport layer change

How to enable

[server.p2p_config]
tls_enabled = true
# Optional: only connect to peers known to support TLS
# tls_required = true
# optional PEM paths:
# tls_certificate_file = "/path/to/p2p.crt"
# tls_certificate_key = "/path/to/p2p.key"

Testing

  • cargo test -p grin_p2p (includes peer_handshake_tls, capability / tls_required helpers)
  • cargo check -p grin_config -p grin_servers
  • Manual: two nodes with tls_enabled = true connecting on testnet/local

Consensus / compatibility

  • Not consensus breaking
  • Default remains plaintext; operators must opt in
  • Nodes with TLS enabled will not successfully complete Grin handshake with plaintext-only peers
  • With tls_required = true, automatic outbound selection prefers the TLS peer mesh once capabilities are known

Closes #1420

Comment thread servers/src/grin/seed.rs Outdated
max_peer_attempts / 2 - new_peers.len() + max_peer_attempts / 4,
max_peer_attempts / 4,
);
// When TLS is required, skip unknown peers — we only know TLS support after a successful handshake.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unknown state is valid for all new peers that are coming from peer list request

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — fixed in the rebase. Unknown peers from peer-list responses are dialed again so handshake can discover TLS (and other) capabilities. tls_required only filters peers that already have stored capabilities (healthy/defunct) via accepts_outbound_peer_capabilities.

@wiesche89 wiesche89 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please fix merge conflicts

Ihor Horobets and others added 2 commits August 2, 2026 05:23
Encrypt peer traffic with rustls for privacy against passive observers.
TLS is opt-in via p2p_config.tls_enabled (default false) so the existing
plaintext network remains compatible. When enabled, peers use self-signed
certificates (auto-generated under the node data dir if paths are not set)
and clients intentionally skip certificate authentication — encryption for
privacy only, matching the issue intent.

Wire a plain/TLS Stream abstraction through handshake, codec, and conn
I/O, and add an integration test for TLS peer handshake + ping/pong.
Advertise Capabilities::TLS when tls_enabled so peers can discover
encrypted endpoints. Add tls_required to filter outbound attempts and
peer-list requests to TLS-capable peers (seeds/preferred/allow still
used for bootstrap).

@iho iho left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebased onto current staging and resolved conflicts in servers/src/grin/seed.rs and servers/src/grin/server.rs.

Also addressed @ardocrat's note: State::Unknown peers from peer-list requests are dialed again under tls_required (capabilities are only known after handshake). Preferred-peer handling from staging is preserved.

cargo test -p grin_p2p and cargo check -p grin_config -p grin_servers pass.

P2P TLS introduced Stream; seedcheck still passed TcpStream and broke
the bin test build (Linux src / macOS CI).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants