Skip to main content

repo/thread_replication/
authority.rs

1//! Select exact historical owner contexts from independently observed Spool
2//! lineage. Public histories supply signatures, never a replacement root.
3use std::collections::BTreeMap;
4
5use api::{
6    heddle::api::{common as host, v1alpha2 as wire},
7    hybrid_codec::Reject,
8};
9use heddleco_capability_verifier::{
10    self as permission, VerificationLimits, VerifiedCloneKeyring, VerifiedOwnerState,
11};
12
13/// Identities whose revocations the native verifier evaluates at statement time.
14pub struct NativeAuthority {
15    envelope: wire::ThreadControlAuthority,
16    publishers: Vec<Vec<u8>>,
17    credentials: Vec<String>,
18}
19
20fn native_authorities(
21    statement: &host::HostedWitnessStatementV1,
22    original_envelope: &[u8],
23    original_publishers: Vec<Vec<u8>>,
24    boundaries: &[wire::ImportBoundaryAcceptanceV1],
25) -> Option<Vec<NativeAuthority>> {
26    let decode = |bytes: &[u8]| {
27        api::mint_root_association::decode_thread_control_authority_for_verification(bytes).ok()
28    };
29    let mut authorities = match statement.basis {
30        1 => vec![NativeAuthority {
31            envelope: decode(original_envelope)?,
32            publishers: original_publishers,
33            credentials: vec![],
34        }],
35        2 if boundaries
36            .iter()
37            .any(|e| e.binding.as_ref() == statement.boundary_acceptance.as_ref())
38            && statement.boundary_acceptance.is_some() =>
39        {
40            Vec::new()
41        }
42        _ => return None,
43    };
44    // Boundary originals retain their own provenance/signature gates. Only the
45    // separately signed acceptors (including dependency acceptances) supply
46    // current revocation identities; no original is relabeled as an acceptor.
47    for evidence in boundaries {
48        let signed = evidence.signed_acceptance.as_ref()?;
49        if signed.format != objects::object::original_boundary_acceptance::FORMAT
50            || signed.signatures.len() != 1
51        {
52            return None;
53        }
54        let acceptance = crypto::original_boundary_acceptance::SignedBoundaryAcceptance {
55            canonical: signed.canonical_record.clone(),
56            signature: signed.signatures[0].signature.clone(),
57        }
58        .verify_signature()
59        .ok()?;
60        if signed.signatures[0].public_key != acceptance.accepting_publisher {
61            return None;
62        }
63        let objects::object::thread_replication::SourceAuthor::Account { authority, .. } =
64            acceptance.accepting_author
65        else {
66            return None;
67        };
68        authorities.push(NativeAuthority {
69            envelope: decode(&authority)?,
70            publishers: vec![acceptance.accepting_publisher.to_vec()],
71            credentials: vec![],
72        });
73    }
74    for authority in &mut authorities {
75        let keys = heddle_biscuit_verifier::parse_ed25519_public_keys_hex(
76            &hex::encode(&authority.envelope.mint_root_public_key),
77            1,
78        )
79        .ok()?;
80        let key = *keys.first()?;
81        let token =
82            heddle_biscuit_verifier::signature_v1::verify(&authority.envelope.sealed_biscuit, key)
83                .ok()?;
84        let facts = heddle_biscuit_verifier::inspect_verified_credential(&token, &key).ok()?;
85        authority.credentials = facts.revocation_identities().map(str::to_owned).collect();
86    }
87    Some(authorities)
88}
89
90/// Typed native and import bundles share verified lineage selection, while
91/// retaining their separate validators and disclosure contracts.
92pub trait PublicEvidence {
93    fn validate(&self) -> Result<(), Reject>;
94    fn public_history(&self) -> crate::thread_replication::delegated_import::PublicHistory<'_>;
95    fn policies(&self) -> &[wire::SignedSpoolPolicyRecord];
96    fn statements(&self) -> &[host::SignedHostedWitnessStatementV1];
97    fn binding_selectors(&self) -> Vec<(Vec<u8>, u64)> {
98        Vec::new()
99    }
100    fn imported(&self) -> Option<&wire::ImportPublicProofBundleV1> {
101        None
102    }
103    fn native(&self) -> Option<&wire::NativePublicProofBundleV1> {
104        None
105    }
106    fn delegations(&self) -> &[wire::SignedImportJobDelegationV1] {
107        &[]
108    }
109    fn member_permission(&self) -> Option<&wire::SignedImportMemberPermissionV1> {
110        None
111    }
112    fn native_authorities(
113        &self,
114        statement: &host::HostedWitnessStatementV1,
115    ) -> Option<Vec<NativeAuthority>>;
116}
117fn authority_envelopes(
118    statement: &host::HostedWitnessStatementV1,
119    authorities: &[wire::ImportAuthorityWitnessV1],
120    landings: &[wire::HostedLandingWitnessV1],
121) -> Option<Vec<NativeAuthority>> {
122    if statement.purpose == 2 {
123        let p = authorities.iter().find(|p| {
124            api::hybrid_codec::canonical(*p).is_ok_and(|b| b == statement.canonical_payload)
125        })?;
126        native_authorities(
127            statement,
128            &p.authority_envelope,
129            p.original
130                .as_ref()?
131                .signatures
132                .iter()
133                .map(|s| s.public_key.clone())
134                .collect(),
135            &p.boundary_acceptances,
136        )
137    } else if statement.purpose == 4 {
138        let p = landings.iter().find(|p| {
139            api::hybrid_codec::canonical(*p).is_ok_and(|b| b == statement.canonical_payload)
140        })?;
141        native_authorities(
142            statement,
143            &p.authority_envelope,
144            vec![p.request.as_ref()?.signature.as_ref()?.public_key.clone()],
145            &[],
146        )
147    } else {
148        None
149    }
150}
151impl PublicEvidence for wire::ImportPublicProofBundleV1 {
152    fn validate(&self) -> Result<(), Reject> {
153        api::import_authority::validate_public_bundle(self)
154    }
155    fn public_history(&self) -> crate::thread_replication::delegated_import::PublicHistory<'_> {
156        self.into()
157    }
158    fn policies(&self) -> &[wire::SignedSpoolPolicyRecord] {
159        &self.policies
160    }
161    fn statements(&self) -> &[host::SignedHostedWitnessStatementV1] {
162        &self.statements
163    }
164    fn imported(&self) -> Option<&wire::ImportPublicProofBundleV1> {
165        Some(self)
166    }
167    fn delegations(&self) -> &[wire::SignedImportJobDelegationV1] {
168        &self.delegations
169    }
170    fn member_permission(&self) -> Option<&wire::SignedImportMemberPermissionV1> {
171        self.member_permission.as_ref()
172    }
173    fn native_authorities(
174        &self,
175        statement: &host::HostedWitnessStatementV1,
176    ) -> Option<Vec<NativeAuthority>> {
177        if statement.purpose != 1 {
178            return authority_envelopes(
179                statement,
180                &self.authority_witnesses,
181                &self.landing_witnesses,
182            );
183        }
184        let p = self.genesis_witnesses.iter().find(|p| {
185            api::hybrid_codec::canonical(*p).is_ok_and(|b| b == statement.canonical_payload)
186        })?;
187        native_authorities(
188            statement,
189            &p.creator_authority_envelope,
190            p.original_genesis
191                .as_ref()?
192                .signatures
193                .iter()
194                .map(|s| s.public_key.clone())
195                .collect(),
196            p.boundary_acceptance.as_slice(),
197        )
198    }
199}
200impl PublicEvidence for wire::NativePublicProofBundleV1 {
201    fn validate(&self) -> Result<(), Reject> {
202        api::native_witness::validate_public_bundle(self)
203    }
204    fn public_history(&self) -> crate::thread_replication::delegated_import::PublicHistory<'_> {
205        self.into()
206    }
207    fn policies(&self) -> &[wire::SignedSpoolPolicyRecord] {
208        &self.policies
209    }
210    fn statements(&self) -> &[host::SignedHostedWitnessStatementV1] {
211        &self.statements
212    }
213    fn native(&self) -> Option<&wire::NativePublicProofBundleV1> {
214        Some(self)
215    }
216    fn binding_selectors(&self) -> Vec<(Vec<u8>, u64)> {
217        self.genesis_witnesses
218            .iter()
219            .filter_map(|p| p.binding.as_ref()?.body.as_ref()?.identity.as_ref())
220            .map(|id| (id.owner_state_hash.clone(), id.ownership_transfer_sequence))
221            .collect()
222    }
223    fn native_authorities(
224        &self,
225        statement: &host::HostedWitnessStatementV1,
226    ) -> Option<Vec<NativeAuthority>> {
227        if statement.purpose != 1 {
228            return authority_envelopes(
229                statement,
230                &self.authority_witnesses,
231                &self.landing_witnesses,
232            );
233        }
234        let p = self.genesis_witnesses.iter().find(|p| {
235            api::hybrid_codec::canonical(*p).is_ok_and(|b| b == statement.canonical_payload)
236        })?;
237        native_authorities(
238            statement,
239            &p.creator_authority_envelope,
240            p.original_genesis
241                .as_ref()?
242                .signatures
243                .iter()
244                .map(|s| s.public_key.clone())
245                .collect(),
246            p.boundary_acceptance.as_slice(),
247        )
248    }
249}
250
251/// Explicit transport dispatch; an import failure never becomes native history.
252#[derive(Clone, Debug, PartialEq)]
253pub enum PublicProof {
254    Import(Box<wire::ImportPublicProofBundleV1>),
255    Native(Box<wire::NativePublicProofBundleV1>),
256}
257impl From<wire::ImportPublicProofBundleV1> for PublicProof {
258    fn from(b: wire::ImportPublicProofBundleV1) -> Self {
259        Self::Import(Box::new(b))
260    }
261}
262impl From<wire::NativePublicProofBundleV1> for PublicProof {
263    fn from(b: wire::NativePublicProofBundleV1) -> Self {
264        Self::Native(Box::new(b))
265    }
266}
267impl PublicProof {
268    pub fn witness_set(&self) -> Option<&host::SignedHostedWitnessSetV1> {
269        match self {
270            Self::Import(b) => b.witness_set.as_ref(),
271            Self::Native(b) => b.witness_set.as_ref(),
272        }
273    }
274    pub fn replace_receiver_metadata(&mut self, refreshed: Self) -> Result<(), Reject> {
275        match (self, refreshed) {
276            (Self::Import(b), Self::Import(r)) => replace_import_metadata(b, *r),
277            (Self::Native(b), Self::Native(r)) => replace_native_metadata(b, *r),
278            _ => Err(Reject::Protocol),
279        }
280    }
281    pub fn encode_to_vec(&self) -> Vec<u8> {
282        use prost::Message;
283        match self {
284            Self::Import(b) => b.encode_to_vec(),
285            Self::Native(b) => b.encode_to_vec(),
286        }
287    }
288}
289impl PublicEvidence for PublicProof {
290    fn validate(&self) -> Result<(), Reject> {
291        match self {
292            Self::Import(b) => b.validate(),
293            Self::Native(b) => b.validate(),
294        }
295    }
296    fn public_history(&self) -> crate::thread_replication::delegated_import::PublicHistory<'_> {
297        match self {
298            Self::Import(b) => b.as_ref().into(),
299            Self::Native(b) => b.as_ref().into(),
300        }
301    }
302    fn policies(&self) -> &[wire::SignedSpoolPolicyRecord] {
303        match self {
304            Self::Import(b) => &b.policies,
305            Self::Native(b) => &b.policies,
306        }
307    }
308    fn statements(&self) -> &[host::SignedHostedWitnessStatementV1] {
309        match self {
310            Self::Import(b) => &b.statements,
311            Self::Native(b) => &b.statements,
312        }
313    }
314    fn binding_selectors(&self) -> Vec<(Vec<u8>, u64)> {
315        match self {
316            Self::Import(b) => b.binding_selectors(),
317            Self::Native(b) => b.binding_selectors(),
318        }
319    }
320    fn imported(&self) -> Option<&wire::ImportPublicProofBundleV1> {
321        match self {
322            Self::Import(b) => Some(b),
323            _ => None,
324        }
325    }
326    fn native(&self) -> Option<&wire::NativePublicProofBundleV1> {
327        match self {
328            Self::Native(b) => Some(b),
329            _ => None,
330        }
331    }
332    fn delegations(&self) -> &[wire::SignedImportJobDelegationV1] {
333        match self {
334            Self::Import(b) => &b.delegations,
335            _ => &[],
336        }
337    }
338    fn member_permission(&self) -> Option<&wire::SignedImportMemberPermissionV1> {
339        match self {
340            Self::Import(b) => b.member_permission.as_ref(),
341            _ => None,
342        }
343    }
344    fn native_authorities(
345        &self,
346        s: &host::HostedWitnessStatementV1,
347    ) -> Option<Vec<NativeAuthority>> {
348        match self {
349            Self::Import(b) => b.native_authorities(s),
350            Self::Native(b) => b.native_authorities(s),
351        }
352    }
353}
354
355/// Historical permission is bound to exact authenticated witness order. The
356/// caller supplies today's disclosure check, which runs again at commit.
357pub struct SelectedAuthority<F, B = wire::ImportPublicProofBundleV1> {
358    history: AcceptedHistory,
359    bundle: B,
360    authorize: F,
361    native: BTreeMap<Vec<u8>, Option<Vec<NativeAuthority>>>,
362}
363fn cache_native_authorities(
364    bundle: &impl PublicEvidence,
365) -> BTreeMap<Vec<u8>, Option<Vec<NativeAuthority>>> {
366    bundle
367        .statements()
368        .iter()
369        .filter_map(|signed| {
370            let s = signed.body.as_ref()?;
371            let id = api::witness_trust::statement_signing_digest(s).ok()?;
372            Some((id, bundle.native_authorities(s)))
373        })
374        .collect()
375}
376impl<F> SelectedAuthority<F> {
377    pub fn new(
378        history: AcceptedHistory,
379        bundle: wire::ImportPublicProofBundleV1,
380        authorize: F,
381    ) -> Self {
382        let native = cache_native_authorities(&bundle);
383        Self {
384            history,
385            bundle,
386            authorize,
387            native,
388        }
389    }
390}
391impl<F> SelectedAuthority<F, wire::NativePublicProofBundleV1> {
392    pub fn new_native(
393        history: AcceptedHistory,
394        bundle: wire::NativePublicProofBundleV1,
395        authorize: F,
396    ) -> Self {
397        let native = cache_native_authorities(&bundle);
398        Self {
399            history,
400            bundle,
401            authorize,
402            native,
403        }
404    }
405}
406impl<F> SelectedAuthority<F, PublicProof> {
407    pub fn from_proof(history: AcceptedHistory, bundle: PublicProof, authorize: F) -> Self {
408        let native = cache_native_authorities(&bundle);
409        Self {
410            history,
411            bundle,
412            authorize,
413            native,
414        }
415    }
416}
417impl<F, B: PublicEvidence> SelectedAuthority<F, B> {
418    fn selection<'a>(
419        &'a self,
420        selected: &'a HistoricalSelection,
421    ) -> permission::import_delegation::Selection<'a> {
422        permission::import_delegation::Selection {
423            owner: &selected.owner,
424            keyring: &selected.keyring,
425            spool_genesis_digest: self.history.genesis(),
426            initial_owner_id: self.history.initial_owner(),
427            limits: self.history.limits(),
428        }
429    }
430    fn policy(
431        &self,
432        statement: &host::HostedWitnessStatementV1,
433    ) -> Option<&wire::SignedSpoolPolicy> {
434        // The native verifier independently authenticates this exact policy.
435        self.bundle.policies().iter().find_map(|signed| {
436            let body = signed.body.as_ref()?;
437            (body.spool_uuid == statement.spool_uuid
438                && body.sequence == statement.policy_sequence
439                && body.policy_state_hash == statement.policy_state_hash)
440                .then_some(body.policy.as_ref())
441                .flatten()
442        })
443    }
444    fn policy_revocations(&self, statement: &host::HostedWitnessStatementV1) -> Option<&[Vec<u8>]> {
445        if statement.policy_sequence == 0 && statement.policy_state_hash == [0; 32] {
446            // Exactly this authenticated head denotes no policy. A signed
447            // record cannot stand in for the implicit genesis policy.
448            if self.bundle.policies().iter().any(|record| {
449                record.body.as_ref().is_some_and(|body| {
450                    body.spool_uuid == statement.spool_uuid
451                        && body.sequence == 0
452                        && body.policy_state_hash == [0; 32]
453                })
454            }) {
455                return None;
456            }
457            return Some(&[]);
458        }
459        self.policy(statement)
460            .map(|policy| policy.revoked_key_ids.as_slice())
461    }
462}
463impl<
464    B: PublicEvidence,
465    F: Fn(
466        &B,
467        i64,
468        &crate::thread_replication::hosted_trust::TrustTransaction<'_>,
469    ) -> crate::thread_replication::Result<()>,
470> crate::thread_replication::delegated_import::AcceptedAuthority for SelectedAuthority<F, B>
471{
472    fn authorize_import(
473        &self,
474        bundle: &wire::ImportPublicProofBundleV1,
475        now: i64,
476        context: &crate::thread_replication::hosted_trust::TrustTransaction<'_>,
477    ) -> crate::thread_replication::Result<()> {
478        if self.bundle.imported() != Some(bundle) {
479            return Err(Reject::StaleContext.into());
480        }
481        (self.authorize)(&self.bundle, now, context)
482    }
483    fn authorize_native(
484        &self,
485        bundle: &wire::NativePublicProofBundleV1,
486        now: i64,
487        context: &crate::thread_replication::hosted_trust::TrustTransaction<'_>,
488    ) -> crate::thread_replication::Result<()> {
489        if self.bundle.native() != Some(bundle) {
490            return Err(Reject::StaleContext.into());
491        }
492        (self.authorize)(&self.bundle, now, context)
493    }
494    fn for_witness(
495        &self,
496        statement: &host::HostedWitnessStatementV1,
497    ) -> crate::thread_replication::Result<permission::import_delegation::Selection<'_>> {
498        let selected = self
499            .history
500            .for_witness(statement)
501            .map_err(authority_error)?;
502        Ok(self.selection(selected))
503    }
504    fn for_native_binding(
505        &self,
506        binding: &wire::NativeGenesisAuthorityV1,
507    ) -> crate::thread_replication::Result<permission::import_delegation::Selection<'_>> {
508        let id = binding.identity.as_ref().ok_or(Reject::GenesisBinding)?;
509        let selected = self
510            .history
511            .bindings
512            .get(&(
513                id.owner_state_hash.clone(),
514                id.ownership_transfer_sequence,
515                binding.owner_chain_digest.clone(),
516            ))
517            .ok_or(Reject::Root)?;
518        Ok(self.selection(selected))
519    }
520    fn for_policy(
521        &self,
522        policy: &wire::SignedPolicyBody,
523    ) -> crate::thread_replication::Result<permission::import_delegation::Selection<'_>> {
524        let selected = self.history.for_policy(policy).map_err(authority_error)?;
525        Ok(self.selection(selected))
526    }
527    fn import_revoked(
528        &self,
529        statement: &host::HostedWitnessStatementV1,
530        revocation: permission::import_delegation::Revocation<'_>,
531    ) -> bool {
532        let (Ok(selected), Some(revoked)) = (
533            self.history.for_witness(statement),
534            self.policy_revocations(statement),
535        ) else {
536            return true;
537        };
538        match revocation {
539            permission::import_delegation::Revocation::Key(id) => {
540                let known = selected
541                    .owner
542                    .authority_public_keys()
543                    .chain(selected.keyring.authority_public_keys().cloned())
544                    .chain(
545                        self.bundle
546                            .delegations()
547                            .iter()
548                            .filter_map(|d| d.body.as_ref())
549                            .flat_map(|d| {
550                                [d.delegating_public_key.clone(), d.job_public_key.clone()]
551                            }),
552                    )
553                    .any(|key| api::hybrid_codec::key_id(&key).as_slice() == id);
554                !known || revoked.iter().any(|key| key == id)
555            }
556            permission::import_delegation::Revocation::Cancellation(namespace, id) => {
557                // Cancellation status is attested at exact accepted order by
558                // the witness; only identifiers actually bound by its signed
559                // delegation history can use that historical acceptance.
560                namespace != api::import_authority::CANCELLATION_NAMESPACE
561                    || !self
562                        .bundle
563                        .delegations()
564                        .iter()
565                        .filter_map(|d| d.body.as_ref())
566                        .any(|d| d.cancellation_id == id)
567                        && !self
568                            .bundle
569                            .member_permission()
570                            .into_iter()
571                            .filter_map(|p| p.body.as_ref())
572                            .any(|p| p.cancellation_id == id)
573            }
574        }
575    }
576    fn native_revoked(
577        &self,
578        statement: &host::HostedWitnessStatementV1,
579        revocation: permission::thread_control_authority::Revocation<'_>,
580    ) -> bool {
581        let (Ok(_), Some(revoked), Some(authorities)) = (
582            self.history.for_witness(statement),
583            self.policy_revocations(statement),
584            api::witness_trust::statement_signing_digest(statement)
585                .ok()
586                .and_then(|id| self.native.get(&id))
587                .and_then(Option::as_ref),
588        ) else {
589            return true;
590        };
591        match revocation {
592            permission::thread_control_authority::Revocation::MintRoot(key) => {
593                !authorities
594                    .iter()
595                    .any(|a| key == a.envelope.mint_root_public_key)
596                    || revoked.contains(&api::hybrid_codec::key_id(key).to_vec())
597            }
598            permission::thread_control_authority::Revocation::Publisher(key) => {
599                !authorities
600                    .iter()
601                    .any(|a| a.publishers.iter().any(|p| p == key))
602                    || revoked.contains(&api::hybrid_codec::key_id(key).to_vec())
603            }
604            permission::thread_control_authority::Revocation::Credential(id) => {
605                // Credential revocation is attested at statement time by the
606                // authenticated witness. Spool policy provides key cuts only.
607                !authorities
608                    .iter()
609                    .any(|a| a.credentials.iter().any(|known| known == id))
610            }
611        }
612    }
613}
614fn authority_error(error: impl std::fmt::Display) -> crate::thread_replication::Error {
615    crate::thread_replication::Error::Invalid(error.to_string())
616}
617
618#[derive(Debug, thiserror::Error)]
619pub enum Error {
620    #[error("HYBRID accepted authority rejected: {0}")]
621    Rejected(#[from] Reject),
622    #[error(transparent)]
623    Owner(#[from] permission::Error),
624}
625
626/// A verified state for one accepted owner/transfer selection. It grants no
627/// current device authority and must be used with an authenticated witness.
628pub struct HistoricalSelection {
629    pub owner: VerifiedOwnerState,
630    pub keyring: VerifiedCloneKeyring,
631}
632pub struct AcceptedHistory {
633    genesis: [u8; 32],
634    initial_owner: [u8; 32],
635    limits: VerificationLimits,
636    states: BTreeMap<(Vec<u8>, u64), HistoricalSelection>,
637    bindings: BTreeMap<(Vec<u8>, u64, Vec<u8>), HistoricalSelection>,
638}
639
640impl AcceptedHistory {
641    pub fn from_selected_spool(
642        bundle: &wire::ImportPublicProofBundleV1,
643        selected: &VerifiedCloneKeyring,
644        now_seconds: i64,
645        limits: VerificationLimits,
646    ) -> Result<Self, Error> {
647        Self::from_public(bundle, selected, now_seconds, limits)
648    }
649    pub fn from_native_spool(
650        bundle: &wire::NativePublicProofBundleV1,
651        selected: &VerifiedCloneKeyring,
652        now_seconds: i64,
653        limits: VerificationLimits,
654    ) -> Result<Self, Error> {
655        Self::from_public(bundle, selected, now_seconds, limits)
656    }
657    pub fn from_public(
658        bundle: &impl PublicEvidence,
659        selected: &VerifiedCloneKeyring,
660        now_seconds: i64,
661        limits: VerificationLimits,
662    ) -> Result<Self, Error> {
663        bundle.validate()?;
664        let public = bundle.public_history();
665        let pinned = selected.wire();
666        if public.owner_genesis != Some(selected.owner_genesis().signed())
667            || !pinned
668                .ownership_transfers
669                .starts_with(public.ownership_transfers)
670        {
671            return Err(Reject::Root.into());
672        }
673        let genesis = permission::creation::spool_genesis_digest(
674            selected
675                .owner_genesis()
676                .signed()
677                .genesis
678                .as_ref()
679                .ok_or(Reject::Root)?,
680        )?;
681        let mut this = Self {
682            genesis,
683            initial_owner: selected.owner_state().owner_id(),
684            limits,
685            states: BTreeMap::new(),
686            bindings: BTreeMap::new(),
687        };
688        let selectors = bundle
689            .statements()
690            .iter()
691            .map(|signed| {
692                let body = signed.body.as_ref().ok_or(Reject::Canonical)?;
693                Ok((
694                    body.owner_state_hash.clone(),
695                    body.ownership_transfer_sequence,
696                ))
697            })
698            .chain(bundle.policies().iter().map(|signed| {
699                let body = signed.body.as_ref().ok_or(Reject::Canonical)?;
700                Ok((
701                    body.owner_state_hash.clone(),
702                    body.ownership_transfer_sequence,
703                ))
704            }))
705            .chain(bundle.binding_selectors().into_iter().map(Ok))
706            .collect::<Result<std::collections::BTreeSet<_>, Reject>>()?;
707        for (hash, sequence) in selectors {
708            let count = usize::try_from(sequence).map_err(|_| Reject::Bounds)?;
709            let transfers = public
710                .ownership_transfers
711                .get(..count)
712                .ok_or(Reject::Root)?;
713            let history = public
714                .owner_histories
715                .iter()
716                .find(|h| h.state_hash == hash)
717                .ok_or(Reject::Root)?;
718            let root = history.root.as_ref().ok_or(Reject::Root)?;
719            let mut owner = permission::verify_owner_root(root)?;
720            for transition in &history.accepted_transitions {
721                owner =
722                    permission::apply_accepted_transition(&owner, transition, now_seconds, limits)?;
723            }
724            if owner.state_hash().as_slice() != hash {
725                return Err(Reject::Root.into());
726            }
727            // Only the initial pinned root or the exact destination of a
728            // verified prefix handoff may supply this historical owner.
729            let expected_root = if let Some(last) = transfers.last() {
730                let handoff = last
731                    .transfer
732                    .as_ref()
733                    .and_then(|t| t.acceptance.as_ref())
734                    .and_then(|a| a.signed_handoff.as_ref())
735                    .and_then(|s| s.handoff.as_ref())
736                    .ok_or(Reject::Root)?;
737                pinned
738                    .transfer_owner_histories
739                    .iter()
740                    .find(|h| h.state_hash == handoff.destination_owner_key_state_hash)
741                    .and_then(|h| h.root.as_ref())
742                    .ok_or(Reject::Root)?
743            } else {
744                pinned.owner_root.as_ref().ok_or(Reject::Root)?
745            };
746            if root != expected_root {
747                return Err(Reject::Root.into());
748            }
749            let initial_history = if let Some(first) = transfers.first() {
750                let handoff = first
751                    .transfer
752                    .as_ref()
753                    .and_then(|t| t.acceptance.as_ref())
754                    .and_then(|a| a.signed_handoff.as_ref())
755                    .and_then(|s| s.handoff.as_ref())
756                    .ok_or(Reject::Root)?;
757                public
758                    .owner_histories
759                    .iter()
760                    .find(|h| h.state_hash == handoff.source_owner_key_state_hash)
761                    .ok_or(Reject::Root)?
762            } else {
763                history
764            };
765            if initial_history.root != pinned.owner_root {
766                return Err(Reject::Root.into());
767            }
768            let mut wire = pinned.clone();
769            wire.accepted_transitions = initial_history.accepted_transitions.clone();
770            wire.accepted_state_hash = initial_history.state_hash.clone();
771            wire.ownership_transfers = transfers.to_vec();
772            // The transfer verifier resolves exact signed parties itself.
773            let mut party_states = std::collections::BTreeSet::new();
774            for transfer in transfers {
775                let handoff = transfer
776                    .transfer
777                    .as_ref()
778                    .and_then(|t| t.acceptance.as_ref())
779                    .and_then(|a| a.signed_handoff.as_ref())
780                    .and_then(|s| s.handoff.as_ref())
781                    .ok_or(Reject::Root)?;
782                party_states.insert(&handoff.source_owner_key_state_hash);
783                party_states.insert(&handoff.destination_owner_key_state_hash);
784            }
785            wire.transfer_owner_histories = public
786                .owner_histories
787                .iter()
788                .filter(|h| party_states.contains(&h.state_hash))
789                .cloned()
790                .collect();
791            let keyring = permission::verify_clone_keyring(wire, now_seconds, limits, &[])?;
792            keyring.verify_current_owner(&owner, now_seconds, limits)?;
793            this.states
794                .insert((hash, sequence), HistoricalSelection { owner, keyring });
795        }
796        if let Some(native) = bundle.native() {
797            for p in &native.genesis_witnesses {
798                let body = p
799                    .binding
800                    .as_ref()
801                    .and_then(|b| b.body.as_ref())
802                    .ok_or(Reject::GenesisBinding)?;
803                let id = body.identity.as_ref().ok_or(Reject::GenesisBinding)?;
804                let witnessed = this
805                    .states
806                    .get(&(id.owner_state_hash.clone(), id.ownership_transfer_sequence))
807                    .ok_or(Reject::Root)?;
808                // The binding retains its original keyring endpoint even when
809                // accepted authority or other genesis chains have advanced.
810                // Every candidate is independently authenticated against the
811                // selected immutable root and complete signed transfer prefix.
812                let mut resolved = None;
813                for history in public
814                    .owner_histories
815                    .iter()
816                    .filter(|h| h.root == witnessed.keyring.wire().owner_root)
817                {
818                    let mut wire = witnessed.keyring.wire().clone();
819                    wire.accepted_transitions = history.accepted_transitions.clone();
820                    wire.accepted_state_hash = history.state_hash.clone();
821                    let Ok(keyring) =
822                        permission::verify_clone_keyring(wire, now_seconds, limits, &[])
823                    else {
824                        continue;
825                    };
826                    let selection = permission::import_delegation::Selection {
827                        owner: &witnessed.owner,
828                        keyring: &keyring,
829                        spool_genesis_digest: &this.genesis,
830                        initial_owner_id: &this.initial_owner,
831                        limits,
832                    };
833                    if permission::import_delegation::native_lineage(&selection).is_ok_and(
834                        |(identity, chain, _)| {
835                            body.identity.as_ref() == Some(&identity)
836                                && chain == body.owner_chain_digest
837                        },
838                    ) {
839                        if resolved.is_some() {
840                            return Err(Reject::Canonical.into());
841                        }
842                        resolved = Some(HistoricalSelection {
843                            owner: witnessed.owner.clone(),
844                            keyring,
845                        });
846                    }
847                }
848                this.bindings.insert(
849                    (
850                        id.owner_state_hash.clone(),
851                        id.ownership_transfer_sequence,
852                        body.owner_chain_digest.clone(),
853                    ),
854                    resolved.ok_or(Reject::Root)?,
855                );
856            }
857        }
858        Ok(this)
859    }
860    pub fn for_witness(
861        &self,
862        statement: &host::HostedWitnessStatementV1,
863    ) -> Result<&HistoricalSelection, Error> {
864        let selected = self
865            .states
866            .get(&(
867                statement.owner_state_hash.clone(),
868                statement.ownership_transfer_sequence,
869            ))
870            .ok_or(Reject::Root)?;
871        if statement.spool_uuid != selected.keyring.owner_genesis().spool_uuid()
872            || statement.spool_genesis_digest != self.genesis
873            || statement.owner_id != selected.owner.owner_id()
874        {
875            return Err(Reject::Root.into());
876        }
877        Ok(selected)
878    }
879    pub fn for_policy(
880        &self,
881        policy: &wire::SignedPolicyBody,
882    ) -> Result<&HistoricalSelection, Error> {
883        let selected = self
884            .states
885            .get(&(
886                policy.owner_state_hash.clone(),
887                policy.ownership_transfer_sequence,
888            ))
889            .ok_or(Reject::Root)?;
890        if policy.spool_uuid != selected.keyring.owner_genesis().spool_uuid()
891            || policy.owner_id != selected.owner.owner_id()
892        {
893            return Err(Reject::Root.into());
894        }
895        Ok(selected)
896    }
897    pub fn genesis(&self) -> &[u8; 32] {
898        &self.genesis
899    }
900    pub fn initial_owner(&self) -> &[u8; 32] {
901        &self.initial_owner
902    }
903    pub fn limits(&self) -> VerificationLimits {
904        self.limits
905    }
906}
907
908fn replace_import_metadata(
909    original: &mut wire::ImportPublicProofBundleV1,
910    refreshed: wire::ImportPublicProofBundleV1,
911) -> Result<(), Reject> {
912    api::import_authority::validate_public_bundle(&refreshed)?;
913    let mut unchanged = refreshed.clone();
914    unchanged.witness_set = original.witness_set.clone();
915    unchanged.history_proofs = original.history_proofs.clone();
916    if &unchanged != original {
917        return Err(Reject::Scope);
918    }
919    *original = refreshed;
920    Ok(())
921}
922
923fn replace_native_metadata(
924    original: &mut wire::NativePublicProofBundleV1,
925    refreshed: wire::NativePublicProofBundleV1,
926) -> Result<(), Reject> {
927    api::native_witness::validate_public_bundle(&refreshed)?;
928    let mut unchanged = refreshed.clone();
929    unchanged.witness_set = original.witness_set.clone();
930    unchanged.history_proofs = original.history_proofs.clone();
931    if &unchanged != original {
932        return Err(Reject::Scope);
933    }
934    *original = refreshed;
935    Ok(())
936}