Skip to main content

rspamd_base32/
alphabet.rs

1//! Provides Alphabet used for Base32 encoding and decoding
2use std::{error, fmt};
3
4pub const ALPHABET_SIZE: usize = 32;
5
6#[derive(Clone, Debug, Eq, PartialEq)]
7pub enum EncodeOrder {
8    OrderInversed,
9    OrderNormal
10}
11
12/// Defines alphabet - 32 characters used for Base32
13#[derive(Clone, Debug, Eq, PartialEq)]
14pub struct Alphabet {
15    pub(crate) encode_symbols: [u8; ALPHABET_SIZE],
16    pub(crate) decode_bytes: [u8; u8::MAX as usize],
17    pub(crate) encode_order : EncodeOrder,
18}
19
20impl Alphabet {
21    /// Performs no checks so that it can be const.
22    /// Used only for known-valid strings.
23    const fn from_str_unsafe(alphabet: &str, encode_order: EncodeOrder) -> Self {
24        let mut symbols = [0_u8; ALPHABET_SIZE];
25        let source_bytes = alphabet.as_bytes();
26        let mut decode_bytes = [0xff_u8; u8::MAX as usize];
27
28        let mut index = 0;
29        while index < ALPHABET_SIZE {
30            let sym = source_bytes[index];
31            symbols[index] = sym;
32            decode_bytes[sym as usize] = index as u8;
33            index = index + 1;
34        }
35        Alphabet { encode_symbols: symbols, decode_bytes, encode_order }
36    }
37
38    /// Checks input for printability and duplicates
39    pub const fn from_str_order(alphabet: &str, encode_order: EncodeOrder) -> Result<Self, ParseAlphabetError> {
40        const FIRST_PRINTABLE: u8 = 32;
41        const LAST_PRINTABLE: u8 = 126;
42        const DUPS_SIZE: usize = (LAST_PRINTABLE - FIRST_PRINTABLE) as usize;
43        let source_bytes = alphabet.as_bytes();
44        let mut dups : [bool; DUPS_SIZE] = [false; DUPS_SIZE];
45
46        if source_bytes.len() != ALPHABET_SIZE {
47            return Err(ParseAlphabetError::InvalidLength);
48        }
49
50        let mut index = 0;
51        while index < ALPHABET_SIZE {
52            let byte = source_bytes[index];
53            // Must be printable for sanity
54            if !(byte >= FIRST_PRINTABLE as u8 && byte <= LAST_PRINTABLE as u8) {
55                return Err(ParseAlphabetError::UnprintableByte(byte));
56            }
57
58            let dup_idx = (byte - FIRST_PRINTABLE) as usize;
59            if dups[dup_idx] {
60                return Err(ParseAlphabetError::DuplicatedByte(byte));
61            }
62
63            dups[dup_idx] = true;
64            index = index + 1;
65        }
66
67        Ok(Self::from_str_unsafe(alphabet, encode_order))
68    }
69
70    pub const fn from_str(alphabet: &str) -> Result<Self, ParseAlphabetError> {
71        Self::from_str_order(alphabet, EncodeOrder::OrderNormal)
72    }
73}
74
75#[derive(Debug, Eq, PartialEq)]
76pub enum ParseAlphabetError {
77    /// Alphabets must be 64 ASCII bytes
78    InvalidLength,
79    /// All bytes must be unique
80    DuplicatedByte(u8),
81    /// All bytes must be printable (in the range `[32, 126]`).
82    UnprintableByte(u8),
83}
84
85#[cfg(any(feature = "std", test))]
86impl fmt::Display for ParseAlphabetError {
87    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88        match self {
89            ParseAlphabetError::InvalidLength => write!(f, "Invalid length - must be {} bytes", ALPHABET_SIZE),
90            ParseAlphabetError::DuplicatedByte(b) => write!(f, "Duplicated byte: {:#04x}", b),
91            ParseAlphabetError::UnprintableByte(b) => write!(f, "Unprintable byte: {:#04x}", b),
92        }
93    }
94}
95
96#[cfg(any(feature = "std", test))]
97impl error::Error for ParseAlphabetError {}
98
99/// ZBase32 alphabet
100/// http://philzimmermann.com/docs/human-oriented-base-32-encoding.txt
101pub const ZBASE32: Alphabet = Alphabet::from_str_unsafe(
102    "ybndrfg8ejkmcpqxot1uwisza345h769",
103    EncodeOrder::OrderInversed,
104);
105
106/// Bech32 alphabet used for bitcoin
107pub const BECH32: Alphabet = Alphabet::from_str_unsafe(
108   "qpzry9x8gf2tvdw0s3jn54khce6mua7l",
109   EncodeOrder::OrderNormal,
110);
111
112/// RFC 4648 base32
113pub const RFC: Alphabet = Alphabet::from_str_unsafe(
114    "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",
115    EncodeOrder::OrderNormal,
116);
117
118#[cfg(test)]
119mod tests {
120    use crate::alphabet::*;
121
122    #[test]
123    fn detects_duplicate_start() {
124        assert_eq!(
125            ParseAlphabetError::DuplicatedByte(b'A'),
126            Alphabet::from_str("AACDEFGHIJKLMNOPQRSTUVWXYZ234567")
127                .unwrap_err()
128        );
129    }
130
131    #[test]
132    fn detects_duplicate_end() {
133        assert_eq!(
134            ParseAlphabetError::DuplicatedByte(b'7'),
135            Alphabet::from_str("ABCDEFGHIJKLMNOPQRSTUVWXYZ234577")
136                .unwrap_err()
137        );
138    }
139
140    #[test]
141    fn detects_duplicate_middle() {
142        assert_eq!(
143            ParseAlphabetError::DuplicatedByte(b'Z'),
144            Alphabet::from_str("ABCDEFGHIJKLMNOPQRSTUVWXZZ234567")
145                .unwrap_err()
146        );
147    }
148
149    #[test]
150    fn detects_length() {
151        assert_eq!(
152            ParseAlphabetError::InvalidLength,
153            Alphabet::from_str("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567xxx")
154                .unwrap_err()
155        );
156    }
157
158    #[test]
159    fn detects_unprintable() {
160        // form feed
161        assert_eq!(
162            ParseAlphabetError::UnprintableByte(0xc),
163            Alphabet::from_str(
164                "\x0cBCDEFGHIJKLMNOPQRSTUVWXYZ234567"
165            )
166                .unwrap_err()
167        );
168    }
169
170    #[test]
171    fn same_as_unchecked() {
172        assert_eq!(
173            RFC,
174            Alphabet::from_str("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567")
175                .unwrap()
176        )
177    }
178}