iroh_db_security/
authority.rs

1use iroh_db_core::{AuthorId, CapabilityId, CommitId, DomainId};
2
3use crate::{
4    IrohSigner,
5    key::{
6        capability_signature_message, control_signature_message,
7        domain_descriptor_signature_message,
8    },
9};
10
11const DESCRIPTOR_VERSION: u16 = 1;
12const CAPABILITY_VERSION: u16 = 2;
13const CONTROL_VERSION: u16 = 3;
14const MAX_DELEGATION_DEPTH: u8 = 8;
15const MAX_CONTROL_COMMITS: usize = 65_536;
16const MAX_CONTROL_REVOKED: usize = 1_024;
17
18/// A domain's immutable replicated write policy.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, minicbor::Encode, minicbor::Decode)]
20#[cbor(index_only)]
21#[repr(u8)]
22pub enum ConsistencyMode {
23    /// Every endpoint with `WRITE` may author data commits.
24    #[n(0)]
25    MultiWriter = 0,
26    /// Only the control state's designated writer may author data commits.
27    #[n(1)]
28    SingleWriter = 1,
29    /// Writers may only create immutable records once.
30    #[n(2)]
31    AppendOnly = 2,
32}
33
34/// One independently grantable domain permission.
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36#[repr(u16)]
37pub enum Permission {
38    Read = 1 << 0,
39    Write = 1 << 1,
40    Invite = 1 << 2,
41    Revoke = 1 << 3,
42    FetchBlobs = 1 << 4,
43    Snapshot = 1 << 5,
44    Admin = 1 << 6,
45}
46
47const ALL_PERMISSION_BITS: u16 = (1 << 7) - 1;
48
49/// A validated compact set of explicit permissions.
50#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
51pub struct PermissionSet(u16);
52
53impl PermissionSet {
54    pub const fn empty() -> Self {
55        Self(0)
56    }
57
58    pub const fn all() -> Self {
59        Self(ALL_PERMISSION_BITS)
60    }
61
62    pub fn from_permissions(permissions: &[Permission]) -> Self {
63        Self(
64            permissions
65                .iter()
66                .fold(0, |bits, permission| bits | *permission as u16),
67        )
68    }
69
70    pub const fn from_bits(bits: u16) -> Result<Self, AuthorityError> {
71        if bits & !ALL_PERMISSION_BITS == 0 {
72            Ok(Self(bits))
73        } else {
74            Err(AuthorityError::UnknownPermissionBits(bits))
75        }
76    }
77
78    pub const fn bits(self) -> u16 {
79        self.0
80    }
81
82    pub const fn contains(self, permission: Permission) -> bool {
83        self.0 & permission as u16 != 0
84    }
85
86    pub const fn contains_all(self, permissions: Self) -> bool {
87        self.0 & permissions.0 == permissions.0
88    }
89}
90
91/// A signed immutable domain genesis descriptor.
92#[derive(Debug, Clone, PartialEq, Eq)]
93pub struct DomainDescriptor {
94    domain_id: DomainId,
95    mode: ConsistencyMode,
96    owner: AuthorId,
97    nonce: [u8; 32],
98    signature: [u8; 64],
99}
100
101impl DomainDescriptor {
102    pub fn issue(
103        domain_id: DomainId,
104        mode: ConsistencyMode,
105        owner: &IrohSigner,
106        nonce: [u8; 32],
107    ) -> Result<Self, AuthorityError> {
108        let mut descriptor = Self {
109            domain_id,
110            mode,
111            owner: owner.author(),
112            nonce,
113            signature: [0; 64],
114        };
115        descriptor.signature = owner.sign_domain_descriptor(&descriptor.signing_bytes()?);
116        Ok(descriptor)
117    }
118
119    pub const fn domain_id(&self) -> DomainId {
120        self.domain_id
121    }
122
123    pub const fn mode(&self) -> ConsistencyMode {
124        self.mode
125    }
126
127    pub const fn owner(&self) -> AuthorId {
128        self.owner
129    }
130
131    pub fn verify(&self) -> Result<(), AuthorityError> {
132        verify_signature(
133            self.owner,
134            &domain_descriptor_signature_message(&self.signing_bytes()?),
135            &self.signature,
136        )
137    }
138
139    pub fn encode_canonical(&self) -> Result<Vec<u8>, AuthorityError> {
140        minicbor::to_vec(DescriptorWire {
141            content: self.signing_bytes()?,
142            signature: self.signature,
143        })
144        .map_err(codec_error)
145    }
146
147    pub fn decode_canonical(bytes: &[u8]) -> Result<Self, AuthorityError> {
148        let wire: DescriptorWire = decode_exact(bytes)?;
149        let content: DescriptorContent = decode_exact(&wire.content)?;
150        if content.version != DESCRIPTOR_VERSION {
151            return Err(AuthorityError::UnsupportedVersion(content.version));
152        }
153        let descriptor = Self {
154            domain_id: content.domain_id,
155            mode: content.mode,
156            owner: content.owner,
157            nonce: content.nonce,
158            signature: wire.signature,
159        };
160        if descriptor.signing_bytes()? != wire.content || descriptor.encode_canonical()? != bytes {
161            return Err(AuthorityError::NonCanonical);
162        }
163        descriptor.verify()?;
164        Ok(descriptor)
165    }
166
167    fn signing_bytes(&self) -> Result<Vec<u8>, AuthorityError> {
168        minicbor::to_vec(DescriptorContent {
169            version: DESCRIPTOR_VERSION,
170            domain_id: self.domain_id,
171            mode: self.mode,
172            owner: self.owner,
173            nonce: self.nonce,
174        })
175        .map_err(codec_error)
176    }
177}
178
179/// A signed endpoint-targeted capability certificate.
180#[derive(Debug, Clone, PartialEq, Eq)]
181pub struct CapabilityCertificate {
182    domain_id: DomainId,
183    subject: AuthorId,
184    issuer_capability: Option<CapabilityId>,
185    permissions: PermissionSet,
186    delegation_permissions: PermissionSet,
187    issued_control_sequence: u64,
188    issued_control_head: Option<[u8; 32]>,
189    valid_from_epoch: u64,
190    valid_through_epoch: Option<u64>,
191    depth: u8,
192    nonce: [u8; 32],
193    issuer_endpoint: AuthorId,
194    signature: [u8; 64],
195}
196
197impl CapabilityCertificate {
198    pub fn issue_root(
199        domain_id: DomainId,
200        permissions: PermissionSet,
201        owner: &IrohSigner,
202        nonce: [u8; 32],
203    ) -> Result<Self, AuthorityError> {
204        Self::issue(
205            domain_id,
206            owner.author(),
207            None,
208            permissions,
209            permissions,
210            0,
211            None,
212            0,
213            None,
214            0,
215            nonce,
216            owner,
217        )
218    }
219
220    #[allow(clippy::too_many_arguments)]
221    pub fn delegate(
222        &self,
223        issuer: &IrohSigner,
224        subject: AuthorId,
225        permissions: PermissionSet,
226        delegation_permissions: PermissionSet,
227        valid_from_epoch: u64,
228        valid_through_epoch: Option<u64>,
229        nonce: [u8; 32],
230    ) -> Result<Self, AuthorityError> {
231        self.delegate_at_control(
232            issuer,
233            subject,
234            permissions,
235            delegation_permissions,
236            self.issued_control_sequence,
237            self.issued_control_head,
238            valid_from_epoch,
239            valid_through_epoch,
240            nonce,
241        )
242    }
243
244    /// Delegates authority anchored to an exact accepted domain-control head.
245    #[allow(clippy::too_many_arguments)]
246    pub fn delegate_at_control(
247        &self,
248        issuer: &IrohSigner,
249        subject: AuthorId,
250        permissions: PermissionSet,
251        delegation_permissions: PermissionSet,
252        issued_control_sequence: u64,
253        issued_control_head: Option<[u8; 32]>,
254        valid_from_epoch: u64,
255        valid_through_epoch: Option<u64>,
256        nonce: [u8; 32],
257    ) -> Result<Self, AuthorityError> {
258        self.verify_signature()?;
259        if issuer.author() != self.subject {
260            return Err(AuthorityError::WrongIssuer);
261        }
262        if !self.delegation_permissions.contains_all(permissions)
263            || !self
264                .delegation_permissions
265                .contains_all(delegation_permissions)
266        {
267            return Err(AuthorityError::PermissionEscalation);
268        }
269        if valid_from_epoch < self.valid_from_epoch
270            || !epoch_end_within(valid_through_epoch, self.valid_through_epoch)
271        {
272            return Err(AuthorityError::EpochEscalation);
273        }
274        if issued_control_sequence < self.issued_control_sequence {
275            return Err(AuthorityError::ControlSequenceRegression);
276        }
277        if issued_control_sequence == self.issued_control_sequence
278            && issued_control_head != self.issued_control_head
279        {
280            return Err(AuthorityError::ControlHeadMismatch);
281        }
282        let depth = self
283            .depth
284            .checked_add(1)
285            .ok_or(AuthorityError::DelegationTooDeep)?;
286        Self::issue(
287            self.domain_id,
288            subject,
289            Some(self.id()?),
290            permissions,
291            delegation_permissions,
292            issued_control_sequence,
293            issued_control_head,
294            valid_from_epoch,
295            valid_through_epoch,
296            depth,
297            nonce,
298            issuer,
299        )
300    }
301
302    pub const fn domain_id(&self) -> DomainId {
303        self.domain_id
304    }
305
306    pub const fn subject(&self) -> AuthorId {
307        self.subject
308    }
309
310    pub const fn issuer_capability(&self) -> Option<CapabilityId> {
311        self.issuer_capability
312    }
313
314    pub const fn permissions(&self) -> PermissionSet {
315        self.permissions
316    }
317
318    pub const fn delegation_permissions(&self) -> PermissionSet {
319        self.delegation_permissions
320    }
321
322    pub const fn issued_control_sequence(&self) -> u64 {
323        self.issued_control_sequence
324    }
325
326    pub const fn issued_control_head(&self) -> Option<[u8; 32]> {
327        self.issued_control_head
328    }
329
330    pub const fn valid_from_epoch(&self) -> u64 {
331        self.valid_from_epoch
332    }
333
334    pub const fn valid_through_epoch(&self) -> Option<u64> {
335        self.valid_through_epoch
336    }
337
338    pub const fn depth(&self) -> u8 {
339        self.depth
340    }
341
342    /// Returns the endpoint that signed this certificate.
343    pub const fn issuer_endpoint(&self) -> AuthorId {
344        self.issuer_endpoint
345    }
346
347    pub fn permits(&self, permission: Permission, epoch: u64) -> bool {
348        self.permissions.contains(permission)
349            && epoch >= self.valid_from_epoch
350            && self
351                .valid_through_epoch
352                .is_none_or(|through| epoch <= through)
353    }
354
355    pub fn id(&self) -> Result<CapabilityId, AuthorityError> {
356        Ok(CapabilityId::from_bytes(
357            *blake3::hash(&self.encode_canonical()?).as_bytes(),
358        ))
359    }
360
361    pub fn verify_signature(&self) -> Result<(), AuthorityError> {
362        verify_signature(
363            self.issuer_endpoint,
364            &capability_signature_message(&self.signing_bytes()?),
365            &self.signature,
366        )
367    }
368
369    pub fn validate_delegation(&self, issuer: &Self) -> Result<(), AuthorityError> {
370        self.verify_signature()?;
371        issuer.verify_signature()?;
372        if self.domain_id != issuer.domain_id
373            || self.issuer_capability != Some(issuer.id()?)
374            || self.issuer_endpoint != issuer.subject
375            || self.depth != issuer.depth.saturating_add(1)
376        {
377            return Err(AuthorityError::WrongIssuer);
378        }
379        if !issuer.delegation_permissions.contains_all(self.permissions)
380            || !issuer
381                .delegation_permissions
382                .contains_all(self.delegation_permissions)
383        {
384            return Err(AuthorityError::PermissionEscalation);
385        }
386        if self.valid_from_epoch < issuer.valid_from_epoch
387            || !epoch_end_within(self.valid_through_epoch, issuer.valid_through_epoch)
388        {
389            return Err(AuthorityError::EpochEscalation);
390        }
391        if self.issued_control_sequence < issuer.issued_control_sequence {
392            return Err(AuthorityError::ControlSequenceRegression);
393        }
394        if self.issued_control_sequence == issuer.issued_control_sequence
395            && self.issued_control_head != issuer.issued_control_head
396        {
397            return Err(AuthorityError::ControlHeadMismatch);
398        }
399        Ok(())
400    }
401
402    pub fn encode_canonical(&self) -> Result<Vec<u8>, AuthorityError> {
403        let bytes = minicbor::to_vec(CapabilityWire {
404            content: self.signing_bytes()?,
405            signature: self.signature,
406        })
407        .map_err(codec_error)?;
408        if bytes.len() > 4096 {
409            return Err(AuthorityError::ObjectTooLarge(bytes.len()));
410        }
411        Ok(bytes)
412    }
413
414    pub fn decode_canonical(bytes: &[u8]) -> Result<Self, AuthorityError> {
415        if bytes.len() > 4096 {
416            return Err(AuthorityError::ObjectTooLarge(bytes.len()));
417        }
418        let wire: CapabilityWire = decode_exact(bytes)?;
419        let content: CapabilityContent = decode_exact(&wire.content)?;
420        if content.version != CAPABILITY_VERSION {
421            return Err(AuthorityError::UnsupportedVersion(content.version));
422        }
423        let capability = Self {
424            domain_id: content.domain_id,
425            subject: content.subject,
426            issuer_capability: content.issuer_capability,
427            permissions: PermissionSet::from_bits(content.permissions)?,
428            delegation_permissions: PermissionSet::from_bits(content.delegation_permissions)?,
429            issued_control_sequence: content.issued_control_sequence,
430            issued_control_head: content.issued_control_head,
431            valid_from_epoch: content.valid_from_epoch,
432            valid_through_epoch: content.valid_through_epoch,
433            depth: content.depth,
434            nonce: content.nonce,
435            issuer_endpoint: content.issuer_endpoint,
436            signature: wire.signature,
437        };
438        capability.validate_shape()?;
439        if capability.signing_bytes()? != wire.content || capability.encode_canonical()? != bytes {
440            return Err(AuthorityError::NonCanonical);
441        }
442        capability.verify_signature()?;
443        Ok(capability)
444    }
445
446    #[allow(clippy::too_many_arguments)]
447    fn issue(
448        domain_id: DomainId,
449        subject: AuthorId,
450        issuer_capability: Option<CapabilityId>,
451        permissions: PermissionSet,
452        delegation_permissions: PermissionSet,
453        issued_control_sequence: u64,
454        issued_control_head: Option<[u8; 32]>,
455        valid_from_epoch: u64,
456        valid_through_epoch: Option<u64>,
457        depth: u8,
458        nonce: [u8; 32],
459        issuer: &IrohSigner,
460    ) -> Result<Self, AuthorityError> {
461        let mut capability = Self {
462            domain_id,
463            subject,
464            issuer_capability,
465            permissions,
466            delegation_permissions,
467            issued_control_sequence,
468            issued_control_head,
469            valid_from_epoch,
470            valid_through_epoch,
471            depth,
472            nonce,
473            issuer_endpoint: issuer.author(),
474            signature: [0; 64],
475        };
476        capability.validate_shape()?;
477        capability.signature = issuer.sign_capability(&capability.signing_bytes()?);
478        Ok(capability)
479    }
480
481    fn validate_shape(&self) -> Result<(), AuthorityError> {
482        if self.depth > MAX_DELEGATION_DEPTH {
483            return Err(AuthorityError::DelegationTooDeep);
484        }
485        if self
486            .valid_through_epoch
487            .is_some_and(|through| through < self.valid_from_epoch)
488        {
489            return Err(AuthorityError::EpochEscalation);
490        }
491        if (self.issued_control_sequence == 0) != self.issued_control_head.is_none() {
492            return Err(AuthorityError::ControlHeadMismatch);
493        }
494        if self.depth == 0 {
495            if self.issuer_capability.is_some() || self.issuer_endpoint != self.subject {
496                return Err(AuthorityError::WrongIssuer);
497            }
498        } else if self.issuer_capability.is_none() {
499            return Err(AuthorityError::WrongIssuer);
500        }
501        Ok(())
502    }
503
504    fn signing_bytes(&self) -> Result<Vec<u8>, AuthorityError> {
505        minicbor::to_vec(CapabilityContent {
506            version: CAPABILITY_VERSION,
507            domain_id: self.domain_id,
508            subject: self.subject,
509            issuer_capability: self.issuer_capability,
510            permissions: self.permissions.bits(),
511            delegation_permissions: self.delegation_permissions.bits(),
512            issued_control_sequence: self.issued_control_sequence,
513            valid_from_epoch: self.valid_from_epoch,
514            valid_through_epoch: self.valid_through_epoch,
515            depth: self.depth,
516            nonce: self.nonce,
517            issuer_endpoint: self.issuer_endpoint,
518            issued_control_head: self.issued_control_head,
519        })
520        .map_err(codec_error)
521    }
522}
523
524/// A signed, immutable, single-successor domain authority transition.
525#[derive(Debug, Clone, PartialEq, Eq)]
526pub struct ControlTransition {
527    domain_id: DomainId,
528    sequence: u64,
529    predecessor: Option<[u8; 32]>,
530    previous_epoch: u64,
531    new_epoch: u64,
532    cut: Vec<CommitId>,
533    revoked_subjects: Vec<AuthorId>,
534    writer: AuthorId,
535    controller: AuthorId,
536    controller_capability: CapabilityId,
537    key_distribution_digest: [u8; 32],
538    author: AuthorId,
539    author_capability: CapabilityId,
540    nonce: [u8; 32],
541    signature: [u8; 64],
542}
543
544impl ControlTransition {
545    #[allow(clippy::too_many_arguments)]
546    pub fn issue(
547        domain_id: DomainId,
548        sequence: u64,
549        predecessor: Option<[u8; 32]>,
550        previous_epoch: u64,
551        cut: Vec<CommitId>,
552        mut revoked_subjects: Vec<AuthorId>,
553        writer: AuthorId,
554        controller: AuthorId,
555        controller_capability: CapabilityId,
556        key_distribution_digest: [u8; 32],
557        author_capability: CapabilityId,
558        nonce: [u8; 32],
559        signer: &IrohSigner,
560    ) -> Result<Self, AuthorityError> {
561        revoked_subjects.sort_unstable();
562        revoked_subjects.dedup();
563        let mut control = Self {
564            domain_id,
565            sequence,
566            predecessor,
567            previous_epoch,
568            new_epoch: previous_epoch
569                .checked_add(1)
570                .ok_or(AuthorityError::InvalidControl)?,
571            cut,
572            revoked_subjects,
573            writer,
574            controller,
575            controller_capability,
576            key_distribution_digest,
577            author: signer.author(),
578            author_capability,
579            nonce,
580            signature: [0; 64],
581        };
582        control.validate_shape()?;
583        control.signature = signer.sign_control(&control.signing_bytes()?);
584        Ok(control)
585    }
586
587    pub const fn domain_id(&self) -> DomainId {
588        self.domain_id
589    }
590    pub const fn sequence(&self) -> u64 {
591        self.sequence
592    }
593    pub const fn predecessor(&self) -> Option<[u8; 32]> {
594        self.predecessor
595    }
596    pub const fn previous_epoch(&self) -> u64 {
597        self.previous_epoch
598    }
599    pub const fn new_epoch(&self) -> u64 {
600        self.new_epoch
601    }
602    pub fn cut(&self) -> &[CommitId] {
603        &self.cut
604    }
605    pub fn revoked_subjects(&self) -> &[AuthorId] {
606        &self.revoked_subjects
607    }
608    pub const fn writer(&self) -> AuthorId {
609        self.writer
610    }
611    /// Returns the endpoint designated to author the next control transition.
612    pub const fn controller(&self) -> AuthorId {
613        self.controller
614    }
615    /// Returns the capability that authorizes the designated successor.
616    pub const fn controller_capability(&self) -> CapabilityId {
617        self.controller_capability
618    }
619    pub const fn key_distribution_digest(&self) -> [u8; 32] {
620        self.key_distribution_digest
621    }
622    pub const fn author(&self) -> AuthorId {
623        self.author
624    }
625    pub const fn author_capability(&self) -> CapabilityId {
626        self.author_capability
627    }
628
629    pub fn id(&self) -> Result<[u8; 32], AuthorityError> {
630        Ok(*blake3::hash(&self.encode_canonical()?).as_bytes())
631    }
632
633    pub fn verify(&self) -> Result<(), AuthorityError> {
634        self.validate_shape()?;
635        verify_signature(
636            self.author,
637            &control_signature_message(&self.signing_bytes()?),
638            &self.signature,
639        )
640    }
641
642    pub fn encode_canonical(&self) -> Result<Vec<u8>, AuthorityError> {
643        let bytes = minicbor::to_vec(ControlWire {
644            content: self.signing_bytes()?,
645            signature: self.signature,
646        })
647        .map_err(codec_error)?;
648        if bytes.len() > 1024 * 1024 {
649            return Err(AuthorityError::ObjectTooLarge(bytes.len()));
650        }
651        Ok(bytes)
652    }
653
654    pub fn decode_canonical(bytes: &[u8]) -> Result<Self, AuthorityError> {
655        if bytes.len() > 1024 * 1024 {
656            return Err(AuthorityError::ObjectTooLarge(bytes.len()));
657        }
658        let wire: ControlWire = decode_exact(bytes)?;
659        let content: ControlContent = decode_exact(&wire.content)?;
660        if content.version != CONTROL_VERSION {
661            return Err(AuthorityError::UnsupportedVersion(content.version));
662        }
663        let control = Self {
664            domain_id: content.domain_id,
665            sequence: content.sequence,
666            predecessor: content.predecessor,
667            previous_epoch: content.previous_epoch,
668            new_epoch: content.new_epoch,
669            cut: content.cut,
670            revoked_subjects: content.revoked_subjects,
671            writer: content.writer,
672            controller: content.controller,
673            controller_capability: content.controller_capability,
674            key_distribution_digest: content.key_distribution_digest,
675            author: content.author,
676            author_capability: content.author_capability,
677            nonce: content.nonce,
678            signature: wire.signature,
679        };
680        if control.signing_bytes()? != wire.content || control.encode_canonical()? != bytes {
681            return Err(AuthorityError::NonCanonical);
682        }
683        control.verify()?;
684        Ok(control)
685    }
686
687    fn validate_shape(&self) -> Result<(), AuthorityError> {
688        if self.sequence == 0
689            || self.previous_epoch == u64::MAX
690            || self.new_epoch != self.previous_epoch + 1
691            || self.cut.len() > MAX_CONTROL_COMMITS
692            || self.revoked_subjects.len() > MAX_CONTROL_REVOKED
693            || !strictly_sorted(&self.cut)
694            || !strictly_sorted(&self.revoked_subjects)
695        {
696            return Err(AuthorityError::InvalidControl);
697        }
698        Ok(())
699    }
700
701    fn signing_bytes(&self) -> Result<Vec<u8>, AuthorityError> {
702        minicbor::to_vec(ControlContent {
703            version: CONTROL_VERSION,
704            domain_id: self.domain_id,
705            sequence: self.sequence,
706            predecessor: self.predecessor,
707            previous_epoch: self.previous_epoch,
708            new_epoch: self.new_epoch,
709            cut: self.cut.clone(),
710            revoked_subjects: self.revoked_subjects.clone(),
711            writer: self.writer,
712            controller: self.controller,
713            controller_capability: self.controller_capability,
714            key_distribution_digest: self.key_distribution_digest,
715            author: self.author,
716            author_capability: self.author_capability,
717            nonce: self.nonce,
718        })
719        .map_err(codec_error)
720    }
721}
722
723#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
724pub enum AuthorityError {
725    #[error("authority object codec failed: {0}")]
726    Codec(String),
727    #[error("authority object is not canonical")]
728    NonCanonical,
729    #[error("unsupported authority version: {0}")]
730    UnsupportedVersion(u16),
731    #[error("unknown permission bits: {0:#x}")]
732    UnknownPermissionBits(u16),
733    #[error("capability delegation expands permissions")]
734    PermissionEscalation,
735    #[error("capability delegation expands epoch scope")]
736    EpochEscalation,
737    #[error("capability control-sequence anchor moves backward")]
738    ControlSequenceRegression,
739    #[error("capability control-head anchor is inconsistent")]
740    ControlHeadMismatch,
741    #[error("capability issuer or chain link is invalid")]
742    WrongIssuer,
743    #[error("capability delegation exceeds depth eight")]
744    DelegationTooDeep,
745    #[error("authority signature is invalid")]
746    InvalidSignature,
747    #[error("authority endpoint public key is invalid")]
748    InvalidAuthor,
749    #[error("authority object is too large: {0}")]
750    ObjectTooLarge(usize),
751    #[error("domain control transition is invalid")]
752    InvalidControl,
753}
754
755#[derive(minicbor::Encode, minicbor::Decode)]
756#[cbor(array)]
757struct DescriptorContent {
758    #[n(0)]
759    version: u16,
760    #[n(1)]
761    domain_id: DomainId,
762    #[n(2)]
763    mode: ConsistencyMode,
764    #[n(3)]
765    owner: AuthorId,
766    #[n(4)]
767    nonce: [u8; 32],
768}
769
770#[derive(minicbor::Encode, minicbor::Decode)]
771#[cbor(array)]
772struct DescriptorWire {
773    #[n(0)]
774    content: Vec<u8>,
775    #[n(1)]
776    signature: [u8; 64],
777}
778
779#[derive(minicbor::Encode, minicbor::Decode)]
780#[cbor(array)]
781struct CapabilityContent {
782    #[n(0)]
783    version: u16,
784    #[n(1)]
785    domain_id: DomainId,
786    #[n(2)]
787    subject: AuthorId,
788    #[n(3)]
789    issuer_capability: Option<CapabilityId>,
790    #[n(4)]
791    permissions: u16,
792    #[n(5)]
793    delegation_permissions: u16,
794    #[n(6)]
795    issued_control_sequence: u64,
796    #[n(7)]
797    valid_from_epoch: u64,
798    #[n(8)]
799    valid_through_epoch: Option<u64>,
800    #[n(9)]
801    depth: u8,
802    #[n(10)]
803    nonce: [u8; 32],
804    #[n(11)]
805    issuer_endpoint: AuthorId,
806    #[n(12)]
807    issued_control_head: Option<[u8; 32]>,
808}
809
810#[derive(minicbor::Encode, minicbor::Decode)]
811#[cbor(array)]
812struct CapabilityWire {
813    #[n(0)]
814    content: Vec<u8>,
815    #[n(1)]
816    signature: [u8; 64],
817}
818
819#[derive(minicbor::Encode, minicbor::Decode)]
820#[cbor(array)]
821struct ControlContent {
822    #[n(0)]
823    version: u16,
824    #[n(1)]
825    domain_id: DomainId,
826    #[n(2)]
827    sequence: u64,
828    #[n(3)]
829    predecessor: Option<[u8; 32]>,
830    #[n(4)]
831    previous_epoch: u64,
832    #[n(5)]
833    new_epoch: u64,
834    #[n(6)]
835    cut: Vec<CommitId>,
836    #[n(7)]
837    revoked_subjects: Vec<AuthorId>,
838    #[n(8)]
839    writer: AuthorId,
840    #[n(9)]
841    key_distribution_digest: [u8; 32],
842    #[n(10)]
843    author: AuthorId,
844    #[n(11)]
845    author_capability: CapabilityId,
846    #[n(12)]
847    nonce: [u8; 32],
848    #[n(13)]
849    controller: AuthorId,
850    #[n(14)]
851    controller_capability: CapabilityId,
852}
853
854#[derive(minicbor::Encode, minicbor::Decode)]
855#[cbor(array)]
856struct ControlWire {
857    #[n(0)]
858    content: Vec<u8>,
859    #[n(1)]
860    signature: [u8; 64],
861}
862
863fn strictly_sorted<T: Ord>(values: &[T]) -> bool {
864    values.windows(2).all(|pair| pair[0] < pair[1])
865}
866
867fn epoch_end_within(child: Option<u64>, parent: Option<u64>) -> bool {
868    match (child, parent) {
869        (Some(child), Some(parent)) => child <= parent,
870        (None | Some(_), None) => true,
871        (None, Some(_)) => false,
872    }
873}
874
875fn verify_signature(
876    author: AuthorId,
877    message: &[u8],
878    signature: &[u8; 64],
879) -> Result<(), AuthorityError> {
880    let public_key = iroh_base::PublicKey::try_from(author.as_bytes())
881        .map_err(|_| AuthorityError::InvalidAuthor)?;
882    public_key
883        .verify(message, &iroh_base::Signature::from_bytes(signature))
884        .map_err(|_| AuthorityError::InvalidSignature)
885}
886
887fn decode_exact<T>(bytes: &[u8]) -> Result<T, AuthorityError>
888where
889    T: for<'bytes> minicbor::Decode<'bytes, ()>,
890{
891    let mut decoder = minicbor::Decoder::new(bytes);
892    let value = decoder.decode().map_err(codec_error)?;
893    if decoder.position() != bytes.len() {
894        return Err(AuthorityError::NonCanonical);
895    }
896    Ok(value)
897}
898
899#[allow(clippy::needless_pass_by_value)]
900fn codec_error(error: impl std::fmt::Display) -> AuthorityError {
901    AuthorityError::Codec(error.to_string())
902}