use std::collections::HashMap;
pub use bc_components::{SSKRGroupSpec, SSKRSecret, SSKRShare, SSKRSpec};
use bc_components::{SymmetricKey, sskr_combine, sskr_generate_using};
use bc_rand::RandomNumberGenerator;
#[cfg(feature = "known_value")]
use known_values;
use crate::{Envelope, Error, Result};
impl Envelope {
fn add_sskr_share(&self, share: &SSKRShare) -> Self {
self.add_assertion(known_values::SSKR_SHARE, share.clone())
}
pub fn sskr_split(
&self,
spec: &SSKRSpec,
content_key: &SymmetricKey,
) -> Result<Vec<Vec<Envelope>>> {
let mut rng = bc_rand::SecureRandomNumberGenerator;
self.sskr_split_using(spec, content_key, &mut rng)
}
pub fn sskr_split_flattened(
&self,
spec: &SSKRSpec,
content_key: &SymmetricKey,
) -> Result<Vec<Envelope>> {
Ok(self
.sskr_split(spec, content_key)?
.into_iter()
.flatten()
.collect())
}
#[doc(hidden)]
pub fn sskr_split_using(
&self,
spec: &SSKRSpec,
content_key: &SymmetricKey,
test_rng: &mut impl RandomNumberGenerator,
) -> Result<Vec<Vec<Envelope>>> {
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.add_sskr_share(&share);
group_result.push(share_result);
}
result.push(group_result);
}
Ok(result)
}
fn sskr_shares_in(
envelopes: &[&Envelope],
) -> Result<HashMap<u16, Vec<SSKRShare>>> {
let mut result: HashMap<u16, Vec<SSKRShare>> = HashMap::new();
for envelope in envelopes {
for assertion in
envelope.assertions_with_predicate(known_values::SSKR_SHARE)
{
let share = assertion
.as_object()
.unwrap()
.extract_subject::<SSKRShare>()?;
let identifier = share.identifier();
result
.entry(identifier)
.and_modify(|shares| shares.push(share.clone()))
.or_insert(vec![share]);
}
}
Ok(result)
}
pub fn sskr_join(envelopes: &[&Envelope]) -> Result<Envelope> {
if envelopes.is_empty() {
return Err(Error::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)
&& let Ok(content_key) = SymmetricKey::from_data_ref(&secret)
&& let Ok(envelope) =
envelopes.first().unwrap().decrypt_subject(&content_key)
{
return Ok(envelope.subject());
}
}
Err(Error::InvalidShares)
}
}
#[cfg(all(test, feature = "sskr", feature = "types", feature = "known_value"))]
mod tests {
use bc_components::{SSKRGroupSpec, SSKRSpec, SymmetricKey};
use crate::prelude::*;
#[test]
fn test_sskr_split_and_join() {
let original = Envelope::new("Secret message")
.add_assertion("metadata", "This is a test");
let content_key = SymmetricKey::new();
let wrapped_original = original.wrap();
let encrypted = wrapped_original.encrypt_subject(&content_key).unwrap();
let group = SSKRGroupSpec::new(2, 3).unwrap();
let spec = SSKRSpec::new(1, vec![group]).unwrap();
let shares = encrypted.sskr_split(&spec, &content_key).unwrap();
assert_eq!(shares[0].len(), 3);
let share1 = &shares[0][0];
let share2 = &shares[0][1];
let recovered_wrapped = Envelope::sskr_join(&[share1, share2]).unwrap();
let recovered = recovered_wrapped.try_unwrap().unwrap();
assert!(recovered.is_identical_to(&original));
}
}