nss_certdata_parser/
syntax.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use nom::{space, not_line_ending, alphanumeric, ErrorKind};
6
7named!(comment<()>,
8       chain!(tag!("#") ~
9              not_line_ending,
10              || ()));
11
12named!(endl<()>,
13       chain!(space? ~
14              comment? ~
15              char!('\r')? ~
16              char!('\n'),
17              || ()));
18
19named!(token<Token>,
20       map_res!(recognize!(many1!(alt!(alphanumeric | tag!("_")))),
21                |bv: &[_]| String::from_utf8(bv.to_owned())));
22
23named!(hex_digit<u8>,
24       map!(one_of!(b"0123456789abcdefABCDEF"), |b| match b {
25           '0' ... '9' => b as u8 - b'0',
26           'a' ... 'f' => b as u8 - b'a' + 10,
27           'A' ... 'F' => b as u8 - b'A' + 10,
28           _ => unreachable!()
29       }));
30named!(octal_digit<u8>, map!(one_of!("01234567"), |b| b as u8 - b'0'));
31named!(quad_digit<u8>, map!(one_of!("0123"), |b| b as u8 - b'0'));
32
33named!(octal_esc<u8>,
34       chain!(tag!("\\") ~
35              a: quad_digit ~
36              b: octal_digit ~
37              c: octal_digit,
38              || { a << 6 | b << 3 | c }));
39
40named!(hex_esc<u8>,
41       chain!(tag!("\\x") ~
42              a: hex_digit ~
43              b: hex_digit,
44              || { a << 4 | b }));
45
46named!(quoted_string<String>,
47       delimited!(tag!("\""),
48                  map_res!(many0!(alt!(map!(none_of!("\\\""), |b| b as u8) | hex_esc)),
49                           String::from_utf8),
50                  tag!("\"")));
51
52named!(multiline_octal<Vec<u8> >,
53       many0!(preceded!(leading_junk, octal_esc)));
54
55named!(type_and_value<Value>,
56       alt!(preceded!(tag!("MULTILINE_OCTAL"),
57                      error!(ErrorKind::Alt,
58                             chain!(endl ~
59                                    bits: multiline_octal ~
60                                    endl ~
61                                    tag!("END") ~
62                                    endl,
63                                    || Value::Binary(bits)))) |
64            preceded!(tag!("UTF8"),
65                      // ASCII7 is also attested but not actually used in certdata.txt
66                      error!(ErrorKind::Alt,
67                             chain!(space ~
68                                    value: quoted_string ~
69                                    endl,
70                                    || Value::String(value)))) |
71            chain!(type_tag: token ~
72                   space ~
73                   value: token ~
74                   endl,
75                   || Value::Token(type_tag, value))));
76
77named!(pub attribute<Attr>,
78       chain!(leading_junk ~
79              key: token ~
80              space ~
81              value: type_and_value,
82              || (key, value)));
83
84named!(pub leading_junk<()>,
85       chain!(fold_many0!(endl, (), |(), ()| ()) ~ space?, || ()));
86
87named!(pub begindata<()>,
88       chain!(leading_junk ~
89              tag!("BEGINDATA") ~
90              endl,
91              || ()));
92
93pub type Token = String;
94pub type Type = Token;
95pub type Attr = (Token, Value);
96
97#[derive(Clone, Debug, PartialEq, Eq)]
98pub enum Value {
99    Token(Type, Token),
100    String(String),
101    Binary(Vec<u8>), // Type is always MULTILINE_OCTAL
102}
103
104impl Value {
105    pub fn get_type(&self) -> &str {
106        match *self {
107            Value::Token(ref ttype, _) => ttype,
108            Value::String(_) => "UTF8",
109            Value::Binary(_) => "MULTILINE_OCTAL",
110        }
111    }
112    pub fn into_type(self) -> String {
113        match self {
114            Value::Token(ttype, _) => ttype,
115            _ => self.get_type().to_owned(),
116        }
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::{comment, endl, token, quad_digit, octal_digit, hex_digit, octal_esc, hex_esc,
123                quoted_string, multiline_octal, type_and_value, attribute, Value};
124    use nom::IResult::*;
125    use nom::{Needed, ErrorKind, Err};
126
127    #[test]
128    fn test_comment() {
129        assert_eq!(comment(b"# thing"), Done(&b""[..], ()));
130        assert_eq!(comment(b"#thing"), Done(&b""[..], ()));
131        assert_eq!(comment("# ýŷỳ".as_bytes()), Done(&b""[..], ()));
132        assert_eq!(comment(b"#"), Done(&b""[..], ()));
133        assert_eq!(comment(b""), Incomplete(Needed::Size(1)));
134        assert_eq!(comment(b"# thing\n stuff"), Done(&b"\n stuff"[..], ()));
135        assert_eq!(comment(b"bees"), Error(Err::Position(ErrorKind::Tag, &b"bees"[..])));
136        assert_eq!(comment(b" #bees"), Error(Err::Position(ErrorKind::Tag, &b" #bees"[..])));
137    }
138
139    #[test]
140    fn test_endl() {
141        assert_eq!(endl(b"\n"), Done(&b""[..], ()));
142        assert_eq!(endl(b"\r\n"), Done(&b""[..], ()));
143        assert_eq!(endl(b"\n\n"), Done(&b"\n"[..], ()));
144        assert_eq!(endl(b"\n "), Done(&b" "[..], ()));
145        assert_eq!(endl(b" \n"), Done(&b""[..], ()));
146        assert_eq!(endl(b"    \n"), Done(&b""[..], ()));
147        assert_eq!(endl(b"\t\n"), Done(&b""[..], ()));
148        assert_eq!(endl(b"# bees \n"), Done(&b""[..], ()));
149        assert_eq!(endl(b"   # bees \n"), Done(&b""[..], ()));
150        assert_eq!(endl(b"    bees \n"), Error(Err::Position(ErrorKind::Char, &b"bees \n"[..])));
151        assert_eq!(endl(b"# bees"), Incomplete(Needed::Size("# bees".len() + 1)));
152    }
153
154    #[test]
155    fn test_token() {
156        assert_eq!(token(b"CK_TRUE"), Done(&b""[..], "CK_TRUE".to_owned()));
157        assert_eq!(token(b"UTF8"), Done(&b""[..], "UTF8".to_owned()));
158        assert_eq!(token(b"CKA_CERT_MD5_HASH"), Done(&b""[..], "CKA_CERT_MD5_HASH".to_owned()));
159        assert_eq!(token(b"UTF8 "), Done(&b" "[..], "UTF8".to_owned()));
160        assert_eq!(token(b"UTF8\n"), Done(&b"\n"[..], "UTF8".to_owned()));
161        assert_eq!(token(b" UTF8"), Error(Err::Position(ErrorKind::Many1, &b" UTF8"[..])));
162        assert_eq!(token(b"\"UTF8\""), Error(Err::Position(ErrorKind::Many1, &b"\"UTF8\""[..])));
163        assert_eq!(token(b"\\x41"), Error(Err::Position(ErrorKind::Many1, &b"\\x41"[..])));
164        assert_eq!(token(b"\\101"), Error(Err::Position(ErrorKind::Many1, &b"\\101"[..])));
165        assert_eq!(token(b""), Incomplete(Needed::Size(1)))
166    }
167
168    #[test]
169    fn test_digits() {
170        assert_eq!(quad_digit(b"0"), Done(&b""[..], 0));
171        assert_eq!(octal_digit(b"0"), Done(&b""[..], 0));
172        assert_eq!(hex_digit(b"0"), Done(&b""[..], 0));
173
174        assert_eq!(quad_digit(b"00"), Done(&b"0"[..], 0));
175        assert_eq!(octal_digit(b"00"), Done(&b"0"[..], 0));
176        assert_eq!(hex_digit(b"00"), Done(&b"0"[..], 0));
177
178        assert_eq!(quad_digit(b"32"), Done(&b"2"[..], 3));
179        assert_eq!(octal_digit(b"76"), Done(&b"6"[..], 7));
180        assert_eq!(hex_digit(b"98"), Done(&b"8"[..], 9));
181
182        assert_eq!(quad_digit(b"4"), Error(Err::Position(ErrorKind::OneOf, &b"4"[..])));
183        assert_eq!(octal_digit(b"8"), Error(Err::Position(ErrorKind::OneOf, &b"8"[..])));
184        assert_eq!(hex_digit(b"g"), Error(Err::Position(ErrorKind::OneOf, &b"g"[..])));
185        assert_eq!(hex_digit(b"G"), Error(Err::Position(ErrorKind::OneOf, &b"G"[..])));
186        assert_eq!(hex_digit(b":"), Error(Err::Position(ErrorKind::OneOf, &b":"[..])));
187        assert_eq!(hex_digit(b"@"), Error(Err::Position(ErrorKind::OneOf, &b"@"[..])));
188
189        assert_eq!(hex_digit(b"a"), Done(&b""[..], 10));
190        assert_eq!(hex_digit(b"A"), Done(&b""[..], 10));
191        assert_eq!(hex_digit(b"f"), Done(&b""[..], 15));
192        assert_eq!(hex_digit(b"F"), Done(&b""[..], 15));
193    }
194
195    #[test]
196    fn test_octal_esc() {
197        assert_eq!(octal_esc(b"\\000"), Done(&b""[..], 0o000));
198        assert_eq!(octal_esc(b"\\007"), Done(&b""[..], 0o007));
199        assert_eq!(octal_esc(b"\\077"), Done(&b""[..], 0o077));
200        assert_eq!(octal_esc(b"\\377"), Done(&b""[..], 0o377));
201
202        assert_eq!(octal_esc(b"\\"), Incomplete(Needed::Size(2)));
203        assert_eq!(octal_esc(b"\\0"), Incomplete(Needed::Size(3)));
204        assert_eq!(octal_esc(b"\\00"), Incomplete(Needed::Size(4)));
205        assert_eq!(octal_esc(b"\\0000"), Done(&b"0"[..], 0));
206        assert_eq!(octal_esc(b"\\3765"), Done(&b"5"[..], 0o376));
207
208        assert_eq!(octal_esc(b"\\080"), Error(Err::Position(ErrorKind::OneOf, &b"80"[..])));
209        assert_eq!(octal_esc(b"\\400"), Error(Err::Position(ErrorKind::OneOf, &b"400"[..])));
210
211        assert_eq!(octal_esc(b"\\x00"), Error(Err::Position(ErrorKind::OneOf, &b"x00"[..])));
212        assert_eq!(octal_esc(b"A"), Error(Err::Position(ErrorKind::Tag, &b"A"[..])));
213        assert_eq!(octal_esc(b" \\000"), Error(Err::Position(ErrorKind::Tag, &b" \\000"[..])));
214    }
215
216    #[test]
217    fn test_hex_esc() {
218        assert_eq!(hex_esc(b"\\x00"), Done(&b""[..], 0x00));
219        assert_eq!(hex_esc(b"\\x0f"), Done(&b""[..], 0x0f));
220        assert_eq!(hex_esc(b"\\xf0"), Done(&b""[..], 0xf0));
221
222        assert_eq!(hex_esc(b"\\"), Incomplete(Needed::Size(2)));
223        assert_eq!(hex_esc(b"\\x"), Incomplete(Needed::Size(3)));
224        assert_eq!(hex_esc(b"\\x0"), Incomplete(Needed::Size(4)));
225        assert_eq!(hex_esc(b"\\x000"), Done(&b"0"[..], 0x00));
226        assert_eq!(hex_esc(b"\\xba9"), Done(&b"9"[..], 0xba));
227
228        assert_eq!(hex_esc(b"0x41"), Error(Err::Position(ErrorKind::Tag, &b"0x41"[..])));
229        assert_eq!(hex_esc(b"\\000"), Error(Err::Position(ErrorKind::Tag, &b"\\000"[..])));
230        assert_eq!(hex_esc(b"\\x0g"), Error(Err::Position(ErrorKind::OneOf, &b"g"[..])));
231    }
232
233    #[test]
234    fn test_quoted_string() {
235        assert_eq!(quoted_string(b"\"Stuff\""), Done(&b""[..], "Stuff".to_owned()));
236        assert_eq!(quoted_string("\"Stũff\"".as_bytes()), Done(&b""[..], "Stũff".to_owned()));
237        assert_eq!(quoted_string(b"\"a\"\"b\""), Done(&b"\"b\""[..], "a".to_owned()));
238
239        assert_eq!(quoted_string(b"\"A\\x42\""), Done(&b""[..], "AB".to_owned()));
240
241        assert_eq!(quoted_string(b"UTF8"), Error(Err::Position(ErrorKind::Tag, &b"UTF8"[..])));
242
243        assert_eq!(quoted_string(b"\"A\\x82\""),
244                   Error(Err::Position(ErrorKind::MapRes, &b"A\\x82\""[..])));
245        assert_eq!(quoted_string(b"\"A\\xce\""),
246                   Error(Err::Position(ErrorKind::MapRes, &b"A\\xce\""[..])));
247        assert_eq!(quoted_string(b"\"A\\xce\\xbb\""),
248                   Done(&b""[..], "Aλ".to_owned()));
249
250        assert_eq!(quoted_string(b"\"A\\\\B\""),
251                   Error(Err::Position(ErrorKind::Tag, &b"\\\\B\""[..])));
252        assert_eq!(quoted_string(b"\"A\\\"B\""),
253                   Error(Err::Position(ErrorKind::Tag, &b"\\\"B\""[..])));
254        assert_eq!(quoted_string(b"\"A\\102\""),
255                   Error(Err::Position(ErrorKind::Tag, &b"\\102\""[..])));
256
257        assert_eq!(quoted_string(b"\"AC Ra\\xC3\\xADz\""),
258                   Done(&b""[..], "AC Raíz".to_owned()));
259        assert_eq!(quoted_string("\"Főtanúsítvány\"".as_bytes()),
260                   Done(&b""[..], "Főtanúsítvány".to_owned()));
261    }
262
263    #[test]
264    fn test_multiline_octal() {
265        assert_eq!(multiline_octal(b"\\101"), Done(&b""[..], vec![65]));
266        assert_eq!(multiline_octal(b"\\101\\033"), Done(&b""[..], vec![65, 27]));
267        assert_eq!(multiline_octal(b"\\101\n\\033"), Done(&b""[..], vec![65, 27]));
268        assert_eq!(multiline_octal(b"\\101\\033\n"),
269                   Incomplete(Needed::Size("\\101\\033\n".len() + 1)));
270        assert_eq!(multiline_octal(b"\n\\101\\033"), Done(&b""[..], vec![65, 27]));
271        assert_eq!(multiline_octal(b"\\101\n\n\n\n\n\n\\033"), Done(&b""[..], vec![65, 27]));
272        assert_eq!(multiline_octal(b"\\101\r\n\r\n\r\n\\033"), Done(&b""[..], vec![65, 27]));
273        assert_eq!(multiline_octal(b"\\101 \\033"), Done(&b""[..], vec![65, 27]));
274        assert_eq!(multiline_octal(b"\\101 # Sixty-five \n\t\\033"), Done(&b""[..], vec![65, 27]));
275        assert_eq!(multiline_octal(b"\\101\\033\nEND"), Done(&b"\nEND"[..], vec![65, 27]));
276        assert_eq!(multiline_octal(b"\\101\\033\n   END"), Done(&b"\n   END"[..], vec![65, 27]));
277    }
278
279    #[test]
280    fn test_token_value() {
281        assert_eq!(type_and_value(b"CK_BBOOL CK_TRUE\n"),
282                   Done(&b""[..], Value::Token("CK_BBOOL".to_owned(), "CK_TRUE".to_owned())));
283        assert_eq!(type_and_value(b"CK_BBOOL   \t   CK_TRUE\n"),
284                   Done(&b""[..], Value::Token("CK_BBOOL".to_owned(), "CK_TRUE".to_owned())));
285        assert_eq!(type_and_value(b"CK_BBOOL CK_TRUE\n\n"),
286                   Done(&b"\n"[..], Value::Token("CK_BBOOL".to_owned(), "CK_TRUE".to_owned())));
287        assert_eq!(type_and_value(b"CK_BBOOL CK_TRUE \n "),
288                   Done(&b" "[..], Value::Token("CK_BBOOL".to_owned(), "CK_TRUE".to_owned())));
289        assert_eq!(type_and_value(b"CK_BBOOL CK_TRUE # Very true. Wow. \n"),
290                   Done(&b""[..], Value::Token("CK_BBOOL".to_owned(), "CK_TRUE".to_owned())));
291        assert_eq!(type_and_value(b"CK_BBOOL CK_TRUE"),
292                   Incomplete(Needed::Size("CK_BBOOL CK_TRUE".len() + 1)));
293        assert!(type_and_value(b"CK_BBOOL\nCK_TRUE\n").is_err());
294
295        let bad_rhs = |ek: ErrorKind| {
296            let inner = Box::new(Err::Position(ek, &b"CK_TRUE\n"[..]));
297            Error(Err::NodePosition(ErrorKind::Alt, &b" CK_TRUE\n"[..], inner))
298        };
299        assert_eq!(type_and_value(b"UTF8 CK_TRUE\n"), bad_rhs(ErrorKind::Tag));
300        assert_eq!(type_and_value(b"MULTILINE_OCTAL CK_TRUE\n"), bad_rhs(ErrorKind::Char));
301    }
302
303    #[test]
304    fn test_string_value() {
305        assert_eq!(type_and_value(b"UTF8 \"0\"\n"),
306                   Done(&b""[..], Value::String("0".to_owned())));
307        assert_eq!(type_and_value(b"UTF8 \"Bogus Mozilla Addons\"\n"),
308                   Done(&b""[..], Value::String("Bogus Mozilla Addons".to_owned())));
309
310        assert!(type_and_value(b"UTF8\n\"0\"\n").is_err());
311        assert!(type_and_value(b"CK_OBJECT_CLASS \"0\"\n").is_err());
312        assert!(type_and_value(b"MULTILINE_OCTAL \"0\"\n").is_err());
313
314        assert_eq!(type_and_value(b"UTF8   "), Incomplete(Needed::Size("UTF8   ".len() + 1)));
315        assert_eq!(type_and_value(b"UTF8 \""), Incomplete(Needed::Size("UTF8 \"".len() + 1)));
316        assert_eq!(type_and_value(b"UTF8 \"x\""), Incomplete(Needed::Size("UTF8 \"x\"".len() + 1)));
317    }
318
319    #[test]
320    fn test_octal_value() {
321        assert_eq!(type_and_value(b"MULTILINE_OCTAL\n\
322                                    \\000\\001\\002\n\
323                                    \\010\\011\\012\n\
324                                    END\n"),
325                   Done(&b""[..], Value::Binary(vec![0, 1, 2, 8, 9, 10])))
326
327        // TODO: more cases?
328    }
329
330    #[test]
331    fn test_attr() {
332        assert_eq!(attribute(b"# This is a thing.\n\
333                               CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE\n"),
334                   Done(&b""[..], ("CKA_CLASS".to_owned(),
335                                   Value::Token("CK_OBJECT_CLASS".to_owned(),
336                                                "CKO_CERTIFICATE".to_owned()))));
337
338        assert_eq!(attribute(b"CKA_SERIAL_NUMBER MULTILINE_OCTAL\n\
339                               \\002\\004\\011\\023\\310\\251\n\
340                               END\n"),
341                   Done(&b""[..], ("CKA_SERIAL_NUMBER".to_owned(),
342                                   Value::Binary(vec![0x02, 4, 0x9, 0x13, 0xc8, 0xa9]))));
343
344        // TODO: more cases?
345    }
346}