1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
use std::{
fmt,
ops::{BitXor, BitXorAssign},
str::FromStr,
};
use bip39::Mnemonic as Inner;
#[derive(Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
pub struct Mnemonic {
inner: Inner,
}
impl Mnemonic {
fn new(inner: Inner) -> Self {
Mnemonic { inner }
}
pub fn inner(&self) -> &Inner {
&self.inner
}
pub fn from_entropy(entropy: &[u8]) -> Result<Self, bip39::Error> {
match Inner::from_entropy(entropy) {
Ok(inner) => Ok(Mnemonic::new(inner)),
Err(err) => Err(err),
}
}
fn xor(&self, rhs: &Self) -> Self {
let mut entropy = self.inner.to_entropy();
let xor_values = rhs.inner.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()))
}
Mnemonic::from_entropy(&entropy).unwrap()
}
}
impl FromStr for Mnemonic {
type Err = bip39::Error;
fn from_str(mnemonic: &str) -> Result<Self, <Self as FromStr>::Err> {
match Inner::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);
}
}