use crate::error::{Error, Result};
use core::ptr;
use nwep_sys as sys;
pub fn split(secret: &[u8], t: usize, n: usize) -> Result<Vec<Vec<u8>>> {
if !(2..=255).contains(&n) || !(2..=n).contains(&t) {
return Err(Error::ConfigInvalid);
}
let mut total = 0usize;
Error::check(unsafe {
sys::nwep_shamir_split(
secret.as_ptr(),
secret.len(),
t,
n,
ptr::null_mut(),
&mut total,
)
})?;
let mut packed = vec![0u8; total];
Error::check(unsafe {
sys::nwep_shamir_split(
secret.as_ptr(),
secret.len(),
t,
n,
packed.as_mut_ptr(),
&mut total,
)
})?;
packed.truncate(total);
let share_len = 1 + secret.len();
Ok(packed.chunks(share_len).map(|c| c.to_vec()).collect())
}
pub fn combine(shares: &[Vec<u8>]) -> Result<Vec<u8>> {
if shares.len() < 2 {
return Err(Error::ConfigInvalid);
}
let share_len = shares[0].len();
if share_len < 2 || shares.iter().any(|s| s.len() != share_len) {
return Err(Error::ConfigInvalid);
}
let mut packed = Vec::with_capacity(shares.len() * share_len);
for s in shares {
packed.extend_from_slice(s);
}
let mut out = vec![0u8; share_len - 1];
let mut out_len = out.len();
Error::check(unsafe {
sys::nwep_shamir_combine(
packed.as_ptr(),
shares.len(),
share_len,
out.as_mut_ptr(),
&mut out_len,
)
})?;
out.truncate(out_len);
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn split_then_combine_round_trips() {
let secret = b"web/1 offline recovery key bytes";
let shares = split(secret, 3, 5).unwrap();
assert_eq!(shares.len(), 5);
assert!(shares.iter().all(|s| s.len() == secret.len() + 1));
let subset = vec![shares[0].clone(), shares[2].clone(), shares[4].clone()];
assert_eq!(combine(&subset).unwrap(), secret);
}
#[test]
fn bad_bounds_are_rejected() {
assert!(split(b"x", 1, 5).is_err()); assert!(split(b"x", 6, 5).is_err()); assert!(combine(&[vec![1u8, 2]]).is_err()); }
}