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}