Skip to main content

bc_xid/
xid_document.rs

1use std::collections::HashSet;
2
3use bc_components::{
4    EncapsulationPublicKey, PrivateKeyBase, PrivateKeys, PrivateKeysProvider,
5    PublicKeys, PublicKeysProvider, Reference, ReferenceProvider, Signer,
6    SigningPrivateKey, SigningPublicKey, URI, XID, XIDProvider, tags::TAG_XID,
7};
8use bc_envelope::prelude::*;
9use dcbor::prelude::CBORError;
10use known_values::{
11    ATTACHMENT_RAW, DELEGATE, DELEGATE_RAW, DEREFERENCE_VIA,
12    DEREFERENCE_VIA_RAW, EDGE_RAW, KEY, KEY_RAW, PROVENANCE, PROVENANCE_RAW,
13    SERVICE, SERVICE_RAW,
14};
15use provenance_mark::{
16    ProvenanceMark, ProvenanceMarkGenerator, ProvenanceMarkResolution,
17    ProvenanceSeed,
18};
19
20use super::{Delegate, Key};
21use crate::{
22    Error, HasNickname, HasPermissions, Provenance, Result, Service,
23    XIDGeneratorOptions, XIDPrivateKeyOptions,
24};
25
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct XIDDocument {
28    xid: XID,
29    resolution_methods: HashSet<URI>,
30    keys: HashSet<Key>,
31    delegates: HashSet<Delegate>,
32    services: HashSet<Service>,
33    provenance: Option<Provenance>,
34    attachments: Attachments,
35    edges: Edges,
36    extra_assertions: Vec<Envelope>,
37}
38
39#[derive(Default)]
40pub enum XIDInceptionKeyOptions {
41    #[default]
42    Default,
43    PublicKeys(PublicKeys),
44    PublicAndPrivateKeys(PublicKeys, PrivateKeys),
45    PrivateKeyBase(PrivateKeyBase),
46}
47
48#[derive(Default)]
49pub enum XIDGenesisMarkOptions {
50    #[default]
51    None,
52    Passphrase(
53        String,
54        Option<ProvenanceMarkResolution>,
55        Option<Date>,
56        Option<CBOR>,
57    ),
58    Seed(
59        ProvenanceSeed,
60        Option<ProvenanceMarkResolution>,
61        Option<Date>,
62        Option<CBOR>,
63    ),
64}
65
66/// Options for signing an envelope.
67#[derive(Clone, Debug, PartialEq, Eq, Default)]
68pub enum XIDSigningOptions {
69    /// Do not sign the envelope (default).
70    #[default]
71    None,
72
73    /// Sign with the XID's inception key (must be available as a signing key).
74    Inception,
75
76    /// Sign with a provided `PrivateKeys`.
77    PrivateKeys(PrivateKeys),
78
79    /// Sign with a provided `SigningPrivateKey`.
80    SigningPrivateKey(SigningPrivateKey),
81}
82
83/// Options for verifying the signature on an envelope when loading an
84/// XIDDocument.
85#[derive(Clone, Debug, PartialEq, Eq, Default)]
86pub enum XIDVerifySignature {
87    /// Do not verify the signature (default).
88    #[default]
89    None,
90
91    /// Verify that the envelope is signed with the inception key.
92    Inception,
93}
94
95impl XIDDocument {
96    pub fn new(
97        key_options: XIDInceptionKeyOptions,
98        mark_options: XIDGenesisMarkOptions,
99    ) -> Self {
100        let inception_key = Self::inception_key_for_options(key_options);
101        let provenance = Self::genesis_mark_with_options(mark_options).map(
102            |(generator, mark)| Provenance::new_with_generator(generator, mark),
103        );
104
105        let mut xid_doc = Self {
106            xid: XID::new(inception_key.public_keys().signing_public_key()),
107            resolution_methods: HashSet::new(),
108            keys: HashSet::new(),
109            delegates: HashSet::new(),
110            services: HashSet::new(),
111            provenance,
112            attachments: Attachments::new(),
113            edges: Edges::new(),
114            extra_assertions: Vec::new(),
115        };
116
117        xid_doc.add_key(inception_key).unwrap();
118
119        xid_doc
120    }
121
122    fn inception_key_for_options(options: XIDInceptionKeyOptions) -> Key {
123        match options {
124            XIDInceptionKeyOptions::Default => {
125                // Default: generate a new key pair and include private key
126                let private_key_base = PrivateKeyBase::new();
127                let public_keys = private_key_base.public_keys();
128                let private_keys = private_key_base.private_keys();
129                Key::new_with_private_keys(private_keys, public_keys)
130            }
131            XIDInceptionKeyOptions::PublicKeys(public_keys) => {
132                // Public key only, no private key
133                Key::new_allow_all(&public_keys)
134            }
135            XIDInceptionKeyOptions::PublicAndPrivateKeys(
136                public_keys,
137                private_keys,
138            ) => {
139                // Both public and private keys
140                Key::new_with_private_keys(private_keys, public_keys)
141            }
142            XIDInceptionKeyOptions::PrivateKeyBase(private_key_base) => {
143                // Derive both keys from private key base
144                let public_keys = private_key_base.public_keys();
145                let private_keys = private_key_base.private_keys();
146                Key::new_with_private_keys(private_keys, public_keys)
147            }
148        }
149    }
150
151    fn genesis_mark_with_options(
152        options: XIDGenesisMarkOptions,
153    ) -> Option<(ProvenanceMarkGenerator, ProvenanceMark)> {
154        use ProvenanceMarkGenerator;
155        match options {
156            XIDGenesisMarkOptions::None => None,
157            XIDGenesisMarkOptions::Passphrase(passphrase, res, date, info) => {
158                let mut generator =
159                    ProvenanceMarkGenerator::new_with_passphrase(
160                        res.unwrap_or(ProvenanceMarkResolution::High),
161                        &passphrase,
162                    );
163                let date = date.unwrap_or_else(dcbor::Date::now);
164                let mark = generator.next(date, info);
165                Some((generator, mark))
166            }
167            XIDGenesisMarkOptions::Seed(seed, res, date, info) => {
168                let mut generator = ProvenanceMarkGenerator::new_with_seed(
169                    res.unwrap_or(ProvenanceMarkResolution::High),
170                    seed,
171                );
172                let date = date.unwrap_or_else(dcbor::Date::now);
173                let mark = generator.next(date, info);
174                Some((generator, mark))
175            }
176        }
177    }
178
179    pub fn from_xid(xid: impl Into<XID>) -> Self {
180        Self {
181            xid: xid.into(),
182            resolution_methods: HashSet::new(),
183            keys: HashSet::new(),
184            delegates: HashSet::new(),
185            services: HashSet::new(),
186            provenance: None,
187            attachments: Attachments::new(),
188            edges: Edges::new(),
189            extra_assertions: Vec::new(),
190        }
191    }
192
193    pub fn resolution_methods(&self) -> &HashSet<URI> {
194        &self.resolution_methods
195    }
196
197    pub fn resolution_methods_mut(&mut self) -> &mut HashSet<URI> {
198        &mut self.resolution_methods
199    }
200
201    pub fn add_resolution_method(&mut self, method: URI) {
202        self.resolution_methods.insert(method);
203    }
204
205    pub fn remove_resolution_method(
206        &mut self,
207        method: impl AsRef<URI>,
208    ) -> Option<URI> {
209        self.resolution_methods.take(method.as_ref())
210    }
211
212    pub fn keys(&self) -> &HashSet<Key> { &self.keys }
213
214    pub fn keys_mut(&mut self) -> &mut HashSet<Key> { &mut self.keys }
215
216    pub fn add_key(&mut self, key: Key) -> Result<()> {
217        if self.find_key_by_public_keys(key.public_keys()).is_some() {
218            return Err(Error::Duplicate { item: "key".to_string() });
219        }
220        self.keys.insert(key);
221        Ok(())
222    }
223
224    pub fn find_key_by_public_keys(
225        &self,
226        key: &dyn PublicKeysProvider,
227    ) -> Option<&Key> {
228        let key = key.public_keys();
229        self.keys.iter().find(|k| k.public_keys() == &key)
230    }
231
232    pub fn find_key_by_reference(&self, reference: &Reference) -> Option<&Key> {
233        self.keys
234            .iter()
235            .find(|k| k.public_keys().reference() == *reference)
236    }
237
238    /// Get the private key envelope for a specific key, optionally decrypting
239    /// it.
240    ///
241    /// # Arguments
242    ///
243    /// * `public_keys` - The public keys identifying the key to retrieve
244    /// * `password` - Optional password for decryption
245    ///
246    /// # Returns
247    ///
248    /// - `Ok(None)` if the key is not found or has no private key
249    /// - `Ok(Some(Envelope))` containing:
250    ///   - Decrypted `PrivateKeys` if unencrypted
251    ///   - Decrypted `PrivateKeys` if encrypted and correct password provided
252    ///   - Encrypted envelope if encrypted and no password provided
253    /// - `Err(Error::InvalidPassword)` if encrypted and wrong password provided
254    ///
255    /// # Examples
256    ///
257    /// ```
258    /// use bc_components::{PrivateKeyBase, PublicKeysProvider};
259    /// use bc_envelope::prelude::*;
260    /// use bc_xid::{XIDDocument, XIDGenesisMarkOptions, XIDInceptionKeyOptions};
261    ///
262    /// let prvkey_base = PrivateKeyBase::new();
263    /// let doc = XIDDocument::new(
264    ///     XIDInceptionKeyOptions::PrivateKeyBase(prvkey_base.clone()),
265    ///     XIDGenesisMarkOptions::None,
266    /// );
267    ///
268    /// // Get unencrypted private key
269    /// let key = doc.keys().iter().next().unwrap();
270    /// let envelope = doc
271    ///     .private_key_envelope_for_key(key.public_keys(), None)
272    ///     .unwrap()
273    ///     .unwrap();
274    /// ```
275    pub fn private_key_envelope_for_key(
276        &self,
277        public_keys: &PublicKeys,
278        password: Option<&str>,
279    ) -> Result<Option<Envelope>> {
280        match self.find_key_by_public_keys(public_keys) {
281            None => Ok(None),
282            Some(key) => key.private_key_envelope(password),
283        }
284    }
285
286    pub fn take_key(&mut self, key: &dyn PublicKeysProvider) -> Option<Key> {
287        if let Some(key) = self.find_key_by_public_keys(key).cloned() {
288            self.keys.take(&key)
289        } else {
290            None
291        }
292    }
293
294    pub fn remove_key(&mut self, key: &dyn PublicKeysProvider) -> Result<()> {
295        if self.services_reference_key(key) {
296            return Err(Error::StillReferenced { item: "key".to_string() });
297        }
298        if self.take_key(key).is_none() {
299            return Err(Error::NotFound { item: "key".to_string() });
300        }
301        Ok(())
302    }
303
304    pub fn set_name_for_key(
305        &mut self,
306        key: &dyn PublicKeysProvider,
307        name: impl Into<String>,
308    ) -> Result<()> {
309        let mut key = self
310            .take_key(key)
311            .ok_or_else(|| Error::NotFound { item: "key".to_string() })?;
312        key.set_nickname(name);
313        self.add_key(key)
314    }
315
316    pub fn is_inception_signing_key(
317        &self,
318        signing_public_key: &SigningPublicKey,
319    ) -> bool {
320        self.xid.validate(signing_public_key)
321    }
322
323    pub fn inception_signing_key(&self) -> Option<&SigningPublicKey> {
324        if let Some(key) = self.keys.iter().find(|k| {
325            self.is_inception_signing_key(k.public_keys().signing_public_key())
326        }) {
327            Some(key.public_keys().signing_public_key())
328        } else {
329            None
330        }
331    }
332
333    pub fn inception_key(&self) -> Option<&Key> {
334        self.keys.iter().find(|k| {
335            self.is_inception_signing_key(k.public_keys().signing_public_key())
336        })
337    }
338
339    pub fn remove_inception_key(&mut self) -> Option<Key> {
340        if let Some(key) = self.inception_key().cloned() {
341            self.keys.take(&key)
342        } else {
343            None
344        }
345    }
346
347    pub fn verification_key(&self) -> Option<&SigningPublicKey> {
348        // Prefer the inception key for verification.
349        if let Some(key) = self.inception_key() {
350            Some(key.public_keys().signing_public_key())
351        } else if let Some(key) = self.keys.iter().next() {
352            Some(key.public_keys().signing_public_key())
353        } else {
354            None
355        }
356    }
357
358    pub fn encryption_key(&self) -> Option<&EncapsulationPublicKey> {
359        // Prefer the inception key for encryption.
360        if let Some(key) = self.inception_key() {
361            Some(key.public_keys().enapsulation_public_key())
362        } else if let Some(key) = self.keys.iter().next() {
363            Some(key.public_keys().enapsulation_public_key())
364        } else {
365            None
366        }
367    }
368
369    /// Get the private keys from the inception key, if available.
370    ///
371    /// Returns `None` if there is no inception key or if the inception key
372    /// does not have private key material (e.g., if it was encrypted and not
373    /// decrypted).
374    pub fn inception_private_keys(&self) -> Option<&PrivateKeys> {
375        self.inception_key().and_then(|key| key.private_keys())
376    }
377
378    /// Extract private keys from an envelope containing an encrypted
379    /// XIDDocument.
380    ///
381    /// This is a convenience method that loads the document with the password
382    /// and returns the inception key's private keys if available.
383    ///
384    /// Returns `None` if:
385    /// - The document has no inception key
386    /// - The inception key has no private key material
387    /// - The password is incorrect
388    pub fn extract_inception_private_keys_from_envelope(
389        envelope: &Envelope,
390        password: &[u8],
391    ) -> Result<Option<PrivateKeys>> {
392        let doc = Self::from_envelope(
393            envelope,
394            Some(password),
395            XIDVerifySignature::None,
396        )?;
397        Ok(doc.inception_private_keys().cloned())
398    }
399
400    pub fn is_empty(&self) -> bool {
401        self.resolution_methods.is_empty()
402            && self.keys.is_empty()
403            && self.delegates.is_empty()
404            && self.provenance.is_none()
405            && self.services.is_empty()
406            && !self.has_attachments()
407            && !self.has_edges()
408            && self.extra_assertions.is_empty()
409    }
410
411    pub fn extra_assertions(&self) -> &[Envelope] { &self.extra_assertions }
412
413    // `Delegate` is internally mutable, but the actual key of the `HashSet`,
414    // the controller's `XID`, is not.
415    #[allow(clippy::mutable_key_type)]
416    pub fn delegates(&self) -> &HashSet<Delegate> { &self.delegates }
417
418    // `Delegate` is internally mutable, but the actual key of the `HashSet`,
419    // the controller's `XID`, is not.
420    #[allow(clippy::mutable_key_type)]
421    pub fn delegates_mut(&mut self) -> &mut HashSet<Delegate> {
422        &mut self.delegates
423    }
424
425    pub fn add_delegate(&mut self, delegate: Delegate) -> Result<()> {
426        if self.find_delegate_by_xid(&delegate).is_some() {
427            return Err(Error::Duplicate { item: "delegate".to_string() });
428        }
429        self.delegates.insert(delegate);
430
431        Ok(())
432    }
433
434    pub fn find_delegate_by_xid(
435        &self,
436        xid_provider: &dyn XIDProvider,
437    ) -> Option<&Delegate> {
438        self.delegates
439            .iter()
440            .find(|d| d.controller().read().xid() == xid_provider.xid())
441    }
442
443    pub fn find_delegate_by_reference(
444        &self,
445        reference: &Reference,
446    ) -> Option<&Delegate> {
447        self.delegates
448            .iter()
449            .find(|d| d.controller().read().xid().reference() == *reference)
450    }
451
452    pub fn take_delegate(
453        &mut self,
454        xid_provider: &dyn XIDProvider,
455    ) -> Option<Delegate> {
456        if let Some(delegate) = self.find_delegate_by_xid(xid_provider).cloned()
457        {
458            self.delegates.take(&delegate)
459        } else {
460            None
461        }
462    }
463
464    pub fn remove_delegate(
465        &mut self,
466        xid_provider: &dyn XIDProvider,
467    ) -> Result<()> {
468        if self.services_reference_delegate(xid_provider) {
469            return Err(Error::StillReferenced {
470                item: "delegate".to_string(),
471            });
472        }
473        if self.take_delegate(xid_provider).is_none() {
474            return Err(Error::NotFound { item: "delegate".to_string() });
475        }
476        Ok(())
477    }
478
479    pub fn find_service_by_uri(
480        &self,
481        uri: impl AsRef<URI>,
482    ) -> Option<&Service> {
483        self.services.iter().find(|s| s.uri() == uri.as_ref())
484    }
485
486    pub fn services(&self) -> &HashSet<Service> { &self.services }
487
488    pub fn add_service(&mut self, service: Service) -> Result<()> {
489        if self.find_service_by_uri(service.uri()).is_some() {
490            return Err(Error::Duplicate { item: "service".to_string() });
491        }
492        self.services.insert(service);
493        Ok(())
494    }
495
496    pub fn take_service(&mut self, uri: impl AsRef<URI>) -> Option<Service> {
497        if let Some(service) = self.find_service_by_uri(uri).cloned() {
498            self.services.take(&service)
499        } else {
500            None
501        }
502    }
503
504    pub fn check_services_consistency(&self) -> Result<()> {
505        for service in &self.services {
506            self.check_service_consistency(service)?;
507        }
508        Ok(())
509    }
510
511    pub fn check_service_consistency(&self, service: &Service) -> Result<()> {
512        if service.key_references().is_empty()
513            && service.delegate_references().is_empty()
514        {
515            return Err(Error::NoReferences { uri: service.uri().to_string() });
516        }
517
518        for key_reference in service.key_references() {
519            if self.find_key_by_reference(key_reference).is_none() {
520                return Err(Error::UnknownKeyReference {
521                    reference: key_reference.to_string(),
522                    uri: service.uri().to_string(),
523                });
524            }
525        }
526
527        for delegate_reference in service.delegate_references() {
528            if self
529                .find_delegate_by_reference(delegate_reference)
530                .is_none()
531            {
532                return Err(Error::UnknownDelegateReference {
533                    reference: delegate_reference.to_string(),
534                    uri: service.uri().to_string(),
535                });
536            }
537        }
538
539        if service.permissions().allow().is_empty() {
540            return Err(Error::NoPermissions {
541                uri: service.uri().to_string(),
542            });
543        }
544
545        Ok(())
546    }
547
548    pub fn check_contains_key(
549        &self,
550        key: &dyn PublicKeysProvider,
551    ) -> Result<()> {
552        if self.find_key_by_public_keys(key).is_none() {
553            return Err(Error::KeyNotFoundInDocument {
554                key: key.public_keys().to_string(),
555            });
556        }
557        Ok(())
558    }
559
560    pub fn check_contains_delegate(
561        &self,
562        xid_provider: &dyn XIDProvider,
563    ) -> Result<()> {
564        if self.find_delegate_by_xid(xid_provider).is_none() {
565            return Err(Error::DelegateNotFoundInDocument {
566                delegate: xid_provider.xid().to_string(),
567            });
568        }
569        Ok(())
570    }
571
572    pub fn services_reference_key(&self, key: &dyn PublicKeysProvider) -> bool {
573        let key_reference = key.public_keys().reference();
574        self.services
575            .iter()
576            .any(|service| service.key_references().contains(&key_reference))
577    }
578
579    pub fn services_reference_delegate(
580        &self,
581        xid_provider: &dyn XIDProvider,
582    ) -> bool {
583        let delegate_reference = xid_provider.xid().reference();
584        self.services.iter().any(|service| {
585            service.delegate_references().contains(&delegate_reference)
586        })
587    }
588
589    pub fn remove_service(&mut self, uri: impl AsRef<URI>) -> Result<()> {
590        if !self.services.iter().any(|s| s.uri() == uri.as_ref()) {
591            return Err(Error::NotFound { item: "service".to_string() });
592        }
593        self.services.retain(|s| s.uri() != uri.as_ref());
594        Ok(())
595    }
596
597    pub fn provenance(&self) -> Option<&ProvenanceMark> {
598        self.provenance.as_ref().map(|p| p.mark())
599    }
600
601    pub fn provenance_generator(&self) -> Option<&ProvenanceMarkGenerator> {
602        self.provenance.as_ref().and_then(|p| p.generator())
603    }
604
605    pub fn set_provenance(&mut self, provenance: Option<ProvenanceMark>) {
606        self.provenance = provenance.map(Provenance::new);
607    }
608
609    pub fn set_provenance_with_generator(
610        &mut self,
611        generator: ProvenanceMarkGenerator,
612        mark: ProvenanceMark,
613    ) {
614        self.provenance = Some(Provenance::new_with_generator(generator, mark));
615    }
616
617    /// Advance the provenance mark using the embedded generator.
618    ///
619    /// This method uses the generator embedded in the document's provenance
620    /// to generate the next provenance mark. If the generator is encrypted,
621    /// it will be decrypted using the provided password. After
622    /// advancement, the generator remains in the document in decrypted
623    /// state.
624    ///
625    /// # Parameters
626    ///
627    /// - `password`: Optional password to decrypt the generator if encrypted.
628    ///   Pass as `Vec<u8>` or `None`.
629    /// - `date`: Optional date for the new mark. If `None`, the current date is
630    ///   used.
631    /// - `info`: Optional CBOR-encodable info to attach to the new mark.
632    ///
633    /// # Errors
634    ///
635    /// Returns an error if:
636    /// - The document does not have a provenance mark
637    ///   (`Error::NoProvenanceMark`)
638    /// - The document does not have a generator (`Error::NoGenerator`)
639    /// - The generator is encrypted and the password is wrong
640    ///   (`Error::InvalidPassword`)
641    pub fn next_provenance_mark_with_embedded_generator(
642        &mut self,
643        password: Option<Vec<u8>>,
644        date: Option<Date>,
645        info: Option<CBOR>,
646    ) -> Result<()> {
647        // Ensure we have a provenance to advance
648        let provenance =
649            self.provenance.as_mut().ok_or(Error::NoProvenanceMark)?;
650
651        // Get the current mark
652        let current_mark = provenance.mark().clone();
653
654        // Get mutable access to the generator, decrypting if necessary
655        let password_ref = password.as_deref();
656        let generator = provenance
657            .generator_mut(password_ref)?
658            .ok_or(Error::NoGenerator)?;
659
660        // Validate chain ID matches
661        if generator.chain_id() != current_mark.chain_id() {
662            return Err(Error::ChainIdMismatch {
663                expected: current_mark.chain_id().to_vec(),
664                actual: generator.chain_id().to_vec(),
665            });
666        }
667
668        // Validate sequence number is correct (next_seq should be current seq +
669        // 1)
670        let expected_seq = current_mark.seq() + 1;
671        if generator.next_seq() != expected_seq {
672            return Err(Error::SequenceMismatch {
673                expected: expected_seq,
674                actual: generator.next_seq(),
675            });
676        }
677
678        // Generate the next mark
679        let date = date.unwrap_or_else(Date::now);
680        let next_mark = generator.next(date, info);
681
682        // Update the provenance mark
683        provenance.set_mark(next_mark);
684
685        Ok(())
686    }
687
688    /// Advance the provenance mark using a provided generator.
689    ///
690    /// This method uses an external generator to generate the next provenance
691    /// mark. The generator is not embedded in the document after
692    /// advancement; the caller maintains ownership of the generator.
693    ///
694    /// # Parameters
695    ///
696    /// - `generator`: Mutable reference to the external
697    ///   `ProvenanceMarkGenerator`.
698    /// - `date`: Optional date for the new mark. If `None`, the current date is
699    ///   used.
700    /// - `info`: Optional CBOR-encodable info to attach to the new mark.
701    ///
702    /// # Errors
703    ///
704    /// Returns an error if:
705    /// - The document does not have a provenance mark
706    ///   (`Error::NoProvenanceMark`)
707    /// - The document already has an embedded generator
708    ///   (`Error::GeneratorConflict`)
709    /// - The provided generator's chain ID doesn't match the current mark's
710    ///   chain ID
711    /// - The provided generator's next_seq doesn't match the expected sequence
712    ///   number
713    pub fn next_provenance_mark_with_provided_generator(
714        &mut self,
715        generator: &mut ProvenanceMarkGenerator,
716        date: Option<Date>,
717        info: Option<CBOR>,
718    ) -> Result<()> {
719        // Ensure we have a provenance to advance
720        let provenance =
721            self.provenance.as_mut().ok_or(Error::NoProvenanceMark)?;
722
723        // Check that the document doesn't already have a generator
724        if provenance.has_generator() || provenance.has_encrypted_generator() {
725            return Err(Error::GeneratorConflict);
726        }
727
728        // Get the current mark
729        let current_mark = provenance.mark().clone();
730
731        // Validate chain ID matches
732        if generator.chain_id() != current_mark.chain_id() {
733            return Err(Error::ChainIdMismatch {
734                expected: current_mark.chain_id().to_vec(),
735                actual: generator.chain_id().to_vec(),
736            });
737        }
738
739        // Validate sequence number is correct (next_seq should be current seq +
740        // 1)
741        let expected_seq = current_mark.seq() + 1;
742        if generator.next_seq() != expected_seq {
743            return Err(Error::SequenceMismatch {
744                expected: expected_seq,
745                actual: generator.next_seq(),
746            });
747        }
748
749        // Generate the next mark
750        let date = date.unwrap_or_else(Date::now);
751        let next_mark = generator.next(date, info);
752
753        // Update the provenance mark
754        provenance.set_mark(next_mark);
755
756        Ok(())
757    }
758
759    /// Convert XIDDocument to an Envelope.
760    pub fn to_envelope(
761        &self,
762        private_key_options: XIDPrivateKeyOptions,
763        generator_options: XIDGeneratorOptions,
764        signing_options: XIDSigningOptions,
765    ) -> Result<Envelope> {
766        let mut envelope = Envelope::new(self.xid);
767
768        // Add an assertion for each resolution method.
769        envelope = self
770            .resolution_methods
771            .iter()
772            .cloned()
773            .fold(envelope, |envelope, method| {
774                envelope.add_assertion(DEREFERENCE_VIA, method)
775            });
776
777        // Add an assertion for each key in the set.
778        envelope = self.keys.iter().cloned().fold(envelope, |envelope, key| {
779            envelope.add_assertion(
780                KEY,
781                key.into_envelope_opt(private_key_options.clone()),
782            )
783        });
784
785        // Add an assertion for each delegate.
786        envelope = self
787            .delegates
788            .iter()
789            .cloned()
790            .fold(envelope, |envelope, delegate| {
791                envelope.add_assertion(DELEGATE, delegate)
792            });
793
794        // Add an assertion for each service.
795        envelope = self
796            .services
797            .iter()
798            .cloned()
799            .fold(envelope, |envelope, service| {
800                envelope.add_assertion(SERVICE, service)
801            });
802
803        // Add the provenance mark with optional generator.
804        if let Some(provenance) = &self.provenance {
805            envelope = envelope.add_assertion(
806                PROVENANCE,
807                provenance.clone().into_envelope_opt(generator_options),
808            );
809        }
810
811        let envelope =
812            envelope.add_assertion_envelopes(&self.extra_assertions)?;
813
814        // Add attachments before signing so they are included in the signature
815        let envelope = self.attachments.add_to_envelope(envelope);
816
817        // Add edges before signing so they are included in the signature
818        let envelope = self.edges.add_to_envelope(envelope);
819
820        // Apply signing options.
821        let envelope = match signing_options {
822            XIDSigningOptions::None => envelope,
823            XIDSigningOptions::Inception => {
824                let inception_key =
825                    self.inception_key().ok_or(Error::MissingInceptionKey)?;
826                let private_keys = inception_key
827                    .private_keys()
828                    .ok_or(Error::MissingInceptionKey)?;
829                envelope.sign(private_keys)
830            }
831            XIDSigningOptions::PrivateKeys(ref keys) => envelope.sign(keys),
832            XIDSigningOptions::SigningPrivateKey(ref key) => envelope.sign(key),
833        };
834
835        Ok(envelope)
836    }
837
838    /// Extract an `XIDDocument` from an envelope.
839    ///
840    /// # Parameters
841    ///
842    /// - `envelope`: The envelope to extract the document from. Can be signed
843    ///   or unsigned.
844    /// - `password`: Optional password to decrypt encrypted private keys. If
845    ///   private keys are encrypted and no password is provided, the keys will
846    ///   be stored without their private key material.
847    /// - `verify_signature`: Signature verification mode. Use
848    ///   `XIDVerifySignature::None` to skip verification, or
849    ///   `XIDVerifySignature::Inception` to verify that the envelope is signed
850    ///   with the inception key.
851    ///
852    /// # Returns
853    ///
854    /// Returns `Ok(XIDDocument)` on success.
855    ///
856    /// # Errors
857    ///
858    /// - `Error::EnvelopeNotSigned`: When `verify_signature` is `Inception` but
859    ///   the envelope is not signed.
860    /// - `Error::MissingInceptionKey`: When `verify_signature` is `Inception`
861    ///   but the inception key is not found in the document.
862    /// - `Error::SignatureVerificationFailed`: When the signature verification
863    ///   fails.
864    /// - `Error::InvalidXid`: When the inception key does not match the XID.
865    /// - Other errors from envelope parsing or key extraction.
866    pub fn from_envelope(
867        envelope: &Envelope,
868        password: Option<&[u8]>,
869        verify_signature: XIDVerifySignature,
870    ) -> Result<Self> {
871        match verify_signature {
872            XIDVerifySignature::None => {
873                // Extract from the envelope directly (unsigned or ignoring
874                // signature)
875                let envelope_to_parse = if envelope.subject().is_wrapped() {
876                    envelope.subject().try_unwrap()?
877                } else {
878                    envelope.clone()
879                };
880
881                // Extract attachments from the envelope we're parsing
882                let attachments =
883                    Attachments::try_from_envelope(&envelope_to_parse)
884                        .map_err(Error::EnvelopeParsing)?;
885
886                // Extract edges from the envelope we're parsing
887                let edges = Edges::try_from_envelope(&envelope_to_parse)
888                    .map_err(Error::EnvelopeParsing)?;
889
890                let mut xid_document =
891                    Self::from_envelope_inner(&envelope_to_parse, password)?;
892                xid_document.attachments = attachments;
893                xid_document.edges = edges;
894                Ok(xid_document)
895            }
896            XIDVerifySignature::Inception => {
897                // Verify that the envelope is signed (subject must be wrapped)
898                if !envelope.subject().is_wrapped() {
899                    return Err(Error::EnvelopeNotSigned);
900                }
901
902                // Unwrap the envelope and construct a provisional XIDDocument
903                let unwrapped = envelope.try_unwrap()?;
904
905                // Extract attachments from the unwrapped (inner) envelope
906                let attachments = Attachments::try_from_envelope(&unwrapped)
907                    .map_err(Error::EnvelopeParsing)?;
908
909                // Extract edges from the unwrapped (inner) envelope
910                let edges = Edges::try_from_envelope(&unwrapped)
911                    .map_err(Error::EnvelopeParsing)?;
912
913                let mut xid_document =
914                    Self::from_envelope_inner(&unwrapped, password)?;
915
916                // Extract the inception key from the provisional XIDDocument
917                let inception_key = xid_document
918                    .inception_signing_key()
919                    .ok_or(Error::MissingInceptionKey)?;
920
921                // Verify the signature on the envelope using the inception key
922                envelope
923                    .verify(inception_key)
924                    .map_err(|_| Error::SignatureVerificationFailed)?;
925
926                // Extract the XID from the provisional XIDDocument
927                let xid = xid_document.xid();
928
929                // Verify that the inception key is the one that generated the
930                // XID
931                if !xid.validate(inception_key) {
932                    Err(Error::InvalidXid)
933                } else {
934                    xid_document.attachments = attachments;
935                    xid_document.edges = edges;
936                    Ok(xid_document)
937                }
938            }
939        }
940    }
941
942    /// Internal helper method to extract an `XIDDocument` from an unwrapped
943    /// envelope.
944    fn from_envelope_inner(
945        envelope: &Envelope,
946        password: Option<&[u8]>,
947    ) -> Result<Self> {
948        let xid: XID = envelope.subject().try_leaf()?.try_into()?;
949        let mut xid_document = XIDDocument::from(xid);
950        for assertion in envelope.assertions() {
951            let Ok(predicate) = assertion.try_predicate() else {
952                xid_document.extra_assertions.push(assertion);
953                continue;
954            };
955            let Ok(predicate) = predicate.try_known_value() else {
956                xid_document.extra_assertions.push(assertion);
957                continue;
958            };
959            let predicate = predicate.value();
960            match predicate {
961                DEREFERENCE_VIA_RAW => {
962                    let object = assertion.try_object()?;
963                    let method: URI = object
964                        .try_leaf()?
965                        .try_into()
966                        .map_err(|_| Error::InvalidResolutionMethod)?;
967                    xid_document.add_resolution_method(method);
968                }
969                KEY_RAW => {
970                    let object = assertion.try_object()?;
971                    let key = Key::try_from_envelope(&object, password)?;
972                    xid_document.add_key(key)?;
973                }
974                DELEGATE_RAW => {
975                    let object = assertion.try_object()?;
976                    let delegate = Delegate::try_from(object)?;
977                    xid_document.add_delegate(delegate)?;
978                }
979                SERVICE_RAW => {
980                    let object = assertion.try_object()?;
981                    let service = Service::try_from(object)?;
982                    xid_document.add_service(service)?;
983                }
984                PROVENANCE_RAW => {
985                    let object = assertion.try_object()?;
986                    let provenance =
987                        Provenance::try_from_envelope(&object, password)?;
988                    if xid_document.provenance.is_some() {
989                        return Err(Error::MultipleProvenanceMarks);
990                    }
991                    xid_document.provenance = Some(provenance);
992                }
993                ATTACHMENT_RAW => {
994                    // Attachment assertions are handled separately by
995                    // Attachments::try_from_envelope() above, so we skip them
996                    // here.
997                }
998                EDGE_RAW => {
999                    // Edge assertions are handled separately by
1000                    // Edges::try_from_envelope() above, so we skip them here.
1001                }
1002                _ => {
1003                    xid_document.extra_assertions.push(assertion);
1004                }
1005            }
1006        }
1007
1008        xid_document.check_services_consistency()?;
1009
1010        Ok(xid_document)
1011    }
1012
1013    pub fn to_signed_envelope(&self, signing_key: &impl Signer) -> Envelope {
1014        self.to_signed_envelope_opt(
1015            signing_key,
1016            XIDPrivateKeyOptions::default(),
1017        )
1018    }
1019
1020    pub fn to_signed_envelope_opt(
1021        &self,
1022        signing_key: &impl Signer,
1023        private_key_options: XIDPrivateKeyOptions,
1024    ) -> Envelope {
1025        self.to_envelope(
1026            private_key_options,
1027            XIDGeneratorOptions::default(),
1028            XIDSigningOptions::None,
1029        )
1030        .expect("envelope should not fail")
1031        .sign(signing_key)
1032    }
1033}
1034
1035impl Default for XIDDocument {
1036    fn default() -> Self {
1037        Self::new(XIDInceptionKeyOptions::Default, XIDGenesisMarkOptions::None)
1038    }
1039}
1040
1041bc_envelope::impl_attachable!(XIDDocument);
1042bc_envelope::impl_edgeable!(XIDDocument);
1043
1044impl XIDProvider for XIDDocument {
1045    fn xid(&self) -> XID { self.xid }
1046}
1047
1048impl ReferenceProvider for XIDDocument {
1049    fn reference(&self) -> Reference { self.xid.reference() }
1050}
1051
1052impl AsRef<XIDDocument> for XIDDocument {
1053    fn as_ref(&self) -> &XIDDocument { self }
1054}
1055
1056impl From<XIDDocument> for XID {
1057    fn from(doc: XIDDocument) -> Self { doc.xid }
1058}
1059
1060impl From<XID> for XIDDocument {
1061    fn from(xid: XID) -> Self { XIDDocument::from_xid(xid) }
1062}
1063
1064impl From<PublicKeys> for XIDDocument {
1065    fn from(inception_key: PublicKeys) -> Self {
1066        XIDDocument::new(
1067            XIDInceptionKeyOptions::PublicKeys(inception_key),
1068            XIDGenesisMarkOptions::None,
1069        )
1070    }
1071}
1072
1073impl From<PrivateKeyBase> for XIDDocument {
1074    fn from(inception_key: PrivateKeyBase) -> Self {
1075        XIDDocument::new(
1076            XIDInceptionKeyOptions::PrivateKeyBase(inception_key),
1077            XIDGenesisMarkOptions::None,
1078        )
1079    }
1080}
1081
1082impl From<&PrivateKeyBase> for XIDDocument {
1083    fn from(inception_key: &PrivateKeyBase) -> Self {
1084        XIDDocument::new(
1085            XIDInceptionKeyOptions::PrivateKeyBase(inception_key.clone()),
1086            XIDGenesisMarkOptions::None,
1087        )
1088    }
1089}
1090
1091impl From<XIDDocument> for Envelope {
1092    fn from(value: XIDDocument) -> Self {
1093        if value.is_empty() {
1094            return value.xid.to_envelope();
1095        }
1096        value
1097            .to_envelope(
1098                XIDPrivateKeyOptions::default(),
1099                XIDGeneratorOptions::default(),
1100                XIDSigningOptions::default(),
1101            )
1102            .expect("envelope should not fail")
1103    }
1104}
1105
1106impl TryFrom<&Envelope> for XIDDocument {
1107    type Error = Error;
1108
1109    fn try_from(envelope: &Envelope) -> Result<Self> {
1110        Self::from_envelope(envelope, None, XIDVerifySignature::None)
1111    }
1112}
1113
1114impl TryFrom<Envelope> for XIDDocument {
1115    type Error = Error;
1116
1117    fn try_from(envelope: Envelope) -> Result<Self> {
1118        XIDDocument::try_from(&envelope)
1119    }
1120}
1121
1122impl CBORTagged for XIDDocument {
1123    fn cbor_tags() -> Vec<Tag> { tags_for_values(&[TAG_XID]) }
1124}
1125
1126impl From<XIDDocument> for CBOR {
1127    fn from(value: XIDDocument) -> Self { value.tagged_cbor() }
1128}
1129
1130impl CBORTaggedEncodable for XIDDocument {
1131    fn untagged_cbor(&self) -> CBOR {
1132        if self.is_empty() {
1133            return self.xid.untagged_cbor();
1134        }
1135        self.to_envelope(
1136            XIDPrivateKeyOptions::default(),
1137            XIDGeneratorOptions::default(),
1138            XIDSigningOptions::None,
1139        )
1140        .expect("envelope should not fail")
1141        .to_cbor()
1142    }
1143}
1144
1145impl TryFrom<CBOR> for XIDDocument {
1146    type Error = dcbor::Error;
1147
1148    fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
1149        Self::from_tagged_cbor(cbor)
1150    }
1151}
1152
1153impl CBORTaggedDecodable for XIDDocument {
1154    fn from_untagged_cbor(cbor: CBOR) -> dcbor::Result<Self> {
1155        if let Some(byte_string) = cbor.clone().into_byte_string() {
1156            let xid = XID::from_data_ref(byte_string)?;
1157            return Ok(Self::from_xid(xid));
1158        }
1159
1160        let envelope = Envelope::try_from(cbor)?;
1161        let xid_doc: Self =
1162            envelope.try_into().map_err(|e: Error| match e {
1163                Error::Cbor(cbor_err) => cbor_err,
1164                _ => CBORError::msg(e.to_string()),
1165            })?;
1166        Ok(xid_doc)
1167    }
1168}