mod.rs

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