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
use std::collections::HashMap;

use anyhow::bail;
pub use bc_components::{SSKRShare, SSKRSpec, SSKRGroupSpec, SSKRSecret, SSKRError};
use bc_components::{sskr_generate_using, sskr_combine, SymmetricKey};
use bc_rand::RandomNumberGenerator;

use crate::{Envelope, EnvelopeError, EnvelopeEncodable, impl_envelope_encodable};
#[cfg(feature = "known_value")]
use crate::extension::known_values;

/// Support for splitting and combining envelopes using SSKR (Shamir's Secret Sharing).
impl Envelope {
    /// Returns a new ``Envelope`` with a `sskrShare: SSKRShare` assertion added.
    fn add_sskr_share(&self, share: &SSKRShare) -> Self {
        self.add_assertion(known_values::SSKR_SHARE, share)
    }

    /// Splits the envelope into a set of SSKR shares.
    ///
    /// The envelope subject should already be encrypted by a specific `SymmetricKey`
    /// known as the `contentKey`.
    ///
    /// Each returned envelope will have an `sskrShare: SSKRShare` assertion added to
    /// it.
    ///
    /// - Parameters:
    ///   - spec: The SSKR split specification.
    ///   - contentKey: The `SymmetricKey` used to encrypt the envelope's subject.
    ///
    /// - Returns: An array of arrays. Each element of the outer array represents an
    /// SSKR group, and the elements of each inner array are the envelope with a unique
    /// `sskrShare: SSKRShare` assertion added to each.
    pub fn sskr_split(&self, spec: &SSKRSpec, content_key: &SymmetricKey) -> Result<Vec<Vec<Envelope>>, SSKRError> {
        let mut rng = bc_rand::SecureRandomNumberGenerator;
        self.sskr_split_using(spec, content_key, &mut rng)
    }

    #[doc(hidden)]
    /// Splits the envelope into a set of SSKR shares.
    ///
    /// The envelope subject should already be encrypted by a specific `SymmetricKey`
    /// known as the `contentKey`.
    ///
    /// Each returned envelope will have an `sskrShare: SSKRShare` assertion added to
    /// it.
    ///
    /// - Parameters:
    ///   - spec: The SSKR split specification.
    ///   - contentKey: The `SymmetricKey` used to encrypt the envelope's subject.
    ///
    /// - Returns: An array of arrays. Each element of the outer array represents an
    /// SSKR group, and the elements of each inner array are the envelope with a unique
    /// `sskrShare: SSKRShare` assertion added to each.
    pub fn sskr_split_using(&self, spec: &SSKRSpec, content_key: &SymmetricKey, test_rng: &mut impl RandomNumberGenerator) -> Result<Vec<Vec<Envelope>>, SSKRError> {
        let master_secret = SSKRSecret::new(content_key.data())?;
        let shares = sskr_generate_using(spec, &master_secret, test_rng)?;
        let mut result: Vec<Vec<Envelope>> = Vec::new();
        for group in shares {
            let mut group_result: Vec<Envelope> = Vec::new();
            for share in group {
                let share_result = self.clone().add_sskr_share(&share);
                group_result.push(share_result);
            }
            result.push(group_result);
        }
        Ok(result)
    }

    fn sskr_shares_in(envelopes: &[Envelope]) -> anyhow::Result<HashMap<u16, Vec<SSKRShare>>> {
        let mut result: HashMap<u16, Vec<SSKRShare>> = HashMap::new();
        for envelope in envelopes {
            for assertion in envelope.clone().assertions_with_predicate(known_values::SSKR_SHARE) {
                let share = assertion.object().unwrap().extract_subject::<SSKRShare>()?;
                let identifier = share.clone().identifier();
                if result.get(&identifier).is_none() {
                    result.insert(identifier, Vec::new());
                }
                result.get_mut(&identifier).unwrap().push(share);
            }
        }
        Ok(result)
    }

    /// Creates a new envelope resulting from the joining a set of envelopes split by SSKR.
    ///
    /// Given a set of envelopes that are ostensibly all part of the same SSKR split,
    /// this method attempts to reconstruct the original envelope subject. It will try
    /// all present `sskrShare: SSKRShare` assertions, grouped by split ID, to achieve a
    /// threshold of shares. If it can do so successfully the initializer succeeds.
    ///
    /// - Parameter envelopes: The envelopes to be joined.
    ///
    /// - Throws: Throws an exception if no quorum of shares can be found to reconstruct
    /// the original envelope.
    pub fn sskr_join(envelopes: &[Envelope]) -> anyhow::Result<Envelope> {
        if envelopes.is_empty() {
            bail!(EnvelopeError::InvalidShares);
        }

        let grouped_shares: Vec<_> = Self::sskr_shares_in(envelopes)?.values().cloned().collect();
        for shares in grouped_shares {
            if let Ok(secret) = sskr_combine(&shares) {
                if let Ok(content_key) = SymmetricKey::from_data_ref(&secret) {
                    if let Ok(envelope) = envelopes.first().unwrap().clone().decrypt_subject(&content_key) {
                        return Ok(envelope.subject());
                    }
                }
            }
        }
        bail!(EnvelopeError::InvalidShares)
    }
}

impl_envelope_encodable!(SSKRShare);