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