Skip to main content

dig_tls/
config.rs

1//! Ready-to-use rustls mutual-auth configurations for a DIG peer.
2//!
3//! These are the two entry points most consumers want: give them a [`NodeCert`] and a
4//! [`BindingPolicy`] and they return a rustls config wired with the DigNetwork-CA verifier, peer_id
5//! pinning, and BLS-binding checking, plus the capture handles to read WHO connected after the
6//! handshake completes. Both configs pin the `ring` crypto provider explicitly, so a consumer never
7//! has to install a process-default provider (and never risks the "multiple CryptoProviders" panic).
8
9use std::sync::Arc;
10
11use rustls::{ClientConfig, ServerConfig};
12
13use crate::binding::BindingPolicy;
14use crate::error::{DigTlsError, Result};
15use crate::identity::PeerId;
16use crate::node_cert::NodeCert;
17use crate::verify::{CapturedBlsPub, CapturedPeerId, DigClientCertVerifier, DigServerCertVerifier};
18
19/// A server-side (inbound) mTLS configuration plus the handles that capture the connecting peer's
20/// identity after the handshake.
21pub struct ServerTls {
22    /// The rustls server configuration to hand to your TLS acceptor.
23    pub config: Arc<ServerConfig>,
24    /// The `peer_id` of the client that connected (populated during the handshake).
25    pub captured_peer_id: CapturedPeerId,
26    /// The BLS G1 pubkey the client's `peer_id` is bound to (populated when a valid binding was seen).
27    pub captured_bls: CapturedBlsPub,
28}
29
30/// A client-side (outbound) mTLS configuration plus the handles that capture the server peer's
31/// identity after the handshake.
32pub struct ClientTls {
33    /// The rustls client configuration to hand to your TLS connector.
34    pub config: Arc<ClientConfig>,
35    /// The `peer_id` of the server that answered (populated during the handshake).
36    pub captured_peer_id: CapturedPeerId,
37    /// The BLS G1 pubkey the server's `peer_id` is bound to (populated when a valid binding was seen).
38    pub captured_bls: CapturedBlsPub,
39}
40
41fn ring_provider() -> Arc<rustls::crypto::CryptoProvider> {
42    Arc::new(rustls::crypto::ring::default_provider())
43}
44
45/// Build an inbound (server) mTLS config that presents `node`'s cert, REQUIRES a client cert chaining
46/// to the DigNetwork CA, and applies `binding_policy` to the client's #1204 binding. Accepts any
47/// DigNetwork-CA peer as the client (servers do not pin a specific caller); read
48/// [`ServerTls::captured_peer_id`] after the handshake to learn who connected.
49pub fn server_config(node: &NodeCert, binding_policy: BindingPolicy) -> Result<ServerTls> {
50    let captured_peer_id = CapturedPeerId::default();
51    let captured_bls = CapturedBlsPub::default();
52    let verifier = Arc::new(DigClientCertVerifier::new(
53        None,
54        captured_peer_id.clone(),
55        binding_policy,
56        captured_bls.clone(),
57    ));
58    let config = ServerConfig::builder_with_provider(ring_provider())
59        .with_safe_default_protocol_versions()
60        .map_err(|e| DigTlsError::RustlsConfig(format!("protocol versions: {e}")))?
61        .with_client_cert_verifier(verifier)
62        .with_single_cert(node.rustls_cert_chain(), node.rustls_private_key())
63        .map_err(|e| DigTlsError::RustlsConfig(format!("server single cert: {e}")))?;
64    Ok(ServerTls {
65        config: Arc::new(config),
66        captured_peer_id,
67        captured_bls,
68    })
69}
70
71/// Build an outbound (client) mTLS config that presents `node`'s cert and verifies the server's leaf
72/// chains to the DigNetwork CA, pinning `expected` (or accepting any DigNetwork-CA peer when `None`)
73/// and applying `binding_policy` to the server's #1204 binding. Read [`ClientTls::captured_peer_id`]
74/// after the handshake to learn who answered.
75pub fn client_config(
76    node: &NodeCert,
77    expected: Option<PeerId>,
78    binding_policy: BindingPolicy,
79) -> Result<ClientTls> {
80    let captured_peer_id = CapturedPeerId::default();
81    let captured_bls = CapturedBlsPub::default();
82    let verifier = Arc::new(DigServerCertVerifier::new(
83        expected,
84        captured_peer_id.clone(),
85        binding_policy,
86        captured_bls.clone(),
87    ));
88    let config = ClientConfig::builder_with_provider(ring_provider())
89        .with_safe_default_protocol_versions()
90        .map_err(|e| DigTlsError::RustlsConfig(format!("protocol versions: {e}")))?
91        .dangerous()
92        .with_custom_certificate_verifier(verifier)
93        .with_client_auth_cert(node.rustls_cert_chain(), node.rustls_private_key())
94        .map_err(|e| DigTlsError::RustlsConfig(format!("client auth cert: {e}")))?;
95    Ok(ClientTls {
96        config: Arc::new(config),
97        captured_peer_id,
98        captured_bls,
99    })
100}
101
102/// Build an inbound (server) mTLS config exactly like [`server_config`], EXCEPT it does not require
103/// the connecting client's leaf to chain to the DigNetwork CA — it accepts a SELF-SIGNED leaf and
104/// authenticates it by `peer_id = SHA-256(SPKI DER)` + rustls proof-of-possession + the #1204 BLS
105/// binding (under `binding_policy`).
106///
107/// Use this on the live network, where DIG peers still present self-signed / chia-ssl certs (the
108/// DIG-CA-everywhere migration #1378 is deferred), so the CA-requiring [`server_config`] would reject
109/// every legit peer with `UnknownIssuer` (#1422; mirrors dig-gossip #1371). Read
110/// [`ServerTls::captured_peer_id`] after the handshake to learn who connected.
111pub fn server_config_spki_pinned(
112    node: &NodeCert,
113    binding_policy: BindingPolicy,
114) -> Result<ServerTls> {
115    let captured_peer_id = CapturedPeerId::default();
116    let captured_bls = CapturedBlsPub::default();
117    let verifier = Arc::new(DigClientCertVerifier::new_spki_pinned(
118        None,
119        captured_peer_id.clone(),
120        binding_policy,
121        captured_bls.clone(),
122    ));
123    let config = ServerConfig::builder_with_provider(ring_provider())
124        .with_safe_default_protocol_versions()
125        .map_err(|e| DigTlsError::RustlsConfig(format!("protocol versions: {e}")))?
126        .with_client_cert_verifier(verifier)
127        .with_single_cert(node.rustls_cert_chain(), node.rustls_private_key())
128        .map_err(|e| DigTlsError::RustlsConfig(format!("server single cert: {e}")))?;
129    Ok(ServerTls {
130        config: Arc::new(config),
131        captured_peer_id,
132        captured_bls,
133    })
134}
135
136/// Build an outbound (client) mTLS config exactly like [`client_config`], EXCEPT it does not require
137/// the server's leaf to chain to the DigNetwork CA — it accepts a SELF-SIGNED leaf and authenticates
138/// it by `peer_id = SHA-256(SPKI DER)` pinning of `expected` (or accept-any when `None`) + rustls
139/// proof-of-possession + the #1204 BLS binding (under `binding_policy`).
140///
141/// This is dig-nat's auto-dialer entry point for the live network's self-signed peers (#1422; #1378
142/// CA-everywhere deferred; mirrors dig-gossip #1371). Read [`ClientTls::captured_peer_id`] after the
143/// handshake to learn who answered.
144///
145/// **SAFETY / USAGE CONTRACT:** Unlike CA mode (where accept-any at least enforces the DIG trust
146/// domain), SPKI-pinned mode drops the CA check, so passing `expected: None` together with a
147/// non-`Required` `BindingPolicy` authenticates NOTHING about which peer answered — any peer
148/// presenting any self-signed leaf is accepted, and an active MITM is undetectable. A dialer MUST
149/// pass `expected: Some(peer_id)` (or use `BindingPolicy::Required`) to authenticate the specific
150/// peer. See #1422 / #1371.
151pub fn client_config_spki_pinned(
152    node: &NodeCert,
153    expected: Option<PeerId>,
154    binding_policy: BindingPolicy,
155) -> Result<ClientTls> {
156    let captured_peer_id = CapturedPeerId::default();
157    let captured_bls = CapturedBlsPub::default();
158    let verifier = Arc::new(DigServerCertVerifier::new_spki_pinned(
159        expected,
160        captured_peer_id.clone(),
161        binding_policy,
162        captured_bls.clone(),
163    ));
164    let config = ClientConfig::builder_with_provider(ring_provider())
165        .with_safe_default_protocol_versions()
166        .map_err(|e| DigTlsError::RustlsConfig(format!("protocol versions: {e}")))?
167        .dangerous()
168        .with_custom_certificate_verifier(verifier)
169        .with_client_auth_cert(node.rustls_cert_chain(), node.rustls_private_key())
170        .map_err(|e| DigTlsError::RustlsConfig(format!("client auth cert: {e}")))?;
171    Ok(ClientTls {
172        config: Arc::new(config),
173        captured_peer_id,
174        captured_bls,
175    })
176}