wordlists/types/
diceware.rs

1use crate::*;
2use itertools::Itertools;
3
4/// A Wordlist where words can be obtained by using dice roles for entropy. They can also
5/// be used as ordered lists of words for index mapping to byte-ranges.
6pub struct DicewareWordlist {
7    /// The human-readable name of this Wordlist and Generator.
8    name: String,
9
10    /// The number of dice for this wordlist generator.
11    dice_count: u8,
12
13    /// The number of sides each die has.
14    dice_size: u8,
15
16    /// The wordlist to use (dice-index, word). Where dice-index is a
17    /// string of digits of size `dice_count`, where each digit is 1
18    /// to `dice_size`.
19    wordlist: Box<[(&'static str, &'static str)]>,
20}
21
22impl DicewareWordlist {
23    /// Create a new Diceware wordlist based on the dice_count/size and the mapping wordlist.
24    pub fn new(
25        name: String,
26        dice_count: u8,
27        dice_size: u8,
28        wordlist: Box<[(&'static str, &'static str)]>,
29    ) -> Self {
30        DicewareWordlist {
31            name,
32            dice_count,
33            dice_size,
34            wordlist,
35        }
36    }
37
38    /// Get the I'th word in the wordlist.
39    pub fn word(&self, index: usize) -> Result<&'static str> {
40        if index > self.wordlist.len() {
41            Err(WordlistError::IndexOutOfBounds(index, self.wordlist.len()))
42        } else {
43            self.wordlist
44                .get(index)
45                .map(|(_, v)| *v)
46                .ok_or(WordlistError::WordlistIsIncomplete)
47        }
48    }
49
50    /// Get the number of dice rolls required.
51    pub fn dice_count(&self) -> u8 {
52        self.dice_count
53    }
54
55    /// Get the size of the die to roll (i.e. 6 for a normal d6).
56    pub fn dice_size(&self) -> u8 {
57        self.dice_size
58    }
59}
60
61// For byte encoding, we just use the Diceware wordlists like Indexed wordlists and
62// use the relative positioning in the list as the byte index. However, this does NOT
63// pack the bits and instead generates large bytes based on the infrastructure usize
64// bit width. This MAY NOT be what you'd want here.
65impl WordlistByteConverter for DicewareWordlist {
66    fn to_words(&self, bytes: &BitVec) -> Result<WordVec> {
67        let mut wordlist = Vec::new();
68        for index in bytes.as_raw_slice().into_iter() {
69            wordlist.push(self.word(*index)?);
70        }
71        Ok(wordlist)
72    }
73    fn to_bytes(&self, words: &WordVec) -> Result<BitVec> {
74        let mut bytes = Vec::new();
75        for word in words.iter() {
76            let b = self
77                .wordlist
78                .iter()
79                .find_position(|(_, v)| v.eq(word))
80                .map(|(index, _)| index)
81                .ok_or_else(|| WordlistError::InvalidWord(word.to_string()))?;
82            bytes.push(b);
83        }
84        Ok(BitVec::from_vec(bytes))
85    }
86}
87
88impl Wordlist for DicewareWordlist {
89    fn name(&self) -> &String {
90        &self.name
91    }
92    fn size(&self) -> usize {
93        self.wordlist.len()
94    }
95    fn contains(&self, word: &str) -> bool {
96        self.wordlist.iter().map(|(_x, y)| *y).any(|v| v.eq(word))
97    }
98}