mod.rs

  1use alloc::borrow::ToOwned;
  2use alloc::collections::BTreeMap;
  3use alloc::string::{FromUtf8Error, String};
  4use alloc::vec::Vec;
  5
  6#[cfg(feature = "scram")]
  7pub mod scram;
  8
  9#[derive(Clone, Debug, PartialEq, Eq)]
 10pub enum Identity {
 11    None,
 12    Username(String),
 13}
 14
 15impl From<String> for Identity {
 16    fn from(s: String) -> Identity {
 17        Identity::Username(s)
 18    }
 19}
 20
 21impl<'a> From<&'a str> for Identity {
 22    fn from(s: &'a str) -> Identity {
 23        Identity::Username(s.to_owned())
 24    }
 25}
 26
 27/// A struct containing SASL credentials.
 28#[derive(Clone, Debug)]
 29pub struct Credentials {
 30    /// The requested identity.
 31    pub identity: Identity,
 32    /// The secret used to authenticate.
 33    pub secret: Secret,
 34    /// Channel binding data, for *-PLUS mechanisms.
 35    pub channel_binding: ChannelBinding,
 36}
 37
 38impl Default for Credentials {
 39    fn default() -> Credentials {
 40        Credentials {
 41            identity: Identity::None,
 42            secret: Secret::None,
 43            channel_binding: ChannelBinding::Unsupported,
 44        }
 45    }
 46}
 47
 48impl Credentials {
 49    /// Creates a new Credentials with the specified username.
 50    pub fn with_username<N: Into<String>>(mut self, username: N) -> Credentials {
 51        self.identity = Identity::Username(username.into());
 52        self
 53    }
 54
 55    /// Creates a new Credentials with the specified plaintext password.
 56    pub fn with_password<P: Into<String>>(mut self, password: P) -> Credentials {
 57        self.secret = Secret::password_plain(password);
 58        self
 59    }
 60
 61    /// Creates a new Credentials with the specified channel binding.
 62    pub fn with_channel_binding(mut self, channel_binding: ChannelBinding) -> Credentials {
 63        self.channel_binding = channel_binding;
 64        self
 65    }
 66}
 67
 68/// Represents a SASL secret, like a password.
 69#[derive(Clone, Debug, PartialEq, Eq)]
 70pub enum Secret {
 71    /// No extra data needed.
 72    None,
 73    /// Password required.
 74    Password(Password),
 75}
 76
 77impl Secret {
 78    pub fn password_plain<S: Into<String>>(password: S) -> Secret {
 79        Secret::Password(Password::Plain(password.into()))
 80    }
 81
 82    pub fn password_pbkdf2<S: Into<String>>(
 83        method: S,
 84        salt: Vec<u8>,
 85        iterations: u32,
 86        data: Vec<u8>,
 87    ) -> Secret {
 88        Secret::Password(Password::Pbkdf2 {
 89            method: method.into(),
 90            salt,
 91            iterations,
 92            data,
 93        })
 94    }
 95}
 96
 97/// Represents a password.
 98#[derive(Clone, Debug, PartialEq, Eq)]
 99pub enum Password {
100    /// A plaintext password.
101    Plain(String),
102    /// A password digest derived using PBKDF2.
103    Pbkdf2 {
104        method: String,
105        salt: Vec<u8>,
106        iterations: u32,
107        data: Vec<u8>,
108    },
109}
110
111impl From<String> for Password {
112    fn from(s: String) -> Password {
113        Password::Plain(s)
114    }
115}
116
117impl<'a> From<&'a str> for Password {
118    fn from(s: &'a str) -> Password {
119        Password::Plain(s.to_owned())
120    }
121}
122
123#[cfg(test)]
124#[test]
125fn xor_works() {
126    assert_eq!(
127        xor(
128            &[135, 94, 53, 134, 73, 233, 140, 221, 150, 12, 96, 111, 54, 66, 11, 76],
129            &[163, 9, 122, 180, 107, 44, 22, 252, 248, 134, 112, 82, 84, 122, 56, 209]
130        ),
131        &[36, 87, 79, 50, 34, 197, 154, 33, 110, 138, 16, 61, 98, 56, 51, 157]
132    );
133}
134
135#[doc(hidden)]
136pub fn xor(a: &[u8], b: &[u8]) -> Vec<u8> {
137    assert_eq!(a.len(), b.len());
138    let mut ret = Vec::with_capacity(a.len());
139    for (a, b) in a.iter().zip(b) {
140        ret.push(a ^ b);
141    }
142    ret
143}
144
145#[doc(hidden)]
146pub fn parse_frame(frame: &[u8]) -> Result<BTreeMap<char, String>, FromUtf8Error> {
147    let inner = String::from_utf8(frame.to_owned())?;
148    let mut ret = BTreeMap::new();
149    for s in inner.split(',') {
150        let mut tmp = s.splitn(2, '=');
151        let key = tmp.next();
152        let val = tmp.next();
153        if let (Some(k), Some(v)) = (key, val) {
154            if let Some(k) = k.chars().next() {
155                ret.insert(k, v.to_owned());
156            }
157        }
158    }
159    Ok(ret)
160}
161
162/// Channel binding configuration.
163#[derive(Clone, Debug, PartialEq, Eq)]
164pub enum ChannelBinding {
165    /// No channel binding data.
166    None,
167    /// Advertise that the client does not think the server supports channel binding.
168    Unsupported,
169    /// p=tls-unique channel binding data (for TLSĀ 1.2).
170    TlsUnique(Vec<u8>),
171    /// p=tls-exporter channel binding data (for TLSĀ 1.3).
172    TlsExporter(Vec<u8>),
173}
174
175impl ChannelBinding {
176    /// Return the gs2 header for this channel binding mechanism.
177    pub fn header(&self) -> &[u8] {
178        match *self {
179            ChannelBinding::None => b"n,,",
180            ChannelBinding::Unsupported => b"y,,",
181            ChannelBinding::TlsUnique(_) => b"p=tls-unique,,",
182            ChannelBinding::TlsExporter(_) => b"p=tls-exporter,,",
183        }
184    }
185
186    /// Return the channel binding data for this channel binding mechanism.
187    pub fn data(&self) -> &[u8] {
188        match *self {
189            ChannelBinding::None => &[],
190            ChannelBinding::Unsupported => &[],
191            ChannelBinding::TlsUnique(ref data) => data,
192            ChannelBinding::TlsExporter(ref data) => data,
193        }
194    }
195
196    /// Checks whether this channel binding mechanism is supported.
197    pub fn supports(&self, mechanism: &str) -> bool {
198        match *self {
199            ChannelBinding::None => false,
200            ChannelBinding::Unsupported => false,
201            ChannelBinding::TlsUnique(_) => mechanism == "tls-unique",
202            ChannelBinding::TlsExporter(_) => mechanism == "tls-exporter",
203        }
204    }
205}