rust_rcs_core/security/authentication/challenge.rs
1// Copyright 2023 宋昊文
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::ffi::log::platform_log;
16use crate::internet::parameter::ParameterParser;
17use crate::internet::syntax;
18
19use super::auth_param::AuthParamParser;
20
21const LOG_TAG: &str = "auth";
22
23const CHARSET_TOKEN_68: [u8; 68] = [
24 b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p',
25 b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', b'A', b'B', b'C', b'D', b'E', b'F',
26 b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V',
27 b'W', b'X', b'Y', b'Z', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'-', b'.',
28 b'_', b'~', b'+', b'/',
29];
30
31pub struct Challenge<'a> {
32 pub auth_scheme: &'a [u8],
33
34 params: &'a [u8],
35}
36
37impl<'a> Challenge<'a> {
38 pub fn get_params(&self) -> ParameterParser<'a> {
39 ParameterParser::new(self.params, b',', true)
40 }
41}
42
43enum State<'a> {
44 ExpectingScheme,
45 ExpectingTokenOrParam(&'a [u8], usize),
46 ExpectingParamOrScheme(&'a [u8], usize),
47}
48
49/// Parse Http/Sip WWW-Authenticate response Challenge per RFC 7235
50///
51/// # Examples
52///
53/// ```
54/// use rust_rcs_core::security::authentication::challenge::ChallengeParser;
55///
56/// let a = b"Newauth realm=\"apps\", type=1,\n title=\"Login to \\\"apps\\\"\", Basic realm=\"simple\"";
57/// let mut parser = ChallengeParser::new(a);
58///
59/// let b = parser.next().unwrap();
60/// assert_eq!(b.auth_scheme, b"Newauth");
61///
62/// let mut p = b.get_params();
63/// let c = p.next().unwrap();
64/// assert_eq!(c.name, b"realm");
65/// assert_eq!(c.value.unwrap(), b"\"apps\"");
66/// let c = p.next().unwrap();
67/// assert_eq!(c.name, b"type");
68/// assert_eq!(c.value.unwrap(), b"1");
69/// let c = p.next().unwrap();
70/// assert_eq!(c.name, b"title");
71/// assert_eq!(c.value.unwrap(), b"\"Login to \\\"apps\\\"\"");
72///
73/// let b = parser.next().unwrap();
74/// assert_eq!(b.auth_scheme, b"Basic");
75///
76/// let mut p = b.get_params();
77/// let c = p.next().unwrap();
78/// assert_eq!(c.name, b"realm");
79/// assert_eq!(c.value.unwrap(), b"\"simple\"");
80///
81/// let a = b"Digest realm=\"example.com\", qop=\"auth\", algorithm=SHA-256, nonce=\"ce696741c46032ba0470d841551f3a8be8e0cc3e2591353bd6bc822436cc8615b56706342a90a3e13c2fd94debdc839000a8b50acb64612f998d93ce628605c1\"";
82/// let mut parser = ChallengeParser::new(a);
83///
84/// let b = parser.next().unwrap();
85/// assert_eq!(b.auth_scheme, b"Digest");
86///
87/// let mut p = b.get_params();
88/// let c = p.next().unwrap();
89/// assert_eq!(c.name, b"realm");
90/// assert_eq!(c.value.unwrap(), b"\"example.com\"");
91///
92/// let c = p.next().unwrap();
93/// assert_eq!(c.name, b"qop");
94/// assert_eq!(c.value.unwrap(), b"\"auth\"");
95///
96/// let c = p.next().unwrap();
97/// assert_eq!(c.name, b"algorithm");
98/// assert_eq!(c.value.unwrap(), b"SHA-256");
99///
100/// let c = p.next().unwrap();
101/// assert_eq!(c.name, b"nonce");
102/// assert_eq!(c.value.unwrap(), b"\"ce696741c46032ba0470d841551f3a8be8e0cc3e2591353bd6bc822436cc8615b56706342a90a3e13c2fd94debdc839000a8b50acb64612f998d93ce628605c1\"");
103///
104/// let c = p.next().is_none();
105/// assert_eq!(c, true);
106///
107/// let b = parser.next().is_none();
108/// assert_eq!(b, true);
109/// ```
110pub struct ChallengeParser<'a> {
111 state: State<'a>,
112 pub s: &'a [u8],
113 pub p: usize,
114}
115
116impl<'a> ChallengeParser<'a> {
117 pub fn new(s: &'a [u8]) -> ChallengeParser<'a> {
118 ChallengeParser {
119 state: State::ExpectingScheme,
120 s,
121 p: 0,
122 }
123 }
124}
125
126impl<'a> Iterator for ChallengeParser<'a> {
127 type Item = Challenge<'a>;
128 fn next(&mut self) -> Option<Challenge<'a>> {
129 if self.p < self.s.len() {
130 loop {
131 match self.state {
132 State::ExpectingScheme => {
133 let (scheme, advance) = if let Some(idx) =
134 syntax::index_with_token_escaping(&self.s[self.p..], b' ')
135 {
136 (&self.s[self.p..self.p + idx], idx)
137 } else {
138 (&self.s[self.p..], self.s.len() - self.p)
139 };
140
141 let scheme = syntax::trim(scheme);
142 if scheme.len() == 0 {
143 self.p = self.p + advance;
144 } else {
145 if self.p + advance + 1 < self.s.len() {
146 self.p = self.p
147 + advance
148 + syntax::index_skipping_ows_and_obs_fold(
149 &self.s[self.p + advance + 1..],
150 )
151 + 1;
152
153 self.state = State::ExpectingTokenOrParam(scheme, self.p);
154 } else {
155 break;
156 }
157 }
158 }
159
160 State::ExpectingTokenOrParam(scheme, start_of_params) => {
161 let (chunk, advance) = if let Some(idx) =
162 syntax::index_with_token_escaping(&self.s[self.p..], b' ')
163 {
164 (&self.s[self.p..self.p + idx], idx)
165 } else {
166 (&self.s[self.p..], self.s.len() - self.p)
167 };
168
169 let chunk = syntax::trim(chunk);
170 let mut is_token = true;
171 for c in chunk {
172 if !CHARSET_TOKEN_68.contains(c) {
173 is_token = false;
174 break;
175 }
176 }
177
178 if is_token {
179 self.state = State::ExpectingScheme;
180
181 self.p = self.p
182 + advance
183 + syntax::index_skipping_ows_and_obs_fold(
184 &self.s[self.p + advance + 1..],
185 )
186 + 1;
187
188 return Some(Challenge {
189 auth_scheme: scheme,
190 params: &self.s[start_of_params..self.p + advance],
191 });
192 } else {
193 if let Some((_, advance)) =
194 self.s[self.p..self.p + advance].try_auth_param()
195 {
196 if self.p + advance + 1 < self.s.len() {
197 self.p = self.p
198 + advance
199 + syntax::index_skipping_ows_and_obs_fold(
200 &self.s[self.p + advance + 1..],
201 )
202 + 1;
203 } else {
204 self.p += advance;
205 }
206 self.state = State::ExpectingParamOrScheme(scheme, start_of_params);
207 } else {
208 platform_log(
209 LOG_TAG,
210 "neither token68 nor auth-param detected in challenge string",
211 );
212 break;
213 }
214 }
215 }
216
217 State::ExpectingParamOrScheme(scheme, start_of_params) => {
218 let (chunk, advance) = if let Some(idx) =
219 syntax::index_with_token_escaping(&self.s[self.p..], b' ')
220 {
221 (&self.s[self.p..self.p + idx], idx)
222 } else {
223 (&self.s[self.p..], self.s.len() - self.p)
224 };
225
226 if let Some((_, advance)) =
227 self.s[self.p..self.p + advance].try_auth_param()
228 {
229 if self.p + advance + 1 < self.s.len() {
230 self.p = self.p
231 + advance
232 + syntax::index_skipping_ows_and_obs_fold(
233 &self.s[self.p + advance + 1..],
234 )
235 + 1;
236 } else {
237 self.p += advance;
238 }
239 } else {
240 let chunk = syntax::trim(chunk);
241
242 let challenge = Challenge {
243 auth_scheme: scheme,
244 params: &self.s[start_of_params..self.p],
245 };
246
247 if self.p + advance + 1 < self.s.len() {
248 self.p = self.p
249 + advance
250 + syntax::index_skipping_ows_and_obs_fold(
251 &self.s[self.p + advance + 1..],
252 )
253 + 1;
254
255 self.state = State::ExpectingTokenOrParam(chunk, self.p);
256
257 return Some(challenge);
258 } else {
259 self.p = self.p + advance;
260
261 self.state = State::ExpectingTokenOrParam(chunk, self.p);
262
263 return Some(challenge);
264 }
265 }
266 }
267 }
268 }
269 }
270
271 None
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::ChallengeParser;
278
279 #[test]
280 fn test_decode() {
281 let a = b"Newauth realm=\"apps\", type=1,\n title=\"Login to \\\"apps\\\"\", Basic realm=\"simple\"";
282 let mut parser = ChallengeParser::new(a);
283
284 let b = parser.next().unwrap();
285 assert_eq!(b.auth_scheme, b"Newauth");
286
287 let mut p = b.get_params();
288 let c = p.next().unwrap();
289 assert_eq!(c.name, b"realm");
290 assert_eq!(c.value.unwrap(), b"\"apps\"");
291 let c = p.next().unwrap();
292 assert_eq!(c.name, b"type");
293 assert_eq!(c.value.unwrap(), b"1");
294 let c = p.next().unwrap();
295 assert_eq!(c.name, b"title");
296 assert_eq!(c.value.unwrap(), b"\"Login to \\\"apps\\\"\"");
297
298 let b = parser.next().unwrap();
299 assert_eq!(b.auth_scheme, b"Basic");
300
301 let mut p = b.get_params();
302 let c = p.next().unwrap();
303 assert_eq!(c.name, b"realm");
304 assert_eq!(c.value.unwrap(), b"\"simple\"");
305
306 let a = b"Digest realm=\"example.com\", qop=\"auth\", algorithm=SHA-256, nonce=\"ce696741c46032ba0470d841551f3a8be8e0cc3e2591353bd6bc822436cc8615b56706342a90a3e13c2fd94debdc839000a8b50acb64612f998d93ce628605c1\"";
307 let mut parser = ChallengeParser::new(a);
308
309 let b = parser.next().unwrap();
310 assert_eq!(b.auth_scheme, b"Digest");
311
312 let mut p = b.get_params();
313 let c = p.next().unwrap();
314 assert_eq!(c.name, b"realm");
315 assert_eq!(c.value.unwrap(), b"\"example.com\"");
316
317 let c = p.next().unwrap();
318 assert_eq!(c.name, b"qop");
319 assert_eq!(c.value.unwrap(), b"\"auth\"");
320
321 let c = p.next().unwrap();
322 assert_eq!(c.name, b"algorithm");
323 assert_eq!(c.value.unwrap(), b"SHA-256");
324
325 let c = p.next().unwrap();
326 assert_eq!(c.name, b"nonce");
327 assert_eq!(c.value.unwrap(), b"\"ce696741c46032ba0470d841551f3a8be8e0cc3e2591353bd6bc822436cc8615b56706342a90a3e13c2fd94debdc839000a8b50acb64612f998d93ce628605c1\"");
328
329 let c = p.next().is_none();
330 assert_eq!(c, true);
331
332 let b = parser.next().is_none();
333 assert_eq!(b, true);
334 }
335}