decode.rs 9.5 KB

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