Skip to main content

Crate aranya_fast_channels

Crate aranya_fast_channels 

Source
Expand description

The core library for Aranya Fast Channels (AFC).

§Overview

AFC provides a high-throughput, low latency encryption engine protected by Aranya’s policy rules. Data encrypted with (or decrypted by) the engine is sent out of band (not though Aranya itself), making it suitable for encrypting network streams and other high-throughput data.

AFC can be configured to use custom cryptography and random number generation.

§Usage

AFC uses the client-daemon model, with AFC being the “client” and Aranya being the “daemon.” However, this is merely a logical distinction; for instance, it’s possible for both to be in the same process, just running as different threads (or tasks).

All AFC operations are handled by the Client, which communicates with the daemon over AfcState and AranyaState. By default, AFC provides a state implementation backed by shared memory.

§Notes

AFC encrypts/seals each message with a deterministic nonce derived from a base nonce and sequence number. Sequence numbers should not be re-used in a given channel but it is possible to do so by passing a “new” AfcState::SealCtx to the seal methods on Client.

§Example

The following example demonstrates two Clients encrypting data for each other. In practice, the two clients are almost always on different machines. The example also uses shared memory for the state, but in practice anything supported by Aranya can be used.

use aranya_crypto::{
    Csprng, EncryptionKey, Engine, IdentityKey, Random, Rng,
    afc::{RawOpenKey, RawSealKey, UniChannel, UniOpenKey, UniSealKey, UniSecrets},
    dangerous::spideroak_crypto::rust::HkdfSha256,
    default::{DefaultCipherSuite, DefaultEngine},
    id::IdExt as _,
    policy::{CmdId, LabelId},
};
use aranya_fast_channels::{
    AfcState, AranyaState, Channel, Client, Directed, Error, LocalChannelId,
    crypto::Aes256Gcm,
    shm::{Flag, Mode, Path, ReadState, WriteState},
};

type E = DefaultEngine;
type CS = DefaultCipherSuite;

// The maximum number of channels supported by the shared
// memory.
//
// You can use any value, this is just an example.
const MAX_CHANS: usize = 42;

let aranya_client_a: WriteState<CS, Rng> = {
    let path = Path::from_bytes(b"/afc_doc_client_a\x00")
        .map_err(|err| Error::SharedMem(err.into()))?;
    WriteState::open(path, Flag::Create, Mode::ReadWrite, MAX_CHANS, Rng)
        .map_err(Error::SharedMem)?
};

let aranya_client_b: WriteState<CS, Rng> = {
    let path = Path::from_bytes(b"/afc_doc_client_b\x00")
        .map_err(|err| Error::SharedMem(err.into()))?;
    WriteState::open(path, Flag::Create, Mode::ReadWrite, MAX_CHANS, Rng)
        .map_err(Error::SharedMem)?
};

let (eng, _) = E::from_entropy(Rng);

let device1_id = IdentityKey::<CS>::new(&eng).id()?;
let device1_enc_sk = EncryptionKey::<CS>::new(&eng);

let device2_id = IdentityKey::<CS>::new(&eng).id()?;
let device2_enc_sk = EncryptionKey::<CS>::new(&eng);

// The label ID used for encryption and decryption.
let label_id = LabelId::random(Rng);

let ch1 = UniChannel {
    parent_cmd_id: CmdId::random(&eng),
    our_sk: &device1_enc_sk,
    seal_id: device1_id,
    their_pk: &device2_enc_sk.public()?,
    open_id: device2_id,
    label_id,
};
let UniSecrets { author, peer } = UniSecrets::new(&eng, &ch1)?;

// Inform device1 about device2.
let seal = UniSealKey::from_author_secret(&ch1, author)?.into_raw_key();
let client_a_channel_id =
    aranya_client_a.add(Directed::SealOnly { seal }, label_id, device2_id)?;

let ch2 = UniChannel {
    parent_cmd_id: ch1.parent_cmd_id,
    our_sk: &device2_enc_sk,
    open_id: device2_id,
    their_pk: &device1_enc_sk.public()?,
    seal_id: device1_id,
    label_id,
};

// Inform device2 about device1.
let open = UniOpenKey::from_peer_encap(&ch2, peer)?.into_raw_key();
let client_b_channel_id =
    aranya_client_b.add(Directed::OpenOnly { open }, label_id, device1_id)?;

let mut afc_client_a = {
    let path = Path::from_bytes(b"/afc_doc_client_a\x00")
        .map_err(|err| Error::SharedMem(err.into()))?;
    let state = ReadState::open(path, Flag::OpenOnly, Mode::ReadWrite, MAX_CHANS)
        .map_err(Error::SharedMem)?;
    Client::<ReadState<CS>>::new(state)
};
let mut afc_client_b = {
    let path = Path::from_bytes(b"/afc_doc_client_b\x00")
        .map_err(|err| Error::SharedMem(err.into()))?;
    let state = ReadState::open(path, Flag::OpenOnly, Mode::ReadWrite, MAX_CHANS)
        .map_err(Error::SharedMem)?;
    Client::<ReadState<CS>>::new(state)
};

const GOLDEN: &str = "hello from APS!";

// Have device1 encrypt data for device2.
let ciphertext = {
    // Encryption has a little overhead, so make sure the
    // ouput buffer is large enough.
    let mut dst = vec![0u8; GOLDEN.len() + Client::<ReadState<CS>>::OVERHEAD];

    // Create the ctx to pass in.
    let mut ctx = afc_client_a.setup_seal_ctx(client_a_channel_id)?;
    afc_client_a.seal(&mut ctx, &mut dst[..], GOLDEN.as_bytes())?;
    dst
};

// Here is where you'd send ciphertext over the network, or
// whatever makes sense for your application.

// Have device2 decrypt the data from device1.
let (label_from_open, seq, plaintext) = {
    let mut dst = vec![0u8; ciphertext.len() - Client::<ReadState<CS>>::OVERHEAD];
    // Create the ctx to pass in.
    let mut ctx = afc_client_b.setup_open_ctx(client_b_channel_id)?;
    let (label_id, seq) = afc_client_b.open(&mut ctx, &mut dst[..], &ciphertext[..])?;
    (label_id, seq, dst)
};

// At this point we can now make a decision on what to do
// with plaintext based on the label. We know it came from
// `device1` and we know it has the label `label_id`.
// Both of those facts (`device1` and `label_id`)
// have been cryptographically verified, so we can make
// decisions based on them. For example, we could forward the
// plaintext data on to another system that ingests "top
// secret" data.
assert_eq!(label_from_open, label_id);
assert_eq!(seq, 0);
assert_eq!(plaintext, GOLDEN.as_bytes());

Modules§

crypto
Cryptographic primitives.
errno
Support for libc’s errno.
memorymemory
Memory implementation of State for same process APS usage
rust
RustCrypto cryptography.
shmsdlib or posix
A shared memory implementation of State.
testingtesting
Utilities for testing AfcState and AranyaState implementations.

Macros§

test_impl
Performs all of the tests in the testing module.

Structs§

AllocError
Unable to allocate memory.
Channel
An AFC channel.
Client
Client is a connection to Aranya.
FixedBuf
A fixed-size buffer.
Header
The per-message header.
LocalChannelId
Uniquely identifies a channel inside the shared state.
Message
An APS message.
RemoveIfParams
The set of Params passed to the closure in AranyaState::remove_if
Seq
Identifies the position of a ciphertext in a channel.

Enums§

ChannelDirection
Describes the flow of data for an AFC channel.
Directed
A directed channel secret.
Error
An error returned from this API.
HeaderError
The header was invalid.
MsgType
Describes the type of message.
ParseError
An error from Message::try_parse.
Payload
The payload of a Message.
Version
The APS protocol version.

Traits§

AfcState
AFC’s view of the shared state.
AranyaState
Aranya’s view of the shared state.
Buf
A generalization over Vec.

Functions§

init_debug_loggingunsafe_debug
Configures the debugging logger.

Type Aliases§

Result
Shorthand for Results that use Error.