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