dup_crypto/mnemonic/
language.rs

1//  Copyright (C) 2019 Eloïs SANCHEZ.
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU Affero General Public License as
5// published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11// GNU Affero General Public License for more details.
12//
13// You should have received a copy of the GNU Affero General Public License
14// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
16use super::error::MnemonicError;
17use super::utils::{Bits, Bits11};
18use std::{collections::HashMap, str::FromStr};
19
20pub struct WordMap {
21    inner: HashMap<&'static str, Bits11>,
22}
23
24pub struct WordList {
25    inner: Vec<&'static str>,
26}
27
28impl WordMap {
29    pub fn get_bits(&self, word: &str) -> Result<Bits11, MnemonicError> {
30        match self.inner.get(word) {
31            Some(n) => Ok(*n),
32            None => Err(MnemonicError::InvalidWord),
33        }
34    }
35}
36
37impl WordList {
38    pub fn get_word(&self, bits: Bits11) -> &'static str {
39        self.inner[bits.bits() as usize]
40    }
41}
42
43mod lazy {
44    use super::{Bits11, WordList, WordMap};
45    use once_cell::sync::Lazy;
46
47    /// lazy generation of the word list
48    fn gen_wordlist(lang_words: &'static str) -> WordList {
49        let inner: Vec<_> = lang_words.split_whitespace().collect();
50
51        debug_assert!(inner.len() == 2048, "Invalid wordlist length");
52
53        WordList { inner }
54    }
55
56    /// lazy generation of the word map
57    fn gen_wordmap(wordlist: &WordList) -> WordMap {
58        let inner = wordlist
59            .inner
60            .iter()
61            .enumerate()
62            .map(|(i, item)| (*item, Bits11::from(i as u16)))
63            .collect();
64
65        WordMap { inner }
66    }
67
68    pub static WORDLIST_ENGLISH: Lazy<WordList> =
69        Lazy::new(|| gen_wordlist(include_str!("langs/english.txt")));
70    #[cfg(feature = "mnemonic_chinese_simplified")]
71    pub static WORDLIST_CHINESE_SIMPLIFIED: Lazy<WordList> =
72        Lazy::new(|| gen_wordlist(include_str!("langs/chinese_simplified.txt")));
73    #[cfg(feature = "mnemonic_chinese_traditional")]
74    pub static WORDLIST_CHINESE_TRADITIONAL: Lazy<WordList> =
75        Lazy::new(|| gen_wordlist(include_str!("langs/chinese_traditional.txt")));
76    #[cfg(feature = "mnemonic_french")]
77    pub static WORDLIST_FRENCH: Lazy<WordList> =
78        Lazy::new(|| gen_wordlist(include_str!("langs/french.txt")));
79    #[cfg(feature = "mnemonic_italian")]
80    pub static WORDLIST_ITALIAN: Lazy<WordList> =
81        Lazy::new(|| gen_wordlist(include_str!("langs/italian.txt")));
82    #[cfg(feature = "mnemonic_japanese")]
83    pub static WORDLIST_JAPANESE: Lazy<WordList> =
84        Lazy::new(|| gen_wordlist(include_str!("langs/japanese.txt")));
85    #[cfg(feature = "mnemonic_korean")]
86    pub static WORDLIST_KOREAN: Lazy<WordList> =
87        Lazy::new(|| gen_wordlist(include_str!("langs/korean.txt")));
88    #[cfg(feature = "mnemonic_spanish")]
89    pub static WORDLIST_SPANISH: Lazy<WordList> =
90        Lazy::new(|| gen_wordlist(include_str!("langs/spanish.txt")));
91
92    pub static WORDMAP_ENGLISH: Lazy<WordMap> = Lazy::new(|| gen_wordmap(&WORDLIST_ENGLISH));
93    #[cfg(feature = "mnemonic_chinese_simplified")]
94    pub static WORDMAP_CHINESE_SIMPLIFIED: Lazy<WordMap> =
95        Lazy::new(|| gen_wordmap(&WORDLIST_CHINESE_SIMPLIFIED));
96    #[cfg(feature = "mnemonic_chinese_traditional")]
97    pub static WORDMAP_CHINESE_TRADITIONAL: Lazy<WordMap> =
98        Lazy::new(|| gen_wordmap(&WORDLIST_CHINESE_TRADITIONAL));
99    #[cfg(feature = "mnemonic_french")]
100    pub static WORDMAP_FRENCH: Lazy<WordMap> = Lazy::new(|| gen_wordmap(&WORDLIST_FRENCH));
101    #[cfg(feature = "mnemonic_italian")]
102    pub static WORDMAP_ITALIAN: Lazy<WordMap> = Lazy::new(|| gen_wordmap(&WORDLIST_ITALIAN));
103    #[cfg(feature = "mnemonic_japanese")]
104    pub static WORDMAP_JAPANESE: Lazy<WordMap> = Lazy::new(|| gen_wordmap(&WORDLIST_JAPANESE));
105    #[cfg(feature = "mnemonic_korean")]
106    pub static WORDMAP_KOREAN: Lazy<WordMap> = Lazy::new(|| gen_wordmap(&WORDLIST_KOREAN));
107    #[cfg(feature = "mnemonic_spanish")]
108    pub static WORDMAP_SPANISH: Lazy<WordMap> = Lazy::new(|| gen_wordmap(&WORDLIST_SPANISH));
109}
110
111/// The language determines which words will be used in a mnemonic phrase, but also indirectly
112/// determines the binary value of each word when a [`Mnemonic`][Mnemonic] is turned into a [`Seed`][Seed].
113///
114/// These are not of much use right now, and may even be removed from the crate, as there is no
115/// official language specified by the standard except English.
116///
117/// [Mnemonic]: ./mnemonic/struct.Mnemonic.html
118/// [Seed]: ./seed/struct.Seed.html
119#[derive(Clone, Copy, Debug, PartialEq)]
120pub enum Language {
121    /// English
122    English,
123    #[cfg(feature = "mnemonic_chinese_simplified")]
124    /// Chinese simplified
125    ChineseSimplified,
126    #[cfg(feature = "mnemonic_chinese_traditional")]
127    /// Chinese traditional
128    ChineseTraditional,
129    #[cfg(feature = "mnemonic_french")]
130    /// French
131    French,
132    #[cfg(feature = "mnemonic_italian")]
133    /// Italian
134    Italian,
135    #[cfg(feature = "mnemonic_japanese")]
136    /// Japanese
137    Japanese,
138    #[cfg(feature = "mnemonic_korean")]
139    /// Korean
140    Korean,
141    #[cfg(feature = "mnemonic_spanish")]
142    /// Spanish
143    Spanish,
144}
145
146impl Language {
147    /// Get the word list for this language
148    pub fn wordlist(self) -> &'static WordList {
149        match self {
150            Language::English => &lazy::WORDLIST_ENGLISH,
151            #[cfg(feature = "mnemonic_chinese_simplified")]
152            Language::ChineseSimplified => &lazy::WORDLIST_CHINESE_SIMPLIFIED,
153            #[cfg(feature = "mnemonic_chinese_traditional")]
154            Language::ChineseTraditional => &lazy::WORDLIST_CHINESE_TRADITIONAL,
155            #[cfg(feature = "mnemonic_french")]
156            Language::French => &lazy::WORDLIST_FRENCH,
157            #[cfg(feature = "mnemonic_italian")]
158            Language::Italian => &lazy::WORDLIST_ITALIAN,
159            #[cfg(feature = "mnemonic_japanese")]
160            Language::Japanese => &lazy::WORDLIST_JAPANESE,
161            #[cfg(feature = "mnemonic_korean")]
162            Language::Korean => &lazy::WORDLIST_KOREAN,
163            #[cfg(feature = "mnemonic_spanish")]
164            Language::Spanish => &lazy::WORDLIST_SPANISH,
165        }
166    }
167
168    /// Get a [`WordMap`][WordMap] that allows word -> index lookups in the word list
169    ///
170    /// The index of an individual word in the word list is used as the binary value of that word
171    /// when the phrase is turned into a [`Seed`][Seed].
172    pub fn wordmap(self) -> &'static WordMap {
173        match self {
174            Language::English => &lazy::WORDMAP_ENGLISH,
175            #[cfg(feature = "mnemonic_chinese_simplified")]
176            Language::ChineseSimplified => &lazy::WORDMAP_CHINESE_SIMPLIFIED,
177            #[cfg(feature = "mnemonic_chinese_traditional")]
178            Language::ChineseTraditional => &lazy::WORDMAP_CHINESE_TRADITIONAL,
179            #[cfg(feature = "mnemonic_french")]
180            Language::French => &lazy::WORDMAP_FRENCH,
181            #[cfg(feature = "mnemonic_italian")]
182            Language::Italian => &lazy::WORDMAP_ITALIAN,
183            #[cfg(feature = "mnemonic_japanese")]
184            Language::Japanese => &lazy::WORDMAP_JAPANESE,
185            #[cfg(feature = "mnemonic_korean")]
186            Language::Korean => &lazy::WORDMAP_KOREAN,
187            #[cfg(feature = "mnemonic_spanish")]
188            Language::Spanish => &lazy::WORDMAP_SPANISH,
189        }
190    }
191
192    #[allow(dead_code)]
193    pub(crate) fn from_u8(source: u8) -> Result<Self, MnemonicError> {
194        match source {
195            0 => Ok(Self::English),
196            #[cfg(feature = "mnemonic_chinese_simplified")]
197            1 => Ok(Self::ChineseSimplified),
198            #[cfg(feature = "mnemonic_chinese_traditional")]
199            2 => Ok(Self::ChineseTraditional),
200            #[cfg(feature = "mnemonic_french")]
201            3 => Ok(Self::French),
202            #[cfg(feature = "mnemonic_italian")]
203            4 => Ok(Self::Italian),
204            #[cfg(feature = "mnemonic_japanese")]
205            5 => Ok(Self::Japanese),
206            #[cfg(feature = "mnemonic_korean")]
207            6 => Ok(Self::Korean),
208            #[cfg(feature = "mnemonic_spanish")]
209            7 => Ok(Self::Spanish),
210            _ => Err(MnemonicError::UnknownLanguage),
211        }
212    }
213
214    #[allow(dead_code)]
215    pub(crate) fn to_u8(self) -> u8 {
216        match self {
217            Language::English => 0,
218            #[cfg(feature = "mnemonic_chinese_simplified")]
219            Language::ChineseSimplified => 1,
220            #[cfg(feature = "mnemonic_chinese_traditional")]
221            Language::ChineseTraditional => 2,
222            #[cfg(feature = "mnemonic_french")]
223            Language::French => 3,
224            #[cfg(feature = "mnemonic_italian")]
225            Language::Italian => 4,
226            #[cfg(feature = "mnemonic_japanese")]
227            Language::Japanese => 5,
228            #[cfg(feature = "mnemonic_korean")]
229            Language::Korean => 6,
230            #[cfg(feature = "mnemonic_spanish")]
231            Language::Spanish => 7,
232        }
233    }
234}
235
236impl Default for Language {
237    fn default() -> Language {
238        Language::English
239    }
240}
241
242impl FromStr for Language {
243    type Err = MnemonicError;
244
245    fn from_str(source: &str) -> Result<Self, Self::Err> {
246        match source {
247            "en" => Ok(Self::English),
248            #[cfg(feature = "mnemonic_chinese_simplified")]
249            "zh_HANS" => Ok(Self::ChineseSimplified),
250            #[cfg(feature = "mnemonic_chinese_traditional")]
251            "zh_HANT" => Ok(Self::ChineseTraditional),
252            #[cfg(feature = "mnemonic_french")]
253            "fr" => Ok(Self::French),
254            #[cfg(feature = "mnemonic_italian")]
255            "it" => Ok(Self::Italian),
256            #[cfg(feature = "mnemonic_japanese")]
257            "ja" => Ok(Self::Japanese),
258            #[cfg(feature = "mnemonic_korean")]
259            "ko" => Ok(Self::Korean),
260            #[cfg(feature = "mnemonic_spanish")]
261            "es" => Ok(Self::Spanish),
262            _ => unreachable!(),
263        }
264    }
265}