dig-tls 0.2.0

Canonical DIG peer mTLS: a shipped public DigNetwork CA, per-peer node certs, rustls mutual-auth configs, peer_id = SHA-256(TLS SPKI DER), and the #1204 BLS-G1 cert binding. Mirrors the chia-blockchain / chia-tls model with the DigNetwork trust domain.
Documentation
//! Ready-to-use rustls mutual-auth configurations for a DIG peer.
//!
//! These are the two entry points most consumers want: give them a [`NodeCert`] and a
//! [`BindingPolicy`] and they return a rustls config wired with the DigNetwork-CA verifier, peer_id
//! pinning, and BLS-binding checking, plus the capture handles to read WHO connected after the
//! handshake completes. Both configs pin the `ring` crypto provider explicitly, so a consumer never
//! has to install a process-default provider (and never risks the "multiple CryptoProviders" panic).

use std::sync::Arc;

use rustls::{ClientConfig, ServerConfig};

use crate::binding::BindingPolicy;
use crate::error::{DigTlsError, Result};
use crate::identity::PeerId;
use crate::node_cert::NodeCert;
use crate::verify::{CapturedBlsPub, CapturedPeerId, DigClientCertVerifier, DigServerCertVerifier};

/// A server-side (inbound) mTLS configuration plus the handles that capture the connecting peer's
/// identity after the handshake.
pub struct ServerTls {
    /// The rustls server configuration to hand to your TLS acceptor.
    pub config: Arc<ServerConfig>,
    /// The `peer_id` of the client that connected (populated during the handshake).
    pub captured_peer_id: CapturedPeerId,
    /// The BLS G1 pubkey the client's `peer_id` is bound to (populated when a valid binding was seen).
    pub captured_bls: CapturedBlsPub,
}

/// A client-side (outbound) mTLS configuration plus the handles that capture the server peer's
/// identity after the handshake.
pub struct ClientTls {
    /// The rustls client configuration to hand to your TLS connector.
    pub config: Arc<ClientConfig>,
    /// The `peer_id` of the server that answered (populated during the handshake).
    pub captured_peer_id: CapturedPeerId,
    /// The BLS G1 pubkey the server's `peer_id` is bound to (populated when a valid binding was seen).
    pub captured_bls: CapturedBlsPub,
}

fn ring_provider() -> Arc<rustls::crypto::CryptoProvider> {
    Arc::new(rustls::crypto::ring::default_provider())
}

/// Build an inbound (server) mTLS config that presents `node`'s cert, REQUIRES a client cert chaining
/// to the DigNetwork CA, and applies `binding_policy` to the client's #1204 binding. Accepts any
/// DigNetwork-CA peer as the client (servers do not pin a specific caller); read
/// [`ServerTls::captured_peer_id`] after the handshake to learn who connected.
pub fn server_config(node: &NodeCert, binding_policy: BindingPolicy) -> Result<ServerTls> {
    let captured_peer_id = CapturedPeerId::default();
    let captured_bls = CapturedBlsPub::default();
    let verifier = Arc::new(DigClientCertVerifier::new(
        None,
        captured_peer_id.clone(),
        binding_policy,
        captured_bls.clone(),
    ));
    let config = ServerConfig::builder_with_provider(ring_provider())
        .with_safe_default_protocol_versions()
        .map_err(|e| DigTlsError::RustlsConfig(format!("protocol versions: {e}")))?
        .with_client_cert_verifier(verifier)
        .with_single_cert(node.rustls_cert_chain(), node.rustls_private_key())
        .map_err(|e| DigTlsError::RustlsConfig(format!("server single cert: {e}")))?;
    Ok(ServerTls {
        config: Arc::new(config),
        captured_peer_id,
        captured_bls,
    })
}

/// Build an outbound (client) mTLS config that presents `node`'s cert and verifies the server's leaf
/// chains to the DigNetwork CA, pinning `expected` (or accepting any DigNetwork-CA peer when `None`)
/// and applying `binding_policy` to the server's #1204 binding. Read [`ClientTls::captured_peer_id`]
/// after the handshake to learn who answered.
pub fn client_config(
    node: &NodeCert,
    expected: Option<PeerId>,
    binding_policy: BindingPolicy,
) -> Result<ClientTls> {
    let captured_peer_id = CapturedPeerId::default();
    let captured_bls = CapturedBlsPub::default();
    let verifier = Arc::new(DigServerCertVerifier::new(
        expected,
        captured_peer_id.clone(),
        binding_policy,
        captured_bls.clone(),
    ));
    let config = ClientConfig::builder_with_provider(ring_provider())
        .with_safe_default_protocol_versions()
        .map_err(|e| DigTlsError::RustlsConfig(format!("protocol versions: {e}")))?
        .dangerous()
        .with_custom_certificate_verifier(verifier)
        .with_client_auth_cert(node.rustls_cert_chain(), node.rustls_private_key())
        .map_err(|e| DigTlsError::RustlsConfig(format!("client auth cert: {e}")))?;
    Ok(ClientTls {
        config: Arc::new(config),
        captured_peer_id,
        captured_bls,
    })
}