1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use std::{collections::HashSet, cell::RefCell, borrow::Cow};

use bc_components::{Digest, DigestProvider};

use crate::Envelope;

use super::{walk::EdgeType, envelope::EnvelopeCase};

/// Support for calculating the digests associated with `Envelope`.

impl DigestProvider for Envelope {
    /// The envelope's digest.
    ///
    /// This digest can be used to compare two envelopes for semantic equivalence, that
    /// is, the two envelopes would contain the same information in their unencrypted
    /// and unelided forms. See <doc:Diffing> for more information.
    fn digest(&self) -> Cow<'_, Digest> {
        match self.case() {
            EnvelopeCase::Node { digest, .. } => Cow::Borrowed(digest),
            EnvelopeCase::Leaf { digest, .. } => Cow::Borrowed(digest),
            EnvelopeCase::Wrapped { digest, .. } => Cow::Borrowed(digest),
            EnvelopeCase::Assertion(assertion) => assertion.digest(),
            EnvelopeCase::Elided(digest) => Cow::Borrowed(digest),
            #[cfg(feature = "known_value")]
            EnvelopeCase::KnownValue { digest, .. } => Cow::Borrowed(digest),
            #[cfg(feature = "encrypt")]
            EnvelopeCase::Encrypted(encrypted_message) => encrypted_message.digest(),
            #[cfg(feature = "compress")]
            EnvelopeCase::Compressed(compressed) => compressed.digest(),
        }
    }
}

/// Support for working with the digest tree of an `Envelope`.
impl Envelope {
    /// Returns the set of digests contained in the envelope's elements, down to the
    /// specified level.
    ///
    /// The digest of the envelope is included as well as the digest of the envelope's
    /// subject (if it is different).
    ///
    /// If no `levelLimit` is provided, all digests in the envelope will be returned.
    ///
    /// A `levelLimit` of zero will return no digests.
    ///
    /// # Arguments
    ///
    /// * `levelLimit` - Return digests at levels below this value.
    ///
    /// # Returns
    ///
    /// * A set of digests down to `levelLimit`.
    pub fn digests(&self, level_limit: usize) -> HashSet<Digest> {
        let result = RefCell::new(HashSet::new());
        let visitor = |envelope: Self, level: usize, _: EdgeType, _: Option<&()>| -> _ {
            if level < level_limit {
                let mut result = result.borrow_mut();
                result.insert(envelope.digest().into_owned());
                result.insert(envelope.subject().digest().into_owned());
            }
            None
        };
        self.walk(false, &visitor);
        result.into_inner()
    }

    /// Returns the set of all digests in the envelope.
    pub fn deep_digests(&self) -> HashSet<Digest> {
        self.digests(usize::MAX)
    }

    /// Returns the set of all digests in the envelope, down to its second level.
    pub fn shallow_digests(&self) -> HashSet<Digest> {
        self.digests(2)
    }

    /// Produce a value that will necessarily be different if two envelopes differ
    /// structurally, even if they are semantically equivalent.
    ///
    /// Comparing the `digest` field of two envelopes (or calling `isEquivalent(to:)`) tests
    /// whether two envelopes are *semantically equivalent*. This is accomplished by
    /// simply comparing the top level digests of the envelopes for equality, and has a
    /// complexity of `O(1)`.
    ///
    /// This means that two envelopes are considered equivalent if they contain
    /// identical information in their completely unencrypted and unelided form.
    ///
    /// Some applications need to determine whether two envelopes are not only
    /// semantically equivalent, but also structurally identical. Two envelopes that are
    /// not semantically equivalent cannot be structurally identical, but two envelopes
    /// that *are* semantically equivalent *may or may not* be structurally identical.
    ///
    /// The `structural_digest` attribute is used to produce a value that will
    /// necessarily be different if two envelopes differ structurally, even if they are
    /// semantically equivalent. It has a complexity of `O(m + n)` where `m` and `n` are
    /// the number of elements in each of the two envelopes when they *are* semantically
    /// equivalent. It is recommended that envelopes be compared for structural equality
    /// by calling `isIdentical(to:)` as this short-circuits to `false` in cases where
    /// the compared envelopes are not semantically equivalent.
    pub fn structural_digest(&self) -> Digest {
        let image = RefCell::new(Vec::new());
        let visitor = |envelope: Self, _: usize, _: EdgeType, _: Option<&()>| -> _ {
            // Add a discriminator to the image for the obscured cases.
            match envelope.case() {
                EnvelopeCase::Elided(_) => image.borrow_mut().push(1),
                #[cfg(feature = "encrypt")]
                EnvelopeCase::Encrypted(_) => image.borrow_mut().push(0),
                #[cfg(feature = "compress")]
                EnvelopeCase::Compressed(_) => image.borrow_mut().push(2),
                _ => {}
            }
            image.borrow_mut().extend_from_slice(envelope.digest().data());
            None
        };
        self.walk(false, &visitor);
        Digest::from_image(image.into_inner())
    }

    /// Tests two envelopes for semantic equivalence.
    ///
    /// Calling `e1.is_equivalent_to(e2)` has a complexity of `O(1)` and simply compares
    /// the two envelope's digests. The means that two envelopes with certain structural
    /// differences (e.g., one envelope is partially elided and the other is not) will
    /// still test as equivalent.
    pub fn is_equivalent_to(&self, other: &Self) -> bool {
        self.digest() == other.digest()
    }

    /// Tests two envelopes for structural equality.
    ///
    /// Calling `e1.is_identical_to(e2)` has a complexity of `O(1)` if the envelopes are
    /// not semantically equivalent (that is, their top-level digests are different, and
    /// thus they *must* have different structures) and a complexity of `O(m + n)` where
    /// `m` and `n` are the number of elements in each of the two envelopes when they
    /// *are* semantically equivalent.
    pub fn is_identical_to(&self, other: &Self) -> bool {
        if !self.is_equivalent_to(other) {
            return true;
        }
        self.structural_digest() == other.structural_digest()
    }
}