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#[derive(Debug, Clone, Copy, PartialEq, Eq, minicbor::Encode, minicbor::Decode)]
20#[cbor(index_only)]
21#[repr(u8)]
22pub enum ConsistencyMode {
23 #[n(0)]
25 MultiWriter = 0,
26 #[n(1)]
28 SingleWriter = 1,
29 #[n(2)]
31 AppendOnly = 2,
32}
33
34#[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#[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#[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#[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 #[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 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#[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 pub const fn controller(&self) -> AuthorId {
613 self.controller
614 }
615 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}