1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
use once_cell::sync::Lazy;
use std::sync::Mutex;

static GLOBAL_CONFIG: Lazy<Mutex<Option<Config>>> = Lazy::new(|| Mutex::new(None));

/// Configuring the cryptid library.
#[derive(Clone)]
pub struct Config<'a> {
    pub(crate) hmac_length: u8,
    pub(crate) key: &'a [u8],
    pub(crate) zero_pad_length: u8,
}

#[derive(Debug)]
pub enum ConfigError {
    InvalidMacLength,
    InvalidVersion,
    InvalidZeroPadLength,
}

impl<'a> Config<'a> {
    /// Creates a new configuration with the given master `key` and other settings in
    /// default values.
    /// - `mac_length` defaults to 4, which is large enough to make guessing impractical
    ///   but still keeps the strings relatively short. High security applications may want
    ///   to use a higher value.
    /// - `zero_pad_length` defaults to 4, which is large enough for most applications
    ///   to never see encoded strings increase in size, while still keeping the strings
    ///   relatively short.
    pub fn new(key: &'a [u8]) -> Self {
        Config {
            hmac_length: 4,
            key,
            zero_pad_length: 4,
        }
    }

    /// Sets the number of bytes in the HMAC.
    /// The value must be between 0 and 8.
    pub fn hmac_length(mut self, hmac_length: u8) -> Result<Self, ConfigError> {
        if hmac_length > 8 {
            Err(ConfigError::InvalidMacLength)
        } else {
            self.hmac_length = hmac_length;
            Ok(self)
        }
    }

    /// Sets the number of bytes to zero-pad numbers before encoding.
    /// The value must be between 0 and 8.
    pub fn zero_pad_length(mut self, zero_pad_length: u8) -> Result<Self, ConfigError> {
        if zero_pad_length > 8 {
            Err(ConfigError::InvalidZeroPadLength)
        } else {
            self.zero_pad_length = zero_pad_length;
            Ok(self)
        }
    }

    /// Sets the global configuration. This should be called before the `Field` type methods
    /// are called.
    pub fn set_global(config: Config<'static>) {
        let mut global_config = GLOBAL_CONFIG.lock().unwrap();
        *global_config = Some(config);
    }

    /// Accesses the global configuration, if set.
    pub fn global() -> Option<Config<'static>> {
        GLOBAL_CONFIG.lock().unwrap().clone()
    }
}