• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 extern crate base64;
2 
3 use base64::*;
4 
5 mod helpers;
6 
7 use self::helpers::*;
8 
9 #[test]
decode_rfc4648_0()10 fn decode_rfc4648_0() {
11     compare_decode("", "");
12 }
13 
14 #[test]
decode_rfc4648_1()15 fn decode_rfc4648_1() {
16     compare_decode("f", "Zg==");
17 }
18 
19 #[test]
decode_rfc4648_1_just_a_bit_of_padding()20 fn decode_rfc4648_1_just_a_bit_of_padding() {
21     // allows less padding than required
22     compare_decode("f", "Zg=");
23 }
24 
25 #[test]
decode_rfc4648_1_no_padding()26 fn decode_rfc4648_1_no_padding() {
27     compare_decode("f", "Zg");
28 }
29 
30 #[test]
decode_rfc4648_2()31 fn decode_rfc4648_2() {
32     compare_decode("fo", "Zm8=");
33 }
34 
35 #[test]
decode_rfc4648_2_no_padding()36 fn decode_rfc4648_2_no_padding() {
37     compare_decode("fo", "Zm8");
38 }
39 
40 #[test]
decode_rfc4648_3()41 fn decode_rfc4648_3() {
42     compare_decode("foo", "Zm9v");
43 }
44 
45 #[test]
decode_rfc4648_4()46 fn decode_rfc4648_4() {
47     compare_decode("foob", "Zm9vYg==");
48 }
49 
50 #[test]
decode_rfc4648_4_no_padding()51 fn decode_rfc4648_4_no_padding() {
52     compare_decode("foob", "Zm9vYg");
53 }
54 
55 #[test]
decode_rfc4648_5()56 fn decode_rfc4648_5() {
57     compare_decode("fooba", "Zm9vYmE=");
58 }
59 
60 #[test]
decode_rfc4648_5_no_padding()61 fn decode_rfc4648_5_no_padding() {
62     compare_decode("fooba", "Zm9vYmE");
63 }
64 
65 #[test]
decode_rfc4648_6()66 fn decode_rfc4648_6() {
67     compare_decode("foobar", "Zm9vYmFy");
68 }
69 
70 #[test]
decode_reject_null()71 fn decode_reject_null() {
72     assert_eq!(
73         DecodeError::InvalidByte(3, 0x0),
74         decode_config("YWx\0pY2U==", config_std_pad()).unwrap_err()
75     );
76 }
77 
78 #[test]
decode_single_pad_byte_after_2_chars_in_trailing_quad_ok()79 fn decode_single_pad_byte_after_2_chars_in_trailing_quad_ok() {
80     for num_quads in 0..25 {
81         let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
82         s.push_str("Zg=");
83 
84         let input_len = num_quads * 3 + 1;
85 
86         // Since there are 3 bytes in the trailing quad, want to be sure this allows for the fact
87         // that it could be bad padding rather than assuming that it will decode to 2 bytes and
88         // therefore allow 1 extra round of fast decode logic (stage 1 / 2).
89 
90         let mut decoded = Vec::new();
91         decoded.resize(input_len, 0);
92 
93         assert_eq!(
94             input_len,
95             decode_config_slice(&s, STANDARD, &mut decoded).unwrap()
96         );
97     }
98 }
99 
100 //this is a MAY in the rfc: https://tools.ietf.org/html/rfc4648#section-3.3
101 #[test]
decode_1_pad_byte_in_fast_loop_then_extra_padding_chunk_error()102 fn decode_1_pad_byte_in_fast_loop_then_extra_padding_chunk_error() {
103     for num_quads in 0..25 {
104         let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
105         s.push_str("YWxpY2U=====");
106 
107         // since the first 8 bytes are handled in stage 1 or 2, the padding is detected as a
108         // generic invalid byte, not specifcally a padding issue.
109         // Could argue that the *next* padding byte (in the next quad) is technically the first
110         // erroneous one, but reporting that accurately is more complex and probably nobody cares
111         assert_eq!(
112             DecodeError::InvalidByte(num_quads * 4 + 7, b'='),
113             decode(&s).unwrap_err()
114         );
115     }
116 }
117 
118 #[test]
decode_2_pad_bytes_in_leftovers_then_extra_padding_chunk_error()119 fn decode_2_pad_bytes_in_leftovers_then_extra_padding_chunk_error() {
120     for num_quads in 0..25 {
121         let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
122         s.push_str("YWxpY2UABB====");
123 
124         // 6 bytes (4 padding) after last 8-byte chunk, so it's decoded by stage 4.
125         // First padding byte is invalid.
126         assert_eq!(
127             DecodeError::InvalidByte(num_quads * 4 + 10, b'='),
128             decode(&s).unwrap_err()
129         );
130     }
131 }
132 
133 #[test]
decode_valid_bytes_after_padding_in_leftovers_error()134 fn decode_valid_bytes_after_padding_in_leftovers_error() {
135     for num_quads in 0..25 {
136         let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
137         s.push_str("YWxpY2UABB=B");
138 
139         // 4 bytes after last 8-byte chunk, so it's decoded by stage 4.
140         // First (and only) padding byte is invalid.
141         assert_eq!(
142             DecodeError::InvalidByte(num_quads * 4 + 10, b'='),
143             decode(&s).unwrap_err()
144         );
145     }
146 }
147 
148 #[test]
decode_absurd_pad_error()149 fn decode_absurd_pad_error() {
150     for num_quads in 0..25 {
151         let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
152         s.push_str("==Y=Wx===pY=2U=====");
153 
154         // Plenty of remaining bytes, so handled by stage 1 or 2.
155         // first padding byte
156         assert_eq!(
157             DecodeError::InvalidByte(num_quads * 4, b'='),
158             decode(&s).unwrap_err()
159         );
160     }
161 }
162 
163 #[test]
decode_extra_padding_after_1_pad_bytes_in_trailing_quad_returns_error()164 fn decode_extra_padding_after_1_pad_bytes_in_trailing_quad_returns_error() {
165     for num_quads in 0..25 {
166         let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
167         s.push_str("EEE===");
168 
169         // handled by stage 1, 2, or 4 depending on length
170         // first padding byte -- which would be legal if it was the only padding
171         assert_eq!(
172             DecodeError::InvalidByte(num_quads * 4 + 3, b'='),
173             decode(&s).unwrap_err()
174         );
175     }
176 }
177 
178 #[test]
decode_extra_padding_after_2_pad_bytes_in_trailing_quad_2_returns_error()179 fn decode_extra_padding_after_2_pad_bytes_in_trailing_quad_2_returns_error() {
180     for num_quads in 0..25 {
181         let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
182         s.push_str("EE====");
183 
184         // handled by stage 1, 2, or 4 depending on length
185         // first padding byte -- which would be legal if it was by itself
186         assert_eq!(
187             DecodeError::InvalidByte(num_quads * 4 + 2, b'='),
188             decode(&s).unwrap_err()
189         );
190     }
191 }
192 
193 #[test]
decode_start_quad_with_padding_returns_error()194 fn decode_start_quad_with_padding_returns_error() {
195     for num_quads in 0..25 {
196         // add enough padding to ensure that we'll hit all 4 stages at the different lengths
197         for pad_bytes in 1..32 {
198             let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
199             let padding: String = std::iter::repeat("=").take(pad_bytes).collect();
200             s.push_str(&padding);
201 
202             if pad_bytes % 4 == 1 {
203                 // detected in early length check
204                 assert_eq!(DecodeError::InvalidLength, decode(&s).unwrap_err());
205             } else {
206                 // padding lengths 2 - 8 are handled by stage 4
207                 // padding length >= 8 will hit at least one chunk at stages 1, 2, 3 at different
208                 // prefix lengths
209                 assert_eq!(
210                     DecodeError::InvalidByte(num_quads * 4, b'='),
211                     decode(&s).unwrap_err()
212                 );
213             }
214         }
215     }
216 }
217 
218 #[test]
decode_padding_followed_by_non_padding_returns_error()219 fn decode_padding_followed_by_non_padding_returns_error() {
220     for num_quads in 0..25 {
221         for pad_bytes in 0..31 {
222             let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
223             let padding: String = std::iter::repeat("=").take(pad_bytes).collect();
224             s.push_str(&padding);
225             s.push_str("E");
226 
227             if pad_bytes % 4 == 0 {
228                 assert_eq!(DecodeError::InvalidLength, decode(&s).unwrap_err());
229             } else {
230                 // pad len 1 - 8 will be handled by stage 4
231                 // pad len 9 (suffix len 10) will have 8 bytes of padding handled by stage 3
232                 // first padding byte
233                 assert_eq!(
234                     DecodeError::InvalidByte(num_quads * 4, b'='),
235                     decode(&s).unwrap_err()
236                 );
237             }
238         }
239     }
240 }
241 
242 #[test]
decode_one_char_in_quad_with_padding_error()243 fn decode_one_char_in_quad_with_padding_error() {
244     for num_quads in 0..25 {
245         let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
246         s.push_str("E=");
247 
248         assert_eq!(
249             DecodeError::InvalidByte(num_quads * 4 + 1, b'='),
250             decode(&s).unwrap_err()
251         );
252 
253         // more padding doesn't change the error
254         s.push_str("=");
255         assert_eq!(
256             DecodeError::InvalidByte(num_quads * 4 + 1, b'='),
257             decode(&s).unwrap_err()
258         );
259 
260         s.push_str("=");
261         assert_eq!(
262             DecodeError::InvalidByte(num_quads * 4 + 1, b'='),
263             decode(&s).unwrap_err()
264         );
265     }
266 }
267 
268 #[test]
decode_one_char_in_quad_without_padding_error()269 fn decode_one_char_in_quad_without_padding_error() {
270     for num_quads in 0..25 {
271         let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect();
272         s.push('E');
273 
274         assert_eq!(DecodeError::InvalidLength, decode(&s).unwrap_err());
275     }
276 }
277 
278 #[test]
decode_reject_invalid_bytes_with_correct_error()279 fn decode_reject_invalid_bytes_with_correct_error() {
280     for length in 1..100 {
281         for index in 0_usize..length {
282             for invalid_byte in " \t\n\r\x0C\x0B\x00%*.".bytes() {
283                 let prefix: String = std::iter::repeat("A").take(index).collect();
284                 let suffix: String = std::iter::repeat("B").take(length - index - 1).collect();
285 
286                 let input = prefix + &String::from_utf8(vec![invalid_byte]).unwrap() + &suffix;
287                 assert_eq!(
288                     length,
289                     input.len(),
290                     "length {} error position {}",
291                     length,
292                     index
293                 );
294 
295                 if length % 4 == 1 && !suffix.is_empty() {
296                     assert_eq!(DecodeError::InvalidLength, decode(&input).unwrap_err());
297                 } else {
298                     assert_eq!(
299                         DecodeError::InvalidByte(index, invalid_byte),
300                         decode(&input).unwrap_err()
301                     );
302                 }
303             }
304         }
305     }
306 }
307 
308 #[test]
decode_imap()309 fn decode_imap() {
310     assert_eq!(
311         decode_config(b"+,,+", crate::IMAP_MUTF7),
312         decode_config(b"+//+", crate::STANDARD_NO_PAD)
313     );
314 }
315 
316 #[test]
decode_invalid_trailing_bytes()317 fn decode_invalid_trailing_bytes() {
318     // The case of trailing newlines is common enough to warrant a test for a good error
319     // message.
320     assert_eq!(
321         Err(DecodeError::InvalidByte(8, b'\n')),
322         decode(b"Zm9vCg==\n")
323     );
324     // extra padding, however, is still InvalidLength
325     assert_eq!(Err(DecodeError::InvalidLength), decode(b"Zm9vCg==="));
326 }
327 
config_std_pad() -> Config328 fn config_std_pad() -> Config {
329     Config::new(CharacterSet::Standard, true)
330 }
331