use std::{
fmt,
ops::{BitXor, BitXorAssign},
str::FromStr,
};
pub trait SeedXor {
fn xor(&self, rhs: &Self) -> Self;
}
impl SeedXor for bip39::Mnemonic {
fn xor(&self, rhs: &Self) -> Self {
let mut entropy = self.to_entropy();
let xor_values = rhs.to_entropy();
entropy
.iter_mut()
.zip(xor_values.iter())
.for_each(|(a, b)| *a ^= b);
if entropy.len() < xor_values.len() {
entropy.extend(xor_values.iter().skip(entropy.len()))
}
bip39::Mnemonic::from_entropy(&entropy).unwrap()
}
}
#[derive(Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
pub struct Mnemonic {
pub inner: bip39::Mnemonic,
}
impl Mnemonic {
fn new(inner: bip39::Mnemonic) -> Self {
Mnemonic { inner }
}
pub fn from_entropy(entropy: &[u8]) -> Result<Self, bip39::Error> {
match bip39::Mnemonic::from_entropy(entropy) {
Ok(inner) => Ok(Mnemonic::new(inner)),
Err(err) => Err(err),
}
}
fn xor(&self, rhs: &Self) -> Self {
Mnemonic::new(self.inner.xor(&rhs.inner))
}
}
impl FromStr for Mnemonic {
type Err = bip39::Error;
fn from_str(mnemonic: &str) -> Result<Self, <Self as FromStr>::Err> {
match bip39::Mnemonic::from_str(mnemonic) {
Ok(inner) => Ok(Mnemonic::new(inner)),
Err(err) => Err(err),
}
}
}
impl fmt::Display for Mnemonic {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, word) in self.inner.word_iter().enumerate() {
if i > 0 {
f.write_str(" ")?;
}
f.write_str(word)?;
}
Ok(())
}
}
impl BitXor for Mnemonic {
type Output = Self;
fn bitxor(self, rhs: Self) -> Self::Output {
self.xor(&rhs)
}
}
impl BitXorAssign for Mnemonic {
fn bitxor_assign(&mut self, rhs: Self) {
*self = self.xor(&rhs)
}
}
#[cfg(test)]
mod tests {
use crate::Mnemonic;
use std::str::FromStr;
#[test]
fn seed_xor_works() {
let a_str = "romance wink lottery autumn shop bring dawn tongue range crater truth ability miss spice fitness easy legal release recall obey exchange recycle dragon room";
let b_str = "lion misery divide hurry latin fluid camp advance illegal lab pyramid unaware eager fringe sick camera series noodle toy crowd jeans select depth lounge";
let c_str = "vault nominee cradle silk own frown throw leg cactus recall talent worry gadget surface shy planet purpose coffee drip few seven term squeeze educate";
let result_str = "silent toe meat possible chair blossom wait occur this worth option bag nurse find fish scene bench asthma bike wage world quit primary indoor";
let a = Mnemonic::from_str(a_str).unwrap();
let b = Mnemonic::from_str(b_str).unwrap();
let c = Mnemonic::from_str(c_str).unwrap();
let result = Mnemonic::from_str(result_str).unwrap();
assert_eq!(result, a.clone() ^ b.clone() ^ c.clone());
assert_eq!(result, b ^ c ^ a); }
#[test]
fn seed_xor_assignment_works() {
let a_str = "romance wink lottery autumn shop bring dawn tongue range crater truth ability miss spice fitness easy legal release recall obey exchange recycle dragon room";
let b_str = "lion misery divide hurry latin fluid camp advance illegal lab pyramid unaware eager fringe sick camera series noodle toy crowd jeans select depth lounge";
let c_str = "vault nominee cradle silk own frown throw leg cactus recall talent worry gadget surface shy planet purpose coffee drip few seven term squeeze educate";
let result_str = "silent toe meat possible chair blossom wait occur this worth option bag nurse find fish scene bench asthma bike wage world quit primary indoor";
let a = Mnemonic::from_str(a_str).unwrap();
let b = Mnemonic::from_str(b_str).unwrap();
let c = Mnemonic::from_str(c_str).unwrap();
let result = Mnemonic::from_str(result_str).unwrap();
let mut assigned = a.xor(&b); assigned ^= c;
assert_eq!(result, assigned);
}
#[test]
fn seed_xor_with_different_lengths_works() {
let str_24 = "romance wink lottery autumn shop bring dawn tongue range crater truth ability miss spice fitness easy legal release recall obey exchange recycle dragon room";
let str_16 = "lion misery divide hurry latin fluid camp advance illegal lab pyramid unaware eager fringe sick camera series number";
let str_12 = "vault nominee cradle silk own frown throw leg cactus recall talent wisdom";
let result_str = "silent toe meat possible chair blossom wait occur this worth option aware since milk mother grace rocket cement recall obey exchange recycle dragon rocket";
let w_24 = Mnemonic::from_str(str_24).unwrap();
let w_16 = Mnemonic::from_str(str_16).unwrap();
let w_12 = Mnemonic::from_str(str_12).unwrap();
let result = Mnemonic::from_str(result_str).unwrap();
assert_eq!(result, w_24.clone() ^ w_16.clone() ^ w_12.clone());
assert_eq!(result, w_12 ^ w_24 ^ w_16); }
}