#![cfg(feature = "afc")]
#![cfg_attr(docsrs, doc(cfg(feature = "afc")))]
use core::fmt;
use std::{
fmt::{Debug, Display},
sync::{Arc, Mutex},
};
use anyhow::Context;
use aranya_daemon_api::{AfcLocalChannelId, AfcShmInfo, DaemonApiClient, CS};
use aranya_fast_channels::{
self as afc,
shm::{Flag, Mode, ReadState},
AfcState, Client as AfcClient,
};
use aranya_id::custom_id;
use derive_where::derive_where;
use serde::{Deserialize, Serialize};
use tracing::debug;
use crate::{
error::{aranya_error, IpcError},
util::{rpc_context, ApiConv as _, ApiId},
DeviceId, LabelId, Result, TeamId,
};
#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, Ord, PartialOrd)]
pub struct Seq(afc::Seq);
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CtrlMsg(Box<[u8]>);
impl CtrlMsg {
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl From<Box<[u8]>> for CtrlMsg {
fn from(value: Box<[u8]>) -> Self {
Self(value)
}
}
custom_id! {
pub struct ChannelId;
}
impl ApiId<aranya_daemon_api::AfcChannelId> for ChannelId {}
#[derive(Debug, thiserror::Error)]
pub struct AfcSealError(
#[allow(dead_code, reason = "Don't expose internal error type in public API")]
aranya_fast_channels::Error,
);
impl Display for AfcSealError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
#[derive(Debug, thiserror::Error)]
pub struct AfcOpenError(
#[allow(dead_code, reason = "Don't expose internal error type in public API")]
aranya_fast_channels::Error,
);
impl Display for AfcOpenError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("unable to seal datagram")]
Seal(#[from] AfcSealError),
#[error("unable to open datagram")]
Open(#[from] AfcOpenError),
#[error("unable to connect to channel keys IPC")]
AfcIpc(#[from] anyhow::Error),
#[error("no channel info found")]
NoChannelInfoFound,
#[error(transparent)]
Bug(#[from] buggy::Bug),
}
pub struct Channels {
daemon: DaemonApiClient,
keys: Arc<ChannelKeys>,
}
impl Debug for Channels {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Channels")
.field("daemon", &self.daemon)
.finish_non_exhaustive()
}
}
impl Channels {
pub const OVERHEAD: usize = AfcClient::<ReadState<CS>>::OVERHEAD;
pub(crate) fn new(daemon: DaemonApiClient, keys: Arc<ChannelKeys>) -> Self {
Self { daemon, keys }
}
pub async fn create_channel(
&self,
team_id: TeamId,
peer_id: DeviceId,
label_id: LabelId,
) -> Result<(SendChannel, CtrlMsg)> {
let info = self
.daemon
.create_afc_channel(
rpc_context(),
team_id.into_api(),
peer_id.into_api(),
label_id.into_api(),
)
.await
.map_err(IpcError::new)?
.map_err(aranya_error)?;
let seal_ctx = self
.keys
.0
.setup_seal_ctx(info.local_channel_id)
.map_err(AfcSealError)
.map_err(Error::Seal)?;
let chan = SendChannel {
daemon: self.daemon.clone(),
keys: self.keys.clone(),
channel_id: ChannelId::from_api(info.channel_id),
local_channel_id: info.local_channel_id,
label_id,
peer_id,
seal_ctx: Box::new(seal_ctx),
};
Ok((chan, CtrlMsg(info.ctrl)))
}
pub async fn accept_channel(&self, team_id: TeamId, ctrl: CtrlMsg) -> Result<ReceiveChannel> {
let info = self
.daemon
.accept_afc_channel(rpc_context(), team_id.into_api(), ctrl.0)
.await
.map_err(IpcError::new)?
.map_err(aranya_error)?;
let open_ctx = self
.keys
.0
.setup_open_ctx(info.local_channel_id)
.map_err(AfcSealError)
.map_err(Error::Seal)?;
Ok(ReceiveChannel {
daemon: self.daemon.clone(),
keys: self.keys.clone(),
channel_id: ChannelId::from_api(info.channel_id),
local_channel_id: info.local_channel_id,
label_id: LabelId::from_api(info.label_id),
peer_id: DeviceId::from_api(info.peer_id),
open_ctx: Arc::new(Mutex::new(open_ctx)),
})
}
}
#[derive_where(Debug)]
pub struct SendChannel {
daemon: DaemonApiClient,
keys: Arc<ChannelKeys>,
channel_id: ChannelId,
local_channel_id: AfcLocalChannelId,
label_id: LabelId,
peer_id: DeviceId,
#[derive_where(skip(Debug))]
seal_ctx: Box<<ReadState<CS> as AfcState>::SealCtx>,
}
impl SendChannel {
pub fn id(&self) -> ChannelId {
self.channel_id
}
pub fn label_id(&self) -> LabelId {
self.label_id
}
pub fn peer_id(&self) -> DeviceId {
self.peer_id
}
pub fn seal(&mut self, dst: &mut [u8], plaintext: &[u8]) -> Result<(), Error> {
debug!(?self.local_channel_id, ?self.label_id, "seal");
self.keys
.0
.seal(&mut self.seal_ctx, dst, plaintext)
.map_err(AfcSealError)
.map_err(Error::Seal)?;
Ok(())
}
pub async fn delete(&self) -> Result<(), crate::Error> {
self.daemon
.delete_afc_channel(rpc_context(), self.local_channel_id)
.await
.map_err(IpcError::new)?
.map_err(aranya_error)?;
Ok(())
}
}
#[derive(Clone)]
#[derive_where(Debug)]
pub struct ReceiveChannel {
daemon: DaemonApiClient,
keys: Arc<ChannelKeys>,
channel_id: ChannelId,
local_channel_id: AfcLocalChannelId,
label_id: LabelId,
peer_id: DeviceId,
#[derive_where(skip(Debug))]
open_ctx: Arc<Mutex<<ReadState<CS> as AfcState>::OpenCtx>>,
}
impl ReceiveChannel {
pub fn id(&self) -> ChannelId {
self.channel_id
}
pub fn label_id(&self) -> LabelId {
self.label_id
}
pub fn peer_id(&self) -> DeviceId {
self.peer_id
}
pub fn open(&self, dst: &mut [u8], ciphertext: &[u8]) -> Result<Seq, Error> {
debug!(?self.local_channel_id, ?self.label_id, "open");
let (label_id, seq) = self
.keys
.0
.open(
&mut *self.open_ctx.lock().expect("poisoned"),
dst,
ciphertext,
)
.map_err(AfcOpenError)
.map_err(Error::Open)?;
debug_assert_eq!(label_id.as_base(), self.label_id.into_api().as_base());
Ok(Seq(seq))
}
pub async fn delete(&self) -> Result<(), crate::Error> {
self.daemon
.delete_afc_channel(rpc_context(), self.local_channel_id)
.await
.map_err(IpcError::new)?
.map_err(aranya_error)?;
Ok(())
}
}
pub(crate) struct ChannelKeys(AfcClient<ReadState<CS>>);
impl ChannelKeys {
pub fn new(afc_shm_info: &AfcShmInfo) -> Result<Self, Error> {
debug!(
"setting up afc shm read side: {:?}",
afc_shm_info.path.clone()
);
let read = ReadState::open(
afc_shm_info.path.clone(),
Flag::OpenOnly,
Mode::ReadWrite,
afc_shm_info.max_chans,
)
.with_context(|| format!("unable to open `ReadState`: {:?}", afc_shm_info.path))
.map_err(Error::AfcIpc)?;
Ok(Self(AfcClient::new(read)))
}
}
impl Debug for ChannelKeys {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ChannelKeys").finish_non_exhaustive()
}
}