1#![allow(clippy::disallowed_macros)] use core::{
4 borrow::Borrow,
5 error, fmt,
6 hash::{Hash, Hasher},
7 net::SocketAddr,
8 ops::Deref,
9 time::Duration,
10};
11use std::collections::hash_map::{self, HashMap};
12
13use anyhow::bail;
14pub use aranya_crypto::aqc::CipherSuiteId;
15use aranya_crypto::{
16 aqc::{BidiPskId, UniPskId},
17 custom_id,
18 default::{DefaultCipherSuite, DefaultEngine},
19 subtle::{Choice, ConstantTimeEq},
20 zeroize::{Zeroize, ZeroizeOnDrop},
21 Id,
22};
23use aranya_util::Addr;
24use buggy::Bug;
25pub use semver::Version;
26use serde::{Deserialize, Serialize};
27use tracing::error;
28
29pub type CE = DefaultEngine;
31pub type CS = DefaultCipherSuite;
33
34#[derive(Serialize, Deserialize, Debug)]
37pub struct Error(String);
38
39impl From<Bug> for Error {
40 fn from(err: Bug) -> Self {
41 error!(?err);
42 Self(format!("{err:?}"))
43 }
44}
45
46impl From<anyhow::Error> for Error {
47 fn from(err: anyhow::Error) -> Self {
48 error!(?err);
49 Self(format!("{err:?}"))
50 }
51}
52
53impl From<semver::Error> for Error {
54 fn from(err: semver::Error) -> Self {
55 error!(?err);
56 Self(format!("{err:?}"))
57 }
58}
59
60impl From<aranya_crypto::id::IdError> for Error {
61 fn from(err: aranya_crypto::id::IdError) -> Self {
62 error!(%err);
63 Self(err.to_string())
64 }
65}
66
67impl fmt::Display for Error {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 self.0.fmt(f)
70 }
71}
72
73impl error::Error for Error {}
74
75pub type Result<T, E = Error> = core::result::Result<T, E>;
76
77custom_id! {
78 pub struct DeviceId;
80}
81
82custom_id! {
83 pub struct TeamId;
85}
86
87custom_id! {
88 pub struct LabelId;
90}
91
92custom_id! {
93 pub struct AqcBidiChannelId;
95}
96
97custom_id! {
98 pub struct AqcUniChannelId;
100}
101
102#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
104pub struct KeyBundle {
105 pub identity: Vec<u8>,
106 pub signing: Vec<u8>,
107 pub encoding: Vec<u8>,
108}
109
110#[derive(Copy, Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
112pub enum Role {
113 Owner,
114 Admin,
115 Operator,
116 Member,
117}
118
119#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
121pub struct TeamConfig {
122 }
124
125#[derive(Clone, Debug, Serialize, Deserialize, Eq, Ord, PartialEq, PartialOrd)]
127pub struct NetIdentifier(pub String);
128
129impl Borrow<str> for NetIdentifier {
130 #[inline]
131 fn borrow(&self) -> &str {
132 &self.0
133 }
134}
135
136impl<T> AsRef<T> for NetIdentifier
137where
138 T: ?Sized,
139 <Self as Deref>::Target: AsRef<T>,
140{
141 #[inline]
142 fn as_ref(&self) -> &T {
143 self.deref().as_ref()
144 }
145}
146
147impl Deref for NetIdentifier {
148 type Target = str;
149
150 #[inline]
151 fn deref(&self) -> &Self::Target {
152 &self.0
153 }
154}
155
156impl fmt::Display for NetIdentifier {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 self.0.fmt(f)
159 }
160}
161
162pub type AqcCtrl = Vec<Box<[u8]>>;
164
165#[derive(Clone, Debug, Serialize, Deserialize)]
167pub struct Secret(Box<[u8]>);
168
169impl Secret {
170 #[inline]
172 pub fn raw_secret_bytes(&self) -> &[u8] {
173 &self.0
174 }
175}
176
177impl<T> From<T> for Secret
178where
179 T: Into<Box<[u8]>>,
180{
181 fn from(value: T) -> Self {
182 Self(value.into())
183 }
184}
185
186impl ConstantTimeEq for Secret {
187 fn ct_eq(&self, other: &Self) -> Choice {
188 self.0.ct_eq(&other.0)
189 }
190}
191
192impl ZeroizeOnDrop for Secret {}
193impl Drop for Secret {
194 fn drop(&mut self) {
195 self.0.zeroize()
196 }
197}
198
199macro_rules! psk_map {
200 (
201 $(#[$meta:meta])*
202 $vis:vis struct $name:ident(PskMap<$psk:ty>);
203 ) => {
204 $(#[$meta])*
205 #[derive(Clone, Debug, Serialize, Deserialize)]
206 #[cfg_attr(test, derive(PartialEq))]
207 $vis struct $name {
208 id: Id,
209 psks: HashMap<CsId, $psk>
210 }
211
212 impl $name {
213 pub fn len(&self) -> usize {
215 self.psks.len()
216 }
217
218 pub fn is_empty(&self) -> bool {
220 self.psks.is_empty()
221 }
222
223 pub fn channel_id(&self) -> &Id {
225 &self.id
226 }
227
228 pub fn get(&self, suite: CipherSuiteId) -> Option<&$psk> {
230 self.psks.get(&CsId(suite))
231 }
232
233 pub fn try_from_fn<I, E, F>(id: I, mut f: F) -> anyhow::Result<Self>
236 where
237 I: Into<Id>,
238 anyhow::Error: From<E>,
239 F: FnMut(CipherSuiteId) -> Result<$psk, E>,
240 {
241 let id = id.into();
242 let mut psks = HashMap::new();
243 for &suite in CipherSuiteId::all() {
244 let psk = f(suite)?;
245 if !bool::from(psk.identity().channel_id().into_id().ct_eq(&id)) {
246 bail!("PSK identity does not match channel ID");
247 }
248 psks.insert(CsId(suite), psk);
249 }
250 Ok(Self { id, psks })
251 }
252 }
253
254 impl IntoIterator for $name {
255 type Item = (CipherSuiteId, $psk);
256 type IntoIter = IntoPsks<$psk>;
257
258 fn into_iter(self) -> Self::IntoIter {
259 IntoPsks {
260 iter: self.psks.into_iter(),
261 }
262 }
263 }
264
265 #[cfg(test)]
266 impl tests::PskMap for $name {
267 type Psk = $psk;
268
269 fn new() -> Self {
270 Self {
271 id: Id::default(),
273 psks: HashMap::new(),
274 }
275 }
276
277 fn len(&self) -> usize {
278 self.psks.len()
279 }
280
281 fn insert(&mut self, psk: Self::Psk) {
282 let suite = psk.cipher_suite();
283 let opt = self.psks.insert(CsId(suite), psk);
284 assert!(opt.is_none());
285 }
286 }
287 };
288}
289psk_map! {
290 pub struct AqcBidiPsks(PskMap<AqcBidiPsk>);
293}
294
295psk_map! {
296 pub struct AqcUniPsks(PskMap<AqcUniPsk>);
299}
300
301#[derive(Clone, Debug, Serialize, Deserialize)]
304pub enum AqcPsks {
305 Bidi(AqcBidiPsks),
306 Uni(AqcUniPsks),
307}
308
309impl IntoIterator for AqcPsks {
310 type IntoIter = AqcPsksIntoIter;
311 type Item = <Self::IntoIter as Iterator>::Item;
312
313 fn into_iter(self) -> Self::IntoIter {
314 match self {
315 AqcPsks::Bidi(psks) => AqcPsksIntoIter::Bidi(psks.into_iter()),
316 AqcPsks::Uni(psks) => AqcPsksIntoIter::Uni(psks.into_iter()),
317 }
318 }
319}
320
321#[derive(Debug)]
323pub enum AqcPsksIntoIter {
324 Bidi(IntoPsks<AqcBidiPsk>),
325 Uni(IntoPsks<AqcUniPsk>),
326}
327
328impl Iterator for AqcPsksIntoIter {
329 type Item = (CipherSuiteId, AqcPsk);
330 fn next(&mut self) -> Option<Self::Item> {
331 match self {
332 AqcPsksIntoIter::Bidi(it) => it.next().map(|(s, k)| (s, AqcPsk::Bidi(k))),
333 AqcPsksIntoIter::Uni(it) => it.next().map(|(s, k)| (s, AqcPsk::Uni(k))),
334 }
335 }
336}
337
338#[derive(Debug)]
340pub struct IntoPsks<V> {
341 iter: hash_map::IntoIter<CsId, V>,
342}
343
344impl<V> Iterator for IntoPsks<V> {
345 type Item = (CipherSuiteId, V);
346
347 fn next(&mut self) -> Option<Self::Item> {
348 self.iter.next().map(|(k, v)| (k.0, v))
349 }
350}
351
352#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
355#[serde(transparent)]
356struct CsId(CipherSuiteId);
357
358impl Hash for CsId {
359 fn hash<H: Hasher>(&self, state: &mut H) {
360 self.0.to_bytes().hash(state);
361 }
362}
363
364#[derive(Clone, Debug, Serialize, Deserialize)]
366pub enum AqcPsk {
367 Bidi(AqcBidiPsk),
369 Uni(AqcUniPsk),
371}
372
373impl AqcPsk {
374 #[inline]
376 pub fn identity(&self) -> AqcPskId {
377 match self {
378 Self::Bidi(psk) => AqcPskId::Bidi(psk.identity),
379 Self::Uni(psk) => AqcPskId::Uni(psk.identity),
380 }
381 }
382
383 #[inline]
385 pub fn cipher_suite(&self) -> CipherSuiteId {
386 self.identity().cipher_suite()
387 }
388
389 #[inline]
391 pub fn secret(&self) -> &[u8] {
392 match self {
393 Self::Bidi(psk) => psk.secret.raw_secret_bytes(),
394 Self::Uni(psk) => match &psk.secret {
395 Directed::Send(secret) | Directed::Recv(secret) => secret.raw_secret_bytes(),
396 },
397 }
398 }
399}
400
401impl From<AqcBidiPsk> for AqcPsk {
402 fn from(psk: AqcBidiPsk) -> Self {
403 Self::Bidi(psk)
404 }
405}
406
407impl From<AqcUniPsk> for AqcPsk {
408 fn from(psk: AqcUniPsk) -> Self {
409 Self::Uni(psk)
410 }
411}
412
413impl ConstantTimeEq for AqcPsk {
414 fn ct_eq(&self, other: &Self) -> Choice {
415 match (self, other) {
418 (Self::Bidi(lhs), Self::Bidi(rhs)) => lhs.ct_eq(rhs),
419 (Self::Uni(lhs), Self::Uni(rhs)) => lhs.ct_eq(rhs),
420 _ => Choice::from(0u8),
421 }
422 }
423}
424
425#[derive(Clone, Debug, Serialize, Deserialize)]
427pub struct AqcBidiPsk {
428 pub identity: BidiPskId,
430 pub secret: Secret,
432}
433
434impl AqcBidiPsk {
435 fn identity(&self) -> &BidiPskId {
436 &self.identity
437 }
438
439 #[cfg(test)]
440 fn cipher_suite(&self) -> CipherSuiteId {
441 self.identity.cipher_suite()
442 }
443}
444
445impl ConstantTimeEq for AqcBidiPsk {
446 fn ct_eq(&self, other: &Self) -> Choice {
447 let id = self.identity.ct_eq(&other.identity);
448 let secret = self.secret.ct_eq(&other.secret);
449 id & secret
450 }
451}
452
453impl ZeroizeOnDrop for AqcBidiPsk {}
454
455#[derive(Clone, Debug, Serialize, Deserialize)]
457pub struct AqcUniPsk {
458 pub identity: UniPskId,
460 pub secret: Directed<Secret>,
462}
463
464impl AqcUniPsk {
465 fn identity(&self) -> &UniPskId {
466 &self.identity
467 }
468
469 #[cfg(test)]
470 fn cipher_suite(&self) -> CipherSuiteId {
471 self.identity.cipher_suite()
472 }
473}
474
475impl ConstantTimeEq for AqcUniPsk {
476 fn ct_eq(&self, other: &Self) -> Choice {
477 let id = self.identity.ct_eq(&other.identity);
478 let secret = self.secret.ct_eq(&other.secret);
479 id & secret
480 }
481}
482
483impl ZeroizeOnDrop for AqcUniPsk {}
484
485#[derive(Clone, Debug, Serialize, Deserialize)]
487pub enum Directed<T> {
488 Send(T),
490 Recv(T),
492}
493
494impl<T: ConstantTimeEq> ConstantTimeEq for Directed<T> {
495 fn ct_eq(&self, other: &Self) -> Choice {
496 match (self, other) {
499 (Self::Send(lhs), Self::Send(rhs)) => lhs.ct_eq(rhs),
500 (Self::Recv(lhs), Self::Recv(rhs)) => lhs.ct_eq(rhs),
501 _ => Choice::from(0u8),
502 }
503 }
504}
505
506#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
508pub enum AqcPskId {
509 Bidi(BidiPskId),
511 Uni(UniPskId),
513}
514
515impl AqcPskId {
516 pub fn channel_id(&self) -> Id {
518 match self {
519 Self::Bidi(v) => (*v.channel_id()).into(),
520 Self::Uni(v) => (*v.channel_id()).into(),
521 }
522 }
523
524 pub fn cipher_suite(&self) -> CipherSuiteId {
526 match self {
527 Self::Bidi(v) => v.cipher_suite(),
528 Self::Uni(v) => v.cipher_suite(),
529 }
530 }
531
532 pub fn as_bytes(&self) -> &[u8; 34] {
534 match self {
535 Self::Bidi(v) => v.as_bytes(),
536 Self::Uni(v) => v.as_bytes(),
537 }
538 }
539}
540
541#[derive(Clone, Debug, Serialize, Deserialize)]
543pub struct SyncPeerConfig {
544 pub interval: Duration,
546 pub sync_now: bool,
548}
549
550#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
552pub enum ChanOp {
553 RecvOnly,
556 SendOnly,
559 SendRecv,
562}
563
564#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
566pub struct Label {
567 pub id: LabelId,
568 pub name: String,
569}
570
571#[tarpc::service]
572pub trait DaemonApi {
573 async fn version() -> Result<Version>;
575
576 async fn aranya_local_addr() -> Result<SocketAddr>;
578
579 async fn get_key_bundle() -> Result<KeyBundle>;
581
582 async fn get_device_id() -> Result<DeviceId>;
584
585 async fn add_sync_peer(addr: Addr, team: TeamId, config: SyncPeerConfig) -> Result<()>;
587
588 async fn sync_now(addr: Addr, team: TeamId, cfg: Option<SyncPeerConfig>) -> Result<()>;
590
591 async fn remove_sync_peer(addr: Addr, team: TeamId) -> Result<()>;
593
594 async fn add_team(team: TeamId, cfg: TeamConfig) -> Result<()>;
596
597 async fn remove_team(team: TeamId) -> Result<()>;
599
600 async fn create_team(cfg: TeamConfig) -> Result<TeamId>;
602 async fn close_team(team: TeamId) -> Result<()>;
604
605 async fn add_device_to_team(team: TeamId, keys: KeyBundle) -> Result<()>;
607 async fn remove_device_from_team(team: TeamId, device: DeviceId) -> Result<()>;
609
610 async fn assign_role(team: TeamId, device: DeviceId, role: Role) -> Result<()>;
612 async fn revoke_role(team: TeamId, device: DeviceId, role: Role) -> Result<()>;
614
615 async fn assign_aqc_net_identifier(
617 team: TeamId,
618 device: DeviceId,
619 name: NetIdentifier,
620 ) -> Result<()>;
621 async fn remove_aqc_net_identifier(
623 team: TeamId,
624 device: DeviceId,
625 name: NetIdentifier,
626 ) -> Result<()>;
627
628 async fn create_label(team: TeamId, name: String) -> Result<LabelId>;
630 async fn delete_label(team: TeamId, label_id: LabelId) -> Result<()>;
632 async fn assign_label(
634 team: TeamId,
635 device: DeviceId,
636 label_id: LabelId,
637 op: ChanOp,
638 ) -> Result<()>;
639 async fn revoke_label(team: TeamId, device: DeviceId, label_id: LabelId) -> Result<()>;
641
642 async fn create_aqc_bidi_channel(
644 team: TeamId,
645 peer: NetIdentifier,
646 label_id: LabelId,
647 ) -> Result<(AqcCtrl, AqcBidiPsks)>;
648 async fn create_aqc_uni_channel(
650 team: TeamId,
651 peer: NetIdentifier,
652 label_id: LabelId,
653 ) -> Result<(AqcCtrl, AqcUniPsks)>;
654 async fn delete_aqc_bidi_channel(chan: AqcBidiChannelId) -> Result<AqcCtrl>;
656 async fn delete_aqc_uni_channel(chan: AqcUniChannelId) -> Result<AqcCtrl>;
658 async fn receive_aqc_ctrl(team: TeamId, ctrl: AqcCtrl) -> Result<(LabelId, AqcPsks)>;
660
661 async fn query_devices_on_team(team: TeamId) -> Result<Vec<DeviceId>>;
663 async fn query_device_role(team: TeamId, device: DeviceId) -> Result<Role>;
665 async fn query_device_keybundle(team: TeamId, device: DeviceId) -> Result<KeyBundle>;
667 async fn query_device_label_assignments(team: TeamId, device: DeviceId) -> Result<Vec<Label>>;
669 async fn query_aqc_net_identifier(
671 team: TeamId,
672 device: DeviceId,
673 ) -> Result<Option<NetIdentifier>>;
674 async fn query_labels(team: TeamId) -> Result<Vec<Label>>;
676 async fn query_label_exists(team: TeamId, label: LabelId) -> Result<bool>;
678}
679
680#[cfg(test)]
681mod tests {
682 use aranya_crypto::Rng;
683 use serde::de::DeserializeOwned;
684
685 use super::*;
686
687 fn secret(secret: &[u8]) -> Secret {
688 Secret(Box::from(secret))
689 }
690
691 pub(super) trait PskMap:
692 fmt::Debug + PartialEq + Serialize + DeserializeOwned + Sized
693 {
694 type Psk;
695 fn new() -> Self;
696 fn len(&self) -> usize;
698 fn insert(&mut self, psk: Self::Psk);
704 }
705
706 impl PartialEq for AqcBidiPsk {
707 fn eq(&self, other: &Self) -> bool {
708 bool::from(self.ct_eq(other))
709 }
710 }
711 impl PartialEq for AqcUniPsk {
712 fn eq(&self, other: &Self) -> bool {
713 bool::from(self.ct_eq(other))
714 }
715 }
716 impl PartialEq for AqcPsk {
717 fn eq(&self, other: &Self) -> bool {
718 bool::from(self.ct_eq(other))
719 }
720 }
721
722 #[track_caller]
723 fn psk_map_test<M, F>(name: &'static str, mut f: F)
724 where
725 M: PskMap,
726 F: FnMut(Secret, Id, CipherSuiteId) -> M::Psk,
727 {
728 let mut psks = M::new();
729 for (i, &suite) in CipherSuiteId::all().iter().enumerate() {
730 let id = Id::random(&mut Rng);
731 let secret = secret(&i.to_le_bytes());
732 psks.insert(f(secret, id, suite));
733 }
734 assert_eq!(psks.len(), CipherSuiteId::all().len(), "{name}");
735
736 let bytes = postcard::to_allocvec(&psks).unwrap();
737 let got = postcard::from_bytes::<M>(&bytes).unwrap();
738 assert_eq!(got, psks, "{name}")
739 }
740
741 #[test]
744 fn test_aqc_bidi_psks_serde() {
745 psk_map_test::<AqcBidiPsks, _>("AqcBidiPsk", |secret, id, suite| AqcBidiPsk {
746 identity: BidiPskId::from((id.into(), suite)),
747 secret,
748 });
749 }
750
751 #[test]
754 fn test_aqc_uni_psks_serde() {
755 psk_map_test::<AqcUniPsks, _>("AqcUniPsk (send)", |secret, id, suite| AqcUniPsk {
756 identity: UniPskId::from((id.into(), suite)),
757 secret: Directed::Send(secret),
758 });
759 psk_map_test::<AqcUniPsks, _>("AqcUniPsk (recv)", |secret, id, suite| AqcUniPsk {
760 identity: UniPskId::from((id.into(), suite)),
761 secret: Directed::Recv(secret),
762 });
763 }
764}