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}