#![allow(clippy::disallowed_macros)]
use core::{error, fmt, hash::Hash, time::Duration};
pub use aranya_crypto::tls::CipherSuiteId;
use aranya_crypto::{
dangerous::spideroak_crypto::hex::Hex,
default::DefaultEngine,
id::IdError,
subtle::{Choice, ConstantTimeEq},
zeroize::{Zeroize, ZeroizeOnDrop},
EncryptionPublicKey, Engine,
};
use aranya_id::custom_id;
pub use aranya_policy_text::{text, InvalidText, Text};
use aranya_util::{error::ReportExt, Addr};
use buggy::Bug;
pub use semver::Version;
use serde::{Deserialize, Serialize};
pub mod afc;
pub mod quic_sync;
#[cfg(feature = "afc")]
pub use self::afc::*;
pub use self::quic_sync::*;
pub type CE = DefaultEngine;
pub type CS = <DefaultEngine as Engine>::CS;
#[derive(Serialize, Deserialize, Debug)]
pub enum Error {
DoesNotExist(String),
Other(String),
}
impl Error {
pub fn from_msg(err: &str) -> Self {
Self::Other(err.into())
}
pub fn from_err<E: error::Error>(err: E) -> Self {
Self::Other(ReportExt::report(&err).to_string())
}
}
impl From<Bug> for Error {
fn from(err: Bug) -> Self {
Self::from_err(err)
}
}
impl From<anyhow::Error> for Error {
fn from(err: anyhow::Error) -> Self {
Self::Other(format!("{err:?}"))
}
}
impl From<InvalidText> for Error {
fn from(err: InvalidText) -> Self {
Self::Other(format!("{err:?}"))
}
}
impl From<semver::Error> for Error {
fn from(err: semver::Error) -> Self {
Self::from_err(err)
}
}
impl From<IdError> for Error {
fn from(err: IdError) -> Self {
Self::from_err(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DoesNotExist(msg) | Self::Other(msg) => msg.fmt(f),
}
}
}
impl error::Error for Error {}
pub type Result<T, E = Error> = core::result::Result<T, E>;
custom_id! {
pub struct DeviceId;
}
custom_id! {
pub struct TeamId;
}
custom_id! {
pub struct LabelId;
}
custom_id! {
pub struct RoleId;
}
custom_id! {
pub struct ObjectId;
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct Rank(i64);
impl Rank {
pub const fn new(value: i64) -> Self {
Self(value)
}
pub const fn value(self) -> i64 {
self.0
}
}
impl fmt::Display for Rank {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl From<i64> for Rank {
fn from(value: i64) -> Self {
Self::new(value)
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct Role {
pub id: RoleId,
pub name: Text,
pub author_id: DeviceId,
pub default: bool,
}
#[derive(Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct PublicKeyBundle {
pub identity: Vec<u8>,
pub signing: Vec<u8>,
pub encryption: Vec<u8>,
}
impl fmt::Debug for PublicKeyBundle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PublicKeyBundle")
.field("identity", &Hex::new(&*self.identity))
.field("signing", &Hex::new(&*self.signing))
.field("encryption", &Hex::new(&*self.encryption))
.finish()
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AddTeamConfig {
pub team_id: TeamId,
pub quic_sync: Option<AddTeamQuicSyncConfig>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CreateTeamConfig {
pub quic_sync: Option<CreateTeamQuicSyncConfig>,
}
#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub struct Label {
pub id: LabelId,
pub name: Text,
pub author_id: DeviceId,
}
#[derive(Clone, Serialize, Deserialize)]
pub struct Ikm([u8; SEED_IKM_SIZE]);
impl Ikm {
#[inline]
pub fn raw_ikm_bytes(&self) -> &[u8; SEED_IKM_SIZE] {
&self.0
}
}
impl From<[u8; SEED_IKM_SIZE]> for Ikm {
fn from(value: [u8; SEED_IKM_SIZE]) -> Self {
Self(value)
}
}
impl ConstantTimeEq for Ikm {
fn ct_eq(&self, other: &Self) -> Choice {
self.0.ct_eq(&other.0)
}
}
impl ZeroizeOnDrop for Ikm {}
impl Drop for Ikm {
fn drop(&mut self) {
self.0.zeroize()
}
}
impl fmt::Debug for Ikm {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Ikm").finish_non_exhaustive()
}
}
#[derive(Clone, Serialize, Deserialize)]
pub struct Secret(Box<[u8]>);
impl Secret {
#[inline]
pub fn raw_secret_bytes(&self) -> &[u8] {
&self.0
}
}
impl<T> From<T> for Secret
where
T: Into<Box<[u8]>>,
{
fn from(value: T) -> Self {
Self(value.into())
}
}
impl ConstantTimeEq for Secret {
fn ct_eq(&self, other: &Self) -> Choice {
self.0.ct_eq(&other.0)
}
}
impl ZeroizeOnDrop for Secret {}
impl Drop for Secret {
fn drop(&mut self) {
self.0.zeroize()
}
}
impl fmt::Debug for Secret {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Secret").finish_non_exhaustive()
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SyncPeerConfig {
pub interval: Option<Duration>,
pub sync_now: bool,
#[cfg(feature = "preview")]
pub sync_on_hello: bool,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub enum ChanOp {
RecvOnly,
SendOnly,
SendRecv,
}
#[non_exhaustive]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Perm {
AddDevice,
RemoveDevice,
TerminateTeam,
ChangeRank,
CreateRole,
DeleteRole,
AssignRole,
RevokeRole,
ChangeRolePerms,
SetupDefaultRole,
CreateLabel,
DeleteLabel,
AssignLabel,
RevokeLabel,
CanUseAfc,
CreateAfcUniChannel,
}
#[cfg(not(feature = "afc"))]
use afc_stub::{AfcReceiveChannelInfo, AfcSendChannelInfo, AfcShmInfo};
#[cfg(not(feature = "afc"))]
mod afc_stub {
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub enum Never {}
pub type AfcShmInfo = Never;
pub type AfcSendChannelInfo = Never;
pub type AfcReceiveChannelInfo = Never;
}
#[tarpc::service]
pub trait DaemonApi {
async fn version() -> Result<Version>;
async fn aranya_local_addr() -> Result<Addr>;
async fn get_public_key_bundle() -> Result<PublicKeyBundle>;
async fn get_device_id() -> Result<DeviceId>;
#[cfg(feature = "test-utils")]
async fn test_trace_id() -> Result<String>;
async fn add_sync_peer(addr: Addr, team: TeamId, config: SyncPeerConfig) -> Result<()>;
async fn sync_now(addr: Addr, team: TeamId, cfg: Option<SyncPeerConfig>) -> Result<()>;
#[cfg(feature = "preview")]
async fn sync_hello_subscribe(
peer: Addr,
team: TeamId,
graph_change_debounce: Duration,
duration: Duration,
schedule_delay: Duration,
) -> Result<()>;
#[cfg(feature = "preview")]
async fn sync_hello_unsubscribe(peer: Addr, team: TeamId) -> Result<()>;
async fn remove_sync_peer(addr: Addr, team: TeamId) -> Result<()>;
async fn add_team(cfg: AddTeamConfig) -> Result<()>;
async fn remove_team(team: TeamId) -> Result<()>;
async fn create_team(cfg: CreateTeamConfig) -> Result<TeamId>;
async fn close_team(team: TeamId) -> Result<()>;
async fn encrypt_psk_seed_for_peer(
team: TeamId,
peer_enc_pk: EncryptionPublicKey<CS>,
) -> Result<WrappedSeed>;
async fn add_device_to_team(
team: TeamId,
keys: PublicKeyBundle,
initial_role: Option<RoleId>,
rank: Rank,
) -> Result<()>;
async fn remove_device_from_team(team: TeamId, device: DeviceId) -> Result<()>;
async fn devices_on_team(team: TeamId) -> Result<Box<[DeviceId]>>;
async fn device_public_key_bundle(team: TeamId, device: DeviceId) -> Result<PublicKeyBundle>;
async fn setup_default_roles(team: TeamId) -> Result<Box<[Role]>>;
async fn create_role(team: TeamId, role_name: Text, rank: Rank) -> Result<Role>;
async fn delete_role(team: TeamId, role_id: RoleId) -> Result<()>;
async fn team_roles(team: TeamId) -> Result<Box<[Role]>>;
async fn add_perm_to_role(team: TeamId, role: RoleId, perm: Perm) -> Result<()>;
async fn remove_perm_from_role(team: TeamId, role: RoleId, perm: Perm) -> Result<()>;
async fn query_role_perms(team: TeamId, role: RoleId) -> Result<Vec<Perm>>;
async fn change_rank(
team: TeamId,
object_id: ObjectId,
old_rank: Rank,
new_rank: Rank,
) -> Result<()>;
async fn query_rank(team: TeamId, object_id: ObjectId) -> Result<Rank>;
#[cfg(feature = "test-utils")]
#[cfg_attr(docsrs, doc(cfg(feature = "test-utils")))]
async fn query_device_generation(team: TeamId, device_id: DeviceId) -> Result<Option<i64>>;
async fn assign_role(team: TeamId, device: DeviceId, role: RoleId) -> Result<()>;
async fn revoke_role(team: TeamId, device: DeviceId, role: RoleId) -> Result<()>;
async fn change_role(
team: TeamId,
device: DeviceId,
old_role: RoleId,
new_role: RoleId,
) -> Result<()>;
async fn device_role(team: TeamId, device: DeviceId) -> Result<Option<Role>>;
async fn create_label(team: TeamId, name: Text, rank: Rank) -> Result<LabelId>;
async fn delete_label(team: TeamId, label_id: LabelId) -> Result<()>;
async fn label(team: TeamId, label: LabelId) -> Result<Label>;
async fn labels(team: TeamId) -> Result<Vec<Label>>;
async fn assign_label_to_device(
team: TeamId,
device: DeviceId,
label: LabelId,
op: ChanOp,
) -> Result<()>;
async fn revoke_label_from_device(team: TeamId, device: DeviceId, label: LabelId)
-> Result<()>;
async fn labels_assigned_to_device(team: TeamId, device: DeviceId) -> Result<Box<[Label]>>;
#[cfg(feature = "afc")]
#[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
async fn afc_shm_info() -> Result<AfcShmInfo>;
#[cfg(feature = "afc")]
#[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
async fn create_afc_channel(
team: TeamId,
peer_id: DeviceId,
label_id: LabelId,
) -> Result<AfcSendChannelInfo>;
#[cfg(feature = "afc")]
#[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
async fn delete_afc_channel(chan: AfcLocalChannelId) -> Result<()>;
#[cfg(feature = "afc")]
#[cfg_attr(docsrs, doc(cfg(feature = "afc")))]
async fn accept_afc_channel(team: TeamId, ctrl: AfcCtrl) -> Result<AfcReceiveChannelInfo>;
}