pem_iterator/
boundary.rs

1use core::str::Chars;
2use core::iter::{Map, once};
3
4use {Void, map_chars, is_whitespace};
5
6#[derive(Debug, PartialEq)]
7pub enum EncapsulationBoundaryError<Location, LabelError> {
8    MissingExpected(char),
9    Mismatch {
10        location: Location,
11        expected: char,
12        found: char,
13    },
14    LabelError{
15        location: Location,
16        error: LabelError
17    },
18}
19
20/// Which boundary to process
21pub enum BoundaryType {
22    Begin,
23    End,
24}
25/// A trait for extracting the label from a boundary
26pub trait Label {
27    /// The type of any errors which might occur while accumulating the label
28    type LabelError;
29
30    /// Adds a character to the label. If it returns an error, the parsing process will terminate.
31    ///
32    /// A return value of `Ok(Some(found))` means the label was expected to end.
33    /// A return value of `Ok(Some(expected))` means a Mismatch error occured.
34    fn push(&mut self, found: char) -> Result<Option<char>, Self::LabelError>;
35
36    /// Signals that the label is complete because a double `'-'` was reached.
37    ///
38    /// Defaults to `Ok(None)`.
39    /// A return value of `Ok(Some(expected))` is interpreted as a Mismatch error.
40    /// A return value of `Ok(Some('-'))` is invalid and reserved for future use.
41    fn complete(&mut self) -> Result<Option<char>, Self::LabelError> {
42        Ok(None)
43    }
44}
45
46/// A label "accumulator" which discards the label
47pub struct DiscardLabel;
48
49/// A label accumulator which calls the specified function
50pub struct LabelFn<F>(pub F);
51
52/// A label accumulator which matches against existing characters
53pub struct LabelMatcher<I>(pub I);
54
55impl<'a, T: 'a+Extend<char>> Label for &'a mut T {
56    type LabelError = Void;
57    fn push(&mut self, found: char) -> Result<Option<char>, Self::LabelError> {
58        self.extend(once(found));
59        Ok(None)
60    }
61}
62
63impl Label for DiscardLabel {
64    type LabelError = Void;
65    fn push(&mut self, _: char) -> Result<Option<char>, Self::LabelError> {
66        Ok(None)
67    }
68}
69
70impl <T, F> Label for LabelFn<F>
71where F: FnMut(char) -> Result<Option<char>, T> {
72    type LabelError = T;
73    fn push(&mut self, found: char) -> Result<Option<char>, T> {
74        self.0(found)
75    }
76}
77
78impl<T: Iterator<Item=char>> Label for LabelMatcher<T> {
79    type LabelError = Void;
80    fn push(&mut self, found: char) -> Result<Option<char>, Self::LabelError> {
81        Ok(match self.0.next() {
82            Some(expected) => if expected == found {
83                None
84            } else {
85                Some(expected)
86            },
87            None => Some(found),
88        })
89    }
90
91    fn complete(&mut self) -> Result<Option<char>, Self::LabelError> {
92        Ok(self.0.next())
93    }
94}
95
96
97
98pub struct BoundaryParser<Loc, Lbl: Label, S> {
99    stream: S,
100    state: Option<BoundaryParserState<Loc, Lbl>>,
101    result: Result<(),EncapsulationBoundaryError<Loc, Lbl::LabelError>>,
102}
103
104enum BoundaryParserState<Loc, Lbl> {
105    EatFirst{
106        label: Lbl,
107        b: BoundaryType,
108    },
109    NotEatFirst(BoundaryParserState2<Loc, Lbl>),
110}
111
112enum BoundaryParserState2<Loc, Lbl> {
113    EatKey{
114        label: Lbl,
115        key: Chars<'static>,
116        expected: char,
117    },
118    NotEatKey(BoundaryParserState3<Loc, Lbl>)
119}
120
121enum BoundaryParserState3<Loc, Lbl> {
122    EatLabel{
123        label: Lbl,
124        prev_dash: Option<Loc>,
125    },
126    EatEnd{
127        end: Chars<'static>,
128        expected: char,
129    },
130}
131
132impl<Loc, Lbl, E, S> BoundaryParser<Loc, Lbl, S>
133where Lbl: Label,
134    S: Iterator<Item = Result<(Loc, char), E>>
135    {
136    pub fn new(b: BoundaryType, stream: S, label: Lbl) -> Self {
137        BoundaryParser{
138            stream, state: Some(BoundaryParserState::EatFirst{label, b}), result: Ok(()),
139        }
140    }
141
142    /// Call after `next` returns None
143    pub fn complete(self) -> Result<(), EncapsulationBoundaryError<Loc, Lbl::LabelError>> {
144        self.result
145    }
146}
147
148impl<Loc, Lbl, S> BoundaryParser<Loc, Lbl, Map<S, fn((Loc, char)) -> Result<(Loc, char), Void>>>
149where Lbl: Label,
150    S: Iterator<Item = (Loc, char)>
151    {
152    pub fn from_chars(b: BoundaryType, stream: S, label: Lbl) -> Self {
153        Self::new(b, stream.map(map_chars), label)
154    }
155}
156
157impl<Loc, Lbl, E, S> Iterator for BoundaryParser<Loc, Lbl, S>
158where Lbl: Label,
159    S: Iterator<Item = Result<(Loc, char), E>>
160{
161    type Item = E;
162    /// Panics if called after it returns `None`
163    fn next(&mut self) -> Option<E> {
164        match self.state.take().unwrap().process(&mut self.stream) {
165            Err(e) => {
166                self.result = Err(e);
167                None
168            },
169            Ok(None) => None,
170            Ok(Some((s, e))) => {
171                self.state = Some(s);
172                Some(e)
173            },
174        }
175    } 
176}
177
178impl<Loc, Lbl: Label> BoundaryParserState<Loc, Lbl> {
179    fn process<'a, E: 'a>(self, stream: &'a mut Iterator<Item = Result<(Loc, char), E>>) -> Result<Option<(Self, E)>, EncapsulationBoundaryError<Loc, Lbl::LabelError>> {
180        use self::EncapsulationBoundaryError::*;
181        use self::BoundaryParserState::*;
182        use self::BoundaryParserState2::*;
183        
184
185        let v = match self {
186            EatFirst{label, b} => {
187                // For BEGIN, eat all the whitespace and the first '-'
188                // END has already had one '-' eaten during body parsing, so don't worry about that
189                let key = match b {
190                    BoundaryType::Begin => {
191                        match stream.skip_while(|c| c.as_ref().ok().map_or(false, is_whitespace)).next() {
192                            Some(Err(e)) => return Ok(Some((EatFirst{label, b}, e))),
193                            None => return Err(MissingExpected('-')),
194                            Some(Ok((location, found))) => if found != '-' {
195                                return Err(Mismatch{found, location, expected: '-'})
196                            },
197                        }
198                        "---BEGIN "
199                    },
200                    BoundaryType::End => "---END ",
201                }.chars();
202
203                EatKey{label, key, expected: '-'}
204            },
205            NotEatFirst(v) => v,
206        };
207
208        v.process(stream).map(|v| v.map(|(v, e)| (NotEatFirst(v), e)))
209    }
210}
211
212impl<Loc, Lbl: Label> BoundaryParserState2<Loc, Lbl> {
213    fn process<'a, E: 'a>(self, stream: &'a mut Iterator<Item = Result<(Loc, char), E>>) -> Result<Option<(Self, E)>, EncapsulationBoundaryError<Loc, Lbl::LabelError>> {
214        use self::EncapsulationBoundaryError::*;
215        use self::BoundaryParserState2::*;
216        use self::BoundaryParserState3::*;
217
218        let v = match self {
219            EatKey{label, mut key, mut expected} => loop {
220                match stream.next() {
221                    Some(Err(e)) => return Ok(Some((EatKey{label, key, expected}, e))),
222                    None => return Err(MissingExpected(expected)),
223                    Some(Ok((location, found))) => if found != expected {
224                        return Err(Mismatch{found, location, expected})
225                    } else if let Some(e) = key.next() {
226                        expected = e;
227                    } else {
228                        break EatLabel{label, prev_dash: None}
229                    },
230                }
231            },
232            NotEatKey(v) => v,
233        };
234
235        v.process(stream).map(|v| v.map(|(v, e)| (NotEatKey(v), e)))
236    }
237}
238
239impl<Loc, Lbl: Label> BoundaryParserState3<Loc, Lbl> {
240    fn process<'a, E: 'a>(self, stream: &'a mut Iterator<Item = Result<(Loc, char), E>>) -> Result<Option<(Self, E)>, EncapsulationBoundaryError<Loc, Lbl::LabelError>> {
241        use self::EncapsulationBoundaryError::*;
242        use self::BoundaryParserState3::*;
243
244        let (mut end, mut expected) = match self {
245            EatLabel{mut label, mut prev_dash} => loop {
246                use self::EncapsulationBoundaryError::*;
247
248                let v = stream.next();
249                let (location, c) = match v {
250                    Some(Err(e)) => return Ok(Some((EatLabel{label, prev_dash}, e))),
251                    None => return Err(MissingExpected('-')),
252                    Some(Ok(c)) => c,
253                };
254
255                // Check for double '-'
256                if c == '-' {
257                    if prev_dash.is_none() {
258                        prev_dash = Some(location);
259                        continue;
260                    }
261
262                    match label.complete() {
263                        Ok(None) => {},
264                        Err(error) => return Err(LabelError{error, location}),
265                        Ok(Some(expected)) => return Err(Mismatch{location, expected, found: c}),
266                    }
267
268                    // Done, find the last 3 dashes
269                    break ("--".chars(), '-');
270                }
271
272                // Add back in any single '-' we skipped over
273                if let Some(prev_location) = prev_dash.take() {
274                    match label.push('-') {
275                        Ok(None) => {},
276                        Err(error) => return Err(LabelError{error, location: prev_location}),
277                        Ok(Some(expected)) => return Err(if expected == '-' {
278                            Mismatch{location, expected, found: c}
279                        } else {
280                            Mismatch{location: prev_location, expected, found: '-'}
281                        })
282                    }
283                }
284
285                match label.push(c) {
286                    Ok(None) => {},
287                    Err(error) => return Err(LabelError{error, location}),
288                    Ok(Some(expected)) => return Err(if expected == c {
289                        Mismatch{location, expected: '-', found: c}
290                    } else {
291                        Mismatch{location, expected, found: c}
292                    }),
293                }
294            },
295            EatEnd{end, expected} => (end, expected),
296        };
297
298        loop {
299            match stream.next() {
300                Some(Err(e)) => return Ok(Some((EatEnd{end, expected}, e))),
301                None => return Err(MissingExpected(expected)),
302                Some(Ok((location, found))) => if found != expected {
303                    return Err(Mismatch{found, location, expected: expected})
304                } else if let Some(e) = end.next() {
305                    expected = e;
306                } else {
307                    return Ok(None)
308                },
309            }
310        }
311    }
312}
313
314#[cfg(test)]
315mod tests {
316    use super::{BoundaryType, BoundaryParser, LabelMatcher};
317    #[cfg(not(feature = "std"))]
318    use super::DiscardLabel;
319
320    #[test]
321    fn test_parse_boundary() {
322        #[cfg(feature = "std")]
323        fn helper(b: BoundaryType, input: &str, _label: &str) {
324            #[cfg(feature = "std")]
325            let mut label_buf = String::new();
326
327            {
328                let mut parser = BoundaryParser::from_chars(b, input.chars().enumerate(), &mut label_buf);
329                assert_eq!(parser.next(), None);
330                assert_eq!(parser.complete(), Ok(()));
331            }
332
333            assert_eq!(_label, label_buf.as_str());
334        }
335        #[cfg(not(feature = "std"))]
336        fn helper(b: BoundaryType, input: &str, _label: &str) {
337            let mut parser = BoundaryParser::from_chars(b, input.chars().enumerate(), DiscardLabel);
338            assert_eq!(parser.next(), None);
339            assert_eq!(parser.complete(), Ok(()));
340        }
341
342        const BEGIN_PRIVATE: &'static str = "-----BEGIN RSA PRIVATE KEY-----";
343        const BEGIN_INT_CERT: &'static str = "-----BEGIN INTERMEDIATE CERT-----";
344        const BEGIN_CERT: &'static str = "-----BEGIN CERTIFICATE-----";
345        const BEGIN_COMPLEX: &'static str = "\t\r -----BEGIN \u{211D}-\u{212D}-----";
346
347        // END has one fewer '-' because body parsing consumes the first one
348        const END_PRIVATE: &'static str = "----END RSA PRIVATE KEY-----";
349        const END_INT_CERT: &'static str = "----END INTERMEDIATE CERT-----";
350        const END_CERT: &'static str = "----END CERTIFICATE-----";
351        const END_COMPLEX: &'static str = "----END \u{211D}-\u{212D}-----";
352
353        helper(BoundaryType::Begin, BEGIN_CERT, "CERTIFICATE");
354        helper(BoundaryType::Begin, BEGIN_INT_CERT, "INTERMEDIATE CERT");
355        helper(BoundaryType::Begin, BEGIN_PRIVATE, "RSA PRIVATE KEY");
356        helper(BoundaryType::Begin, BEGIN_COMPLEX, "\u{211D}-\u{212D}");
357
358        helper(BoundaryType::End, END_CERT, "CERTIFICATE");
359        helper(BoundaryType::End, END_INT_CERT, "INTERMEDIATE CERT");
360        helper(BoundaryType::End, END_PRIVATE, "RSA PRIVATE KEY");
361        helper(BoundaryType::End, END_COMPLEX, "\u{211D}-\u{212D}");
362    }
363    
364    #[test]
365    fn test_verify_boundary() {
366        fn helper(b: BoundaryType, input: &str, label: &str) {
367            let mut verifier = BoundaryParser::from_chars(b, input.chars().enumerate(), LabelMatcher(label.chars()));
368            assert_eq!(verifier.next(), None);
369            assert_eq!(verifier.complete(), Ok(()));
370        }
371
372        const BEGIN_PRIVATE: &'static str = "-----BEGIN RSA PRIVATE KEY-----";
373        const BEGIN_INT_CERT: &'static str = "-----BEGIN INTERMEDIATE CERT-----";
374        const BEGIN_CERT: &'static str = "-----BEGIN CERTIFICATE-----";
375        const BEGIN_COMPLEX: &'static str = "\t\r -----BEGIN \u{211D}-\u{212D}-----";
376
377        // END has one fewer '-' because body parsing consumes the first one
378        const END_PRIVATE: &'static str = "----END RSA PRIVATE KEY-----";
379        const END_INT_CERT: &'static str = "----END INTERMEDIATE CERT-----";
380        const END_CERT: &'static str = "----END CERTIFICATE-----";
381        const END_COMPLEX: &'static str = "----END \u{211D}-\u{212D}-----";
382
383        helper(BoundaryType::Begin, BEGIN_CERT, "CERTIFICATE");
384        helper(BoundaryType::Begin, BEGIN_INT_CERT, "INTERMEDIATE CERT");
385        helper(BoundaryType::Begin, BEGIN_PRIVATE, "RSA PRIVATE KEY");
386        helper(BoundaryType::Begin, BEGIN_COMPLEX, "\u{211D}-\u{212D}");
387
388        helper(BoundaryType::End, END_CERT, "CERTIFICATE");
389        helper(BoundaryType::End, END_INT_CERT, "INTERMEDIATE CERT");
390        helper(BoundaryType::End, END_PRIVATE, "RSA PRIVATE KEY");
391        helper(BoundaryType::End, END_COMPLEX, "\u{211D}-\u{212D}");
392    }
393}