base_d/wordlists/
mod.rs

1//! Built-in word lists for word-based encoding.
2//!
3//! Currently includes:
4//! - BIP-39 English (2048 words)
5//! - EFF Long (7776 words)
6//! - EFF Short 1 (1296 words)
7//! - EFF Short 2 (1296 words)
8//! - Diceware (7776 words)
9//! - PGP Even (256 words, 2-syllable)
10//! - PGP Odd (256 words, 3-syllable)
11//! - NATO Phonetic Alphabet (26 words)
12//! - Corporate Buzzwords (61 words)
13//! - Klingon (3838 words)
14//! - Pokemon (1092 words)
15
16use crate::core::word_dictionary::WordDictionary;
17
18// Security word lists
19/// The BIP-39 English word list (2048 words).
20/// Used for cryptocurrency seed phrases. Each word encodes 11 bits.
21pub const BIP39_ENGLISH: &str = include_str!("../../dictionaries/word/security/bip39-english.txt");
22
23/// The EFF Long word list (7776 words).
24/// Improved diceware list with longer, more memorable words.
25pub const EFF_LONG: &str = include_str!("../../dictionaries/word/security/eff-long.txt");
26
27/// The EFF Short word list #1 (1296 words).
28/// Shorter words, 4 dice rolls per word.
29pub const EFF_SHORT1: &str = include_str!("../../dictionaries/word/security/eff-short1.txt");
30
31/// The EFF Short word list #2 (1296 words).
32/// Longer memorable words, 4 dice rolls per word.
33pub const EFF_SHORT2: &str = include_str!("../../dictionaries/word/security/eff-short2.txt");
34
35/// The original Diceware word list (7776 words).
36/// Classic passphrase generation list by Arnold Reinhold.
37pub const DICEWARE: &str = include_str!("../../dictionaries/word/security/diceware.txt");
38
39/// PGP word list - even positions (256 words, 2-syllable).
40/// Used for fingerprint verification.
41pub const PGP_EVEN: &str = include_str!("../../dictionaries/word/security/pgp-even.txt");
42
43/// PGP word list - odd positions (256 words, 3-syllable).
44/// Used for fingerprint verification.
45pub const PGP_ODD: &str = include_str!("../../dictionaries/word/security/pgp-odd.txt");
46
47// Fun word lists
48/// NATO Phonetic Alphabet (26 words).
49/// Alfa, Bravo, Charlie... Used for radio communication.
50pub const NATO: &str = include_str!("../../dictionaries/word/fun/nato.txt");
51
52/// Corporate Buzzwords (61 words).
53/// Synergy, leverage, paradigm... For your next meeting.
54pub const BUZZWORDS: &str = include_str!("../../dictionaries/word/fun/buzzwords.txt");
55
56/// Klingon words (3838 words).
57/// tlhIngan Hol - from Star Trek.
58pub const KLINGON: &str = include_str!("../../dictionaries/word/fun/klingon.txt");
59
60/// Pokemon names (1092 words).
61/// Gotta encode 'em all.
62pub const POKEMON: &str = include_str!("../../dictionaries/word/fun/pokemon.txt");
63
64/// Creates a WordDictionary from the built-in BIP-39 English word list.
65///
66/// # Example
67///
68/// ```
69/// use base_d::wordlists::bip39_english;
70/// use base_d::word;
71///
72/// let dict = bip39_english();
73/// assert_eq!(dict.base(), 2048);
74///
75/// let encoded = word::encode(b"hello", &dict);
76/// let decoded = word::decode(&encoded, &dict).unwrap();
77/// assert_eq!(decoded, b"hello");
78/// ```
79pub fn bip39_english() -> WordDictionary {
80    WordDictionary::builder()
81        .words_from_str(BIP39_ENGLISH)
82        .delimiter(" ")
83        .case_sensitive(false)
84        .build()
85        .expect("BIP-39 English word list should be valid")
86}
87
88/// Creates a WordDictionary from the EFF Long word list (7776 words).
89pub fn eff_long() -> WordDictionary {
90    WordDictionary::builder()
91        .words_from_str(EFF_LONG)
92        .delimiter(" ")
93        .case_sensitive(false)
94        .build()
95        .expect("EFF Long word list should be valid")
96}
97
98/// Creates a WordDictionary from the EFF Short word list #1 (1296 words).
99pub fn eff_short1() -> WordDictionary {
100    WordDictionary::builder()
101        .words_from_str(EFF_SHORT1)
102        .delimiter(" ")
103        .case_sensitive(false)
104        .build()
105        .expect("EFF Short 1 word list should be valid")
106}
107
108/// Creates a WordDictionary from the EFF Short word list #2 (1296 words).
109pub fn eff_short2() -> WordDictionary {
110    WordDictionary::builder()
111        .words_from_str(EFF_SHORT2)
112        .delimiter(" ")
113        .case_sensitive(false)
114        .build()
115        .expect("EFF Short 2 word list should be valid")
116}
117
118/// Creates a WordDictionary from the Diceware word list (7776 words).
119pub fn diceware() -> WordDictionary {
120    WordDictionary::builder()
121        .words_from_str(DICEWARE)
122        .delimiter(" ")
123        .case_sensitive(false)
124        .build()
125        .expect("Diceware word list should be valid")
126}
127
128/// Creates a WordDictionary from the PGP even (2-syllable) word list (256 words).
129pub fn pgp_even() -> WordDictionary {
130    WordDictionary::builder()
131        .words_from_str(PGP_EVEN)
132        .delimiter("-")
133        .case_sensitive(false)
134        .build()
135        .expect("PGP Even word list should be valid")
136}
137
138/// Creates a WordDictionary from the PGP odd (3-syllable) word list (256 words).
139pub fn pgp_odd() -> WordDictionary {
140    WordDictionary::builder()
141        .words_from_str(PGP_ODD)
142        .delimiter("-")
143        .case_sensitive(false)
144        .build()
145        .expect("PGP Odd word list should be valid")
146}
147
148/// Creates a WordDictionary from the NATO phonetic alphabet (26 words).
149pub fn nato() -> WordDictionary {
150    WordDictionary::builder()
151        .words_from_str(NATO)
152        .delimiter("-")
153        .case_sensitive(false)
154        .build()
155        .expect("NATO word list should be valid")
156}
157
158/// Creates a WordDictionary from corporate buzzwords (61 words).
159pub fn buzzwords() -> WordDictionary {
160    WordDictionary::builder()
161        .words_from_str(BUZZWORDS)
162        .delimiter(" ")
163        .case_sensitive(false)
164        .build()
165        .expect("Buzzwords word list should be valid")
166}
167
168/// Creates a WordDictionary from Klingon words (3838 words).
169pub fn klingon() -> WordDictionary {
170    WordDictionary::builder()
171        .words_from_str(KLINGON)
172        .delimiter(" ")
173        .case_sensitive(true) // Klingon has case-sensitive orthography
174        .build()
175        .expect("Klingon word list should be valid")
176}
177
178/// Creates a WordDictionary from Pokemon names (1092 words).
179pub fn pokemon() -> WordDictionary {
180    WordDictionary::builder()
181        .words_from_str(POKEMON)
182        .delimiter(" ")
183        .case_sensitive(false)
184        .build()
185        .expect("Pokemon word list should be valid")
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191
192    #[test]
193    fn test_bip39_english_word_count() {
194        let dict = bip39_english();
195        assert_eq!(dict.base(), 2048);
196    }
197
198    #[test]
199    fn test_bip39_english_first_word() {
200        let dict = bip39_english();
201        assert_eq!(dict.encode_word(0), Some("abandon"));
202    }
203
204    #[test]
205    fn test_bip39_english_last_word() {
206        let dict = bip39_english();
207        assert_eq!(dict.encode_word(2047), Some("zoo"));
208    }
209
210    #[test]
211    fn test_bip39_english_roundtrip() {
212        use crate::encoders::algorithms::word;
213
214        let dict = bip39_english();
215        let data = b"The quick brown fox jumps over the lazy dog";
216        let encoded = word::encode(data, &dict);
217        let decoded = word::decode(&encoded, &dict).unwrap();
218        assert_eq!(decoded, data);
219    }
220
221    #[test]
222    fn test_bip39_english_case_insensitive() {
223        let dict = bip39_english();
224        assert_eq!(dict.decode_word("abandon"), Some(0));
225        assert_eq!(dict.decode_word("ABANDON"), Some(0));
226        assert_eq!(dict.decode_word("Abandon"), Some(0));
227    }
228}