sasl/common/
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}