aranya_fast_channels/lib.rs
1//! The core library for Aranya Fast Channels (AFC).
2//!
3//! # Overview
4//!
5//! AFC provides a high-throughput, low latency encryption engine
6//! protected by Aranya's policy rules. Data encrypted with (or
7//! decrypted by) the engine is sent out of band (not though
8//! Aranya itself), making it suitable for encrypting network
9//! streams and other high-throughput data.
10//!
11//! AFC can be configured to use custom cryptography and random
12//! number generation.
13//!
14//! # Usage
15//!
16//! AFC uses the client-daemon model, with AFC being the
17//! "client" and Aranya being the "daemon." However, this is
18//! merely a logical distinction; for instance, it's possible for
19//! both to be in the same process, just running as different
20//! threads (or tasks).
21//!
22//! All AFC operations are handled by the [`Client`], which
23//! communicates with the daemon over [`AfcState`] and
24//! [`AranyaState`]. By default, AFC provides a state
25//! implementation backed by shared memory.
26//!
27//! # Notes
28//!
29//! AFC encrypts/seals each message with a deterministic nonce derived from a
30//! base nonce and sequence number. Sequence numbers should not be re-used in a given channel
31//! but it is possible to do so by passing a "new" [`AfcState::SealCtx`] to the seal methods
32//! on [`Client`].
33//!
34//! # Example
35//!
36//! The following example demonstrates two [`Client`]s encrypting
37//! data for each other. In practice, the two clients are almost
38//! always on different machines. The example also uses shared
39//! memory for the state, but in practice anything supported by
40//! Aranya can be used.
41//!
42//! ```
43//! # fn main() -> Result<(), Box<dyn core::error::Error>> {
44//! # #[cfg(all(feature = "posix", not(feature = "trng")))]
45//! # {
46//! use aranya_crypto::{
47//! Csprng, EncryptionKey, Engine, IdentityKey, Random, Rng,
48//! afc::{RawOpenKey, RawSealKey, UniChannel, UniOpenKey, UniSealKey, UniSecrets},
49//! dangerous::spideroak_crypto::rust::HkdfSha256,
50//! default::{DefaultCipherSuite, DefaultEngine},
51//! id::IdExt as _,
52//! policy::{CmdId, LabelId},
53//! };
54//! use aranya_fast_channels::{
55//! AfcState, AranyaState, Channel, Client, Directed, Error, LocalChannelId,
56//! crypto::Aes256Gcm,
57//! shm::{Flag, Mode, Path, ReadState, WriteState},
58//! };
59//!
60//! type E = DefaultEngine;
61//! type CS = DefaultCipherSuite;
62//!
63//! // The maximum number of channels supported by the shared
64//! // memory.
65//! //
66//! // You can use any value, this is just an example.
67//! const MAX_CHANS: usize = 42;
68//!
69//! let aranya_client_a: WriteState<CS, Rng> = {
70//! let path = Path::from_bytes(b"/afc_doc_client_a\x00")
71//! .map_err(|err| Error::SharedMem(err.into()))?;
72//! # aranya_fast_channels::shm::unlink(path);
73//! WriteState::open(path, Flag::Create, Mode::ReadWrite, MAX_CHANS, Rng)
74//! .map_err(Error::SharedMem)?
75//! };
76//!
77//! let aranya_client_b: WriteState<CS, Rng> = {
78//! let path = Path::from_bytes(b"/afc_doc_client_b\x00")
79//! .map_err(|err| Error::SharedMem(err.into()))?;
80//! # aranya_fast_channels::shm::unlink(path);
81//! WriteState::open(path, Flag::Create, Mode::ReadWrite, MAX_CHANS, Rng)
82//! .map_err(Error::SharedMem)?
83//! };
84//!
85//! let (eng, _) = E::from_entropy(Rng);
86//!
87//! let device1_id = IdentityKey::<CS>::new(&eng).id()?;
88//! let device1_enc_sk = EncryptionKey::<CS>::new(&eng);
89//!
90//! let device2_id = IdentityKey::<CS>::new(&eng).id()?;
91//! let device2_enc_sk = EncryptionKey::<CS>::new(&eng);
92//!
93//! // The label ID used for encryption and decryption.
94//! let label_id = LabelId::random(Rng);
95//!
96//! let ch1 = UniChannel {
97//! parent_cmd_id: CmdId::random(&eng),
98//! our_sk: &device1_enc_sk,
99//! seal_id: device1_id,
100//! their_pk: &device2_enc_sk.public()?,
101//! open_id: device2_id,
102//! label_id,
103//! };
104//! let UniSecrets { author, peer } = UniSecrets::new(&eng, &ch1)?;
105//!
106//! // Inform device1 about device2.
107//! let seal = UniSealKey::from_author_secret(&ch1, author)?.into_raw_key();
108//! let client_a_channel_id =
109//! aranya_client_a.add(Directed::SealOnly { seal }, label_id, device2_id)?;
110//!
111//! let ch2 = UniChannel {
112//! parent_cmd_id: ch1.parent_cmd_id,
113//! our_sk: &device2_enc_sk,
114//! open_id: device2_id,
115//! their_pk: &device1_enc_sk.public()?,
116//! seal_id: device1_id,
117//! label_id,
118//! };
119//!
120//! // Inform device2 about device1.
121//! let open = UniOpenKey::from_peer_encap(&ch2, peer)?.into_raw_key();
122//! let client_b_channel_id =
123//! aranya_client_b.add(Directed::OpenOnly { open }, label_id, device1_id)?;
124//!
125//! let mut afc_client_a = {
126//! let path = Path::from_bytes(b"/afc_doc_client_a\x00")
127//! .map_err(|err| Error::SharedMem(err.into()))?;
128//! let state = ReadState::open(path, Flag::OpenOnly, Mode::ReadWrite, MAX_CHANS)
129//! .map_err(Error::SharedMem)?;
130//! Client::<ReadState<CS>>::new(state)
131//! };
132//! let mut afc_client_b = {
133//! let path = Path::from_bytes(b"/afc_doc_client_b\x00")
134//! .map_err(|err| Error::SharedMem(err.into()))?;
135//! let state = ReadState::open(path, Flag::OpenOnly, Mode::ReadWrite, MAX_CHANS)
136//! .map_err(Error::SharedMem)?;
137//! Client::<ReadState<CS>>::new(state)
138//! };
139//!
140//! const GOLDEN: &str = "hello from APS!";
141//!
142//! // Have device1 encrypt data for device2.
143//! let ciphertext = {
144//! // Encryption has a little overhead, so make sure the
145//! // ouput buffer is large enough.
146//! let mut dst = vec![0u8; GOLDEN.len() + Client::<ReadState<CS>>::OVERHEAD];
147//!
148//! // Create the ctx to pass in.
149//! let mut ctx = afc_client_a.setup_seal_ctx(client_a_channel_id)?;
150//! afc_client_a.seal(&mut ctx, &mut dst[..], GOLDEN.as_bytes())?;
151//! dst
152//! };
153//!
154//! // Here is where you'd send ciphertext over the network, or
155//! // whatever makes sense for your application.
156//!
157//! // Have device2 decrypt the data from device1.
158//! let (label_from_open, seq, plaintext) = {
159//! let mut dst = vec![0u8; ciphertext.len() - Client::<ReadState<CS>>::OVERHEAD];
160//! // Create the ctx to pass in.
161//! let mut ctx = afc_client_b.setup_open_ctx(client_b_channel_id)?;
162//! let (label_id, seq) = afc_client_b.open(&mut ctx, &mut dst[..], &ciphertext[..])?;
163//! (label_id, seq, dst)
164//! };
165//!
166//! // At this point we can now make a decision on what to do
167//! // with plaintext based on the label. We know it came from
168//! // `device1` and we know it has the label `label_id`.
169//! // Both of those facts (`device1` and `label_id`)
170//! // have been cryptographically verified, so we can make
171//! // decisions based on them. For example, we could forward the
172//! // plaintext data on to another system that ingests "top
173//! // secret" data.
174//! assert_eq!(label_from_open, label_id);
175//! assert_eq!(seq, 0);
176//! assert_eq!(plaintext, GOLDEN.as_bytes());
177//!
178//! # }
179//! # Ok(())
180//! # }
181//! ```
182
183#![allow(unstable_name_collisions)]
184#![cfg_attr(docsrs, feature(doc_cfg))]
185#![cfg_attr(feature = "allocator_api", feature(allocator_api))]
186#![cfg_attr(
187 feature = "core_intrinsics",
188 allow(internal_features),
189 feature(core_intrinsics)
190)]
191#![cfg_attr(feature = "try_find", feature(try_find))]
192#![cfg_attr(not(any(feature = "std", test)), no_std)]
193#![warn(
194 clippy::alloc_instead_of_core,
195 clippy::implicit_saturating_sub,
196 clippy::undocumented_unsafe_blocks,
197 clippy::expect_used,
198 clippy::indexing_slicing,
199 clippy::missing_panics_doc,
200 clippy::string_slice,
201 clippy::unimplemented,
202 missing_docs
203)]
204#![cfg_attr(not(any(feature = "std", test)), deny(clippy::std_instead_of_core))]
205#![expect(
206 clippy::arithmetic_side_effects,
207 reason = "https://github.com/aranya-project/aranya-core/issues/253"
208)]
209
210#[cfg(feature = "alloc")]
211extern crate alloc;
212
213#[macro_use]
214mod features;
215
216mod buf;
217mod client;
218pub mod crypto;
219pub mod errno;
220mod error;
221mod header;
222pub mod memory;
223mod mutex;
224pub mod rust;
225pub mod shm;
226mod state;
227pub mod testing;
228mod util;
229
230pub use buf::*;
231pub use client::*;
232pub use error::*;
233pub use header::*;
234pub use state::*;
235#[cfg(feature = "unsafe_debug")]
236pub use util::init_debug_logging;