liminal-server 0.25.4

Standalone server for the liminal messaging bus
Documentation
//! Certificate/key binding precedes the ordinary deliberate link decision.

use super::{
    ConnectionProcessState, ConnectionRuntime, DeliberateDecision, Link, LinkAction, LinkHandshake,
    LinkLife, LinkOffer, NOISE_OVERHEAD, OpenLink, OpenRefusal, unix_seconds_now,
};

pub(super) fn open_link(
    runtime: &ConnectionRuntime,
    state: &ConnectionProcessState,
    stream_id: u32,
    offer: &LinkOffer,
) -> Result<(OpenLink, Vec<u8>), OpenRefusal> {
    let Some(session) = state.lys_session.as_ref() else {
        return Err(OpenRefusal::Refused(
            "a link is opened on a connection admitted on a Lys pass".to_owned(),
        ));
    };
    let Some((_, network_id)) = runtime.lys_admitter() else {
        return Err(OpenRefusal::Refused(
            "no lys admitter is installed".to_owned(),
        ));
    };
    let Some(host) = runtime.link_host() else {
        return Err(OpenRefusal::Refused(
            "this liminal holds no machine identity; [auth.lys.machine] is not configured"
                .to_owned(),
        ));
    };
    if state
        .links
        .contains_key(stream_id)
        .map_err(OpenRefusal::Refused)?
    {
        return Err(OpenRefusal::Refused(format!(
            "a link is already open on stream {stream_id}"
        )));
    }
    let mut peer = offered_peer(host, session.holder(), offer)?;
    let recorded = peer.machine;
    let first = peer.first;
    let remote_end = LinkAction::from(offer.end);
    let local = host.local().machine();
    let (exporter, importer) = match remote_end {
        LinkAction::Export => (recorded, local),
        LinkAction::Import => (local, recorded),
    };
    let link = Link::new(
        network_id.to_owned(),
        offer.link_id.clone(),
        exporter,
        importer,
    )
    .map_err(|error| OpenRefusal::Refused(error.to_string()))?;
    let handshake_refused = |error: crate::link_handshake::LinkHandshakeError| {
        OpenRefusal::Refused(format!("handshake refused: {error}"))
    };
    let mut handshake =
        LinkHandshake::responder(host.local(), recorded).map_err(handshake_refused)?;
    if peer.certificate_required {
        handshake.read(first).map_err(handshake_refused)?;
        prove_standing(
            runtime,
            host,
            network_id,
            session.holder(),
            &mut peer,
            &link,
            remote_end,
        )?;
    }
    match session.open_link(&link.resource(), remote_end, unix_seconds_now()) {
        Ok(DeliberateDecision::Outright(_)) => {}
        Ok(
            DeliberateDecision::ByDraft { grant, draft }
            | DeliberateDecision::ByTwo { grant, draft },
        ) => return Err(OpenRefusal::Held { grant, draft }),
        Err(error) => return Err(OpenRefusal::Pass(error)),
    }
    if !peer.certificate_required {
        handshake.read(first).map_err(handshake_refused)?;
    }
    if peer.until.is_some_and(|until| unix_seconds_now() >= until) {
        return Err(OpenRefusal::Refused("certificate_pass_expired".into()));
    }
    let mut answer = vec![0_u8; NOISE_OVERHEAD];
    let written = handshake.write(&mut answer).map_err(handshake_refused)?;
    answer.truncate(written);
    let transport = handshake.finish().map_err(handshake_refused)?;
    let life = LinkLife::new(*transport.peer());
    Ok((
        OpenLink {
            link,
            remote_end,
            transport,
            life,
            calls: super::calls::IncomingCalls::new(
                host.peers()
                    .map_or(0, |peers| peers.max_pending_calls.get()),
            ),
            certificate_until: peer.until,
        },
        answer,
    ))
}

struct PeerOffer<'a> {
    machine: crate::link_handshake::MachineIdentity,
    first: &'a [u8],
    certificate: Option<crate::link::certificate::offer::OfferedCertificate<'a>>,
    certificate_required: bool,
    until: Option<u64>,
}

fn offered_peer<'a>(
    host: &crate::link::LinkHost,
    holder: &str,
    offer: &'a LinkOffer,
) -> Result<PeerOffer<'a>, OpenRefusal> {
    let bound = host
        .peers()
        .map_or(NOISE_OVERHEAD, |peers| peers.max_certificate_bytes.get());
    let proof_bound = host.peers().map_or(0, |peers| peers.max_proof_bytes.get());
    let (certificate, first) =
        crate::link::certificate::offer::unpack(&offer.handshake, bound, proof_bound)
            .map_err(|error| OpenRefusal::Refused(error.to_string()))?;
    let recorded = host.recorded_machine(holder);
    let mut until = None;
    let machine = if let Some(recorded) = recorded {
        if recorded.ed25519_public() != &offer.machine {
            return Err(OpenRefusal::Refused(
                "the offered machine key is not the holder's recorded key".into(),
            ));
        }
        recorded
    } else {
        let offered = certificate.as_ref().ok_or_else(|| {
            OpenRefusal::Refused("no machine key is recorded for this holder".into())
        })?;
        let key = host.service_key().ok_or_else(|| {
            OpenRefusal::Refused(
                crate::link::certificate::CertificateRefusal::ServiceKey.to_string(),
            )
        })?;
        let certified = crate::link::certificate::verify(
            offered.der,
            key,
            holder,
            crate::link_handshake::MachineIdentity::new(offer.machine),
            unix_seconds_now(),
            bound,
        )
        .map_err(|error| OpenRefusal::Refused(error.to_string()))?;
        until = Some(certified.not_after().saturating_add(1));
        certified.machine()
    };
    Ok(PeerOffer {
        machine,
        first,
        certificate,
        certificate_required: recorded.is_none(),
        until,
    })
}

fn prove_standing(
    runtime: &ConnectionRuntime,
    host: &crate::link::LinkHost,
    network: &str,
    holder: &str,
    peer: &mut PeerOffer<'_>,
    link: &Link,
    end: LinkAction,
) -> Result<(), OpenRefusal> {
    let unavailable = || {
        OpenRefusal::Refused(
            crate::link::certificate::CertificateRefusal::StatusUnavailable.to_string(),
        )
    };
    let standing = host.standing().ok_or_else(unavailable)?;
    let offered = peer.certificate.as_ref().ok_or_else(unavailable)?;
    let token = standing
        .prove(
            holder,
            peer.machine,
            offered.der,
            offered.proof,
            unix_seconds_now(),
        )
        .map_err(|error| OpenRefusal::Refused(error.to_string()))?;
    peer.until = Some(
        peer.until
            .map_or_else(|| token.expires_at(), |until| until.min(token.expires_at())),
    );
    let (admitter, _) = runtime.lys_admitter().ok_or_else(unavailable)?;
    let fresh = admitter
        .admit(token.as_bytes(), unix_seconds_now())
        .map_err(|_| {
            OpenRefusal::Refused(crate::link::certificate::CertificateRefusal::Proof.to_string())
        })?;
    let binding = crate::auth_lys::SubjectBinding::link(
        network.into(),
        link.resource().id,
        "net.link.open".into(),
    )
    .map_err(|error| OpenRefusal::Refused(error.to_string()))?;
    fresh
        .decide_link(&binding, end, unix_seconds_now())
        .map_err(OpenRefusal::Pass)?;
    Ok(())
}