cs_mwc_bch/wallet/
mnemonic.rs1use ring::digest::{digest, SHA256};
4use std::str;
5use util::{Bits, Error, Result};
6
7pub enum Wordlist {
9 ChineseSimplified,
10 ChineseTraditional,
11 English,
12 French,
13 Italian,
14 Japanese,
15 Korean,
16 Spanish,
17}
18
19pub fn load_wordlist(wordlist: Wordlist) -> Vec<String> {
21 match wordlist {
22 Wordlist::ChineseSimplified => {
23 load_wordlist_internal(include_bytes!("wordlists/chinese_simplified.txt"))
24 }
25 Wordlist::ChineseTraditional => {
26 load_wordlist_internal(include_bytes!("wordlists/chinese_traditional.txt"))
27 }
28 Wordlist::English => load_wordlist_internal(include_bytes!("wordlists/english.txt")),
29 Wordlist::French => load_wordlist_internal(include_bytes!("wordlists/french.txt")),
30 Wordlist::Italian => load_wordlist_internal(include_bytes!("wordlists/italian.txt")),
31 Wordlist::Japanese => load_wordlist_internal(include_bytes!("wordlists/japanese.txt")),
32 Wordlist::Korean => load_wordlist_internal(include_bytes!("wordlists/korean.txt")),
33 Wordlist::Spanish => load_wordlist_internal(include_bytes!("wordlists/spanish.txt")),
34 }
35}
36
37fn load_wordlist_internal(bytes: &[u8]) -> Vec<String> {
38 let text: String = str::from_utf8(bytes).unwrap().to_string();
39 text.lines().map(|s| s.to_string()).collect()
40}
41
42pub fn mnemonic_encode(data: &[u8], word_list: &[String]) -> Vec<String> {
44 let hash = digest(&SHA256, &data);
45 let mut words = Vec::with_capacity((data.len() * 8 + data.len() / 32 + 10) / 11);
46 let mut bits = Bits::from_slice(data, data.len() * 8);
47 bits.append(&Bits::from_slice(hash.as_ref(), data.len() / 4));
48 for i in 0..bits.len / 11 {
49 words.push(word_list[bits.extract(i * 11, 11) as usize].clone());
50 }
51 let rem = bits.len % 11;
52 if rem != 0 {
53 let n = bits.extract(bits.len / 11 * 11, rem) << (8 - rem);
54 words.push(word_list[n as usize].clone());
55 }
56 words
57}
58
59pub fn mnemonic_decode(mnemonic: &[String], word_list: &[String]) -> Result<Vec<u8>> {
61 let mut bits = Bits::with_capacity(mnemonic.len() * 11);
62 for word in mnemonic {
63 let value = match word_list.binary_search(word) {
64 Ok(value) => value,
65 Err(_) => return Err(Error::BadArgument(format!("Bad word: {}", word))),
66 };
67 let word_bits = Bits::from_slice(&[(value >> 3) as u8, ((value & 7) as u8) << 5], 11);
68 bits.append(&word_bits);
69 }
70 let data_len = bits.len * 32 / 33;
71 let cs_len = bits.len / 33;
72 let cs = digest(&SHA256, &bits.data[0..data_len / 8]);
73 let cs_bits = Bits::from_slice(cs.as_ref(), cs_len);
74 if cs_bits.extract(0, cs_len) != bits.extract(data_len, cs_len) {
75 return Err(Error::BadArgument("Invalid checksum".to_string()));
76 }
77 Ok(bits.data[0..data_len / 8].to_vec())
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use hex;
84
85 #[test]
86 fn wordlists() {
87 assert!(load_wordlist(Wordlist::ChineseSimplified).len() == 2048);
88 assert!(load_wordlist(Wordlist::ChineseTraditional).len() == 2048);
89 assert!(load_wordlist(Wordlist::English).len() == 2048);
90 assert!(load_wordlist(Wordlist::French).len() == 2048);
91 assert!(load_wordlist(Wordlist::Italian).len() == 2048);
92 assert!(load_wordlist(Wordlist::Japanese).len() == 2048);
93 assert!(load_wordlist(Wordlist::Korean).len() == 2048);
94 assert!(load_wordlist(Wordlist::Spanish).len() == 2048);
95 }
96
97 #[test]
98 fn encode_decode() {
99 let mut data = Vec::new();
100 for i in 0..16 {
101 data.push(i);
102 }
103 let wordlist = load_wordlist(Wordlist::English);
104 assert!(mnemonic_decode(&mnemonic_encode(&data, &wordlist), &wordlist).unwrap() == data);
105 }
106
107 #[test]
108 fn invalid() {
109 let wordlist = load_wordlist(Wordlist::English);
110 assert!(mnemonic_encode(&[], &wordlist).len() == 0);
111 assert!(mnemonic_decode(&[], &wordlist).unwrap().len() == 0);
112
113 let mut data = Vec::new();
114 for i in 0..16 {
115 data.push(i);
116 }
117 let mnemonic = mnemonic_encode(&data, &wordlist);
118
119 let mut bad_checksum = mnemonic.clone();
120 bad_checksum[0] = "hello".to_string();
121 assert!(mnemonic_decode(&bad_checksum, &wordlist).is_err());
122
123 let mut bad_word = mnemonic.clone();
124 bad_word[0] = "123".to_string();
125 assert!(mnemonic_decode(&bad_word, &wordlist).is_err());
126 }
127
128 #[test]
129 fn test_vectors() {
130 let wordlist = load_wordlist(Wordlist::English);
131
132 let h = hex::decode("00000000000000000000000000000000").unwrap();
133 let n = mnemonic_encode(&h, &wordlist).join(" ");
134 assert!(n == "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about");
135
136 let h = hex::decode("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f").unwrap();
137 let n = mnemonic_encode(&h, &wordlist).join(" ");
138 assert!(n == "legal winner thank year wave sausage worth useful legal winner thank yellow");
139
140 let h = hex::decode("80808080808080808080808080808080").unwrap();
141 let n = mnemonic_encode(&h, &wordlist).join(" ");
142 assert!(
143 n == "letter advice cage absurd amount doctor acoustic avoid letter advice cage above"
144 );
145
146 let h = hex::decode("ffffffffffffffffffffffffffffffff").unwrap();
147 let n = mnemonic_encode(&h, &wordlist).join(" ");
148 assert!(n == "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong");
149
150 let h = hex::decode("000000000000000000000000000000000000000000000000").unwrap();
151 let n = mnemonic_encode(&h, &wordlist).join(" ");
152 assert!(n == "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent");
153
154 let h = hex::decode("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f").unwrap();
155 let n = mnemonic_encode(&h, &wordlist).join(" ");
156 assert!(n == "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal will");
157
158 let h = hex::decode("808080808080808080808080808080808080808080808080").unwrap();
159 let n = mnemonic_encode(&h, &wordlist).join(" ");
160 assert!(n == "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter always");
161
162 let h = hex::decode("ffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
163 let n = mnemonic_encode(&h, &wordlist).join(" ");
164 assert!(n == "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo when");
165
166 let h = hex::decode("0000000000000000000000000000000000000000000000000000000000000000")
167 .unwrap();
168 let n = mnemonic_encode(&h, &wordlist).join(" ");
169 assert!(n == "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art");
170
171 let h = hex::decode("7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f")
172 .unwrap();
173 let n = mnemonic_encode(&h, &wordlist).join(" ");
174 assert!(n == "legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth useful legal winner thank year wave sausage worth title");
175
176 let h = hex::decode("8080808080808080808080808080808080808080808080808080808080808080")
177 .unwrap();
178 let n = mnemonic_encode(&h, &wordlist).join(" ");
179 assert!(n == "letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic avoid letter advice cage absurd amount doctor acoustic bless");
180
181 let h = hex::decode("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")
182 .unwrap();
183 let n = mnemonic_encode(&h, &wordlist).join(" ");
184 assert!(n == "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote");
185
186 let h = hex::decode("9e885d952ad362caeb4efe34a8e91bd2").unwrap();
187 let n = mnemonic_encode(&h, &wordlist).join(" ");
188 assert!(
189 n == "ozone drill grab fiber curtain grace pudding thank cruise elder eight picnic"
190 );
191
192 let h = hex::decode("6610b25967cdcca9d59875f5cb50b0ea75433311869e930b").unwrap();
193 let n = mnemonic_encode(&h, &wordlist).join(" ");
194 assert!(n == "gravity machine north sort system female filter attitude volume fold club stay feature office ecology stable narrow fog");
195
196 let h = hex::decode("68a79eaca2324873eacc50cb9c6eca8cc68ea5d936f98787c60c7ebc74e6ce7c")
197 .unwrap();
198 let n = mnemonic_encode(&h, &wordlist).join(" ");
199 assert!(n == "hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length");
200
201 let h = hex::decode("c0ba5a8e914111210f2bd131f3d5e08d").unwrap();
202 let n = mnemonic_encode(&h, &wordlist).join(" ");
203 assert!(n == "scheme spot photo card baby mountain device kick cradle pact join borrow");
204
205 let h = hex::decode("6d9be1ee6ebd27a258115aad99b7317b9c8d28b6d76431c3").unwrap();
206 let n = mnemonic_encode(&h, &wordlist).join(" ");
207 assert!(n == "horn tenant knee talent sponsor spell gate clip pulse soap slush warm silver nephew swap uncle crack brave");
208
209 let h = hex::decode("9f6a2878b2520799a44ef18bc7df394e7061a224d2c33cd015b157d746869863")
210 .unwrap();
211 let n = mnemonic_encode(&h, &wordlist).join(" ");
212 assert!(n == "panda eyebrow bullet gorilla call smoke muffin taste mesh discover soft ostrich alcohol speed nation flash devote level hobby quick inner drive ghost inside");
213
214 let h = hex::decode("23db8160a31d3e0dca3688ed941adbf3").unwrap();
215 let n = mnemonic_encode(&h, &wordlist).join(" ");
216 assert!(n == "cat swing flag economy stadium alone churn speed unique patch report train");
217
218 let h = hex::decode("8197a4a47f0425faeaa69deebc05ca29c0a5b5cc76ceacc0").unwrap();
219 let n = mnemonic_encode(&h, &wordlist).join(" ");
220 assert!(n == "light rule cinnamon wrap drastic word pride squirrel upgrade then income fatal apart sustain crack supply proud access");
221
222 let h = hex::decode("066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad")
223 .unwrap();
224 let n = mnemonic_encode(&h, &wordlist).join(" ");
225 assert!(n == "all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform");
226
227 let h = hex::decode("f30f8c1da665478f49b001d94c5fc452").unwrap();
228 let n = mnemonic_encode(&h, &wordlist).join(" ");
229 assert!(
230 n == "vessel ladder alter error federal sibling chat ability sun glass valve picture"
231 );
232
233 let h = hex::decode("c10ec20dc3cd9f652c7fac2f1230f7a3c828389a14392f05").unwrap();
234 let n = mnemonic_encode(&h, &wordlist).join(" ");
235 assert!(n == "scissors invite lock maple supreme raw rapid void congress muscle digital elegant little brisk hair mango congress clump");
236
237 let h = hex::decode("f585c11aec520db57dd353c69554b21a89b20fb0650966fa0a9d6f74fd989d8f")
238 .unwrap();
239 let n = mnemonic_encode(&h, &wordlist).join(" ");
240 assert!(n == "void come effort suffer camp survey warrior heavy shoot primary clutch crush open amazing screen patrol group space point ten exist slush involve unfold");
241 }
242}