Skip to main content

aranya_client/
afc.rs

1//! AFC support.
2
3#![cfg(feature = "afc")]
4#![cfg_attr(docsrs, doc(cfg(feature = "afc")))]
5
6use core::fmt;
7use std::{
8    fmt::{Debug, Display},
9    sync::{Arc, Mutex},
10};
11
12use anyhow::Context;
13use aranya_daemon_api::{AfcChannelId, AfcLocalChannelId, AfcShmInfo, DaemonApiClient, CS};
14use aranya_fast_channels::{
15    self as afc,
16    shm::{Flag, Mode, ReadState},
17    AfcState, Client as AfcClient,
18};
19use derive_where::derive_where;
20use serde::{Deserialize, Serialize};
21use tracing::debug;
22
23use crate::{
24    client::create_ctx,
25    error::{aranya_error, IpcError},
26    util::ApiConv as _,
27    DeviceId, LabelId, Result, TeamId,
28};
29
30/// The sequence number (position) of a message sent in a channel.
31///
32/// Sequence numbers are monotonically increasing; each call to `seal`
33/// produces a ciphertext whose sequence number is greater than the
34/// sequence number for the ciphertext produced by the previous call to
35/// `seal`.
36///
37/// Each call to `open` cryptographically verifies that the ciphertext's sequence
38/// number has not been tampered with. It does not verify anything else about
39/// the sequence number.
40///
41/// Sequence numbers are [comparable][Ord] and can be used to implement
42/// message reordering or replay protection (by rejecting duplicate sequence numbers).
43#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, Ord, PartialOrd)]
44pub struct Seq(afc::Seq);
45
46/// Control message sent to a peer when creating a channel.
47#[derive(Clone, Debug, Serialize, Deserialize)]
48pub struct CtrlMsg(Box<[u8]>);
49
50impl CtrlMsg {
51    /// Convert control message to bytes.
52    pub fn as_bytes(&self) -> &[u8] {
53        &self.0
54    }
55}
56
57impl From<Box<[u8]>> for CtrlMsg {
58    fn from(value: Box<[u8]>) -> Self {
59        Self(value)
60    }
61}
62
63/// A globally unique channel ID.
64#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
65#[serde(transparent)]
66pub struct ChannelId {
67    #[doc(hidden)]
68    pub __id: AfcChannelId,
69}
70
71impl Display for ChannelId {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        Display::fmt(&self.__id, f)
74    }
75}
76
77/// AFC seal error.
78#[derive(Debug, thiserror::Error)]
79pub struct AfcSealError(
80    #[allow(dead_code, reason = "Don't expose internal error type in public API")]
81    aranya_fast_channels::Error,
82);
83
84impl Display for AfcSealError {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        Display::fmt(&self.0, f)
87    }
88}
89
90/// AFC open error.
91#[derive(Debug, thiserror::Error)]
92pub struct AfcOpenError(
93    #[allow(dead_code, reason = "Don't expose internal error type in public API")]
94    aranya_fast_channels::Error,
95);
96
97impl Display for AfcOpenError {
98    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99        Display::fmt(&self.0, f)
100    }
101}
102
103/// Possible errors that could happen when using Aranya Fast Channels.
104#[derive(Debug, thiserror::Error)]
105#[non_exhaustive]
106pub enum Error {
107    /// Unable to seal datagram.
108    #[error("unable to seal datagram")]
109    Seal(#[from] AfcSealError),
110
111    /// Unable to open datagram.
112    #[error("unable to open datagram")]
113    Open(#[from] AfcOpenError),
114
115    /// Unable to connect to channel keys IPC.
116    #[error("unable to connect to channel keys IPC")]
117    AfcIpc(#[from] anyhow::Error),
118
119    /// No channel info found.
120    #[error("no channel info found")]
121    NoChannelInfoFound,
122
123    /// An internal bug was discovered.
124    #[error(transparent)]
125    Bug(#[from] buggy::Bug),
126}
127
128/// Aranya Fast Channels handler for managing channels which allow encrypting/decrypting application data buffers.
129pub struct Channels {
130    daemon: DaemonApiClient,
131    keys: Arc<ChannelKeys>,
132}
133
134// TODO: derive Debug on [`keys`] when [`AfcClient`] implements it.
135impl Debug for Channels {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        f.debug_struct("Channels")
138            .field("daemon", &self.daemon)
139            .finish_non_exhaustive()
140    }
141}
142
143impl Channels {
144    /// The number of additional octets required to encrypt
145    /// plaintext data.
146    pub const OVERHEAD: usize = AfcClient::<ReadState<CS>>::OVERHEAD;
147
148    pub(crate) fn new(daemon: DaemonApiClient, keys: Arc<ChannelKeys>) -> Self {
149        Self { daemon, keys }
150    }
151
152    /// Create a unidirectional send-only channel [`SendChannel`].
153    ///
154    /// The creator of the channel will have a unidirectional channel [`SendChannel`] that can only `seal()` data.
155    ///
156    /// Once the peer processes the [`CtrlMsg`] message with `accept_channel()`,
157    /// it will have a corresponding unidirectional channel [`ReceiveChannel`] object that can only `open()` data.
158    ///
159    /// To send data from the creator of the channel to the peer:
160    /// - Invoke `seal()` on the data to obtain a ciphertext buffer.
161    /// - Send the ciphertext to the peer via any network transport.
162    /// - On the peer, invoke `open()` on the ciphertext to obtain the plaintext.
163    ///
164    /// Returns:
165    /// - A unidirectional channel [`SendChannel`] object that can only `seal()` data.
166    /// - A [`CtrlMsg`] message to send to the peer.
167    pub async fn create_channel(
168        &self,
169        team_id: TeamId,
170        peer_id: DeviceId,
171        label_id: LabelId,
172    ) -> Result<(SendChannel, CtrlMsg)> {
173        let info = self
174            .daemon
175            .create_afc_channel(
176                create_ctx(),
177                team_id.into_api(),
178                peer_id.into_api(),
179                label_id.into_api(),
180            )
181            .await
182            .map_err(IpcError::new)?
183            .map_err(aranya_error)?;
184        let seal_ctx = self
185            .keys
186            .0
187            .setup_seal_ctx(info.local_channel_id)
188            .map_err(AfcSealError)
189            .map_err(Error::Seal)?;
190        let chan = SendChannel {
191            daemon: self.daemon.clone(),
192            keys: self.keys.clone(),
193            channel_id: ChannelId {
194                __id: info.channel_id,
195            },
196            local_channel_id: info.local_channel_id,
197            label_id,
198            peer_id,
199            seal_ctx: Box::new(seal_ctx),
200        };
201        Ok((chan, CtrlMsg(info.ctrl)))
202    }
203
204    /// Receive a [`CtrlMsg`] message from a peer to create a corresponding receive channel.
205    pub async fn accept_channel(&self, team_id: TeamId, ctrl: CtrlMsg) -> Result<ReceiveChannel> {
206        let info = self
207            .daemon
208            .accept_afc_channel(create_ctx(), team_id.into_api(), ctrl.0)
209            .await
210            .map_err(IpcError::new)?
211            .map_err(aranya_error)?;
212        let open_ctx = self
213            .keys
214            .0
215            .setup_open_ctx(info.local_channel_id)
216            .map_err(AfcSealError)
217            .map_err(Error::Seal)?;
218        Ok(ReceiveChannel {
219            daemon: self.daemon.clone(),
220            keys: self.keys.clone(),
221            channel_id: ChannelId {
222                __id: info.channel_id,
223            },
224            local_channel_id: info.local_channel_id,
225            label_id: LabelId::from_api(info.label_id),
226            peer_id: DeviceId::from_api(info.peer_id),
227            open_ctx: Arc::new(Mutex::new(open_ctx)),
228        })
229    }
230}
231
232/// A unidirectional channel that can only send.
233#[derive_where(Debug)]
234pub struct SendChannel {
235    daemon: DaemonApiClient,
236    keys: Arc<ChannelKeys>,
237    channel_id: ChannelId,
238    local_channel_id: AfcLocalChannelId,
239    label_id: LabelId,
240    peer_id: DeviceId,
241    #[derive_where(skip(Debug))]
242    seal_ctx: Box<<ReadState<CS> as AfcState>::SealCtx>,
243}
244
245impl SendChannel {
246    /// The channel's unique ID.
247    pub fn id(&self) -> ChannelId {
248        self.channel_id
249    }
250
251    /// The channel's label ID.
252    pub fn label_id(&self) -> LabelId {
253        self.label_id
254    }
255
256    /// The device ID of the peer on the other side of the channel.
257    pub fn peer_id(&self) -> DeviceId {
258        self.peer_id
259    }
260
261    /// Encrypts and authenticates `plaintext` for a channel.
262    ///
263    /// The resulting ciphertext is written to `dst`, which must
264    /// be at least `plaintext.len() + Channels::OVERHEAD` bytes
265    /// long.
266    ///
267    /// Note: it is an error to invoke this method after the channel has been deleted.
268    ///
269    /// # Panics
270    ///
271    /// Will panic on poisoned internal mutexes.
272    pub fn seal(&mut self, dst: &mut [u8], plaintext: &[u8]) -> Result<(), Error> {
273        debug!(?self.local_channel_id, ?self.label_id, "seal");
274        self.keys
275            .0
276            .seal(&mut self.seal_ctx, dst, plaintext)
277            .map_err(AfcSealError)
278            .map_err(Error::Seal)?;
279        Ok(())
280    }
281
282    /// Delete the channel.
283    pub async fn delete(&self) -> Result<(), crate::Error> {
284        self.daemon
285            .delete_afc_channel(create_ctx(), self.local_channel_id)
286            .await
287            .map_err(IpcError::new)?
288            .map_err(aranya_error)?;
289        Ok(())
290    }
291}
292
293/// A unidirectional channel that can only receive.
294#[derive(Clone)]
295#[derive_where(Debug)]
296pub struct ReceiveChannel {
297    daemon: DaemonApiClient,
298    keys: Arc<ChannelKeys>,
299    channel_id: ChannelId,
300    local_channel_id: AfcLocalChannelId,
301    label_id: LabelId,
302    peer_id: DeviceId,
303    #[derive_where(skip(Debug))]
304    open_ctx: Arc<Mutex<<ReadState<CS> as AfcState>::OpenCtx>>,
305}
306
307impl ReceiveChannel {
308    /// The channel's unique ID.
309    pub fn id(&self) -> ChannelId {
310        self.channel_id
311    }
312
313    /// The channel's label ID.
314    pub fn label_id(&self) -> LabelId {
315        self.label_id
316    }
317
318    /// The device ID of the peer on the other side of the channel.
319    pub fn peer_id(&self) -> DeviceId {
320        self.peer_id
321    }
322
323    /// Decrypts and authenticates `ciphertext` received from
324    /// from `peer`.
325    ///
326    /// The resulting plaintext is written to `dst`, which must
327    /// be at least `ciphertext.len() - Channels::OVERHEAD` bytes
328    /// long.
329    ///
330    /// It returns the cryptographically verified label and
331    /// sequence number associated with the ciphertext.
332    ///
333    /// Note: it is an error to invoke this method after the channel has been deleted.
334    ///
335    /// # Panics
336    ///
337    /// Will panic on poisoned internal mutexes.
338    pub fn open(&self, dst: &mut [u8], ciphertext: &[u8]) -> Result<Seq, Error> {
339        debug!(?self.local_channel_id, ?self.label_id, "open");
340        let (label_id, seq) = self
341            .keys
342            .0
343            .open(
344                &mut *self.open_ctx.lock().expect("poisoned"),
345                dst,
346                ciphertext,
347            )
348            .map_err(AfcOpenError)
349            .map_err(Error::Open)?;
350        debug_assert_eq!(label_id.as_base(), self.label_id.into_api().as_base());
351        Ok(Seq(seq))
352    }
353
354    /// Delete the channel.
355    pub async fn delete(&self) -> Result<(), crate::Error> {
356        self.daemon
357            .delete_afc_channel(create_ctx(), self.local_channel_id)
358            .await
359            .map_err(IpcError::new)?
360            .map_err(aranya_error)?;
361        Ok(())
362    }
363}
364
365/// Channel Keys.
366pub(crate) struct ChannelKeys(AfcClient<ReadState<CS>>);
367
368impl ChannelKeys {
369    /// Open shared-memory client to daemon's channel key list.
370    pub fn new(afc_shm_info: &AfcShmInfo) -> Result<Self, Error> {
371        // TODO(#496): fix shm issue on some environments
372        debug!(
373            "setting up afc shm read side: {:?}",
374            afc_shm_info.path.clone()
375        );
376        let read = ReadState::open(
377            afc_shm_info.path.clone(),
378            Flag::OpenOnly,
379            Mode::ReadWrite,
380            afc_shm_info.max_chans,
381        )
382        .with_context(|| format!("unable to open `ReadState`: {:?}", afc_shm_info.path))
383        .map_err(Error::AfcIpc)?;
384
385        Ok(Self(AfcClient::new(read)))
386    }
387}
388
389impl Debug for ChannelKeys {
390    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391        f.debug_struct("ChannelKeys").finish_non_exhaustive()
392    }
393}