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::DefaultEngine,
19 id::IdError,
20 subtle::{Choice, ConstantTimeEq},
21 zeroize::{Zeroize, ZeroizeOnDrop},
22 EncryptionPublicKey, Engine, Id,
23};
24pub use aranya_policy_text::{text, Text};
25use aranya_util::Addr;
26use buggy::Bug;
27pub use semver::Version;
28use serde::{Deserialize, Serialize};
29use tracing::error;
30
31pub mod quic_sync;
32pub use quic_sync::*;
33
34pub type CE = DefaultEngine;
36pub type CS = <DefaultEngine as Engine>::CS;
38
39#[derive(Serialize, Deserialize, Debug)]
42pub struct Error(String);
43
44impl Error {
45 pub fn from_msg(err: &str) -> Self {
46 error!(?err);
47 Self(err.into())
48 }
49
50 pub fn from_err<E: error::Error>(err: E) -> Self {
51 error!(?err);
52 Self(format!("{err:?}"))
53 }
54}
55
56impl From<Bug> for Error {
57 fn from(err: Bug) -> Self {
58 error!(?err);
59 Self(format!("{err:?}"))
60 }
61}
62
63impl From<anyhow::Error> for Error {
64 fn from(err: anyhow::Error) -> Self {
65 error!(?err);
66 Self(format!("{err:?}"))
67 }
68}
69
70impl From<semver::Error> for Error {
71 fn from(err: semver::Error) -> Self {
72 error!(?err);
73 Self(format!("{err:?}"))
74 }
75}
76
77impl From<IdError> for Error {
78 fn from(err: IdError) -> Self {
79 error!(%err);
80 Self(err.to_string())
81 }
82}
83
84impl fmt::Display for Error {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 self.0.fmt(f)
87 }
88}
89
90impl error::Error for Error {}
91
92pub type Result<T, E = Error> = core::result::Result<T, E>;
93
94custom_id! {
95 pub struct DeviceId;
97}
98
99custom_id! {
100 pub struct TeamId;
102}
103
104custom_id! {
105 pub struct LabelId;
107}
108
109custom_id! {
110 pub struct AqcBidiChannelId;
112}
113
114custom_id! {
115 pub struct AqcUniChannelId;
117}
118
119#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
121pub struct KeyBundle {
122 pub identity: Vec<u8>,
123 pub signing: Vec<u8>,
124 pub encoding: Vec<u8>,
125}
126
127#[derive(Copy, Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
129pub enum Role {
130 Owner,
131 Admin,
132 Operator,
133 Member,
134}
135
136#[derive(Debug, Serialize, Deserialize)]
139pub struct TeamConfig {
140 pub quic_sync: Option<QuicSyncConfig>,
141}
142
143#[derive(Clone, Debug, Serialize, Deserialize, Eq, Ord, PartialEq, PartialOrd)]
145pub struct NetIdentifier(pub Text);
146
147impl Borrow<str> for NetIdentifier {
148 #[inline]
149 fn borrow(&self) -> &str {
150 &self.0
151 }
152}
153
154impl<T> AsRef<T> for NetIdentifier
155where
156 T: ?Sized,
157 <Self as Deref>::Target: AsRef<T>,
158{
159 #[inline]
160 fn as_ref(&self) -> &T {
161 self.deref().as_ref()
162 }
163}
164
165impl Deref for NetIdentifier {
166 type Target = str;
167
168 #[inline]
169 fn deref(&self) -> &Self::Target {
170 &self.0
171 }
172}
173
174impl fmt::Display for NetIdentifier {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 self.0.fmt(f)
177 }
178}
179
180pub type AqcCtrl = Vec<Box<[u8]>>;
182
183#[derive(Clone, Debug, Serialize, Deserialize)]
185pub struct Secret(Box<[u8]>);
186
187impl Secret {
188 #[inline]
190 pub fn raw_secret_bytes(&self) -> &[u8] {
191 &self.0
192 }
193}
194
195impl<T> From<T> for Secret
196where
197 T: Into<Box<[u8]>>,
198{
199 fn from(value: T) -> Self {
200 Self(value.into())
201 }
202}
203
204impl ConstantTimeEq for Secret {
205 fn ct_eq(&self, other: &Self) -> Choice {
206 self.0.ct_eq(&other.0)
207 }
208}
209
210impl ZeroizeOnDrop for Secret {}
211impl Drop for Secret {
212 fn drop(&mut self) {
213 self.0.zeroize()
214 }
215}
216
217macro_rules! psk_map {
218 (
219 $(#[$meta:meta])*
220 $vis:vis struct $name:ident(PskMap<$psk:ty>);
221 ) => {
222 $(#[$meta])*
223 #[derive(Clone, Debug, Serialize, Deserialize)]
224 #[cfg_attr(test, derive(PartialEq))]
225 $vis struct $name {
226 id: Id,
227 psks: HashMap<CsId, $psk>
228 }
229
230 impl $name {
231 pub fn len(&self) -> usize {
233 self.psks.len()
234 }
235
236 pub fn is_empty(&self) -> bool {
238 self.psks.is_empty()
239 }
240
241 pub fn channel_id(&self) -> &Id {
243 &self.id
244 }
245
246 pub fn get(&self, suite: CipherSuiteId) -> Option<&$psk> {
248 self.psks.get(&CsId(suite))
249 }
250
251 pub fn try_from_fn<I, E, F>(id: I, mut f: F) -> anyhow::Result<Self>
254 where
255 I: Into<Id>,
256 anyhow::Error: From<E>,
257 F: FnMut(CipherSuiteId) -> Result<$psk, E>,
258 {
259 let id = id.into();
260 let mut psks = HashMap::new();
261 for &suite in CipherSuiteId::all() {
262 let psk = f(suite)?;
263 if !bool::from(psk.identity().channel_id().into_id().ct_eq(&id)) {
264 bail!("PSK identity does not match channel ID");
265 }
266 psks.insert(CsId(suite), psk);
267 }
268 Ok(Self { id, psks })
269 }
270 }
271
272 impl IntoIterator for $name {
273 type Item = (CipherSuiteId, $psk);
274 type IntoIter = IntoPsks<$psk>;
275
276 fn into_iter(self) -> Self::IntoIter {
277 IntoPsks {
278 iter: self.psks.into_iter(),
279 }
280 }
281 }
282
283 #[cfg(test)]
284 impl tests::PskMap for $name {
285 type Psk = $psk;
286
287 fn new() -> Self {
288 Self {
289 id: Id::default(),
291 psks: HashMap::new(),
292 }
293 }
294
295 fn len(&self) -> usize {
296 self.psks.len()
297 }
298
299 fn insert(&mut self, psk: Self::Psk) {
300 let suite = psk.cipher_suite();
301 let opt = self.psks.insert(CsId(suite), psk);
302 assert!(opt.is_none());
303 }
304 }
305 };
306}
307psk_map! {
308 pub struct AqcBidiPsks(PskMap<AqcBidiPsk>);
311}
312
313psk_map! {
314 pub struct AqcUniPsks(PskMap<AqcUniPsk>);
317}
318
319#[derive(Clone, Debug, Serialize, Deserialize)]
322pub enum AqcPsks {
323 Bidi(AqcBidiPsks),
324 Uni(AqcUniPsks),
325}
326
327impl IntoIterator for AqcPsks {
328 type IntoIter = AqcPsksIntoIter;
329 type Item = <Self::IntoIter as Iterator>::Item;
330
331 fn into_iter(self) -> Self::IntoIter {
332 match self {
333 AqcPsks::Bidi(psks) => AqcPsksIntoIter::Bidi(psks.into_iter()),
334 AqcPsks::Uni(psks) => AqcPsksIntoIter::Uni(psks.into_iter()),
335 }
336 }
337}
338
339#[derive(Debug)]
341pub enum AqcPsksIntoIter {
342 Bidi(IntoPsks<AqcBidiPsk>),
343 Uni(IntoPsks<AqcUniPsk>),
344}
345
346impl Iterator for AqcPsksIntoIter {
347 type Item = (CipherSuiteId, AqcPsk);
348 fn next(&mut self) -> Option<Self::Item> {
349 match self {
350 AqcPsksIntoIter::Bidi(it) => it.next().map(|(s, k)| (s, AqcPsk::Bidi(k))),
351 AqcPsksIntoIter::Uni(it) => it.next().map(|(s, k)| (s, AqcPsk::Uni(k))),
352 }
353 }
354}
355
356#[derive(Debug)]
358pub struct IntoPsks<V> {
359 iter: hash_map::IntoIter<CsId, V>,
360}
361
362impl<V> Iterator for IntoPsks<V> {
363 type Item = (CipherSuiteId, V);
364
365 fn next(&mut self) -> Option<Self::Item> {
366 self.iter.next().map(|(k, v)| (k.0, v))
367 }
368}
369
370#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
373#[serde(transparent)]
374struct CsId(CipherSuiteId);
375
376impl Hash for CsId {
377 fn hash<H: Hasher>(&self, state: &mut H) {
378 self.0.to_bytes().hash(state);
379 }
380}
381
382#[derive(Clone, Debug, Serialize, Deserialize)]
384pub enum AqcPsk {
385 Bidi(AqcBidiPsk),
387 Uni(AqcUniPsk),
389}
390
391impl AqcPsk {
392 #[inline]
394 pub fn identity(&self) -> AqcPskId {
395 match self {
396 Self::Bidi(psk) => AqcPskId::Bidi(psk.identity),
397 Self::Uni(psk) => AqcPskId::Uni(psk.identity),
398 }
399 }
400
401 #[inline]
403 pub fn cipher_suite(&self) -> CipherSuiteId {
404 self.identity().cipher_suite()
405 }
406
407 #[inline]
409 pub fn secret(&self) -> &[u8] {
410 match self {
411 Self::Bidi(psk) => psk.secret.raw_secret_bytes(),
412 Self::Uni(psk) => match &psk.secret {
413 Directed::Send(secret) | Directed::Recv(secret) => secret.raw_secret_bytes(),
414 },
415 }
416 }
417}
418
419impl From<AqcBidiPsk> for AqcPsk {
420 fn from(psk: AqcBidiPsk) -> Self {
421 Self::Bidi(psk)
422 }
423}
424
425impl From<AqcUniPsk> for AqcPsk {
426 fn from(psk: AqcUniPsk) -> Self {
427 Self::Uni(psk)
428 }
429}
430
431impl ConstantTimeEq for AqcPsk {
432 fn ct_eq(&self, other: &Self) -> Choice {
433 match (self, other) {
436 (Self::Bidi(lhs), Self::Bidi(rhs)) => lhs.ct_eq(rhs),
437 (Self::Uni(lhs), Self::Uni(rhs)) => lhs.ct_eq(rhs),
438 _ => Choice::from(0u8),
439 }
440 }
441}
442
443#[derive(Clone, Debug, Serialize, Deserialize)]
445pub struct AqcBidiPsk {
446 pub identity: BidiPskId,
448 pub secret: Secret,
450}
451
452impl AqcBidiPsk {
453 fn identity(&self) -> &BidiPskId {
454 &self.identity
455 }
456
457 #[cfg(test)]
458 fn cipher_suite(&self) -> CipherSuiteId {
459 self.identity.cipher_suite()
460 }
461}
462
463impl ConstantTimeEq for AqcBidiPsk {
464 fn ct_eq(&self, other: &Self) -> Choice {
465 let id = self.identity.ct_eq(&other.identity);
466 let secret = self.secret.ct_eq(&other.secret);
467 id & secret
468 }
469}
470
471impl ZeroizeOnDrop for AqcBidiPsk {}
472
473#[derive(Clone, Debug, Serialize, Deserialize)]
475pub struct AqcUniPsk {
476 pub identity: UniPskId,
478 pub secret: Directed<Secret>,
480}
481
482impl AqcUniPsk {
483 fn identity(&self) -> &UniPskId {
484 &self.identity
485 }
486
487 #[cfg(test)]
488 fn cipher_suite(&self) -> CipherSuiteId {
489 self.identity.cipher_suite()
490 }
491}
492
493impl ConstantTimeEq for AqcUniPsk {
494 fn ct_eq(&self, other: &Self) -> Choice {
495 let id = self.identity.ct_eq(&other.identity);
496 let secret = self.secret.ct_eq(&other.secret);
497 id & secret
498 }
499}
500
501impl ZeroizeOnDrop for AqcUniPsk {}
502
503#[derive(Clone, Debug, Serialize, Deserialize)]
505pub enum Directed<T> {
506 Send(T),
508 Recv(T),
510}
511
512impl<T: ConstantTimeEq> ConstantTimeEq for Directed<T> {
513 fn ct_eq(&self, other: &Self) -> Choice {
514 match (self, other) {
517 (Self::Send(lhs), Self::Send(rhs)) => lhs.ct_eq(rhs),
518 (Self::Recv(lhs), Self::Recv(rhs)) => lhs.ct_eq(rhs),
519 _ => Choice::from(0u8),
520 }
521 }
522}
523
524#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
526pub enum AqcPskId {
527 Bidi(BidiPskId),
529 Uni(UniPskId),
531}
532
533impl AqcPskId {
534 pub fn channel_id(&self) -> Id {
536 match self {
537 Self::Bidi(v) => (*v.channel_id()).into(),
538 Self::Uni(v) => (*v.channel_id()).into(),
539 }
540 }
541
542 pub fn cipher_suite(&self) -> CipherSuiteId {
544 match self {
545 Self::Bidi(v) => v.cipher_suite(),
546 Self::Uni(v) => v.cipher_suite(),
547 }
548 }
549
550 pub fn as_bytes(&self) -> &[u8; 34] {
552 match self {
553 Self::Bidi(v) => v.as_bytes(),
554 Self::Uni(v) => v.as_bytes(),
555 }
556 }
557}
558
559#[derive(Clone, Debug, Serialize, Deserialize)]
561pub struct SyncPeerConfig {
562 pub interval: Duration,
564 pub sync_now: bool,
566}
567
568#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
570pub enum ChanOp {
571 RecvOnly,
574 SendOnly,
577 SendRecv,
580}
581
582#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
584pub struct Label {
585 pub id: LabelId,
586 pub name: Text,
587}
588
589#[tarpc::service]
590pub trait DaemonApi {
591 async fn version() -> Result<Version>;
593
594 async fn aranya_local_addr() -> Result<SocketAddr>;
596
597 async fn get_key_bundle() -> Result<KeyBundle>;
599
600 async fn get_device_id() -> Result<DeviceId>;
602
603 async fn add_sync_peer(addr: Addr, team: TeamId, config: SyncPeerConfig) -> Result<()>;
605
606 async fn sync_now(addr: Addr, team: TeamId, cfg: Option<SyncPeerConfig>) -> Result<()>;
608
609 async fn remove_sync_peer(addr: Addr, team: TeamId) -> Result<()>;
611
612 async fn add_team(team: TeamId, cfg: TeamConfig) -> Result<()>;
614
615 async fn remove_team(team: TeamId) -> Result<()>;
617
618 async fn create_team(cfg: TeamConfig) -> Result<TeamId>;
620 async fn close_team(team: TeamId) -> Result<()>;
622
623 async fn encrypt_psk_seed_for_peer(
624 team: TeamId,
625 peer_enc_pk: EncryptionPublicKey<CS>,
626 ) -> Result<WrappedSeed>;
627
628 async fn add_device_to_team(team: TeamId, keys: KeyBundle) -> Result<()>;
630 async fn remove_device_from_team(team: TeamId, device: DeviceId) -> Result<()>;
632
633 async fn assign_role(team: TeamId, device: DeviceId, role: Role) -> Result<()>;
635 async fn revoke_role(team: TeamId, device: DeviceId, role: Role) -> Result<()>;
637
638 async fn assign_aqc_net_identifier(
640 team: TeamId,
641 device: DeviceId,
642 name: NetIdentifier,
643 ) -> Result<()>;
644 async fn remove_aqc_net_identifier(
646 team: TeamId,
647 device: DeviceId,
648 name: NetIdentifier,
649 ) -> Result<()>;
650
651 async fn create_label(team: TeamId, name: Text) -> Result<LabelId>;
653 async fn delete_label(team: TeamId, label_id: LabelId) -> Result<()>;
655 async fn assign_label(
657 team: TeamId,
658 device: DeviceId,
659 label_id: LabelId,
660 op: ChanOp,
661 ) -> Result<()>;
662 async fn revoke_label(team: TeamId, device: DeviceId, label_id: LabelId) -> Result<()>;
664
665 async fn create_aqc_bidi_channel(
667 team: TeamId,
668 peer: NetIdentifier,
669 label_id: LabelId,
670 ) -> Result<(AqcCtrl, AqcBidiPsks)>;
671 async fn create_aqc_uni_channel(
673 team: TeamId,
674 peer: NetIdentifier,
675 label_id: LabelId,
676 ) -> Result<(AqcCtrl, AqcUniPsks)>;
677 async fn delete_aqc_bidi_channel(chan: AqcBidiChannelId) -> Result<AqcCtrl>;
679 async fn delete_aqc_uni_channel(chan: AqcUniChannelId) -> Result<AqcCtrl>;
681 async fn receive_aqc_ctrl(team: TeamId, ctrl: AqcCtrl) -> Result<(LabelId, AqcPsks)>;
683
684 async fn query_devices_on_team(team: TeamId) -> Result<Vec<DeviceId>>;
686 async fn query_device_role(team: TeamId, device: DeviceId) -> Result<Role>;
688 async fn query_device_keybundle(team: TeamId, device: DeviceId) -> Result<KeyBundle>;
690 async fn query_device_label_assignments(team: TeamId, device: DeviceId) -> Result<Vec<Label>>;
692 async fn query_aqc_net_identifier(
694 team: TeamId,
695 device: DeviceId,
696 ) -> Result<Option<NetIdentifier>>;
697 async fn query_labels(team: TeamId) -> Result<Vec<Label>>;
699 async fn query_label_exists(team: TeamId, label: LabelId) -> Result<bool>;
701}
702
703#[cfg(test)]
704mod tests {
705 use aranya_crypto::Rng;
706 use serde::de::DeserializeOwned;
707
708 use super::*;
709
710 fn secret(secret: &[u8]) -> Secret {
711 Secret(Box::from(secret))
712 }
713
714 pub(super) trait PskMap:
715 fmt::Debug + PartialEq + Serialize + DeserializeOwned + Sized
716 {
717 type Psk;
718 fn new() -> Self;
719 fn len(&self) -> usize;
721 fn insert(&mut self, psk: Self::Psk);
727 }
728
729 impl PartialEq for AqcBidiPsk {
730 fn eq(&self, other: &Self) -> bool {
731 bool::from(self.ct_eq(other))
732 }
733 }
734 impl PartialEq for AqcUniPsk {
735 fn eq(&self, other: &Self) -> bool {
736 bool::from(self.ct_eq(other))
737 }
738 }
739 impl PartialEq for AqcPsk {
740 fn eq(&self, other: &Self) -> bool {
741 bool::from(self.ct_eq(other))
742 }
743 }
744
745 #[track_caller]
746 fn psk_map_test<M, F>(name: &'static str, mut f: F)
747 where
748 M: PskMap,
749 F: FnMut(Secret, Id, CipherSuiteId) -> M::Psk,
750 {
751 let mut psks = M::new();
752 for (i, &suite) in CipherSuiteId::all().iter().enumerate() {
753 let id = Id::random(&mut Rng);
754 let secret = secret(&i.to_le_bytes());
755 psks.insert(f(secret, id, suite));
756 }
757 assert_eq!(psks.len(), CipherSuiteId::all().len(), "{name}");
758
759 let bytes = postcard::to_allocvec(&psks).unwrap();
760 let got = postcard::from_bytes::<M>(&bytes).unwrap();
761 assert_eq!(got, psks, "{name}")
762 }
763
764 #[test]
767 fn test_aqc_bidi_psks_serde() {
768 psk_map_test::<AqcBidiPsks, _>("AqcBidiPsk", |secret, id, suite| AqcBidiPsk {
769 identity: BidiPskId::from((id.into(), suite)),
770 secret,
771 });
772 }
773
774 #[test]
777 fn test_aqc_uni_psks_serde() {
778 psk_map_test::<AqcUniPsks, _>("AqcUniPsk (send)", |secret, id, suite| AqcUniPsk {
779 identity: UniPskId::from((id.into(), suite)),
780 secret: Directed::Send(secret),
781 });
782 psk_map_test::<AqcUniPsks, _>("AqcUniPsk (recv)", |secret, id, suite| AqcUniPsk {
783 identity: UniPskId::from((id.into(), suite)),
784 secret: Directed::Recv(secret),
785 });
786 }
787}