use crate::bytewords::*;
use crate::sskr_shares::*;
use anyhow::{anyhow, bail, Error};
use bip39::{Language, Mnemonic};
use dcbor::CBOR;
use sskr::sskr_combine;
use std::collections::HashMap;
pub fn recover(lines: Vec<String>) -> Result<Mnemonic, Error> {
let mut shares: Vec<Vec<u8>> = vec![];
for line in lines {
let bytes = byteword_string_to_bytes(line)?;
let cbor = CBOR::from_data(bytes.as_slice())?;
let cbor_bytes = cbor.expect_tagged_value(309)?;
let share = cbor_bytes.expect_byte_string()?;
shares.push(share.to_vec());
}
let mut share_ids: Vec<u16> = vec![];
let mut share_meta: Vec<[usize; 5]> = vec![];
for share in shares.clone() {
let (id, meta) = share_metadata(&share)?;
share_ids.push(id);
share_meta.push(meta);
}
let identifier = share_ids[0];
if share_ids.iter().any(|id| id != &identifier) {
bail!("Mismatched identifiers, shares don't go together");
}
let group_threshold = share_meta[0][1];
let group_count = share_meta[0][2];
if share_meta
.iter()
.any(|meta| meta[1] != group_threshold || meta[2] != group_count)
{
bail!("Mismatched group threshold or count, shares don't go together");
}
let mut shares_by_group: HashMap<usize, Vec<(usize, Vec<u8>)>> = HashMap::new();
for (i, share) in shares.clone().iter().enumerate() {
let share_group_num = share_meta[i][0];
if !shares_by_group.contains_key(&share_group_num) {
shares_by_group.insert(share_group_num, vec![]);
}
shares_by_group
.get_mut(&share_group_num)
.unwrap()
.push((i, share.to_vec()));
}
let mut recoverable_groups: Vec<usize> = vec![];
for (group_num, shares) in &shares_by_group {
let member_threshold = share_meta[shares[0].0][4];
if shares
.iter()
.any(|(i, _share)| share_meta[*i][4] != member_threshold)
{
bail!(
"Mismatched share member thresholds in group {}, shares don't go together",
group_num + 1
);
}
if shares.len() >= member_threshold {
recoverable_groups.push(*group_num);
}
}
if recoverable_groups.len() < group_threshold {
bail!(
"Not enough groups, need to satisfy at least {} but only {} are satisfied ({})",
group_threshold,
recoverable_groups.len(),
recoverable_groups
.iter()
.map(|g| (g + 1).to_string())
.collect::<Vec<String>>()
.join(" and ")
)
}
let mut shares_for_recovery: Vec<Vec<u8>> = vec![];
for group_num in recoverable_groups.iter().take(group_threshold) {
let group_shares = shares_by_group[&group_num]
.iter()
.map(|(_i, share)| share.to_vec())
.collect::<Vec<Vec<u8>>>();
for share in group_shares {
shares_for_recovery.push(share);
}
}
let secret = sskr_combine(&shares_for_recovery)
.map_err(|e| anyhow!("Error during SSKR combination: {}", e))?;
Mnemonic::from_entropy(secret.data(), Language::English).map_err(|e| {
anyhow!(
"Recovered entropy 0x{} but unable to make mnemonic: {}",
hex::encode(secret.data()),
e
)
})
}