use bitcoin::{
bip32::{self, Xpriv},
NetworkKind,
};
use thiserror::Error;
pub(crate) const INDICES_MASK: [u8; 7] = [
0b0100_0000,
0b0010_0000,
0b0001_0000,
0b0000_1000,
0b0000_0100,
0b0000_0010,
0b0000_0001,
];
pub(crate) const INDICES_ALL: u8 = 0b0111_1111;
pub(crate) const VERSION_MASK: u8 = 0b1000_0000;
pub trait Diagram {
type Item;
fn new() -> Self;
fn is_empty(&self) -> bool;
fn from_items(items: Vec<Self::Item>, indices: &[(usize, usize)]) -> DiagramResult<Self>
where
Self: Sized;
fn get(&self, row: usize, col: usize) -> Option<&Self::Item>;
fn set(&mut self, value: Self::Item, row: usize, col: usize) -> DiagramResult<()>;
fn from_secret(secret: Vec<u8>) -> DiagramResult<Self>
where
Self: Sized;
fn to_secret(&self) -> DiagramResult<Vec<u8>>;
fn indices(&self) -> impl Iterator<Item = (usize, usize)> {
(0..7).flat_map(move |row| {
(0..7).filter_map(move |col| self.get(row, col).map_or(None, |_| Some((row, col))))
})
}
fn to_entropy(&self, salt: &[u8]) -> DiagramResult<[u8; 32]> {
let secret = self.to_secret()?;
let mut s1 = {
let secret = [secret.as_slice(), &[1u8]].concat();
let salt = [salt, &[1u8]].concat();
let mut output: [u8; 32] = [0; 32];
let param = crypto::scrypt::ScryptParams::new(18, 8, 1);
crypto::scrypt::scrypt(&secret, &salt, ¶m, &mut output);
output
};
let s2 = {
let secret = [secret.as_slice(), &[2u8]].concat();
let salt = [salt, &[2u8]].concat();
let mut output: [u8; 32] = [0; 32];
let mut mac = crypto::hmac::Hmac::new(crypto::sha2::Sha256::new(), &secret);
crypto::pbkdf2::pbkdf2(&mut mac, &salt, 65536, &mut output);
output
};
s1.iter_mut().zip(s2.iter()).for_each(|(a, b)| *a = *a ^ b);
Ok(s1)
}
fn to_master(&self, salt: &[u8]) -> DiagramResult<Xpriv> {
let seed = self.to_entropy(salt)?;
Ok(Xpriv::new_master(NetworkKind::Main, &seed)?)
}
}
#[derive(Error, Debug, PartialEq)]
pub enum DiagramError {
#[error("out of bounds")]
Overflows(&'static str),
#[error("invalid parameter: {0}")]
InvalidParameter(&'static str),
#[error("invalid diagram version")]
InvalidVersion,
#[error("diagram is empty")]
EmptyDiagram,
#[error("bip32 error")]
Bip32Error(#[from] bip32::Error),
}
pub(crate) type DiagramResult<T = ()> = Result<T, DiagramError>;