Files
tsunagi/crates/tsunagi-tcp-tls/tests/cert_tests.rs
T
Aleksandr Bogomiakov fac3bd7bb9 Add tcp-tls transport and tunnel protocol disguised as HTTPS
Implement the `tcp-tls` protocol crate and generic tunnel codec abstractions
alongside `wg-quic` to circumvent deep packet inspection (DPI/TSPU), UDP
blocking, and QUIC throttling. Traffic uses standard TCP on port 443 with
TLS 1.3 framing, looking identical to regular HTTPS traffic to network
observers.

Architectural highlights:
- TunnelCodec & GenericTunnelPlugin: Provide an extensible plugin abstraction
  in `crates/tsunagi/src/dataplane/tunnel.rs` that encapsulates background task
  lifecycles, per-peer channels, and sink dispatch, so new transport plugins
  only define cryptographic transformations and handshake payloads.
- MultiPacketTransport & Protocol Priority Selection: Support multiple
  simultaneous transports in `AgentConfig` and dynamically dispatch `open()`
  calls by protocol identifier. In `ensure_links`, choose the highest-priority
  mutually supported protocol per peer to avoid duplicate links.
- TLS 1.3 & Certificate Pinning: Generate self-signed Ed25519 X.509 certificates
  derived from the persistent `DeviceIdentity` secret. Clients pin peer
  certificates using `PinnedEndpointVerifier` against `iroh::EndpointId`.
- End-to-end Encryption: `PeerCipher` uses X25519 static-static ECDH, HKDF-SHA256,
  and ChaCha20-Poly1305 with incrementing 64-bit nonces. Relayed traffic across
  intermediate nodes remains confidential and tamper-proof.
- Privilege handling: Default to TCP port 443 with fallback to 8443 on
  `PermissionDenied`, providing an actionable message recommending:
  `sudo setcap cap_net_admin,cap_net_bind_service+p <path>`.

Validation on Linux:
- `cargo fmt --all -- --check` passed.
- `cargo clippy --locked --workspace --all-targets -- -D warnings` passed.
- `cargo test --locked --workspace --all-targets` passed (all workspace unit,
  integration, CLI, and new `tsunagi-tcp-tls` tests passed).
2026-09-30 16:41:51 +01:00

53 lines
1.7 KiB
Rust

#![allow(missing_docs)]
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use rustls::client::danger::ServerCertVerifier;
use rustls::pki_types::{ServerName, UnixTime};
use tsunagi::identity::DeviceIdentity;
use tsunagi_tcp_tls::cert::{PinnedEndpointVerifier, generate_self_signed_cert};
#[test]
fn test_generate_and_pin_certificate() {
let identity = DeviceIdentity::generate();
let peer_id = identity.endpoint_id();
let (cert, _key) = generate_self_signed_cert(&identity.secret_bytes())
.expect("cert generation should succeed");
let verifier = PinnedEndpointVerifier::new(peer_id);
let server_name = ServerName::try_from("tsunagi.local").unwrap();
let verified = verifier.verify_server_cert(&cert, &[], &server_name, &[], UnixTime::now());
assert!(
verified.is_ok(),
"certificate matching peer endpoint id must verify"
);
let other_identity = DeviceIdentity::generate();
let wrong_verifier = PinnedEndpointVerifier::new(other_identity.endpoint_id());
let wrong_verified =
wrong_verifier.verify_server_cert(&cert, &[], &server_name, &[], UnixTime::now());
assert!(
wrong_verified.is_err(),
"certificate with wrong endpoint id must fail verification"
);
}
#[test]
fn test_make_client_and_server_config() {
let identity = DeviceIdentity::generate();
let peer_id = identity.endpoint_id();
let (cert, key) = generate_self_signed_cert(&identity.secret_bytes())
.expect("cert generation should succeed");
let server_config = tsunagi_tcp_tls::cert::make_server_config(cert, key);
assert!(server_config.is_ok());
let client_config = tsunagi_tcp_tls::cert::make_client_config(peer_id);
assert!(client_config.is_ok());
}